From 4f5800de9e6cb1cfb32f4cb4f69b4f0958a78e04 Mon Sep 17 00:00:00 2001 From: mooiweertje Date: Sat, 21 Mar 2026 15:18:24 +0100 Subject: [PATCH 1/2] NVS --- .github/workflows/main.yml | 7 +- .vscode/c_cpp_properties.json | 1 - main/CMakeLists.txt | 4 +- main/Kconfig.projbuild | 4 + main/bat.cpp | 45 +++++----- main/main.cpp | 21 +---- main/states/motor_off.cpp | 4 +- main/storage.cpp | 165 ++++++++++++++++++++++------------ main/storage.h | 31 ++++--- main/trip.cpp | 49 ++++------ main/trip.h | 7 +- partitions.csv | 3 +- sdkconfig.defaults | 2 +- sdkconfig.supermini | 32 ++++--- sdkconfig.tesla.10Ah | 65 ++++++++++++++ sdkconfig.tesla.15Ah | 65 ++++++++++++++ sdkconfig.tesla.20Ah | 65 ++++++++++++++ sdkconfig.teslatour.15Ah | 65 ++++++++++++++ 18 files changed, 468 insertions(+), 167 deletions(-) create mode 100644 sdkconfig.tesla.10Ah create mode 100644 sdkconfig.tesla.15Ah create mode 100644 sdkconfig.tesla.20Ah create mode 100644 sdkconfig.teslatour.15Ah diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4066e8f..23817de 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,9 +1,9 @@ name: CI on: push: - branches: [ "master" ] + branches: [ "**" ] pull_request: - branches: [ "master" ] + branches: [ "**" ] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: @@ -38,6 +38,9 @@ jobs: - target: esp32c3 display: cu2 custom: supermini + - target: esp32c3 + display: cu3 + custom: supermini steps: - name: Checkout repo uses: actions/checkout@v3 diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 3e21a85..c6eeaca 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -98,7 +98,6 @@ "${workspaceFolder}/sdk/idf/components/soc/esp32/include", "${workspaceFolder}/sdk/idf/components/soc/include", "${workspaceFolder}/sdk/idf/components/spi_flash/include", - "${workspaceFolder}/sdk/idf/components/spiffs/include", "${workspaceFolder}/sdk/idf/components/tcp_transport/include", "${workspaceFolder}/sdk/idf/components/ulp/ulp_common/include", "${workspaceFolder}/sdk/idf/components/ulp/ulp_common/include/esp32", diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index ab0e6c1..99d4c7c 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,2 +1,4 @@ idf_component_register(SRC_DIRS "." "states" - INCLUDE_DIRS ".") + INCLUDE_DIRS "." + REQUIRES esp32-button driver esp_timer nvs_flash esp_adc +) \ No newline at end of file diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index 085e674..b942e2e 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -87,4 +87,8 @@ menu "Sparta Ion Config" int "The actual battery voltage in mv for full (=100%). For example 42000mv for a 10s battery" default 42000 + config ION_KEEPALIVE + bool "Enable keepalive heartbeat. Will reset the ESP32 when main loop is stuck for more than a minute." + default n + endmenu diff --git a/main/bat.cpp b/main/bat.cpp index ebc61a9..f595d90 100644 --- a/main/bat.cpp +++ b/main/bat.cpp @@ -10,7 +10,7 @@ static const char *TAG = "bat"; // ADC Attenuation // 11DB = 3.55 voltage gain, reference voltage should be around 1100mv, // so max theoretical measurement would be 3905mv, actual/recommended(?) is a lot lower. -#define ADC_ATTEN ADC_ATTEN_DB_11 +#define ADC_ATTEN ADC_ATTEN_DB_12 static bool cali_enable = false; static adc_oneshot_unit_handle_t adc1_handle = NULL; @@ -19,11 +19,12 @@ static adc_cali_handle_t adc1_cali_handle = NULL; // Use a fake value of 27.6v when we don't have ADC. static uint32_t batMv = 27600; -// We try to measure every 100ms, so 100 points gives us 10 seconds history. -static uint8_t history[100]; -static size_t historyIndex = 0; -static size_t historySize = 0; +static uint32_t history; +static uint8_t batPercentage; +// Get lower/upper limit from configuration +static uint32_t emptyMv = CONFIG_ION_ADC_EMPTY_MV; +static uint32_t fullMv = CONFIG_ION_ADC_FULL_MV; static void adc_calibration_init(adc_unit_t unit, adc_atten_t atten) { esp_err_t ret = ESP_FAIL; @@ -85,6 +86,7 @@ void adc_init() { config.atten = ADC_ATTEN; config.bitwidth = ADC_BITWIDTH_DEFAULT; + // Voltage channel ESP_ERROR_CHECK(adc_oneshot_config_channel(adc1_handle, (adc_channel_t)CONFIG_ION_ADC_CHAN, &config)); adc_calibration_init(ADC_UNIT_1, ADC_ATTEN); @@ -108,18 +110,12 @@ uint32_t measureBatMv() { static uint8_t batMvToPercentage(uint32_t batMv) { - // Get lower/upper limit from configuration - uint32_t emptyMv = CONFIG_ION_ADC_EMPTY_MV; - uint32_t fullMv = CONFIG_ION_ADC_FULL_MV; - // Calculate the percentage uint32_t percentage = (batMv < emptyMv) ? 0 : ((batMv - emptyMv) * 100) / (fullMv - emptyMv); // Limit to 0-100 uint8_t batterypercentage = 0; - if (percentage < 0) { - batterypercentage = 0; - } else if (percentage > 100) { + if (percentage > 100) { batterypercentage = 100; } else { batterypercentage = (uint8_t)percentage; @@ -130,11 +126,17 @@ static uint8_t batMvToPercentage(uint32_t batMv) { void measureBat() { batMv = measureBatMv(); - history[historyIndex] = batMvToPercentage(batMv); - historyIndex = (historyIndex + 1) % sizeof(history); - if(historySize < sizeof(history)) { - historySize++; - } + + // This is provided by 'mooiweertje' and is pretty much similar to Simple Exponential Smoothing (https://en.wikipedia.org/wiki/Exponential_smoothing). + // By using an alpha of 1/128, and storing the smoothed value scaled by 128 in history, this can be written very efficiently though, + // and the scaled value allows us to work with integers instead of floating point. + // It should take about 5 x 128 (640) calls to settle on a value (at 99.3%), and we try to measure every 100ms, + // which puts us a bit over 60 seconds. That's quite slow, but for a battery indicator should be ok. + history += batMv; + uint32_t avg = history >> 7; + history -= avg; + + batPercentage = batMvToPercentage(avg); } uint32_t getBatMv() { @@ -142,17 +144,12 @@ uint32_t getBatMv() { } uint8_t getBatPercentage() { - if(historySize == 0) { + if(batMv == 0) { // Use a fake value of 50% when we don't have ADC. return 50; } - uint32_t historyTotal = 0; - for(int index = 0; index < historySize; index++) { - historyTotal += history[index]; - } - - return historyTotal / historySize; + return batPercentage; } void adc_teardown() { diff --git a/main/main.cpp b/main/main.cpp index d319148..299c297 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -134,21 +134,8 @@ static messageHandlingResult handleMotorMessage(ion_state * state) { writeMessage(cmdResp(message.source, MSG_BMS, message.command, payload, sizeof(payload))); return CONTROL_TO_SENDER; } else if(message.type == MSG_CMD_REQ && message.payloadSize == 4 && message.command == CMD_GET_DATA && message.payload[1] == 0x38 && message.payload[3] == 0x3a) { - // GET DATA 9438283a 14:38(Calibration A) 28:3a(Calibration B) - uint8_t payload[] = {0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; // Data (last 10 bytes) to be replaced - - if(calibrationFileExists()) { - if(!readCalibrationData(payload + 1)) { - return CONTROL_TO_SENDER; - } - } else { - // Backup data - // Gold small test: 94 38 4b 13 28 3a 3e 98 ed f3 - uint8_t data[] = {0x94, 0x38, 0x4b, 0x15, 0x28, 0x3a, 0x3e, 0x91, 0x79, 0x50}; // This needs to be good calibration data! - memcpy(payload + 1, data, 10); - } - - writeMessage(cmdResp(message.source, MSG_BMS, message.command, payload, sizeof(payload))); + uint8_t *payload = calibrationLoad(); + writeMessage(cmdResp(message.source, MSG_BMS, message.command, payload, 11)); return CONTROL_TO_SENDER; } else if(message.type == MSG_CMD_REQ && message.payloadSize == 10 && message.command == CMD_PUT_DATA && message.payload[1] == 0xc0 && message.payload[5] == 0xc1) { // PUT DATA c0/c1 @@ -161,7 +148,7 @@ static messageHandlingResult handleMotorMessage(ion_state * state) { return CONTROL_TO_SENDER; } else if(message.type == MSG_CMD_REQ && message.payloadSize == 10 && message.command == CMD_PUT_DATA && message.payload[1] == 0x38 && message.payload[5] == 0x3a) { // PUT DATA 38/3a - if(!writeCalibrationData(message.payload)) { + if(!calibrationSave(message.payload)) { return CONTROL_TO_SENDER; } @@ -229,7 +216,7 @@ static void my_task(void *pvParameter) { adc_init(); #endif - init_spiffs(); + storageInit(); initUart(); diff --git a/main/states/motor_off.cpp b/main/states/motor_off.cpp index 364e2e0..32e65f2 100644 --- a/main/states/motor_off.cpp +++ b/main/states/motor_off.cpp @@ -3,14 +3,14 @@ #include "freertos/event_groups.h" #include "esp_log.h" #include "blink.h" -#include "trip.h" +#include "storage.h" #include "states.h" void toMotorOffState(ion_state * state) { queueBlink(4, 100, 300); - saveDistances(); + batDataSave(); state->state = MOTOR_OFF; state->step = 0; diff --git a/main/storage.cpp b/main/storage.cpp index 8c9570d..7425359 100644 --- a/main/storage.cpp +++ b/main/storage.cpp @@ -1,83 +1,132 @@ -#include -#include "esp_log.h" -#include "esp_spiffs.h" #include "storage.h" +#include "nvs_flash.h" +#include "nvs.h" +#include "esp_log.h" +#include // <-- toegevoegd voor memcpy() static const char *TAG = "storage"; -#define CALIBRATION_FILE "/spiffs/calibration.bin" +#define NVS_NAMESPACE "storage" +#define NVS_KEY_BATDATA "batdata" +#define NVS_KEY_CALIB "calibration" -void init_spiffs() { - ESP_LOGI(TAG, "Initializing SPIFFS"); +// Centrale instantie van batData +static struct batData bat; - esp_vfs_spiffs_conf_t spiffs_conf = {}; - spiffs_conf.base_path = "/spiffs"; - spiffs_conf.partition_label = NULL; - spiffs_conf.max_files = 5; - spiffs_conf.format_if_mount_failed = true; +// ----------------------------------------------------------------------------- +// Initialisatie +// ----------------------------------------------------------------------------- - ESP_ERROR_CHECK(esp_vfs_spiffs_register(&spiffs_conf)); - - size_t total = 0, used = 0; - esp_err_t ret = esp_spiffs_info(spiffs_conf.partition_label, &total, &used); - if(ret != ESP_OK) { - ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s)", esp_err_to_name(ret)); - } else { - ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used); - } - - struct stat st; - if(stat(CALIBRATION_FILE, &st) == 0) { - FILE *fp = fopen(CALIBRATION_FILE, "r"); - if(fp == NULL) { - ESP_LOGE(TAG, "Failed to open calibration file for reading"); - } else { - uint8_t data[10]; - size_t read = fread(data, 1, sizeof(data), fp); - fclose(fp); - ESP_LOGI(TAG, "Calibration file found. Size: %lu, content:", st.st_size); - ESP_LOG_BUFFER_HEX(TAG, data, read); - } +void storageInit(void) +{ + // NVS init + esp_err_t err = nvs_flash_init(); + if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) { + nvs_flash_erase(); + nvs_flash_init(); } -} -bool calibrationFileExists() { - return fileExists(CALIBRATION_FILE); -} + // Defaults voor batData + bat.trip1 = 0; + bat.trip2 = 0; + bat.total = 0; + + bat.percentage = 0; + bat.mv = 0; + bat.mah = 0; -bool readCalibrationData(uint8_t * target) { - return readData(CALIBRATION_FILE, target, 10); + ESP_LOGI(TAG, "Storage initialized"); } -bool writeCalibrationData(uint8_t * source){ - return writeData(CALIBRATION_FILE, source, 10); +// ----------------------------------------------------------------------------- +// batData API +// ----------------------------------------------------------------------------- + +struct batData *batDataGet(void) +{ + return &bat; } -bool fileExists(const char * path) { - struct stat st; - return stat(path, &st) == 0; +bool batDataLoad(void) +{ + nvs_handle_t handle; + if (nvs_open(NVS_NAMESPACE, NVS_READONLY, &handle) != ESP_OK) + return false; + + size_t size = sizeof(bat); + esp_err_t err = nvs_get_blob(handle, NVS_KEY_BATDATA, &bat, &size); + nvs_close(handle); + + return (err == ESP_OK); } -bool readData(const char * path, void * target, size_t size) { - FILE *fp = fopen(path, "r"); - if(fp == NULL) { - ESP_LOGE(TAG, "Failed to open file %s for reading", path); +bool batDataSave(void) +{ + nvs_handle_t handle; + if (nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle) != ESP_OK) + return false; + + esp_err_t err = nvs_set_blob(handle, NVS_KEY_BATDATA, &bat, sizeof(bat)); + if (err != ESP_OK) { + nvs_close(handle); return false; } - fread(target, 1, size, fp); - fclose(fp); - return true; + err = nvs_commit(handle); + nvs_close(handle); + + return (err == ESP_OK); } -bool writeData(const char * path, void * source, size_t size) { - FILE *fp = fopen(path, "w"); - if(fp == NULL) { - ESP_LOGE(TAG, "Failed to open file %s for writing", path); +// ----------------------------------------------------------------------------- +// Calibration API +// ----------------------------------------------------------------------------- + +uint8_t *calibrationLoad(void) +{ + static uint8_t payload[11]; + + // Byte 0 is altijd 0x00 + payload[0] = 0x00; + + // Fallback calibratie (10 bytes) + static const uint8_t fallback[10] = { + 0x94, 0x38, 0x4b, 0x15, 0x28, 0x3a, 0x3e, 0x91, 0x79, 0x50 + }; + + // Probeer calibratie uit NVS te lezen + nvs_handle_t handle; + esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READONLY, &handle); + + if (err == ESP_OK) { + size_t size = 10; + err = nvs_get_blob(handle, NVS_KEY_CALIB, payload + 1, &size); + nvs_close(handle); + + if (err == ESP_OK) { + return payload; // Succesvol geladen + } + } + + // Geen calibratie ? fallback + memcpy(payload + 1, fallback, 10); + return payload; +} + +bool calibrationSave(uint8_t *source) +{ + nvs_handle_t handle; + if (nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle) != ESP_OK) + return false; + + esp_err_t err = nvs_set_blob(handle, NVS_KEY_CALIB, source, 10); + if (err != ESP_OK) { + nvs_close(handle); return false; } - fwrite(source, 1, size, fp); - fclose(fp); - return true; + err = nvs_commit(handle); + nvs_close(handle); + + return (err == ESP_OK); } diff --git a/main/storage.h b/main/storage.h index 65c3a42..495e03c 100644 --- a/main/storage.h +++ b/main/storage.h @@ -1,18 +1,25 @@ #pragma once +#include +#include -#include +struct batData { + uint32_t trip1; + uint32_t trip2; + uint32_t total; -void init_spiffs(); + uint8_t percentage; + uint32_t mv; + uint32_t mah; +}; -// Is a calibration file stored? -bool calibrationFileExists(); +// Init NVS + defaults +void storageInit(void); -// Reads calibration data to the given buffer, data is 10 bytes long -bool readCalibrationData(uint8_t * target); +// batData API +struct batData *batDataGet(void); +bool batDataLoad(void); +bool batDataSave(void); -// Writes calibration data from the given buffer, data is 10 bytes long -bool writeCalibrationData(uint8_t * source); - -bool fileExists(const char * path); -bool readData(const char * path, void * target, size_t size); -bool writeData(const char * path, void * source, size_t size); +// calibration API +uint8_t *calibrationLoad(void); +bool calibrationSave(uint8_t *source); diff --git a/main/trip.cpp b/main/trip.cpp index 2e555dd..48cea6a 100644 --- a/main/trip.cpp +++ b/main/trip.cpp @@ -1,62 +1,49 @@ #include "storage.h" #include "trip.h" -#define DISTANCE_FILE "/spiffs/distance.bin" - -struct distancesStruct { - // Trip-1 in 10m increments - uint32_t trip1; - - // Trip-2 in 10m increments - uint32_t trip2; - - // Total in 10m increments - uint32_t total; -}; - -static distancesStruct distances; +static struct batData *bat; static uint32_t lastDistance = 0; void resetTrip1(uint32_t distance) { - distances.trip1 = distance; + bat->trip1 = distance; } uint32_t getTrip1() { - return distances.trip1; + return bat->trip1; } uint32_t getTrip2() { - return distances.trip2; + return bat->trip2; } uint32_t getTotal() { - return distances.total; + return bat->total; } void distanceUpdate(uint32_t distance) { - if(distance < lastDistance) { - // We expect this only happens when the motor reset (powered off and on). - // Which means the motor started at 0 again. - // We could reset when we know we power off the motor instead, but what if we don't have a relay (or it's broken)? + + if (distance < lastDistance) { + // Motor is opnieuw opgestart ? teller terug naar 0 lastDistance = 0; } uint32_t delta = distance - lastDistance; - distances.trip1 += delta; - distances.trip2 += delta; - distances.total += delta; + bat->trip1 += delta; + bat->trip2 += delta; + bat->total += delta; lastDistance = distance; } void loadDistances() { - if(fileExists(DISTANCE_FILE)) { - readData(DISTANCE_FILE, &distances, sizeof(distances)); - } -} + bat = batDataGet(); -void saveDistances() { - writeData(DISTANCE_FILE, &distances, sizeof(distances)); + if (!batDataLoad()) { + // Defaults als er nog geen data in NVS staat + bat->trip1 = 0; + bat->trip2 = 0; + bat->total = 0; + } } diff --git a/main/trip.h b/main/trip.h index 2506aed..98f4707 100644 --- a/main/trip.h +++ b/main/trip.h @@ -1,6 +1,6 @@ #pragma once -#include +#include // To reset on long press mode button void resetTrip1(uint32_t distance); @@ -18,7 +18,4 @@ uint32_t getTotal(); void distanceUpdate(uint32_t distance); // Load distances from flash -void loadDistances(); - -// Write distances to flash -void saveDistances(); +void loadDistances(); \ No newline at end of file diff --git a/partitions.csv b/partitions.csv index 7fbc28b..1a85d02 100644 --- a/partitions.csv +++ b/partitions.csv @@ -1,5 +1,4 @@ # Name, Type, SubType, Offset, Size, Flags -nvs, data, nvs, , 0x4000, +nvs, data, nvs, , 0x10000, phy_init, data, phy, , 0x1000, factory, app, factory, , 1M, -spiffs, data, spiffs, , 1M, diff --git a/sdkconfig.defaults b/sdkconfig.defaults index 6d5b1e8..9a07c8f 100644 --- a/sdkconfig.defaults +++ b/sdkconfig.defaults @@ -4,7 +4,7 @@ CONFIG_ION_CU3=y # My chips are 4MB flash, once you use OTA this settings matters CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y -# Set up partitions for SPIFFS +# Set up partitions CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" diff --git a/sdkconfig.supermini b/sdkconfig.supermini index 7b1ba63..a2c83ed 100644 --- a/sdkconfig.supermini +++ b/sdkconfig.supermini @@ -20,25 +20,35 @@ CONFIG_ION_UART=1 CONFIG_ION_RXD=20 CONFIG_ION_TXD=21 -CONFIG_ION_LIGHT=n -CONFIG_ION_LIGHT_PIN=1 -CONFIG_ION_LIGHT_PIN_INVERTED=n - -CONFIG_ION_RELAY=n +CONFIG_ION_RELAY=y CONFIG_ION_RELAY_PIN=0 CONFIG_ION_RELAY_PIN_INVERTED=n +CONFIG_ION_LIGHT=y +CONFIG_ION_LIGHT_PIN=3 +CONFIG_ION_LIGHT_PIN_INVERTED=n + CONFIG_ESP32C3_DEFAULT_CPU_FREQ_160=y +# --- ADC measuring --- +# For ESP32-C3 +# ADC channel 0 is pin 0 +# ADC channel 1 is pin 1 +# ADC channel 2 is pin 2 +# ADC channel 3 is pin 3 +# ADC channel 4 is pin 4 CONFIG_ION_ADC=y -# ADC channel 1 is pin 4 -CONFIG_ION_ADC_CHAN=4 + +# --- Voltage measuring --- +# ADC channel 1 is pin 2 +CONFIG_ION_ADC_CHAN=2 # The scale of the divider, times 1000, from APM power module CONFIG_ION_DIVIDER_SCALE=10829 -# Consider 18V (3.0V/cell for 6s) empty -CONFIG_ION_ADC_EMPTY_MV=18000 +# Consider 23V (2,875V/cell for 8s LiFePo4) empty +CONFIG_ION_ADC_EMPTY_MV=23000 + +# Consider 27V (3,375V/cell for 8s LiFePo4) full +CONFIG_ION_ADC_FULL_MV=27000 -# Consider 25.2V (4.2V/cell for 6s) full -CONFIG_ION_ADC_FULL_MV=25200 diff --git a/sdkconfig.tesla.10Ah b/sdkconfig.tesla.10Ah new file mode 100644 index 0000000..6c4d2c7 --- /dev/null +++ b/sdkconfig.tesla.10Ah @@ -0,0 +1,65 @@ +# Configuration for use with ESP32 C3 supermini and APM power module +# https://nl.aliexpress.com/item/1005006170575141.html +# https://nl.aliexpress.com/item/1005005831224524.html + +# Pins: +# 1(06) - GND - GND +# 2(04) - GP4 - IO4 > Optional ADC use for battery level, voltage +# 3(06) - GP5 - IO5 > Optional ADC use for battery level, current +# 4(00) - GP0 - IO0 > Motor on/off (active low) +# 5(01) - GP1 - IO1 > Light on/off (active high) + jumper for debug +# 6(20) - RXD - RX > Bus send +# 7(21) - TXD - TX > Bus receive +# 8(07) - VCC - 5V + + +CONFIG_ION_BUTTON=n +CONFIG_ION_LED_PIN=8 + +CONFIG_ION_UART=1 +CONFIG_ION_RXD=20 +CONFIG_ION_TXD=21 + +CONFIG_ION_RELAY=y +CONFIG_ION_RELAY_PIN=0 +CONFIG_ION_RELAY_PIN_INVERTED=n + +CONFIG_ION_LIGHT=y +CONFIG_ION_LIGHT_PIN=3 +CONFIG_ION_LIGHT_PIN_INVERTED=n + +CONFIG_ESP32C3_DEFAULT_CPU_FREQ_160=y + +# --- ADC measuring --- +# For ESP32-C3 +# ADC channel 0 is pin 0 +# ADC channel 1 is pin 1 +# ADC channel 2 is pin 2 +# ADC channel 3 is pin 3 +# ADC channel 4 is pin 4 +CONFIG_ION_ADC=y + +# --- Voltage measuring --- +# ADC channel 1 is pin 2 +CONFIG_ION_ADC_CHAN=2 + +# The scale of the divider, times 1000, from APM power module +CONFIG_ION_DIVIDER_SCALE=10829 + +# Consider 23V (2,875V/cell for 8s LiFePo4) empty +CONFIG_ION_ADC_EMPTY_MV=23000 + +# Consider 27V (3,375V/cell for 8s LiFePo4) full +CONFIG_ION_ADC_FULL_MV=27000 + +# --- Current measuring --- +CONFIG_ION_CURR_ADC=y + +# ADC channel 2 is pin 1 +CONFIG_ION_CURR_ADC_CHAN=1 + +# Full battery charge in mAh. +CONFIG_ION_BAT_CHARGE=10000 + +# keepalive +CONFIG_ION_KEEPALIVE=y diff --git a/sdkconfig.tesla.15Ah b/sdkconfig.tesla.15Ah new file mode 100644 index 0000000..c6ffe83 --- /dev/null +++ b/sdkconfig.tesla.15Ah @@ -0,0 +1,65 @@ +# Configuration for use with ESP32 C3 supermini and APM power module +# https://nl.aliexpress.com/item/1005006170575141.html +# https://nl.aliexpress.com/item/1005005831224524.html + +# Pins: +# 1(06) - GND - GND +# 2(04) - GP4 - IO4 > Optional ADC use for battery level, voltage +# 3(06) - GP5 - IO5 > Optional ADC use for battery level, current +# 4(00) - GP0 - IO0 > Motor on/off (active low) +# 5(01) - GP1 - IO1 > Light on/off (active high) + jumper for debug +# 6(20) - RXD - RX > Bus send +# 7(21) - TXD - TX > Bus receive +# 8(07) - VCC - 5V + + +CONFIG_ION_BUTTON=n +CONFIG_ION_LED_PIN=8 + +CONFIG_ION_UART=1 +CONFIG_ION_RXD=20 +CONFIG_ION_TXD=21 + +CONFIG_ION_RELAY=y +CONFIG_ION_RELAY_PIN=0 +CONFIG_ION_RELAY_PIN_INVERTED=n + +CONFIG_ION_LIGHT=y +CONFIG_ION_LIGHT_PIN=3 +CONFIG_ION_LIGHT_PIN_INVERTED=n + +CONFIG_ESP32C3_DEFAULT_CPU_FREQ_160=y + +# --- ADC measuring --- +# For ESP32-C3 +# ADC channel 0 is pin 0 +# ADC channel 1 is pin 1 +# ADC channel 2 is pin 2 +# ADC channel 3 is pin 3 +# ADC channel 4 is pin 4 +CONFIG_ION_ADC=y + +# --- Voltage measuring --- +# ADC channel 1 is pin 2 +CONFIG_ION_ADC_CHAN=2 + +# The scale of the divider, times 1000, from APM power module +CONFIG_ION_DIVIDER_SCALE=10829 + +# Consider 23V (2,875V/cell for 8s LiFePo4) empty +CONFIG_ION_ADC_EMPTY_MV=23000 + +# Consider 27V (3,375V/cell for 8s LiFePo4) full +CONFIG_ION_ADC_FULL_MV=27000 + +# --- Current measuring --- +CONFIG_ION_CURR_ADC=y + +# ADC channel 2 is pin 1 +CONFIG_ION_CURR_ADC_CHAN=1 + +# Full battery charge in mAh. +CONFIG_ION_BAT_CHARGE=15000 + +# keepalive +CONFIG_ION_KEEPALIVE=y diff --git a/sdkconfig.tesla.20Ah b/sdkconfig.tesla.20Ah new file mode 100644 index 0000000..33b4881 --- /dev/null +++ b/sdkconfig.tesla.20Ah @@ -0,0 +1,65 @@ +# Configuration for use with ESP32 C3 supermini and APM power module +# https://nl.aliexpress.com/item/1005006170575141.html +# https://nl.aliexpress.com/item/1005005831224524.html + +# Pins: +# 1(06) - GND - GND +# 2(04) - GP4 - IO4 > Optional ADC use for battery level, voltage +# 3(06) - GP5 - IO5 > Optional ADC use for battery level, current +# 4(00) - GP0 - IO0 > Motor on/off (active low) +# 5(01) - GP1 - IO1 > Light on/off (active high) + jumper for debug +# 6(20) - RXD - RX > Bus send +# 7(21) - TXD - TX > Bus receive +# 8(07) - VCC - 5V + + +CONFIG_ION_BUTTON=n +CONFIG_ION_LED_PIN=8 + +CONFIG_ION_UART=1 +CONFIG_ION_RXD=20 +CONFIG_ION_TXD=21 + +CONFIG_ION_RELAY=y +CONFIG_ION_RELAY_PIN=0 +CONFIG_ION_RELAY_PIN_INVERTED=n + +CONFIG_ION_LIGHT=y +CONFIG_ION_LIGHT_PIN=3 +CONFIG_ION_LIGHT_PIN_INVERTED=n + +CONFIG_ESP32C3_DEFAULT_CPU_FREQ_160=y + +# --- ADC measuring --- +# For ESP32-C3 +# ADC channel 0 is pin 0 +# ADC channel 1 is pin 1 +# ADC channel 2 is pin 2 +# ADC channel 3 is pin 3 +# ADC channel 4 is pin 4 +CONFIG_ION_ADC=y + +# --- Voltage measuring --- +# ADC channel 1 is pin 2 +CONFIG_ION_ADC_CHAN=2 + +# The scale of the divider, times 1000, from APM power module +CONFIG_ION_DIVIDER_SCALE=10829 + +# Consider 23V (2,875V/cell for 8s LiFePo4) empty +CONFIG_ION_ADC_EMPTY_MV=23000 + +# Consider 27V (3,375V/cell for 8s LiFePo4) full +CONFIG_ION_ADC_FULL_MV=27000 + +# --- Current measuring --- +CONFIG_ION_CURR_ADC=y + +# ADC channel 2 is pin 1 +CONFIG_ION_CURR_ADC_CHAN=1 + +# Full battery charge in mAh. +CONFIG_ION_BAT_CHARGE=20000 + +# keepalive +CONFIG_ION_KEEPALIVE=y diff --git a/sdkconfig.teslatour.15Ah b/sdkconfig.teslatour.15Ah new file mode 100644 index 0000000..c6ffe83 --- /dev/null +++ b/sdkconfig.teslatour.15Ah @@ -0,0 +1,65 @@ +# Configuration for use with ESP32 C3 supermini and APM power module +# https://nl.aliexpress.com/item/1005006170575141.html +# https://nl.aliexpress.com/item/1005005831224524.html + +# Pins: +# 1(06) - GND - GND +# 2(04) - GP4 - IO4 > Optional ADC use for battery level, voltage +# 3(06) - GP5 - IO5 > Optional ADC use for battery level, current +# 4(00) - GP0 - IO0 > Motor on/off (active low) +# 5(01) - GP1 - IO1 > Light on/off (active high) + jumper for debug +# 6(20) - RXD - RX > Bus send +# 7(21) - TXD - TX > Bus receive +# 8(07) - VCC - 5V + + +CONFIG_ION_BUTTON=n +CONFIG_ION_LED_PIN=8 + +CONFIG_ION_UART=1 +CONFIG_ION_RXD=20 +CONFIG_ION_TXD=21 + +CONFIG_ION_RELAY=y +CONFIG_ION_RELAY_PIN=0 +CONFIG_ION_RELAY_PIN_INVERTED=n + +CONFIG_ION_LIGHT=y +CONFIG_ION_LIGHT_PIN=3 +CONFIG_ION_LIGHT_PIN_INVERTED=n + +CONFIG_ESP32C3_DEFAULT_CPU_FREQ_160=y + +# --- ADC measuring --- +# For ESP32-C3 +# ADC channel 0 is pin 0 +# ADC channel 1 is pin 1 +# ADC channel 2 is pin 2 +# ADC channel 3 is pin 3 +# ADC channel 4 is pin 4 +CONFIG_ION_ADC=y + +# --- Voltage measuring --- +# ADC channel 1 is pin 2 +CONFIG_ION_ADC_CHAN=2 + +# The scale of the divider, times 1000, from APM power module +CONFIG_ION_DIVIDER_SCALE=10829 + +# Consider 23V (2,875V/cell for 8s LiFePo4) empty +CONFIG_ION_ADC_EMPTY_MV=23000 + +# Consider 27V (3,375V/cell for 8s LiFePo4) full +CONFIG_ION_ADC_FULL_MV=27000 + +# --- Current measuring --- +CONFIG_ION_CURR_ADC=y + +# ADC channel 2 is pin 1 +CONFIG_ION_CURR_ADC_CHAN=1 + +# Full battery charge in mAh. +CONFIG_ION_BAT_CHARGE=15000 + +# keepalive +CONFIG_ION_KEEPALIVE=y From a75882f8ef3f880a340af984d4bccd077c18ddc3 Mon Sep 17 00:00:00 2001 From: mooiweertje Date: Sat, 21 Mar 2026 16:17:30 +0100 Subject: [PATCH 2/2] keepalive heartbeat --- main/main.cpp | 23 +++++++++++++++++++++++ sdkconfig.supermini | 3 +++ 2 files changed, 26 insertions(+) diff --git a/main/main.cpp b/main/main.cpp index 299c297..fbacd55 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -77,6 +77,20 @@ static TimerHandle_t measureBatTimer; static void measureBatTimerCallback(TimerHandle_t xTimer) { xEventGroupSetBits(controlEventGroup, MEASURE_BAT_BIT); } +#if CONFIG_ION_KEEPALIVE +volatile bool myTaskAlive = false; +TimerHandle_t healthCheckTimer ; + +static void checkMyTaskHealth(TimerHandle_t xTimer) { + if (!myTaskAlive) { + batDataSave(); + esp_restart(); + } + myTaskAlive = false; // Reset voor volgende check +} +#endif + + static messageHandlingResult handleMotorMessage(ion_state * state) { messageType message = {}; readResult result; @@ -238,6 +252,11 @@ static void my_task(void *pvParameter) { measureBatTimer = xTimerCreate("measureBatTimer", (100 / portTICK_PERIOD_MS), pdTRUE, (void *)0, measureBatTimerCallback); xTimerStart(measureBatTimer, 0); + +#if CONFIG_ION_KEEPALIVE + healthCheckTimer = xTimerCreate("healthCheckTimer", 60000 / portTICK_PERIOD_MS, pdTRUE, NULL, checkMyTaskHealth); + xTimerStart(healthCheckTimer, 0); +#endif ion_state state = { .state = IDLE, @@ -253,6 +272,10 @@ static void my_task(void *pvParameter) { while(true) { +#if CONFIG_ION_KEEPALIVE + myTaskAlive = true; // sign of life +#endif + // TODO: // More use of timeouts // See if we really need 8k stack (copying message structure a lot I guess) diff --git a/sdkconfig.supermini b/sdkconfig.supermini index a2c83ed..9c56603 100644 --- a/sdkconfig.supermini +++ b/sdkconfig.supermini @@ -52,3 +52,6 @@ CONFIG_ION_ADC_EMPTY_MV=23000 # Consider 27V (3,375V/cell for 8s LiFePo4) full CONFIG_ION_ADC_FULL_MV=27000 + +# keepalive +CONFIG_ION_KEEPALIVE=y \ No newline at end of file