Skip to content

Commit 82eb64c

Browse files
author
Ravi Singh
committed
feat(firmware): INA219 + voltage-divider auto-detect, power telemetry
TX firmware 2.0.4, RX firmware 2.2.0 (DevKit + C3 SuperMini). Adds INA219 over I2C as Variant B alongside the existing voltage- divider Variant A. Boot-time I2C probe at 0x40 auto-selects the mode; NVS key 'pwr_mode_ovr' (auto/voltage/ina219/disabled) provides a manual override accessible via the wifi_ota web UI dropdown. TX: battery_monitor extended with power_init/read API (legacy battery_init/read preserved as wrappers). INA219 driver inlined: bus voltage from register 0x02, signed shunt current from 0x01, power computed as V*I in software (no calibration register). GPIO1=SDA, GPIO2=SCL, default I2C addr 0x40, 0.1 ohm shunt. LoRa packet format extended (forward-compatible): TANK:dist:pct:v:msgid:fwver:MODE:CURR:POW where MODE is one char (v/i/n), CURR is signed mA, POW is signed mW. Older RXs ignore the new optional fields. wifi_ota mode adds Power Sensor dropdown in SETTINGS card. /api/info returns power_override; /api/config accepts power_override. RX (DevKit + C3, identical changes): lora_rx_packet_t and tx_data_t extended with power_mode, current_ma, power_mw, charging. Parser reads optional fields (defaults to '?'/0 for older TX). /api/transmitters JSON exposes the new fields per TX. MQTT publishes 4 new HA-style sub-topics per TX: tanksync/<slug>/{power_mode,current_ma,power_mw,charging} Skipped when TX reports unknown mode (pre-2.0.4 firmware).
1 parent a6697af commit 82eb64c

32 files changed

Lines changed: 789 additions & 113 deletions

File tree

firmware/receiver-c3/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
cmake_minimum_required(VERSION 3.16)
22

33
# Version embedded in esp_app_desc_t — extracted by receiver for OTA display
4-
set(PROJECT_VER "2.1.0")
4+
set(PROJECT_VER "2.2.0")
55

66
set(EXTRA_COMPONENT_DIRS "components")
77

firmware/receiver-c3/components/lora_rylr998/lora_rylr998.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,36 @@ static bool parse_rcv(const char *line, lora_rx_packet_t *pkt) {
250250
} else {
251251
pkt->fw_version[0] = '\0';
252252
}
253+
254+
// Optional 7th field: power-monitor mode tag (single char) — absent for TX <v2.0.4
255+
// Optional 8th field: signed current_ma (decimal integer)
256+
// Optional 9th field: signed power_mw (decimal integer)
257+
pkt->power_mode = '?';
258+
pkt->current_ma = 0;
259+
pkt->power_mw = 0;
260+
pkt->charging = false;
261+
262+
char *mode_s = strtok_r(NULL, ":\r\n", &sp2);
263+
if (mode_s && mode_s[0] != '\0') {
264+
char m = mode_s[0];
265+
if (m == 'v' || m == 'V' || m == 'i' || m == 'I' || m == 'n' || m == 'N') {
266+
pkt->power_mode = (char)((m >= 'A' && m <= 'Z') ? (m + 32) : m);
267+
}
268+
}
269+
270+
char *curr_s = strtok_r(NULL, ":\r\n", &sp2);
271+
if (curr_s) {
272+
pkt->current_ma = (int32_t)strtol(curr_s, NULL, 10);
273+
}
274+
275+
char *pow_s = strtok_r(NULL, ":\r\n", &sp2);
276+
if (pow_s) {
277+
pkt->power_mw = (int32_t)strtol(pow_s, NULL, 10);
278+
}
279+
280+
// Charging is true only if INA219 mode AND current is negative (current flowing into battery).
281+
pkt->charging = (pkt->power_mode == 'i' && pkt->current_ma < 0);
282+
253283
return true;
254284
}
255285

firmware/receiver-c3/components/lora_rylr998/lora_rylr998.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,14 @@ typedef struct {
4444
int snr; // Signal-to-noise ratio (dB)
4545
bool data_valid; // true if TANK packet parsed successfully
4646
char fw_version[16]; // transmitter firmware version (empty if old TX)
47+
48+
// Power telemetry (since TX firmware v2.0.4) — present only if the TX
49+
// packet includes the optional fields appended after fw_version.
50+
// Old TX firmware that omits these → power_mode='?' and *_ma/*_mw=0.
51+
char power_mode; // 'v'=voltage divider, 'i'=INA219, 'n'=disabled, '?'=unknown/old
52+
int32_t current_ma; // signed; +ve discharge, -ve charge; 0 in voltage mode
53+
int32_t power_mw; // V × I; 0 in voltage mode
54+
bool charging; // explicit flag derived from current sign / unknown for old TX
4755
} lora_rx_packet_t;
4856

