firmware(esp-01s): real GoPro Hero 3 status read (was the shutter bug)
Build (Dev) / build (push) Successful in 10s
CI / quality (push) Successful in 10s
CI / quality (pull_request) Successful in 10s

Validated against a Hero 3 Silver:
- fetchStatus() now GETs the status endpoint /camera/se (was /bacpac/SH?p=%01,
  which *started recording* every poll), at the correct host 10.5.5.9.
- Read the response from the stream, not getString(): the blob is binary and
  starts with 0x00, which truncated the Arduino String to empty.
- Offsets: recording = byte 29 (confirmed by not-recording vs recording diff),
  battery_raw = byte 19 (drains with charge; calibrate on the hub),
  video_remaining = bytes 25-26 (provisional).
- Default config set to this camera (goprosilver-1 / 10.5.5.9); per-camera
  values can still be overridden at runtime via set_config.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Joshua King
2026-06-05 15:21:14 -04:00
parent f03dbb056d
commit ee947485d1
2 changed files with 30 additions and 17 deletions
+3 -3
View File
@@ -1,6 +1,6 @@
{ {
"camera_ssid": "GOPRO-BP-", "camera_ssid": "goprosilver-1",
"camera_password": "goprohero", "camera_password": "Bzyeatn421",
"camera_ip": "10.5.5.1", "camera_ip": "10.5.5.9",
"poll_interval_sec": 30 "poll_interval_sec": 30
} }
+24 -11
View File
@@ -41,9 +41,11 @@
// ──────────────────────────────────────────── // ────────────────────────────────────────────
struct Config { struct Config {
String camera_ssid = "GOPRO-BP-"; // Defaults validated against a GoPro Hero 3 Silver. Per-camera values can
String camera_password = "goprohero"; // be overridden at runtime via the set_config command (no reflash).
String camera_ip = "10.5.5.1"; String camera_ssid = "goprosilver-1";
String camera_password = "Bzyeatn421";
String camera_ip = "10.5.5.9"; // Hero 3 HTTP API host (not .1)
int poll_interval_sec = 30; int poll_interval_sec = 30;
} cfg; } cfg;
@@ -96,26 +98,37 @@ struct CamStatus {
CamStatus fetchStatus() { CamStatus fetchStatus() {
CamStatus s; CamStatus s;
String url = "http://" + cfg.camera_ip + // READ status — must NOT be the shutter endpoint. Hero 3 status blob
"/bacpac/SH?t=" + cfg.camera_password + "&p=%01"; // (validated on a Hero 3 Silver, ~31 bytes):
// [29] recording flag (0 idle / 1 recording) — confirmed
// [19] battery level (raw; drains with charge) — calibrate on the hub
// [25..26] video-remaining (provisional)
// The body is binary and starts with 0x00, so read the stream directly —
// Arduino String truncates at the first null byte.
String url = "http://" + cfg.camera_ip + "/camera/se?t=" + cfg.camera_password;
HTTPClient http; HTTPClient http;
http.useHTTP10(true); http.useHTTP10(true);
http.begin(goproClient, url); http.begin(goproClient, url);
http.setTimeout(5000); http.setTimeout(5000);
int code = http.GET(); int code = http.GET();
if (code != 200) { http.end(); return s; } if (code != 200) { http.end(); return s; }
String raw = http.getString(); uint8_t buf[40] = {0};
WiFiClient* stream = http.getStreamPtr();
size_t n = 0;
unsigned long t0 = millis();
while (n < sizeof(buf) && millis() - t0 < 1500) {
if (stream && stream->available()) buf[n++] = (uint8_t)stream->read();
else delay(5);
}
http.end(); http.end();
if (raw.length() < 58) return s; if (n < 30) return s;
const uint8_t* buf = (const uint8_t*)raw.c_str();
s.valid = true; s.valid = true;
s.video_remaining_sec = buf[25] | (buf[26] << 8);
s.recording = (buf[29] == 1); s.recording = (buf[29] == 1);
s.battery_raw = buf[57]; s.battery_raw = buf[19];
s.video_remaining_sec = buf[25] | (buf[26] << 8);
return s; return s;
} }