Added LIPO 2 Reading
This commit is contained in:
@@ -52,7 +52,7 @@ Zielbild für den Host: Befehle an den Master senden; der Master steuert Slaves
|
|||||||
| BMA456 Interrupt | 10 | `bosch456.c` |
|
| BMA456 Interrupt | 10 | `bosch456.c` |
|
||||||
| Taster | 12 | `board_input.c` |
|
| Taster | 12 | `board_input.c` |
|
||||||
| LiPo ADC 1 | 1 | `board_input.c` |
|
| LiPo ADC 1 | 1 | `board_input.c` |
|
||||||
| LiPo ADC 2 | 12 | Entfällt wenn = Taster-GPIO |
|
| LiPo ADC 2 | 11 | `board_input.c` |
|
||||||
|
|
||||||
**UART (Host-Protokoll):** `UART_NUM_1`, **921600** Baud, 8N1, kein Flow-Control.
|
**UART (Host-Protokoll):** `UART_NUM_1`, **921600** Baud, 8N1, kein Flow-Control.
|
||||||
|
|
||||||
@@ -405,7 +405,7 @@ Ohne Sensor: `bma456_is_ready() == false`, Firmware läuft weiter.
|
|||||||
`board_input.c`:
|
`board_input.c`:
|
||||||
|
|
||||||
- **Taster** GPIO 12 — Logging bei Druck.
|
- **Taster** GPIO 12 — Logging bei Druck.
|
||||||
- **LiPo-ADC** GPIO 1 (und optional 2, wenn nicht Taster).
|
- **LiPo-ADC** GPIO 1 und GPIO 11.
|
||||||
- Master: `master_monitor_task` aktualisiert Master-Batterie alle 30 s.
|
- Master: `master_monitor_task` aktualisiert Master-Batterie alle 30 s.
|
||||||
- Slave: `slave_send_battery_report_to_master` nach Join, Heartbeat und Query.
|
- Slave: `slave_send_battery_report_to_master` nach Join, Heartbeat und Query.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
win:
|
||||||
|
GOOS=windows GOARCH=386 go build .
|
||||||
+1
-1
@@ -633,7 +633,7 @@ func runBatteryPoller(link *managedSerial, hub *wsHub, interval time.Duration, s
|
|||||||
case <-stop:
|
case <-stop:
|
||||||
return
|
return
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
if hub.clientCount() == 0 {
|
if !link.IsConnected() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
bat, err := link.BatteryStatusPoll(&pb.BatteryStatusRequest{AllClients: true})
|
bat, err := link.BatteryStatusPoll(&pb.BatteryStatusRequest{AllClients: true})
|
||||||
|
|||||||
+23
-9
@@ -769,21 +769,35 @@
|
|||||||
},
|
},
|
||||||
applyBatterySamples(samples) {
|
applyBatterySamples(samples) {
|
||||||
if (!samples?.length) return;
|
if (!samples?.length) return;
|
||||||
|
const master = { ...(this.state.master || {}) };
|
||||||
|
const clients = [...(this.state.clients || [])];
|
||||||
|
let masterChanged = false;
|
||||||
|
let clientsChanged = false;
|
||||||
for (const s of samples) {
|
for (const s of samples) {
|
||||||
if (s.client_id === 0) {
|
if (s.client_id === 0) {
|
||||||
if (!this.state.master) this.state.master = {};
|
master.lipo1 = s.lipo1;
|
||||||
this.state.master.lipo1 = s.lipo1;
|
master.lipo2 = s.lipo2;
|
||||||
this.state.master.lipo2 = s.lipo2;
|
master.battery_age_ms = s.age_ms;
|
||||||
this.state.master.battery_age_ms = s.age_ms;
|
masterChanged = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const c = (this.state.clients || []).find((x) => x.id === s.client_id);
|
const idx = clients.findIndex((x) => x.id === s.client_id);
|
||||||
if (c) {
|
if (idx >= 0) {
|
||||||
c.lipo1 = s.lipo1;
|
clients[idx] = {
|
||||||
c.lipo2 = s.lipo2;
|
...clients[idx],
|
||||||
c.battery_age_ms = s.age_ms;
|
lipo1: s.lipo1,
|
||||||
|
lipo2: s.lipo2,
|
||||||
|
battery_age_ms: s.age_ms,
|
||||||
|
};
|
||||||
|
clientsChanged = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!masterChanged && !clientsChanged) return;
|
||||||
|
this.state = {
|
||||||
|
...this.state,
|
||||||
|
...(masterChanged ? { master } : {}),
|
||||||
|
...(clientsChanged ? { clients } : {}),
|
||||||
|
};
|
||||||
},
|
},
|
||||||
async refreshBattery() {
|
async refreshBattery() {
|
||||||
if (!this.state?.uart_connected) return;
|
if (!this.state?.uart_connected) return;
|
||||||
|
|||||||
+2
-2
@@ -55,7 +55,7 @@ Pins (`powerpod.h`):
|
|||||||
| BMA456 INT | 10 |
|
| BMA456 INT | 10 |
|
||||||
| Button (Taster) | 12 |
|
| Button (Taster) | 12 |
|
||||||
| LiPo sense 1 (ADC) | 1 |
|
| LiPo sense 1 (ADC) | 1 |
|
||||||
| LiPo sense 2 (ADC) | 12 (skipped if same as button) |
|
| LiPo sense 2 (ADC) | 11 |
|
||||||
|
|
||||||
> **TODO:** GPIO assignments above are provisional; confirm pinning against the real board before release.
|
> **TODO:** GPIO assignments above are provisional; confirm pinning against the real board before release.
|
||||||
|
|
||||||
@@ -563,7 +563,7 @@ Target: ESP32-S3. Close serial monitor on the UART adapter port before running `
|
|||||||
| `bosch456.c/h` | BMA456H I2C driver, accel poll, on-demand read, tap INT, deadzone filter |
|
| `bosch456.c/h` | BMA456H I2C driver, accel poll, on-demand read, tap INT, deadzone filter |
|
||||||
| `cmd/cmd_tap_notify.c` | UART `TAP_NOTIFY` — ESP-NOW tap notify config |
|
| `cmd/cmd_tap_notify.c` | UART `TAP_NOTIFY` — ESP-NOW tap notify config |
|
||||||
| `cmd/cmd_cache_status.c` | UART `CACHE_STATUS` — subscribed accel + tap cache poll |
|
| `cmd/cmd_cache_status.c` | UART `CACHE_STATUS` — subscribed accel + tap cache poll |
|
||||||
| `board_input.c/h` | Taster GPIO12, LiPo ADC on GPIO1 / GPIO12 |
|
| `board_input.c/h` | Taster GPIO12, LiPo ADC on GPIO1 / GPIO11 |
|
||||||
| `pod_settings.c/h` | NVS persistence (accel deadzone, …) |
|
| `pod_settings.c/h` | NVS persistence (accel deadzone, …) |
|
||||||
| `led_ring.c/h` | LED ring (digit display, progress bar) |
|
| `led_ring.c/h` | LED ring (digit display, progress bar) |
|
||||||
| `cmd/cmd_led_ring.c` | UART `LED_RING` progress command |
|
| `cmd/cmd_led_ring.c` | UART `LED_RING` progress command |
|
||||||
|
|||||||
+43
-33
@@ -1,5 +1,6 @@
|
|||||||
#include "board_input.h"
|
#include "board_input.h"
|
||||||
#include "powerpod.h"
|
#include "powerpod.h"
|
||||||
|
#include "client_registry.h"
|
||||||
#include "driver/gpio.h"
|
#include "driver/gpio.h"
|
||||||
#include "esp_adc/adc_oneshot.h"
|
#include "esp_adc/adc_oneshot.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
@@ -18,34 +19,51 @@ static const char *TAG_LIPO = "[LIPO]";
|
|||||||
#define LIPO_ADC_MAX_RAW 4095
|
#define LIPO_ADC_MAX_RAW 4095
|
||||||
|
|
||||||
static QueueHandle_t s_button_queue;
|
static QueueHandle_t s_button_queue;
|
||||||
static adc_oneshot_unit_handle_t s_adc;
|
|
||||||
static bool s_lipo1_ok;
|
|
||||||
static bool s_lipo2_ok;
|
|
||||||
static adc_channel_t s_lipo1_ch;
|
|
||||||
static adc_channel_t s_lipo2_ch;
|
|
||||||
|
|
||||||
static esp_err_t adc_init_channel(int gpio, adc_channel_t *out_ch, bool *out_ok) {
|
typedef struct {
|
||||||
adc_unit_t unit;
|
adc_oneshot_unit_handle_t unit;
|
||||||
esp_err_t err = adc_oneshot_io_to_channel(gpio, &unit, out_ch);
|
adc_channel_t ch;
|
||||||
|
bool ok;
|
||||||
|
} lipo_adc_t;
|
||||||
|
|
||||||
|
static lipo_adc_t s_lipo1;
|
||||||
|
static lipo_adc_t s_lipo2;
|
||||||
|
|
||||||
|
static esp_err_t adc_init_gpio(int gpio, lipo_adc_t *out) {
|
||||||
|
out->unit = NULL;
|
||||||
|
out->ok = false;
|
||||||
|
|
||||||
|
adc_unit_t unit_id;
|
||||||
|
esp_err_t err = adc_oneshot_io_to_channel(gpio, &unit_id, &out->ch);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGW(TAG_LIPO, "GPIO%d not an ADC channel: %s", gpio, esp_err_to_name(err));
|
ESP_LOGW(TAG_LIPO, "GPIO%d not an ADC channel: %s", gpio, esp_err_to_name(err));
|
||||||
*out_ok = false;
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
if (unit != ADC_UNIT_1) {
|
|
||||||
ESP_LOGW(TAG_LIPO, "GPIO%d on ADC unit %d (expected ADC1)", gpio, (int)unit);
|
adc_oneshot_unit_init_cfg_t init_cfg = {
|
||||||
|
.unit_id = unit_id,
|
||||||
|
};
|
||||||
|
err = adc_oneshot_new_unit(&init_cfg, &out->unit);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGW(TAG_LIPO, "ADC unit %d init GPIO%d failed: %s", (int)unit_id, gpio,
|
||||||
|
esp_err_to_name(err));
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
adc_oneshot_chan_cfg_t chan_cfg = {
|
adc_oneshot_chan_cfg_t chan_cfg = {
|
||||||
.atten = ADC_ATTEN_DB_12,
|
.atten = ADC_ATTEN_DB_12,
|
||||||
.bitwidth = ADC_BITWIDTH_DEFAULT,
|
.bitwidth = ADC_BITWIDTH_DEFAULT,
|
||||||
};
|
};
|
||||||
err = adc_oneshot_config_channel(s_adc, *out_ch, &chan_cfg);
|
err = adc_oneshot_config_channel(out->unit, out->ch, &chan_cfg);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGW(TAG_LIPO, "ADC config GPIO%d failed: %s", gpio, esp_err_to_name(err));
|
ESP_LOGW(TAG_LIPO, "ADC config GPIO%d failed: %s", gpio, esp_err_to_name(err));
|
||||||
*out_ok = false;
|
adc_oneshot_del_unit(out->unit);
|
||||||
|
out->unit = NULL;
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
*out_ok = true;
|
|
||||||
|
out->ok = true;
|
||||||
|
ESP_LOGI(TAG_LIPO, "GPIO%d ready (ADC unit %d)", gpio, (int)unit_id);
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,15 +74,15 @@ static uint32_t raw_to_mv(int raw) {
|
|||||||
return (uint32_t)((raw * LIPO_ADC_FULL_SCALE_MV) / LIPO_ADC_MAX_RAW);
|
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,
|
static void sample_one_channel(const lipo_adc_t *adc, uint32_t *mv_out,
|
||||||
bool *valid_out) {
|
bool *valid_out) {
|
||||||
*valid_out = false;
|
*valid_out = false;
|
||||||
*mv_out = 0;
|
*mv_out = 0;
|
||||||
if (!ok || s_adc == NULL) {
|
if (adc == NULL || !adc->ok || adc->unit == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int raw = 0;
|
int raw = 0;
|
||||||
if (adc_oneshot_read(s_adc, ch, &raw) == ESP_OK) {
|
if (adc_oneshot_read(adc->unit, adc->ch, &raw) == ESP_OK) {
|
||||||
*valid_out = true;
|
*valid_out = true;
|
||||||
*mv_out = raw_to_mv(raw);
|
*mv_out = raw_to_mv(raw);
|
||||||
}
|
}
|
||||||
@@ -75,8 +93,8 @@ void board_input_read_lipo(board_lipo_reading_t *out) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
memset(out, 0, sizeof(*out));
|
memset(out, 0, sizeof(*out));
|
||||||
sample_one_channel(s_lipo1_ch, s_lipo1_ok, &out->lipo1_mv, &out->lipo1_valid);
|
sample_one_channel(&s_lipo1, &out->lipo1_mv, &out->lipo1_valid);
|
||||||
sample_one_channel(s_lipo2_ch, s_lipo2_ok, &out->lipo2_mv, &out->lipo2_valid);
|
sample_one_channel(&s_lipo2, &out->lipo2_mv, &out->lipo2_valid);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void lipo_monitor_task(void *param) {
|
static void lipo_monitor_task(void *param) {
|
||||||
@@ -87,6 +105,7 @@ static void lipo_monitor_task(void *param) {
|
|||||||
while (1) {
|
while (1) {
|
||||||
board_lipo_reading_t reading;
|
board_lipo_reading_t reading;
|
||||||
board_input_read_lipo(&reading);
|
board_input_read_lipo(&reading);
|
||||||
|
client_registry_set_master_battery(&reading);
|
||||||
|
|
||||||
ESP_LOGI(TAG_LIPO,
|
ESP_LOGI(TAG_LIPO,
|
||||||
"LIPO1 GPIO%d %s %lu mV LIPO2 GPIO%d %s %lu mV",
|
"LIPO1 GPIO%d %s %lu mV LIPO2 GPIO%d %s %lu mV",
|
||||||
@@ -166,28 +185,19 @@ static esp_err_t init_button(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t init_lipo_adc(void) {
|
static esp_err_t init_lipo_adc(void) {
|
||||||
adc_oneshot_unit_init_cfg_t init_cfg = {
|
memset(&s_lipo1, 0, sizeof(s_lipo1));
|
||||||
.unit_id = ADC_UNIT_1,
|
memset(&s_lipo2, 0, sizeof(s_lipo2));
|
||||||
};
|
|
||||||
esp_err_t err = adc_oneshot_new_unit(&init_cfg, &s_adc);
|
|
||||||
if (err != ESP_OK) {
|
|
||||||
ESP_LOGW(TAG_LIPO, "ADC init failed: %s", esp_err_to_name(err));
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
adc_init_channel(V_LIPO_1_GPIO, &s_lipo1_ch, &s_lipo1_ok);
|
adc_init_gpio(V_LIPO_1_GPIO, &s_lipo1);
|
||||||
|
|
||||||
if (V_LIPO_2_GPIO == TASTER_GPIO) {
|
if (V_LIPO_2_GPIO == TASTER_GPIO) {
|
||||||
ESP_LOGW(TAG_LIPO, "LIPO2 on GPIO%d skipped (button uses same pin)",
|
ESP_LOGW(TAG_LIPO, "LIPO2 on GPIO%d skipped (button uses same pin)",
|
||||||
V_LIPO_2_GPIO);
|
V_LIPO_2_GPIO);
|
||||||
s_lipo2_ok = false;
|
|
||||||
} else {
|
} else {
|
||||||
adc_init_channel(V_LIPO_2_GPIO, &s_lipo2_ch, &s_lipo2_ok);
|
adc_init_gpio(V_LIPO_2_GPIO, &s_lipo2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!s_lipo1_ok && !s_lipo2_ok) {
|
if (!s_lipo1.ok && !s_lipo2.ok) {
|
||||||
adc_oneshot_del_unit(s_adc);
|
|
||||||
s_adc = NULL;
|
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
-10
@@ -33,18 +33,14 @@ static bool append_battery_sample(alox_BatteryStatusResponse *resp,
|
|||||||
return lipo1_valid || lipo2_valid;
|
return lipo1_valid || lipo2_valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool append_master_cached(alox_BatteryStatusResponse *resp) {
|
static bool append_master_sample(alox_BatteryStatusResponse *resp) {
|
||||||
board_lipo_reading_t reading;
|
board_lipo_reading_t reading;
|
||||||
uint32_t age_ms = 0;
|
|
||||||
|
|
||||||
if (!client_registry_get_master_battery(&reading, &age_ms)) {
|
board_input_read_lipo(&reading);
|
||||||
board_input_read_lipo(&reading);
|
client_registry_set_master_battery(&reading);
|
||||||
client_registry_set_master_battery(&reading);
|
|
||||||
age_ms = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return append_battery_sample(resp, 0, reading.lipo1_valid, reading.lipo1_mv,
|
return append_battery_sample(resp, 0, reading.lipo1_valid, reading.lipo1_mv,
|
||||||
reading.lipo2_valid, reading.lipo2_mv, age_ms);
|
reading.lipo2_valid, reading.lipo2_mv, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool append_slave_cached(alox_BatteryStatusResponse *resp,
|
static bool append_slave_cached(alox_BatteryStatusResponse *resp,
|
||||||
@@ -102,7 +98,7 @@ static void handle_battery_status(const uint8_t *data, size_t len) {
|
|||||||
bool any = false;
|
bool any = false;
|
||||||
|
|
||||||
if (req.all_clients) {
|
if (req.all_clients) {
|
||||||
any |= append_master_cached(resp);
|
any |= append_master_sample(resp);
|
||||||
for (size_t i = 0; i < client_registry_count(); i++) {
|
for (size_t i = 0; i < client_registry_count(); i++) {
|
||||||
const client_info_t *client = client_registry_at(i);
|
const client_info_t *client = client_registry_at(i);
|
||||||
if (client == NULL) {
|
if (client == NULL) {
|
||||||
@@ -113,7 +109,7 @@ static void handle_battery_status(const uint8_t *data, size_t len) {
|
|||||||
ESP_LOGI(TAG, "battery cache all_clients → %u samples",
|
ESP_LOGI(TAG, "battery cache all_clients → %u samples",
|
||||||
(unsigned)resp->samples_count);
|
(unsigned)resp->samples_count);
|
||||||
} else if (req.client_id == 0) {
|
} else if (req.client_id == 0) {
|
||||||
any = append_master_cached(resp);
|
any = append_master_sample(resp);
|
||||||
ESP_LOGI(TAG, "battery cache master");
|
ESP_LOGI(TAG, "battery cache master");
|
||||||
} else {
|
} else {
|
||||||
const client_info_t *client = client_registry_find_by_id(req.client_id);
|
const client_info_t *client = client_registry_find_by_id(req.client_id);
|
||||||
|
|||||||
+2
-3
@@ -11,9 +11,8 @@
|
|||||||
/** Front-panel button (active low, internal pull-up). */
|
/** Front-panel button (active low, internal pull-up). */
|
||||||
#define TASTER_GPIO 12
|
#define TASTER_GPIO 12
|
||||||
|
|
||||||
/** LiPo voltage sense inputs (ADC1-capable GPIOs). */
|
/** LiPo voltage sense inputs (ADC-capable GPIOs; GPIO11 = ADC2 on ESP32-S3). */
|
||||||
#define V_LIPO_1_GPIO 1
|
#define V_LIPO_1_GPIO 1
|
||||||
/** Shares GPIO with TASTER on current bench wiring; second ADC is skipped in board_input.c. */
|
#define V_LIPO_2_GPIO 11
|
||||||
#define V_LIPO_2_GPIO 12
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user