4957
// ── Hardware state ────────────────────────────────────────────────────────────

firmware/receiver-c3/components/mqtt_client/mqtt_manager.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,28 @@ void mqtt_publish_tank(int idx) {
235235

236236
make_topic(topic, sizeof(topic), slug, "state");
237237
pub(topic, registry_state_str(data.state), 1);
238+
239+
// Power telemetry (TX v2.0.4+) — published only when TX has reported a real mode.
240+
// '?' (unknown / pre-v2.0.4 TX) is skipped to avoid polluting topics for old hardware.
241+
if (data.power_mode == 'v' || data.power_mode == 'i' || data.power_mode == 'n') {
242+
const char *pmode_str = (data.power_mode == 'i') ? "ina219"
243+
: (data.power_mode == 'v') ? "voltage"
244+
: "none";
245+
make_topic(topic, sizeof(topic), slug, "power_mode");
246+
pub(topic, pmode_str, 1);
247+
248+
snprintf(val, sizeof(val), "%ld", (long)data.current_ma);
249+
make_topic(topic, sizeof(topic), slug, "current_ma");
250+
pub(topic, val, 1);
251+
252+
snprintf(val, sizeof(val), "%ld", (long)data.power_mw);
253+
make_topic(topic, sizeof(topic), slug, "power_mw");
254+
pub(topic, val, 1);
255+
256+
// HA-style binary sensor convention: ON / OFF
257+
make_topic(topic, sizeof(topic), slug, "charging");
258+
pub(topic, data.charging ? "ON" : "OFF", 1);
259+
}
238260
}
239261

