Add LiPo battery monitoring with ESP-NOW cache and dashboard API.
Slaves report pack voltages every 30s; the master caches them for fast BATTERY_STATUS reads. goTool exposes REST/WebSocket and shows values in the dashboard, with a nanopb fix so optional lipo submessages encode. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+40
-20
@@ -6,7 +6,7 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/idf_additions.h"
|
||||
#include "freertos/queue.h"
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
static const char *TAG_BTN = "[BTN]";
|
||||
static const char *TAG_LIPO = "[LIPO]";
|
||||
@@ -14,6 +14,8 @@ static const char *TAG_LIPO = "[LIPO]";
|
||||
#define LIPO_SAMPLE_INTERVAL_MS 10000
|
||||
#define BUTTON_QUEUE_LEN 4
|
||||
#define BUTTON_DEBOUNCE_MS 80
|
||||
#define LIPO_ADC_FULL_SCALE_MV 3300
|
||||
#define LIPO_ADC_MAX_RAW 4095
|
||||
|
||||
static QueueHandle_t s_button_queue;
|
||||
static adc_oneshot_unit_handle_t s_adc;
|
||||
@@ -47,33 +49,51 @@ static esp_err_t adc_init_channel(int gpio, adc_channel_t *out_ch, bool *out_ok)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static uint32_t raw_to_mv(int raw) {
|
||||
if (raw < 0) {
|
||||
return 0;
|
||||
}
|
||||
return (uint32_t)((raw * LIPO_ADC_FULL_SCALE_MV) / LIPO_ADC_MAX_RAW);
|
||||
}
|
||||
|
||||
static void sample_one_channel(adc_channel_t ch, bool ok, uint32_t *mv_out,
|
||||
bool *valid_out) {
|
||||
*valid_out = false;
|
||||
*mv_out = 0;
|
||||
if (!ok || s_adc == NULL) {
|
||||
return;
|
||||
}
|
||||
int raw = 0;
|
||||
if (adc_oneshot_read(s_adc, ch, &raw) == ESP_OK) {
|
||||
*valid_out = true;
|
||||
*mv_out = raw_to_mv(raw);
|
||||
}
|
||||
}
|
||||
|
||||
void board_input_read_lipo(board_lipo_reading_t *out) {
|
||||
if (out == NULL) {
|
||||
return;
|
||||
}
|
||||
memset(out, 0, sizeof(*out));
|
||||
sample_one_channel(s_lipo1_ch, s_lipo1_ok, &out->lipo1_mv, &out->lipo1_valid);
|
||||
sample_one_channel(s_lipo2_ch, s_lipo2_ok, &out->lipo2_mv, &out->lipo2_valid);
|
||||
}
|
||||
|
||||
static void lipo_monitor_task(void *param) {
|
||||
(void)param;
|
||||
|
||||
ESP_LOGI(TAG_LIPO, "monitor task (interval %d ms)", LIPO_SAMPLE_INTERVAL_MS);
|
||||
|
||||
while (1) {
|
||||
int raw1 = -1;
|
||||
int raw2 = -1;
|
||||
int mv1 = -1;
|
||||
int mv2 = -1;
|
||||
|
||||
if (s_lipo1_ok) {
|
||||
raw1 = 0;
|
||||
if (adc_oneshot_read(s_adc, s_lipo1_ch, &raw1) == ESP_OK) {
|
||||
mv1 = (raw1 * 3300) / 4095;
|
||||
}
|
||||
}
|
||||
if (s_lipo2_ok) {
|
||||
raw2 = 0;
|
||||
if (adc_oneshot_read(s_adc, s_lipo2_ch, &raw2) == ESP_OK) {
|
||||
mv2 = (raw2 * 3300) / 4095;
|
||||
}
|
||||
}
|
||||
board_lipo_reading_t reading;
|
||||
board_input_read_lipo(&reading);
|
||||
|
||||
ESP_LOGI(TAG_LIPO,
|
||||
"LIPO1 GPIO%d raw=%d (~%d mV) LIPO2 GPIO%d raw=%d (~%d mV)",
|
||||
V_LIPO_1_GPIO, raw1, mv1, V_LIPO_2_GPIO, raw2, mv2);
|
||||
"LIPO1 GPIO%d %s %lu mV LIPO2 GPIO%d %s %lu mV",
|
||||
V_LIPO_1_GPIO, reading.lipo1_valid ? "ok" : "n/a",
|
||||
(unsigned long)reading.lipo1_mv, V_LIPO_2_GPIO,
|
||||
reading.lipo2_valid ? "ok" : "n/a",
|
||||
(unsigned long)reading.lipo2_mv);
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(LIPO_SAMPLE_INTERVAL_MS));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user