Camera IP: 10.10.11.35
Base Path: /wpd/VNCX02/
Auth: HTTP Basic (admin:admin)
Product ID: VNCX02
These were queried live from your C200 at 10.10.11.35:
| Setting | Current | Options | Enabled |
|---|---|---|---|
| Aperture (av) | F5.6 | F1.8-F16 (20 steps) | Yes |
| Aperture Type (atype) | T-stop | F, T | Yes |
| Aperture Step (avst) | 3fine | 2, 3, 2fine, 3fine | Yes |
| Iris Mode (am) | --- | autoiris, maniris | No (disabled) |
| ISO/Gain Mode (gcm) | ISO | iso, gain | Yes |
| ISO Value (gcv) | 1000 | 160-25600 (23 steps) | Yes |
| Gain Step (gcvst) | 1/3 | 1, 1/3 | Yes |
| Gain Extended (gcext) | off | off, on | Yes |
| Shutter Mode (ssm) | angle | speed, angle, cls, slow, off | Yes |
| Shutter Value (ssv) | 180.00° | 11.25°-360° (15 steps) | Yes |
| Shutter Step (ssvst) | --- | 3, 4 | No (disabled) |
| AE Shift (aesv) | 0 | -2.0 to +2.0 (17 steps) | Yes |
| Setting | Current | Options | Enabled |
|---|---|---|---|
| ND Value (ndv) | 2 stops | 0, 2, 4, 6, 8, 10 | Yes |
| ND Extended (ndext) | on | off, on | Yes |
| Setting | Current | Options | Enabled |
|---|---|---|---|
| WB Mode (wbm) | user1 | awb, seta, setb, daylight, tungsten, user1-5 | Yes |
| WB Kelvin (wbvk) | 4760K | 2000K-15000K (88 steps) | Yes |
| WB Color Comp (wbvc) | -2 | -20 to +20 (41 steps) | Yes |
| Setting | Current | Options | Enabled |
|---|---|---|---|
| AF Mode (afm) | oneshot | oneshot, afboostedmf, continuous | No |
| AF Position (fpos) | selectable | selectable, center | No |
| Face Detection (fdat) | off | off, on | Yes |
| Face AF (faf) | only | priority, only | No |
| Focus Speed (fsv) | --- | +2 to -7 (10 steps) | No |
| Focus Guide (fguide) | on | off, on | Yes |
GET /api/acnt/login
Returns: {"res":"ok"} or {"res":"errsession"} (another client connected)
Important: Only ONE client can be connected at a time. The ESP32 will be the sole client.
GET /api/sys/getdevinfo
Returns: modelName, manufacturer, serialNum, lang, mode, productId
GET /api/cam/getcurprop?seq={N}
- Poll this endpoint every ~1 second
seqstarts at 0, camera returns next sequence number- Returns JSON with all current camera state
Response Properties:
| Property | Description | Values |
|---|---|---|
res |
Result | ok, busy, errsession |
seq |
Next sequence number | integer |
mode |
Camera mode | ctrl, nonctrl, ctrlnonlv |
rec |
Recording status | stby, rec, sf_stby, sf_rec, frm_stby, frm_rec, int_stby, int_rec, pre_rec, pre_rec_stby, pre_rec_rec |
extrec |
External recording | off, stby, rec |
contrec |
Continuous recording | off, stby, rec |
tc |
Timecode | string HH:MM:SS:FF |
recfmt |
Recording format | mp4, xf-avc, raw, non |
camid |
Camera ID string | string |
msg |
Warning/error message | string |
Nested Status Objects:
| Object | Contents |
|---|---|
Opower |
Battery: Obatt.percent, Obatt.rtime |
Omedia |
Cards: Ocfa, Osda, Osdb with state, rtime |
Owbinfo |
White balance mode, kelvin, CC values |
Ondinfo |
ND filter value, extended range |
Oirisinfo |
Iris mode, step, value |
Oisogaininfo |
ISO/Gain mode, step, value, extended range |
Oshutterinfo |
Shutter mode, step, value |
Oaesinfo |
AE shift value |
Ofocusinfo |
AF mode, position, face detection, speed |
Olensinfo |
Lens name, type |
Opz |
Power zoom step |
GET /api/cam/rec?cmd=trig
Starts recording if stopped, stops if recording.
GET /api/cam/rec?cmd=slot
Switches between SD card slots.
GET /api/cam/markclip?type=ok
GET /api/cam/markclip?type=check
GET /api/cam/markclip?type=shot1
GET /api/cam/setprop?wbm={mode}
| Mode | Description |
|---|---|
awb |
Auto White Balance |
seta |
Preset A |
setb |
Preset B |
daylight |
Daylight (~5600K) |
tungsten |
Tungsten (~3200K) |
user1 |
User Preset 1 |
GET /api/cam/cmdwb?awbhold=trig
GET /api/cam/cmdwb?wbset=a
GET /api/cam/cmdwb?wbset=b
GET /api/cam/drivelens?nd=plus
GET /api/cam/drivelens?nd=minus
GET /api/cam/drivelens?iris=plus
GET /api/cam/drivelens?iris=minus
GET /api/cam/drivelens?ai=push
GET /api/cam/drivelens?iso=plus
GET /api/cam/drivelens?iso=minus
GET /api/cam/drivelens?gain=plus
GET /api/cam/drivelens?gain=minus
GET /api/cam/drivelens?shutter=plus
GET /api/cam/drivelens?shutter=minus
GET /api/cam/drivelens?aes=plus
GET /api/cam/drivelens?aes=minus
GET /api/cam/drivelens?focus=oneshotaf
GET /api/cam/drivelens?focus=aflock
GET /api/cam/drivelens?focus=track
GET /api/cam/drivelens?focus=trackcancel
For continuous focus movement (hold button):
GET /api/cam/drivelens?fl=near1start # Start slow near
GET /api/cam/drivelens?fl=near2start # Start medium near
GET /api/cam/drivelens?fl=near3start # Start fast near
GET /api/cam/drivelens?fl=far1start # Start slow far
GET /api/cam/drivelens?fl=far2start # Start medium far
GET /api/cam/drivelens?fl=far3start # Start fast far
GET /api/cam/drivelens?fl=near1stop # Stop movement
For single-step focus (tap):
GET /api/cam/drivelens?fl=near1 # Step slow near
GET /api/cam/drivelens?fl=near2 # Step medium near
GET /api/cam/drivelens?fl=near3 # Step fast near
GET /api/cam/drivelens?fl=far1 # Step slow far
GET /api/cam/drivelens?fl=far2 # Step medium far
GET /api/cam/drivelens?fl=far3 # Step fast far
| Value | Description |
|---|---|
stby |
Standby (ready to record) |
rec |
Recording |
sf_stby |
Slow/Fast standby |
sf_rec |
Slow/Fast recording |
frm_stby |
Frame recording standby |
frm_rec |
Frame recording |
int_stby |
Interval recording standby |
int_rec |
Interval recording |
pre_rec |
Pre-recording enabled |
pre_rec_stby |
Pre-recording standby |
pre_rec_rec |
Pre-recording active |
{
"Omode": {"pv": "awb", "en": 1},
"Oawb": {"kelvinvalue": "5500", "ccvalue": "0", "en": 1},
"Oseta": {"Ovalue": {...}, "Osts": {"pv": "comp"}},
"Osetb": {"Ovalue": {...}, "Osts": {"pv": "comp"}},
"Odaylight": {"kelvinvalue": "5600", "ccvalue": "0"},
"Otungsten": {"kelvinvalue": "3200", "ccvalue": "0"},
"Ouser1": {"kelvinvalue": "5500", "ccvalue": "0"},
"Oawbhold": {"pv": "off", "en": 1}
}{
"Omode": {"pv": "maniris", "en": 1}, // maniris or autoiris
"Ostep": {"pv": "1/3", "en": 1}, // 1/2, 1/3, fine
"Ovalue": {"pv": "F2.8", "en": 1}, // F-stop or T-stop
"Odisp": {"pv": "F", "en": 1}, // Display type F or T
"Opushai": {"pv": "stop", "en": 1} // Push auto iris state
}{
"Omode": {"pv": "iso", "en": 1}, // iso or gain
"Ostep": {"pv": "1/3", "en": 1}, // 1/3, normal, fine
"Ovalue": {"pv": "800", "en": 1}, // ISO value or dB
"Oextrange": {"pv": "off", "en": 1} // Extended range
}{
"Omode": {"pv": "speed", "en": 1}, // speed, angle, cls, slow, off
"Ostep": {"pv": "1/3", "en": 1},
"Ovalue": {"pv": "1/50", "en": 1} // Speed or angle value
}{
"Ovalue": {"pv": "1/4", "en": 1}, // off, 1/4, 1/16, 1/64, etc.
"Oextrange": {"pv": "off", "en": 1} // Extended range
}{
"Oafmode": {"pv": "continuous", "en": 1}, // oneshot, continuous
"Oafpos": {"pv": "center", "en": 1}, // center, selectable
"Ofacedat": {"pv": "on", "en": 1}, // Face detection
"Ofaceaf": {"pv": "priority", "en": 1}, // priority, only
"Oafspeed": {"pv": "0", "en": 1}, // AF speed 0-7
"Ofguide": {"pv": "on", "en": 1}, // Focus guide
"afctrlen": 1, // AF control enabled
"trctrlen": 0, // Tracking control
"tcctrlen": 0, // Track cancel control
"Ofctrl": {"pv": "", "en": 1} // Focus control state
}{
"Ocfa": {"state": "n", "rtime": -1}, // CFast: n=none, sel=selected
"Osda": {"state": "sel", "rtime": 120}, // SD A: minutes remaining
"Osdb": {"state": "n", "rtime": -1} // SD B
}{
"Obatt": {"percent": "75", "rtime": "120"} // Battery % and minutes
}All status values follow a pattern:
{"pv": "value", "en": 1}pv= present valueen= enabled (1) or disabled (0)
When en is 0, the control is locked/unavailable.
| Response | Meaning |
|---|---|
{"res":"ok"} |
Success |
{"res":"busy"} |
Camera busy, retry |
{"res":"errsession"} |
Another client connected |
{"res":"failparam"} |
Invalid parameter |
{"res":"failid"} |
Invalid ID |
{"res":"rootredirect"} |
Redirect to root |
The live view is MJPEG served from:
GET /lv/lv.mjpg
Requires authenticated session.
The getcurprop endpoint may return "busy", but individual properties work reliably:
GET /api/cam/getprop?r={property_name}
| Property | Description |
|---|---|
wbm |
White balance mode |
wbvk |
White balance kelvin |
wbvc |
White balance color compensation |
av |
Aperture value |
avst |
Aperture step |
atype |
Aperture display type (F/T) |
am |
Iris mode (auto/manual) |
gcm |
ISO/Gain mode |
gcv |
ISO/Gain value |
gcvst |
ISO/Gain step |
gcext |
ISO/Gain extended range |
ssm |
Shutter mode |
ssv |
Shutter value |
ssvst |
Shutter step |
aesv |
AE shift value |
ndv |
ND filter value |
ndext |
ND extended range |
afm |
AF mode |
fpos |
AF position |
fdat |
Face detection |
faf |
Face AF mode |
fsv |
Focus speed |
fguide |
Focus guide |
{
"res": "ok",
"O{property}": {
"pv": "current_value", // Present value
"en": 1, // Enabled (1) or disabled (0)
"rvn": 10, // Number of options
"rv": ["opt1", "opt2"] // Array of valid options
}
}GET /api/cam/lv?cmd=start&sz=l
sz= size:l(large),s(small)
GET /api/cam/lv?cmd=stop
GET /api/cam/lvgetimg?d={timestamp}
- Poll every 100ms for smooth video
- Returns JPEG image
GET /api/cam/drivelens?xcoord={X}&ycoord={Y}
- Coordinates relative to center of frame
- X range: -543 to 543 (DCI) or -511 to 511 (HD)
- Y range: -287 to 287
- Single Client: Only one Browser Remote session at a time
- Polling: Use
getcurpropfor status polling (returns everything includingrecfield) - Authentication: Use HTTP Basic Auth with every request
- Cookies: See CRITICAL COOKIE REQUIREMENTS section below - this is essential!
- Live View: Must start LV session for
getcurpropto return data - Poll Interval: Camera expects ~1 second between status polls
For weeks, getcurprop returned {"res":"busy"} 90%+ of the time when accessed from ESP32 or curl, but worked perfectly in the browser. This made external recording detection impossible.
Root Cause: The camera requires 4 specific cookies to return full data from getcurprop. The browser sent all 4, but we were only sending 2.
| Cookie | Value | Source | Purpose |
|---|---|---|---|
acid |
Session ID (e.g., 6f55) |
Set by camera on login | Session identifier |
authlevel |
full |
Set by camera on login | Authorization level |
brlang |
0 |
Must add manually | Browser language setting |
productId |
VNCX02 |
Must add manually | Camera product identifier |
After login, append the missing cookies:
// After successful login
strcat(camera_cookies, "; brlang=0; productId=VNCX02");- Camera on main network (10.10.11.36) - could test with curl directly
- Used curl with only
acid+authlevelcookies →{"res":"busy"} - Checked browser DevTools → saw 4 cookies being sent
- Added all 4 cookies to curl → FULL DATA RETURNED!
Working curl command:
curl -s -b "acid=6f55; authlevel=full; brlang=0; productId=VNCX02" \
"http://10.10.11.36/api/cam/getcurprop?seq=1"Response with correct cookies:
{
"res": "ok",
"seq": 3,
"com": 6,
"mode": "ctrl",
"camid": "C200 ",
"rec": "stby", // ← Recording status!
"tc": "15:32:00.12", // ← Timecode!
"Opower": {"Obatt": {"percent": "25", "rtime": "35"}},
"Omedia": {...},
"Owbinfo": {...},
// ... full camera state
}The productId cookie value (VNCX02) comes from the camera's URL path:
- Camera serves pages from:
/wpd/VNCX02/... - This is the Canon C200's internal product identifier
- It's embedded in the URL when you access Browser Remote
When examining Chrome's Network tab → Cookies:
| Cookie | Value | Domain | Path |
|---|---|---|---|
| acid | 6f55 | camera IP | / |
| authlevel | full | camera IP | / |
| brlang | 0 | camera IP | / |
| productId | VNCX02 | camera IP | / |
The browser JavaScript sets brlang and productId cookies automatically. We need to set them manually.
- ✅ Dashboard-triggered recording - Works perfectly
- ✅ Physical button detection - NOW WORKS with cookie fix!
- ✅
recfield available - Returns"stby"or"rec" - ✅ Timecode available - Returns
"HH:MM:SS.FF"
ESP32 (1.1.1.1) <--Ethernet--> Camera (1.1.1.2)
|
WiFi
|
Dashboard/Companion
- ESP32 Ethernet IP: 1.1.1.1
- Camera Ethernet IP: 1.1.1.2
- Direct Ethernet connection between ESP32 and camera
- ESP32 exposes API over WiFi for external control
The rec field in getcurprop response indicates recording state:
rec Value |
Meaning |
|---|---|
stby |
Standby (not recording) |
rec |
Recording |
sf_stby |
Slow/Fast standby |
sf_rec |
Slow/Fast recording |
frm_stby |
Frame recording standby |
frm_rec |
Frame recording |
int_stby |
Interval recording standby |
int_rec |
Interval recording |
cJSON *rec = cJSON_GetObjectItem(json, "rec");
if (rec && cJSON_IsString(rec)) {
if (strcmp(rec->valuestring, "rec") == 0) {
camera_recording = true;
} else if (strcmp(rec->valuestring, "stby") == 0) {
camera_recording = false;
}
}{
"res": "ok",
"seq": 1,
"com": 1,
"mode": "ctrl",
"camid": "C200 ",
"rec": "rec",
"extrec": "off",
"tc": "15:32:20.23",
"lvactfarea": "hd_80x80",
"Opower": {
"Obatt": {"percent": "25", "rtime": "32"}
},
"Omedia": {
"Ocfa": {"state": "n", "rtime": -1},
"Osda": {"state": "sel", "rtime": 93},
"Osdb": {"state": "n", "rtime": -1}
},
"Owbinfo": {
"Omode": {"pv": "user1", "en": 1},
"Oawb": {"kelvinvalue": "non", "ccvalue": "non", "en": 1},
"Oseta": {"Ovalue": {"kelvinvalue": "5600", "ccvalue": "0", "en": 1}},
"Osetb": {"Ovalue": {"kelvinvalue": "5600", "ccvalue": "0", "en": 1}},
"Odaylight": {"kelvinvalue": "5600", "ccvalue": "0", "en": 1},
"Otungsten": {"kelvinvalue": "3200", "ccvalue": "0", "en": 1},
"Ouser1": {"kelvinvalue": "4760", "ccvalue": "-2", "en": 1},
"Oawbhold": {"pv": "off", "en": 1}
},
"Ondinfo": {
"Ovalue": {"pv": "0", "en": 1},
"Oextrange": {"pv": "on", "en": 1}
},
"Oirisinfo": {
"Omode": {"pv": "---", "en": 0},
"Ostep": {"pv": "3fine", "en": 1},
"Ovalue": {"pv": "F2.2", "en": 1},
"Opushai": {"pv": "stop", "en": 1}
}
// ... additional fields truncated
}- When recording:
tcfield shows running timecode (e.g.,"15:32:20.23") - When stopped:
tcfield still present with last recorded timecode - Format:
HH:MM:SS.FF(hours:minutes:seconds.frames)
-
Login to camera
GET /api/acnt/loginResponse:
{"res":"ok"}Camera sets:acid=XXXX; authlevel=full -
Add required cookies manually
strcat(camera_cookies, "; brlang=0; productId=VNCX02");
Full cookie string:
acid=XXXX; authlevel=full; brlang=0; productId=VNCX02 -
Start Live View session
GET /api/cam/lv?cmd=start&sz=sResponse:
{"res":"ok"}This enablesgetcurpropto return data. -
Poll for status
GET /api/cam/getcurprop?seq=0Now returns full camera state including
recfield!
Without an active Live View session, getcurprop returns minimal data or {"res":"busy"}. The camera needs to know a client is actively viewing before it reports full status.
| Approach | Result | Why It Failed |
|---|---|---|
getcurprop?seq=N |
{"res":"busy"} |
Missing cookies |
getprop?r=rec |
{"res":"failparam"} |
"rec" is not a property, it's a status field |
| Start LV first | Still busy | Missing cookies |
| Fetch HTML first | Still busy | Missing cookies |
| Different seq values | Still busy | Missing cookies |
| Longer delays | Still busy | Missing cookies |
| Timecode detection | Worked but not ideal | tc present even when not recording |
- Moved camera from ESP32 Ethernet to main network (10.10.11.36)
- Used curl to test directly (no ESP32 in the middle)
- Compared browser's cookies to our cookies
- Found browser sends 4 cookies, we sent 2
- Added missing
brlang=0andproductId=VNCX02 - getcurprop now returns full data including
recfield!
// Session cookies from camera login
static char camera_cookies[256] = "";static esp_err_t camera_login(void)
{
// Clear old cookies before login
camera_cookies[0] = 0;
char response[512];
esp_err_t err = camera_request("/api/acnt/login", response, sizeof(response));
if (err == ESP_OK) {
cJSON *json = cJSON_Parse(response);
if (json) {
cJSON *res = cJSON_GetObjectItem(json, "res");
if (res && strcmp(res->valuestring, "ok") == 0) {
camera_logged_in = true;
cJSON_Delete(json);
// CRITICAL: Add required cookies for getcurprop to work
// Without these, camera returns {"res":"busy"}
if (strlen(camera_cookies) + 30 < sizeof(camera_cookies)) {
strcat(camera_cookies, "; brlang=0; productId=VNCX02");
}
// Start live view session
char lv_response[256];
camera_request("/api/cam/lv?cmd=start&sz=s", lv_response, sizeof(lv_response));
return ESP_OK;
}
}
}
return ESP_FAIL;
}// Add session cookies if we have them
if (strlen(camera_cookies) > 0) {
esp_http_client_set_header(client, "Cookie", camera_cookies);
}static void update_recording_state(void)
{
if (!camera_logged_in) return;
char path[64];
char response[1024];
snprintf(path, sizeof(path), "/api/cam/getcurprop?seq=%d", getcurprop_seq);
esp_err_t err = camera_request(path, response, sizeof(response));
if (err != ESP_OK) return;
cJSON *json = cJSON_Parse(response);
if (!json) return;
// Update seq from response
cJSON *seq = cJSON_GetObjectItem(json, "seq");
if (seq && cJSON_IsNumber(seq)) {
getcurprop_seq = seq->valueint;
}
// Check for session error
cJSON *res = cJSON_GetObjectItem(json, "res");
if (res && cJSON_IsString(res) && strcmp(res->valuestring, "errsession") == 0) {
camera_logged_in = false;
cJSON_Delete(json);
return;
}
// Check "rec" field for recording state
cJSON *rec = cJSON_GetObjectItem(json, "rec");
bool was_recording = camera_recording;
if (rec && cJSON_IsString(rec)) {
if (strcmp(rec->valuestring, "rec") == 0) {
camera_recording = true;
} else if (strcmp(rec->valuestring, "stby") == 0) {
camera_recording = false;
}
}
cJSON_Delete(json);
}GET /api/status
Response:
{
"wifi_connected": true,
"eth_connected": true,
"camera_connected": true,
"is_recording": false,
"wifi_ip": "10.10.11.229",
"eth_ip": "1.1.1.1",
"camera_ip": "1.1.1.2"
}GET /api/proxy/{camera_path}
Forwards requests to camera and returns response. Useful for testing camera APIs from a computer.
Example:
curl "http://10.10.11.229/api/proxy/api/cam/getcurprop?seq=1"Cause: Missing cookies
Fix: Ensure all 4 cookies are sent:
acid(from login)authlevel(from login)brlang=0(add manually)productId=VNCX02(add manually)
Cause: Another client connected (browser, etc.)
Fix: Close other Browser Remote sessions. Only one client at a time.
Causes:
- Live View not started
- Cookies missing
- Session expired
Fix: Re-login and ensure LV session is started.
Cause: Basic Auth credentials wrong
Fix: Check username/password (default: admin/admin)
- Cookies are CRITICAL - The camera requires specific cookies beyond just session auth
- Browser DevTools reveals all - Compare what browser sends vs. what you send
- Live View enables status - Must start LV session for full
getcurpropdata recis NOT a property - It's a status field ingetcurprop, not queryable viagetprop- Product ID from URL - The
VNCX02value comes from the camera's URL path structure