240262
void mqtt_publish_system(void) {

firmware/receiver-c3/components/ota_manager/ota_manager.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

2424
// OTA configuration defaults
2525
#ifndef FIRMWARE_VERSION
26-
#define FIRMWARE_VERSION "2.1.0"
26+
#define FIRMWARE_VERSION "2.2.0"
2727
#endif
2828
#ifndef OTA_GITHUB_OWNER
2929
#define OTA_GITHUB_OWNER "Techposts"
@@ -53,7 +53,7 @@ typedef enum {
5353

5454
typedef struct {
5555
ota_status_t status;
56-
char latest_version[32]; // e.g. "2.1.0" (from GitHub tag, strip "v" prefix)
56+
char latest_version[32]; // e.g. "2.2.0" (from GitHub tag, strip "v" prefix)
5757
char download_url[256];
5858
int progress_pct; // 0-100 during download
5959
char error_msg[128];

firmware/receiver-c3/components/transmitter_registry/transmitter_registry.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,8 @@ bool registry_set_enabled(uint16_t addr, bool enabled) {
292292

293293
bool registry_update_data(uint16_t addr, int raw_dist, int bat_pct,
294294
float bat_v, uint32_t msg_id, int rssi, int snr,
295-
const char *fw_version) {
295+
const char *fw_version,
296+
char power_mode, int32_t current_ma, int32_t power_mw) {
296297
LOCK();
297298
int idx = -1;
298299
for (int i = 0; i < s_count; i++) {
@@ -314,6 +315,11 @@ bool registry_update_data(uint16_t addr, int raw_dist, int bat_pct,
314315
s_data[idx].packets_rx++;
315316
s_data[idx].data_valid = true;
316317
s_data[idx].state = TX_STATE_CONNECTED;
318+
s_data[idx].power_mode = (power_mode == 'v' || power_mode == 'i' ||
319+
power_mode == 'n') ? power_mode : '?';
320+
s_data[idx].current_ma = current_ma;
321+
s_data[idx].power_mw = power_mw;
322+
s_data[idx].charging = (s_data[idx].power_mode == 'i' && current_ma < 0);
317323
bool ver_changed = false;
318324
if (fw_version && fw_version[0]) {
319325
if (strcmp(s_data[idx].fw_version, fw_version) != 0) {

firmware/receiver-c3/components/transmitter_registry/transmitter_registry.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@ typedef struct {
5858
bool data_valid;
5959
tx_state_t state;
6060
char fw_version[16]; // firmware version reported by transmitter
61+
62+
// Power telemetry (from TX firmware v2.0.4 onward)
63+
char power_mode; // 'v'=voltage, 'i'=ina219, 'n'=disabled, '?'=unknown/old TX
64+
int32_t current_ma; // signed; +ve = discharging, -ve = charging
65+
int32_t power_mw; // V × I; 0 in voltage mode
66+
bool charging; // true if INA219 reports negative current
6167
} tx_data_t;
6268

6369
// ── Registry init ─────────────────────────────────────────────────────────────
@@ -97,9 +103,12 @@ void registry_persist(void);
97103
void registry_get_spiffs_info(size_t *total, size_t *used);
98104

99105
// ── Data update ──────────────────────────────────────────────────────────────
106+
// `power_mode` is a single-char tag from the LoRa packet ('v'/'i'/'n'/'?').
107+
// `current_ma`/`power_mw` are 0 when TX is in voltage mode or absent (old TX).
100108
bool registry_update_data(uint16_t addr, int raw_dist, int bat_pct,
101109
float bat_v, uint32_t msg_id, int rssi, int snr,
102-
const char *fw_version);
110+
const char *fw_version,
111+
char power_mode, int32_t current_ma, int32_t power_mw);
103112

104113
// ── Queries ──────────────────────────────────────────────────────────────────
105114
int registry_count(void);

firmware/receiver-c3/components/web_server/web_server.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,15 @@ static esp_err_t handle_api_data(httpd_req_t *req) {
532532
cJSON_AddNumberToObject(t, "battery_v", data.battery_voltage);
533533
cJSON_AddNumberToObject(t, "rssi", data.rssi);
534534
cJSON_AddStringToObject(t, "state", registry_state_str(data.state));
535+
// Power telemetry (TX firmware v2.0.4+; '?' for older TX firmware)
536+
const char *pmode_str = (data.power_mode == 'i') ? "ina219"
537+
: (data.power_mode == 'v') ? "voltage"
538+
: (data.power_mode == 'n') ? "none"
539+
: "unknown";
540+
cJSON_AddStringToObject(t, "power_mode", pmode_str);
541+
cJSON_AddNumberToObject(t, "current_ma", (double)data.current_ma);
542+
cJSON_AddNumberToObject(t, "power_mw", (double)data.power_mw);
543+
cJSON_AddBoolToObject (t, "charging", data.charging);
535544
int64_t age_s = data.last_update_us > 0
536545
? (esp_timer_get_time() - data.last_update_us) / 1000000LL : -1;
537546
cJSON_AddNumberToObject(t, "last_seen_s", (double)age_s);
@@ -664,6 +673,15 @@ static esp_err_t handle_get_transmitters(httpd_req_t *req) {
664673
cJSON_AddStringToObject(t, "fw_version", data.fw_version[0] ? data.fw_version : "unknown");
665674
cJSON_AddBoolToObject (t, "ota_pending", ota_p);
666675
cJSON_AddNumberToObject(t, "ota_offset", ota_o);
676+
// Power telemetry (TX firmware v2.0.4+; '?' for older TX firmware)
677+
const char *pmode_str = (data.power_mode == 'i') ? "ina219"
678+
: (data.power_mode == 'v') ? "voltage"
679+
: (data.power_mode == 'n') ? "none"
680+
: "unknown";
681+
cJSON_AddStringToObject(t, "power_mode", pmode_str);
682+
cJSON_AddNumberToObject(t, "current_ma", (double)data.current_ma);
683+
cJSON_AddNumberToObject(t, "power_mw", (double)data.power_mw);
684+
cJSON_AddBoolToObject (t, "charging", data.charging);
667685
cJSON_AddItemToArray(arr, t);
668686
}
669687
char *json = cJSON_PrintUnformatted(root); cJSON_Delete(root); send_json(req, json); free(json);

firmware/receiver-c3/components/web_server/web_server.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
#define WEB_MAX_SOCKETS 5
3737
#endif
3838
#ifndef FIRMWARE_VERSION
39-
#define FIRMWARE_VERSION "2.1.0"
39+
#define FIRMWARE_VERSION "2.2.0"
4040
#endif
4141
#ifndef TANKSYNC_CLOUD_URL
4242
#define TANKSYNC_CLOUD_URL "https://your-tanksync-server.example.com"

firmware/receiver-c3/main/config.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
// ============================================================================
1212
// FIRMWARE VERSION - update this on every release
1313
// ============================================================================
14-
#define FIRMWARE_VERSION "2.1.0"
14+
#define FIRMWARE_VERSION "2.2.0"
1515
#define FIRMWARE_TYPE "receiver"
1616

1717
// ============================================================================

0 commit comments

Comments
 (0)