Difference between revisions of "API reference"

From bcmeter.org
Jump to navigation Jump to search
(Wiki update: sync with manual and dev docs (2026-03))
 
(Restructure: platform separation (ESP32 commercial prioritised, Raspberry Pi DIY in own section); verified against firmware/Pi source; complete serial command list)
 
(4 intermediate revisions by the same user not shown)
Line 1: Line 1:
== API Reference ==
== API Reference ==


The bcMeter exposes a REST API on port 80. All endpoints are accessible without authentication when connected to the device network. This API is identical between the ESP32 firmware and the [https://github.com/dahljo/bcmeter-dev bcmeter-dev] open-source implementation.
The bcMeter exposes a REST API on port 80. All endpoints are accessible without authentication when connected to the device network. CORS is enabled for all responses (<code>Access-Control-Allow-Origin: *</code>).
 
The device identifies its platform via the <code>"env"</code> field in <code>/api/status</code>: <code>"env": "esp32"</code> for bcMeter (commercial) and <code>"env": "pi"</code> for bcMeter (DIY / Raspberry Pi).
 
The software is source-available — freely available for private, educational and research use (CC BY-NC 4.0): [https://github.com/dahljo/bcmeter-pi github.com/dahljo/bcmeter-pi] (Pi / DIY version).


=== Status & Diagnostics ===
=== Status & Diagnostics ===


{| class="wikitable"
{| class="wikitable"
! Method !! Endpoint !! Description
! Method !! Endpoint !! Description !! Platforms
|-
| GET || <code>/api/status</code> || Current measurement state, last sensor readings (BC, ATN, sensor/reference voltages, flow, filter loading), error codes, sample count, session active, WiFi mode/SSID/RSSI, internet flag, firmware version, OTA result flag, time-sync state. || Both
|-
|-
| GET || <code>/api/status</code> || Current measurement state, last sensor readings, error codes, sample count, WiFi mode, firmware version
| GET || <code>/api/system</code> || Hardware info: IP, MAC address, heap and PSRAM usage, flash usage, modem status and signal, GPS detail, barometric altitude, geolocation source, current time, cycle timing statistics. || Both
|-
|-
| GET || <code>/api/system</code> || Hardware info: MAC address, memory, time, last calibration, GPS position, barometric altitude
| GET || <code>/api/logs</code> || Structured health report as JSON with sections: <code>hardware</code>, <code>measurement</code>, <code>system</code>, <code>network</code>, <code>incidents</code>. Each entry has <code>{k, v, s}</code> (key, value, status: ok/warn/error/info). || Both
|-
|-
| GET || <code>/api/logs</code> || Structured health report with sections: hardware, measurement, system, network
| GET || <code>/api/debug_mobile/status</code> || Returns modem debug email send progress (from email handler). || Both
|}
|}


=== Measurement Control ===
=== Measurement Control ===
All control is dispatched through a single endpoint with an <code>action</code> query parameter:


{| class="wikitable"
{| class="wikitable"
! Method !! Endpoint !! Description
! Method !! Endpoint !! Description !! Platforms
|-
| GET || <code>/api/control?action=start</code> || Begin measurement session. Returns 409 if time not synced, 423 if never calibrated, 424 if calibration stale (>28 days). Add <code>&force=1</code> to override preflight checks. Optional <code>&indoor=0|1</code> for one-shot indoor/outdoor override. || Both
|-
| GET || <code>/api/control?action=stop</code> || Stop measurement and finalise session file. || Both
|-
| GET || <code>/api/control?action=calibrate</code> || Start optical calibration routine (returns 202 immediately — poll <code>/api/calibration</code> for progress). Requires sampling to be stopped. || Both
|-
| GET || <code>/api/calibration</code> || Calibration status: <code>{running, done, ok, elapsed_ms, log}</code>. || Both
|-
| GET || <code>/api/control?action=clear_error</code> || Reset error state to ERR_NONE. || Both
|-
| GET || <code>/api/control?action=identify</code> || Triggers LED identify blink pattern (Morse code "bcmeter", ~30 seconds). Useful for locating a specific device when multiple units are deployed. || Both
|-
| GET || <code>/api/control?action=debug_mobile</code> || Triggers modem debug email via Lambda. Requires modem present. Returns 409 if modem unavailable or already running. || Both
|-
|-
| GET || <code>/api/control?action=start</code> || Begin measurement session (requires time sync + calibration). Add <code>&force=1</code> to override preflight checks.
| GET || <code>/api/control?action=synctime&ts=EPOCH&tz=IANA</code> || Sync device clock. <code>ts</code> = Unix timestamp, <code>tz</code> = IANA timezone string (e.g. <code>Europe/Berlin</code>). || Both
|-
|-
| GET || <code>/api/control?action=stop</code> || Stop measurement and save session file
| GET || <code>/api/control?action=cleardata</code> || Delete all stored log files. || Both
|-
|-
| GET || <code>/api/control?action=calibrate</code> || Start calibration routine (async — poll <code>/api/calibration</code> for progress)
| GET || <code>/api/control?action=reboot</code> || End session and reboot the device. || Both
|-
|-
| GET || <code>/api/calibration</code> || Calibration status: running, elapsed time, log output, success/failure
| GET || <code>/api/control?action=shutdown</code> || Safely shut down the device. || Both
|-
|-
| GET || <code>/api/control?action=clear_error</code> || Reset error state
| GET || <code>/api/control?action=factory_reset</code> || Permanently erase all logs, WiFi credentials, calibration data, and settings. Reboots into factory state. || Both
|}
|}


Line 34: Line 56:


{| class="wikitable"
{| class="wikitable"
! Method !! Endpoint !! Description
! Method !! Endpoint !! Description !! Platforms
|-
| GET || <code>/api/config</code> || All settings as JSON. Each parameter includes type, value, label, tab, and description. || Both
|-
| POST || <code>/api/config</code> || Update settings. JSON body with key-value pairs. Accepts <code>{key: value}</code> or <code>{key: {value: ...}}</code> format. Only changed values need to be sent. || Both
|-
|-
| GET || <code>/api/config</code> || All settings as JSON. Each parameter includes type, value, label, tab, and description.
| POST || <code>/api/device/rename</code> || Set device name. Body: <code>{"name": "bcmeter-A3AB"}</code> (1–32 chars). Updates mDNS/hostname accordingly. || Both
|-
|-
| POST || <code>/api/config</code> || Update settings. JSON body with key-value pairs. Only changed values need to be sent.
| GET || <code>/api/ap-security</code> || Hotspot security settings: <code>{secured: bool, password: str}</code>. || Both
|-
|-
| POST || <code>/api/device/rename</code> || Set device name. Body: <code>{"name": "bcmeter01"}</code>
| POST || <code>/api/ap-security</code> || Update hotspot password. Body: <code>{"secured": true, "password": "..."}</code> (min 8 chars). || Both
|-
|-
| GET || <code>/api/control?action=synctime&ts=EPOCH&tz=IANA</code> || Sync device clock. <code>ts</code> = Unix timestamp, <code>tz</code> = IANA timezone string.
| POST || <code>/api/email/validate</code> || Validate email API key against the Lambda endpoint. Body: <code>{"api_key": "..."}</code>. Returns <code>{valid: bool, error: str}</code>. || Both
|}
|}


Line 48: Line 74:


{| class="wikitable"
{| class="wikitable"
! Method !! Endpoint !! Description
! Method !! Endpoint !! Description !! Platforms
|-
|-
| GET || <code>/api/wifi/scan</code> || List nearby networks. Add <code>?refresh=1</code> to trigger a new scan.
| GET || <code>/api/wifi/scan</code> || List nearby networks: <code>{scanning: bool, networks: [{ssid, rssi, secure}]}</code>. Add <code>?refresh=1</code> to trigger a new background scan. || Both
|-
|-
| GET || <code>/api/wifi/status</code> || Current connection: mode (sta/ap), SSID, IP address
| GET || <code>/api/wifi/status</code> || Current connection: <code>{mode, status, ssid, ip, rssi, quality (0-4), internet, timeSynced}</code>. || Both
|-
|-
| POST || <code>/api/wifi/connect</code> || Connect to network. Body: <code>{"ssid": "...", "pass": "...", "hidden": false}</code>
| POST || <code>/api/wifi</code> || Save WiFi credentials. Body: <code>{"ssid": "...", "pass": "..."}</code> (password min 8 chars). Initiates reconnect. || Both
|-
|-
| GET || <code>/api/wifi/connect/status</code> || Connection progress: state, hostname, log messages
| POST || <code>/api/wifi/connect</code> || Save credentials and initiate background connection attempt (returns 202). Password must be 8–63 chars. Body: <code>{"ssid": "...", "pass": "..."}</code>. || Both
|-
|-
| POST || <code>/api/wifi/delete</code> || Clear saved WiFi credentials, switch to AP mode
| GET || <code>/api/wifi/connect/status</code> || Connection progress: <code>{state: 0-3, elapsed, log, mode, ip, ssid, device_name, hostname}</code>. States: 0=idle, 1=connecting, 2=success, 3=failed. Poll after POST to <code>/api/wifi/connect</code>. || Both
|-
|-
| GET || <code>/api/ap-security</code> || Hotspot security settings: secured (bool), password
| POST || <code>/api/wifi/delete</code> || Delete credentials and switch to AP mode (deferred 2s background switch). || Both
|-
|-
| POST || <code>/api/ap-security</code> || Update hotspot password. Body: <code>{"secured": true, "password": "..."}</code>
| POST || <code>/api/wifi/scan/refresh</code> || Explicitly trigger a new WiFi scan (returns 202, or 409 if already scanning). || Both
|}
|}


Line 68: Line 94:


{| class="wikitable"
{| class="wikitable"
! Method !! Endpoint !! Description
! Method !! Endpoint !! Description !! Platforms
|-
| GET || <code>/api/csv</code> || Current session CSV data. Returns header-only if no active session. Add <code>?file=FILENAME</code> for archived files. || Both
|-
| GET || <code>/api/files</code> || List stored CSV logs: <code>[{name, size, lines, date}]</code> sorted newest first. Add <code>?name=FILENAME</code> to download a specific file. || Both
|}
 
=== Firmware Updates ===
 
{| class="wikitable"
! Method !! Endpoint !! Description !! Platforms
|-
|-
| GET || <code>/api/csv</code> || Current session CSV data. Add <code>?file=FILENAME</code> for archived files.
| GET || <code>/api/ota/status</code> || Check if update available: <code>{available, version, notes, apply_state, apply_progress, apply_error}</code>. || Both
|-
|-
| GET || <code>/api/files</code> || List stored CSV logs as JSON: <code>[{"name": "...", "size": 1234}, ...]</code>
| POST || <code>/api/ota/check</code> || Force check for new firmware. || Both
|-
|-
| GET || <code>/api/control?action=cleardata</code> || Delete all stored log files
| POST || <code>/api/ota/skip</code> || Skip current available update. || Both
|-
|-
| GET || <code>/api/control?action=cleanempty</code> || Remove empty (0 byte) log files
| POST || <code>/api/ota/apply</code> || Download and apply firmware update. Returns 409 if none pending. || Both
|-
|-
| GET || <code>/api/control?action=export</code> || Export all logs to SD card (ESP32 only)
| POST || <code>/api/update</code> || Direct firmware upload. ESP32: accepts a raw <code>.bin</code> file, flashes to the OTA partition, reboots. Pi: accepts a <code>.tar.gz</code> or <code>.zip</code> archive, extracts over the code directory, restarts the <code>bcMeter.service</code>. || Both
|}
|}


=== Firmware Updates ===
=== Captive Portal ===
 
These endpoints handle captive portal detection on mobile devices and redirect to the main interface in AP mode:


{| class="wikitable"
{| class="wikitable"
! Method !! Endpoint !! Description
! Endpoint !! Purpose
|-
| <code>/generate_204</code> || Android captive portal check
|-
| <code>/hotspot-detect.html</code> || Apple captive portal check
|-
|-
| GET || <code>/api/ota/status</code> || Check if update available: version, release notes, apply state/progress
| <code>/canonical.html</code> || Firefox captive portal check
|-
|-
| POST || <code>/api/ota/check</code> || Force check for new firmware
| <code>/ncsi.txt</code> || Windows captive portal check
|-
|-
| POST || <code>/api/ota/skip</code> || Skip current available update
| <code>/success.txt</code> || Generic connectivity probe (returns <code>"success"</code>)
|}
 
=== Static Routes ===
 
{| class="wikitable"
! Path !! Description
|-
|-
| POST || <code>/api/ota/apply</code> || Download and flash firmware update
| <code>/</code> || Serves the SPA frontend
|-
|-
| POST || <code>/api/update</code> || Direct firmware upload (multipart form-data, .tar.gz)
| <code>/discover</code> || Standalone device discovery page
|-
| <code>/current_log.csv</code> || Legacy redirect to <code>/api/csv</code>
|}
|}


=== System Operations ===
=== bcMeter (DIY) — Raspberry Pi OS-level extras ===
 
The following endpoints exist '''only on the Raspberry Pi''' version. They use Linux system commands and are not applicable to the ESP32.


{| class="wikitable"
{| class="wikitable"
! Method !! Endpoint !! Description
! Method !! Endpoint !! Description
|-
|-
| GET || <code>/api/control?action=reboot</code> || Reboot the device
| GET || <code>/api/maintenance-logs</code> || Downloads a zip bundle containing: rotating bcmeter.log files, syslog, 10 newest CSV sessions, dmesg, journalctl output for bcmeter and pigpiod services, config JSON, incident log. Uses <code>journalctl</code> and <code>dmesg</code> — Linux only.
|}
 
Additionally, on the Pi:
 
* <code>/api/control?action=reboot</code> runs <code>sudo reboot</code> (Pi: <code>routes_control.py:151</code>)
* <code>/api/control?action=shutdown</code> runs <code>sudo shutdown -h now</code> (Pi: <code>routes_control.py:161</code>)
* <code>/api/control?action=synctime</code> sets the timezone via <code>sudo timedatectl set-timezone</code> (Pi: <code>routes_control.py:276</code>)
* <code>/api/device/rename</code> calls <code>sudo hostnamectl set-hostname</code> and restarts avahi (Pi: <code>bcmeter/identity.py:41</code>)
* <code>/api/update</code> runs <code>sudo systemctl restart bcMeter.service</code> after extracting the archive (Pi: <code>routes_update.py:134</code>)
 
The following endpoints exist only on the Pi and have no ESP32 equivalent:
 
{| class="wikitable"
! Method !! Endpoint !! Description
|-
| GET || <code>/api/qc/pi/report</code> || Pi QC test report as JSON.
|-
| GET || <code>/api/qc/pi/report.html</code> || Pi QC test report as HTML.
|-
|-
| GET || <code>/api/control?action=shutdown</code> || Safely shut down
| POST || <code>/api/qc/pi/start</code> || Start the Pi QC test sequence.
|-
|-
| GET || <code>/api/control?action=factory_reset</code> || Erase all data and settings, reboot to factory state
| GET || <code>/api/qc/pi/status</code> || Pi QC test status.
|-
|-
| POST || <code>/api/email/validate</code> || Validate email API key. Body: <code>{"api_key": "..."}</code>
| GET || <code>/api/lab/run</code> || Lab/engineering raw ADC capture (Pi equivalent of ESP32 serial LAB mode).
|-
| GET || <code>/api/lab/info</code> || Lab session status.
|}
|}


=== Captive Portal ===
The following endpoints exist only on the '''ESP32''' and have no Pi equivalent:
 
These endpoints handle captive portal detection on mobile devices:


{| class="wikitable"
{| class="wikitable"
! Endpoint !! Purpose
! Method !! Endpoint !! Description
|-
| GET || <code>/api/sdfiles</code> || List CSV files on the SD card.
|-
| GET || <code>/api/crash</code> || Last crash/reset report from CrashReport module.
|-
| GET || <code>/api/coredump</code> || Stream the raw ELF coredump from the coredump flash partition. Add <code>?erase=1</code> to erase after download.
|-
| GET || <code>/api/modem/test</code> || Run modem AT diagnostic and return results as JSON.
|-
| POST || <code>/api/lab/start</code> || Start a lab ADC capture session (params: <code>led_ch</code>, <code>led_duty</code>, <code>pump_duty</code>, <code>pump_hz</code>).
|-
| POST || <code>/api/lab/stop</code> || Stop the lab capture session.
|-
| GET || <code>/api/lab/status</code> || Lab session status: active, pairs captured, LED/pump settings.
|-
| POST || <code>/api/raw_capture/enable</code> || Toggle raw ADC sample capture ring buffer (param: <code>on=0|1</code>).
|-
| GET || <code>/api/raw_capture/read</code> || Drain the raw capture ring buffer as binary float32 pairs (8 bytes each). <code>X-Dropped</code> header reports overflow count.
|-
| GET || <code>/api/qc/sensors</code> || Live SPS30 + env-sensor read for QC (409 while sampling).
|-
|-
| <code>/generate_204</code> || Android captive portal check
| GET || <code>/api/qc/diag</code> || Passive diagnostic snapshot: heap, PSRAM, uptime, reset reason, WiFi, modem state. No AT calls.
|-
|-
| <code>/hotspot-detect.html</code> || Apple captive portal check
| GET || <code>/api/qc/pump_ramp/status</code> || QC pump ramp test status.
|-
|-
| <code>/success.txt</code> || Generic connectivity probe
| GET || <code>/api/qc/led_880/status</code> || QC LED stability test status.
|}
|}


Line 129: Line 219:
<pre>
<pre>
# Sync time first
# Sync time first
curl "http://bcmeter.local/api/control?action=synctime&ts=$(date +%s)&tz=Europe/Berlin"
curl "http://bcmeter-XXXX.local/api/control?action=synctime&ts=$(date +%s)&tz=Europe/Berlin"


# Start measurement
# Start measurement
curl "http://bcmeter.local/api/control?action=start"
curl "http://bcmeter-XXXX.local/api/control?action=start"


# Poll status
# Poll status
curl "http://bcmeter.local/api/status"
curl "http://bcmeter-XXXX.local/api/status"


# Download current data
# Download current data
curl "http://bcmeter.local/api/csv" -o measurement.csv
curl "http://bcmeter-XXXX.local/api/csv" -o measurement.csv
</pre>
</pre>

Latest revision as of 20:59, 8 June 2026

API Reference

The bcMeter exposes a REST API on port 80. All endpoints are accessible without authentication when connected to the device network. CORS is enabled for all responses (Access-Control-Allow-Origin: *).

The device identifies its platform via the "env" field in /api/status: "env": "esp32" for bcMeter (commercial) and "env": "pi" for bcMeter (DIY / Raspberry Pi).

The software is source-available — freely available for private, educational and research use (CC BY-NC 4.0): github.com/dahljo/bcmeter-pi (Pi / DIY version).

Status & Diagnostics

Method Endpoint Description Platforms
GET /api/status Current measurement state, last sensor readings (BC, ATN, sensor/reference voltages, flow, filter loading), error codes, sample count, session active, WiFi mode/SSID/RSSI, internet flag, firmware version, OTA result flag, time-sync state. Both
GET /api/system Hardware info: IP, MAC address, heap and PSRAM usage, flash usage, modem status and signal, GPS detail, barometric altitude, geolocation source, current time, cycle timing statistics. Both
GET /api/logs Structured health report as JSON with sections: hardware, measurement, system, network, incidents. Each entry has {k, v, s} (key, value, status: ok/warn/error/info). Both
GET /api/debug_mobile/status Returns modem debug email send progress (from email handler). Both

Measurement Control

All control is dispatched through a single endpoint with an action query parameter:

Method Endpoint Description Platforms
GET /api/control?action=start 1 for one-shot indoor/outdoor override. Both
GET /api/control?action=stop Stop measurement and finalise session file. Both
GET /api/control?action=calibrate Start optical calibration routine (returns 202 immediately — poll /api/calibration for progress). Requires sampling to be stopped. Both
GET /api/calibration Calibration status: {running, done, ok, elapsed_ms, log}. Both
GET /api/control?action=clear_error Reset error state to ERR_NONE. Both
GET /api/control?action=identify Triggers LED identify blink pattern (Morse code "bcmeter", ~30 seconds). Useful for locating a specific device when multiple units are deployed. Both
GET /api/control?action=debug_mobile Triggers modem debug email via Lambda. Requires modem present. Returns 409 if modem unavailable or already running. Both
GET /api/control?action=synctime&ts=EPOCH&tz=IANA Sync device clock. ts = Unix timestamp, tz = IANA timezone string (e.g. Europe/Berlin). Both
GET /api/control?action=cleardata Delete all stored log files. Both
GET /api/control?action=reboot End session and reboot the device. Both
GET /api/control?action=shutdown Safely shut down the device. Both
GET /api/control?action=factory_reset Permanently erase all logs, WiFi credentials, calibration data, and settings. Reboots into factory state. Both

Configuration

Method Endpoint Description Platforms
GET /api/config All settings as JSON. Each parameter includes type, value, label, tab, and description. Both
POST /api/config Update settings. JSON body with key-value pairs. Accepts {key: value} or {key: {value: ...}} format. Only changed values need to be sent. Both
POST /api/device/rename Set device name. Body: {"name": "bcmeter-A3AB"} (1–32 chars). Updates mDNS/hostname accordingly. Both
GET /api/ap-security Hotspot security settings: {secured: bool, password: str}. Both
POST /api/ap-security Update hotspot password. Body: {"secured": true, "password": "..."} (min 8 chars). Both
POST /api/email/validate Validate email API key against the Lambda endpoint. Body: {"api_key": "..."}. Returns {valid: bool, error: str}. Both

WiFi Management

Method Endpoint Description Platforms
GET /api/wifi/scan List nearby networks: {scanning: bool, networks: [{ssid, rssi, secure}]}. Add ?refresh=1 to trigger a new background scan. Both
GET /api/wifi/status Current connection: {mode, status, ssid, ip, rssi, quality (0-4), internet, timeSynced}. Both
POST /api/wifi Save WiFi credentials. Body: {"ssid": "...", "pass": "..."} (password min 8 chars). Initiates reconnect. Both
POST /api/wifi/connect Save credentials and initiate background connection attempt (returns 202). Password must be 8–63 chars. Body: {"ssid": "...", "pass": "..."}. Both
GET /api/wifi/connect/status Connection progress: {state: 0-3, elapsed, log, mode, ip, ssid, device_name, hostname}. States: 0=idle, 1=connecting, 2=success, 3=failed. Poll after POST to /api/wifi/connect. Both
POST /api/wifi/delete Delete credentials and switch to AP mode (deferred 2s background switch). Both
POST /api/wifi/scan/refresh Explicitly trigger a new WiFi scan (returns 202, or 409 if already scanning). Both

Data Management

Method Endpoint Description Platforms
GET /api/csv Current session CSV data. Returns header-only if no active session. Add ?file=FILENAME for archived files. Both
GET /api/files List stored CSV logs: [{name, size, lines, date}] sorted newest first. Add ?name=FILENAME to download a specific file. Both

Firmware Updates

Method Endpoint Description Platforms
GET /api/ota/status Check if update available: {available, version, notes, apply_state, apply_progress, apply_error}. Both
POST /api/ota/check Force check for new firmware. Both
POST /api/ota/skip Skip current available update. Both
POST /api/ota/apply Download and apply firmware update. Returns 409 if none pending. Both
POST /api/update Direct firmware upload. ESP32: accepts a raw .bin file, flashes to the OTA partition, reboots. Pi: accepts a .tar.gz or .zip archive, extracts over the code directory, restarts the bcMeter.service. Both

Captive Portal

These endpoints handle captive portal detection on mobile devices and redirect to the main interface in AP mode:

Endpoint Purpose
/generate_204 Android captive portal check
/hotspot-detect.html Apple captive portal check
/canonical.html Firefox captive portal check
/ncsi.txt Windows captive portal check
/success.txt Generic connectivity probe (returns "success")

Static Routes

Path Description
/ Serves the SPA frontend
/discover Standalone device discovery page
/current_log.csv Legacy redirect to /api/csv

bcMeter (DIY) — Raspberry Pi OS-level extras

The following endpoints exist only on the Raspberry Pi version. They use Linux system commands and are not applicable to the ESP32.

Method Endpoint Description
GET /api/maintenance-logs Downloads a zip bundle containing: rotating bcmeter.log files, syslog, 10 newest CSV sessions, dmesg, journalctl output for bcmeter and pigpiod services, config JSON, incident log. Uses journalctl and dmesg — Linux only.

Additionally, on the Pi:

  • /api/control?action=reboot runs sudo reboot (Pi: routes_control.py:151)
  • /api/control?action=shutdown runs sudo shutdown -h now (Pi: routes_control.py:161)
  • /api/control?action=synctime sets the timezone via sudo timedatectl set-timezone (Pi: routes_control.py:276)
  • /api/device/rename calls sudo hostnamectl set-hostname and restarts avahi (Pi: bcmeter/identity.py:41)
  • /api/update runs sudo systemctl restart bcMeter.service after extracting the archive (Pi: routes_update.py:134)

The following endpoints exist only on the Pi and have no ESP32 equivalent:

Method Endpoint Description
GET /api/qc/pi/report Pi QC test report as JSON.
GET /api/qc/pi/report.html Pi QC test report as HTML.
POST /api/qc/pi/start Start the Pi QC test sequence.
GET /api/qc/pi/status Pi QC test status.
GET /api/lab/run Lab/engineering raw ADC capture (Pi equivalent of ESP32 serial LAB mode).
GET /api/lab/info Lab session status.

The following endpoints exist only on the ESP32 and have no Pi equivalent:

Method Endpoint Description
GET /api/sdfiles List CSV files on the SD card.
GET /api/crash Last crash/reset report from CrashReport module.
GET /api/coredump Stream the raw ELF coredump from the coredump flash partition. Add ?erase=1 to erase after download.
GET /api/modem/test Run modem AT diagnostic and return results as JSON.
POST /api/lab/start Start a lab ADC capture session (params: led_ch, led_duty, pump_duty, pump_hz).
POST /api/lab/stop Stop the lab capture session.
GET /api/lab/status Lab session status: active, pairs captured, LED/pump settings.
POST /api/raw_capture/enable 1).
GET /api/raw_capture/read Drain the raw capture ring buffer as binary float32 pairs (8 bytes each). X-Dropped header reports overflow count.
GET /api/qc/sensors Live SPS30 + env-sensor read for QC (409 while sampling).
GET /api/qc/diag Passive diagnostic snapshot: heap, PSRAM, uptime, reset reason, WiFi, modem state. No AT calls.
GET /api/qc/pump_ramp/status QC pump ramp test status.
GET /api/qc/led_880/status QC LED stability test status.

Example: Start a Measurement

# Sync time first
curl "http://bcmeter-XXXX.local/api/control?action=synctime&ts=$(date +%s)&tz=Europe/Berlin"

# Start measurement
curl "http://bcmeter-XXXX.local/api/control?action=start"

# Poll status
curl "http://bcmeter-XXXX.local/api/status"

# Download current data
curl "http://bcmeter-XXXX.local/api/csv" -o measurement.csv