powerpods/main/board_input.c
simon 1ad527119d Add board input driver for button and LiPo ADC logging.
TODO: Hardware pinning in powerpod.h (TASTER_GPIO, V_LIPO_1/2_GPIO) does not
match final hardware yet — verify against schematic before production.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-19 00:16:40 +02:00

194 lines
4.8 KiB
C

#include "board_input.h"
#include "powerpod.h"
#include "driver/gpio.h"
#include "esp_adc/adc_oneshot.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/idf_additions.h"
#include "freertos/queue.h"
#include <stdint.h>
static const char *TAG_BTN = "[BTN]";
static const char *TAG_LIPO = "[LIPO]";
#define LIPO_SAMPLE_INTERVAL_MS 10000
#define BUTTON_QUEUE_LEN 4
#define BUTTON_DEBOUNCE_MS 80
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) {
adc_unit_t unit;
esp_err_t err = adc_oneshot_io_to_channel(gpio, &unit, out_ch);
if (err != ESP_OK) {
ESP_LOGW(TAG_LIPO, "GPIO%d not an ADC channel: %s", gpio, esp_err_to_name(err));
*out_ok = false;
return err;
}
if (unit != ADC_UNIT_1) {
ESP_LOGW(TAG_LIPO, "GPIO%d on ADC unit %d (expected ADC1)", gpio, (int)unit);
}
adc_oneshot_chan_cfg_t chan_cfg = {
.atten = ADC_ATTEN_DB_12,
.bitwidth = ADC_BITWIDTH_DEFAULT,
};
err = adc_oneshot_config_channel(s_adc, *out_ch, &chan_cfg);
if (err != ESP_OK) {
ESP_LOGW(TAG_LIPO, "ADC config GPIO%d failed: %s", gpio, esp_err_to_name(err));
*out_ok = false;
return err;
}
*out_ok = true;
return ESP_OK;
}
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;
}
}
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);
vTaskDelay(pdMS_TO_TICKS(LIPO_SAMPLE_INTERVAL_MS));
}
}
static void IRAM_ATTR button_isr(void *arg) {
(void)arg;
uint8_t one = 1;
BaseType_t wake = pdFALSE;
if (s_button_queue != NULL) {
xQueueSendFromISR(s_button_queue, &one, &wake);
if (wake) {
portYIELD_FROM_ISR();
}
}
}
static void button_task(void *param) {
(void)param;
uint8_t evt;
while (1) {
if (xQueueReceive(s_button_queue, &evt, portMAX_DELAY) != pdTRUE) {
continue;
}
vTaskDelay(pdMS_TO_TICKS(BUTTON_DEBOUNCE_MS));
if (gpio_get_level(TASTER_GPIO) == 0) {
ESP_LOGI(TAG_BTN, "pressed (GPIO%d)", TASTER_GPIO);
}
}
}
static esp_err_t init_button(void) {
if (V_LIPO_2_GPIO == TASTER_GPIO) {
ESP_LOGW(TAG_BTN,
"GPIO%d shared with V_LIPO_2 — button only, no ADC on that pin",
TASTER_GPIO);
}
s_button_queue = xQueueCreate(BUTTON_QUEUE_LEN, sizeof(uint8_t));
if (s_button_queue == NULL) {
return ESP_ERR_NO_MEM;
}
gpio_config_t cfg = {
.pin_bit_mask = 1ULL << TASTER_GPIO,
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_NEGEDGE,
};
ESP_ERROR_CHECK(gpio_config(&cfg));
esp_err_t err = gpio_install_isr_service(0);
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
return err;
}
err = gpio_isr_handler_add(TASTER_GPIO, button_isr, NULL);
if (err != ESP_OK) {
return err;
}
if (xTaskCreate(button_task, "btn", 2048, NULL, 2, NULL) != pdPASS) {
return ESP_ERR_NO_MEM;
}
ESP_LOGI(TAG_BTN, "ready GPIO%d (active low)", TASTER_GPIO);
return ESP_OK;
}
static esp_err_t init_lipo_adc(void) {
adc_oneshot_unit_init_cfg_t init_cfg = {
.unit_id = ADC_UNIT_1,
};
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);
if (V_LIPO_2_GPIO == TASTER_GPIO) {
ESP_LOGW(TAG_LIPO, "LIPO2 on GPIO%d skipped (button uses same pin)",
V_LIPO_2_GPIO);
s_lipo2_ok = false;
} else {
adc_init_channel(V_LIPO_2_GPIO, &s_lipo2_ch, &s_lipo2_ok);
}
if (!s_lipo1_ok && !s_lipo2_ok) {
adc_oneshot_del_unit(s_adc);
s_adc = NULL;
return ESP_FAIL;
}
if (xTaskCreate(lipo_monitor_task, "lipo_mon", 3072, NULL, 1, NULL) != pdPASS) {
return ESP_ERR_NO_MEM;
}
return ESP_OK;
}
esp_err_t board_input_init(void) {
esp_err_t err = init_button();
if (err != ESP_OK) {
ESP_LOGW(TAG_BTN, "init failed: %s", esp_err_to_name(err));
}
err = init_lipo_adc();
if (err != ESP_OK) {
ESP_LOGW(TAG_LIPO, "init failed: %s", esp_err_to_name(err));
}
return ESP_OK;
}