MCU Integration

Overview

VBatPower accepts telemetry data from any device that can make HTTP POST requests. The data format is intentionally simple — a flat CSV that can be generated with printf on any 8-bit MCU.

The typical flow:

  1. Create a project in the VBatPower app
  2. Generate an API key in Settings > API
  3. Send telemetry from your MCU to POST /api/v1/telemetry
  4. View graphs and analysis in the app

Arduino / ESP32

Using the WiFi HTTP client to send telemetry data:

// Global state
uint32_t nonce = 0;
uint32_t runid = 1;

// Read sensors and send data
void sendTelemetry() {
    float batteryV = analogRead(VBAT_PIN) * 3.3 / 4095.0 * 2.0;
    float currentMA = readINA219();
    uint32_t elapsed = millis();
    nonce++;

    String json = "{\"records\":[";
    // Battery voltage (type: v = value measurement)
    json += "{\"id\":\"0\",\"type\":\"v\",\"value\":";
    json += String(batteryV, 3);
    json += ",\"unit\":\"V\",\"nonce\":";
    json += String(nonce);
    json += ",\"runid\":";
    json += String(runid);
    json += "},";
    // State execution duration (type: t = telemetry timing)
    json += "{\"id\":\"0.0.0\",\"type\":\"t\",\"value\":";
    json += String(500);  // execution duration in ms
    json += ",\"unit\":\"ms\",\"nonce\":";
    json += String(nonce);
    json += ",\"runid\":";
    json += String(runid);
    json += "},";
    // Current draw (type: v = value measurement)
    json += "{\"id\":\"0.0.0\",\"type\":\"v\",\"value\":";
    json += String(currentMA, 2);
    json += ",\"unit\":\"mA\",\"nonce\":";
    json += String(nonce);
    json += ",\"runid\":";
    json += String(runid);
    json += "},";
    // Elapsed time from boot (type: n = nonce timing)
    json += "{\"id\":\"0.0.0\",\"type\":\"n\",\"value\":";
    json += String(elapsed);
    json += ",\"unit\":\"ms\",\"nonce\":";
    json += String(nonce);
    json += ",\"runid\":";
    json += String(runid);
    json += "}]}";

    HTTPClient http;
    http.begin("https://api.vbatpower.com/api/v1/telemetry");
    http.addHeader("Content-Type", "application/json");
    http.addHeader("Authorization", "Bearer " + String(API_KEY));
    int code = http.POST(json);
    http.end();
}

STM32 (bare metal / HAL)

For STM32 with a UART-to-WiFi module (ESP8266 AT commands) or Ethernet, format the JSON payload with snprintf:

char buf[512];
int len = snprintf(buf, sizeof(buf),
    "{\"records\":["
    "{\"id\":\"0.0.0\",\"type\":\"t\","
    "\"value\":%lu,\"unit\":\"ms\","
    "\"nonce\":%lu,\"runid\":%lu},"
    "{\"id\":\"0.0.0\",\"type\":\"v\","
    "\"value\":%.2f,\"unit\":\"mA\","
    "\"nonce\":%lu,\"runid\":%lu},"
    "{\"id\":\"0.0.0\",\"type\":\"n\","
    "\"value\":%lu,\"unit\":\"ms\","
    "\"nonce\":%lu,\"runid\":%lu}"
    "]}",
    exec_duration_ms, nonce, runid,
    current_ma, nonce, runid,
    HAL_GetTick(), nonce, runid
);

Send buf as the HTTP POST body to /api/v1/telemetry with headers Content-Type: application/json and Authorization: Bearer your-key.

Offline Collection

If your MCU lacks network connectivity, log data to SD card or serial in CSV format:

// Print CSV to Serial or SD file
void logSample(uint32_t nonce, uint32_t runid) {
    float vbat = readBattery();
    float current = readCurrent();
    uint32_t elapsed = millis();

    Serial.printf("0,v,%.3f,V,%lu,%lu\n", vbat, nonce, runid);
    Serial.printf("0.0.0,t,500,ms,%lu,%lu\n", nonce, runid);
    Serial.printf("0.0.0,v,%.2f,mA,%lu,%lu\n", current, nonce, runid);
    Serial.printf("0.0.0,n,%lu,ms,%lu,%lu\n", elapsed, nonce, runid);
}

Capture the serial output to a .csv file and import it in the Data page.

Nonce Strategy

The nonce is a monotonically increasing counter that groups measurements taken at the same point in time. Rules:

  • Increment once per measurement cycle (not per record)
  • All records in one cycle share the same nonce
  • Never reset within a run — use runid to separate experiments
  • Use a 32-bit unsigned integer — at 1 sample/second, this overflows after 136 years

Tips

  • Batch uploads — buffer multiple nonces and send them in a single POST to reduce WiFi wake-ups and save power.
  • 8-bit MCUs — the CSV format requires no JSON parsing on the device side. Use printf to format rows and upload as a file later.
  • Multiple states — measure different operating modes (sleep, active, TX) by assigning each to a different state ID (e.g. 0.0.0 for sleep, 0.0.1 for active).
  • Battery monitoring — include a voltage measurement at depth 1 (e.g. 0,v,3.28,V,...) to track discharge over time.