From d6ee17045d326d7a6bd18bb14399ddefc623df57 Mon Sep 17 00:00:00 2001 From: mooiweertje Date: Tue, 30 Apr 2024 23:55:20 +0200 Subject: [PATCH 01/34] supermini --- .github/workflows/main.yml | 3 +++ sdkconfig.supermini | 44 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 sdkconfig.supermini diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e8e962a..c724d24 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -35,6 +35,9 @@ jobs: - target: esp32 display: cu3 custom: dvjcodec + - target: esp32c3 + display: cu2 + custom: supermini steps: - name: Checkout repo uses: actions/checkout@v3 diff --git a/sdkconfig.supermini b/sdkconfig.supermini new file mode 100644 index 0000000..3f79229 --- /dev/null +++ b/sdkconfig.supermini @@ -0,0 +1,44 @@ +# 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) - GP2 - IO2 > Optional ADC use for battery level, voltage +# 3(06) - GP2 - IO2 > Optional ADC use for battery level, current +# 4(??) - GP0 - IO9 > Motor on/off (active low) +# 5(??) - TXD - TX > 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_LIGHT=n +# CONFIG_ION_LIGHT_PIN=21 +# CONFIG_ION_LIGHT_PIN_INVERTED=n + +CONFIG_ION_RELAY=n +# CONFIG_ION_RELAY_PIN=9 +# CONFIG_ION_RELAY_PIN_INVERTED=y + +CONFIG_ESP32C3_DEFAULT_CPU_FREQ_160=y + +CONFIG_ION_ADC=y +# ADC channel 2 is pin 2 +CONFIG_ION_ADC_CHAN=4 + +# The scale of the divider, times 1000, from APM power module +CONFIG_ION_DIVIDER_SCALE=10829 + +# Consider 20V (3.2V/cell for 6s) empty +CONFIG_ION_ADC_EMPTY_MV=20000 + +# Consider 25.2V (4.2V/cell for 6s) full +CONFIG_ION_ADC_FULL_MV=25200 From 68ad22b195d15d63ffdb25bdcc62b225a39c5325 Mon Sep 17 00:00:00 2001 From: mooiweertje Date: Mon, 13 May 2024 16:19:03 +0200 Subject: [PATCH 02/34] supermini --- sdkconfig.supermini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdkconfig.supermini b/sdkconfig.supermini index 3f79229..1e97e28 100644 --- a/sdkconfig.supermini +++ b/sdkconfig.supermini @@ -37,8 +37,8 @@ CONFIG_ION_ADC_CHAN=4 # The scale of the divider, times 1000, from APM power module CONFIG_ION_DIVIDER_SCALE=10829 -# Consider 20V (3.2V/cell for 6s) empty -CONFIG_ION_ADC_EMPTY_MV=20000 +# Consider 18V (3.0V/cell for 6s) empty +CONFIG_ION_ADC_EMPTY_MV=18000 # Consider 25.2V (4.2V/cell for 6s) full CONFIG_ION_ADC_FULL_MV=25200 From 9f049a0bc3b7f201db4ba3748d28c1196d5529f2 Mon Sep 17 00:00:00 2001 From: mooiweertje Date: Mon, 20 May 2024 15:10:00 +0200 Subject: [PATCH 03/34] reset distance on mode long press --- main/main.cpp | 4 ++++ main/trip.cpp | 4 ++++ main/trip.h | 3 +++ 3 files changed, 11 insertions(+) diff --git a/main/main.cpp b/main/main.cpp index 634edb0..d319148 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -297,6 +297,10 @@ static void my_task(void *pvParameter) { requestDisplayUpdate(); } + if(modeLongPress) { + resetTrip1(0); + requestDisplayUpdate(); + } #if CONFIG_ION_ADC EventBits_t bitsToCheck = MEASURE_BAT_BIT; diff --git a/main/trip.cpp b/main/trip.cpp index 7061e9c..2e555dd 100644 --- a/main/trip.cpp +++ b/main/trip.cpp @@ -18,6 +18,10 @@ static distancesStruct distances; static uint32_t lastDistance = 0; +void resetTrip1(uint32_t distance) { + distances.trip1 = distance; +} + uint32_t getTrip1() { return distances.trip1; } diff --git a/main/trip.h b/main/trip.h index d094962..2506aed 100644 --- a/main/trip.h +++ b/main/trip.h @@ -2,6 +2,9 @@ #include +// To reset on long press mode button +void resetTrip1(uint32_t distance); + // Trip-1 in 10m increments uint32_t getTrip1(); From 0ae164be0d3748916a2fc7a7900898b50408cab5 Mon Sep 17 00:00:00 2001 From: mooiweertje Date: Tue, 21 May 2024 11:10:57 +0200 Subject: [PATCH 04/34] test --- sdkconfig.supermini | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/sdkconfig.supermini b/sdkconfig.supermini index 1e97e28..7b1ba63 100644 --- a/sdkconfig.supermini +++ b/sdkconfig.supermini @@ -4,10 +4,10 @@ # Pins: # 1(06) - GND - GND -# 2(04) - GP2 - IO2 > Optional ADC use for battery level, voltage -# 3(06) - GP2 - IO2 > Optional ADC use for battery level, current -# 4(??) - GP0 - IO9 > Motor on/off (active low) -# 5(??) - TXD - TX > Light on/off (active high) + jumper for debug +# 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 @@ -21,17 +21,17 @@ CONFIG_ION_RXD=20 CONFIG_ION_TXD=21 CONFIG_ION_LIGHT=n -# CONFIG_ION_LIGHT_PIN=21 -# CONFIG_ION_LIGHT_PIN_INVERTED=n +CONFIG_ION_LIGHT_PIN=1 +CONFIG_ION_LIGHT_PIN_INVERTED=n CONFIG_ION_RELAY=n -# CONFIG_ION_RELAY_PIN=9 -# CONFIG_ION_RELAY_PIN_INVERTED=y +CONFIG_ION_RELAY_PIN=0 +CONFIG_ION_RELAY_PIN_INVERTED=n CONFIG_ESP32C3_DEFAULT_CPU_FREQ_160=y CONFIG_ION_ADC=y -# ADC channel 2 is pin 2 +# ADC channel 1 is pin 4 CONFIG_ION_ADC_CHAN=4 # The scale of the divider, times 1000, from APM power module From 2dd9bee52041a7e40eff67dfd589536eb7cd0105 Mon Sep 17 00:00:00 2001 From: mooiweertje Date: Tue, 18 Feb 2025 16:22:27 +0100 Subject: [PATCH 05/34] actions/upload-artifact@v4 --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c724d24..1d0075b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -61,7 +61,7 @@ jobs: target: ${{ matrix.target }} path: '' - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: firmware-${{ matrix.target }}-${{ matrix.display }}-${{ matrix.custom }} path: | From eedc26d90d92102e94ea3584a95d024e3f9df9b3 Mon Sep 17 00:00:00 2001 From: mooiweertje Date: Thu, 13 Mar 2025 18:21:27 +0100 Subject: [PATCH 06/34] KirianLifePo4 base --- .github/workflows/main.yml | 28 +++------------------------- sdkconfig.supermini | 18 +++++++++--------- 2 files changed, 12 insertions(+), 34 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1d0075b..5c13586 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -13,31 +13,9 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - target: [esp32, esp32c3] - display: [cu2, cu3] - custom: [std] - include: - - target: esp32 - display: cu3 - custom: mike - - target: esp32 - display: cu3 - custom: wroom2xrelay - - target: esp32 - display: cu2 - custom: gpu7990 - - target: esp32 - display: cu3 - custom: gpu7990 - - target: esp32 - display: cu2 - custom: dvjcodec - - target: esp32 - display: cu3 - custom: dvjcodec - - target: esp32c3 - display: cu2 - custom: supermini + target: [esp32c3] + display: [cu3] + custom: [supermini] steps: - name: Checkout repo uses: actions/checkout@v3 diff --git a/sdkconfig.supermini b/sdkconfig.supermini index 7b1ba63..c2623d4 100644 --- a/sdkconfig.supermini +++ b/sdkconfig.supermini @@ -20,13 +20,13 @@ 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_PIN=0 -CONFIG_ION_RELAY_PIN_INVERTED=n +CONFIG_ION_RELAY_PIN_INVERTED=y + +CONFIG_ION_LIGHT=n +CONFIG_ION_LIGHT_PIN=1 +CONFIG_ION_LIGHT_PIN_INVERTED=y CONFIG_ESP32C3_DEFAULT_CPU_FREQ_160=y @@ -37,8 +37,8 @@ CONFIG_ION_ADC_CHAN=4 # 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 22V (3.0V/cell for 8s LifePo4) empty +CONFIG_ION_ADC_EMPTY_MV=21000 -# Consider 25.2V (4.2V/cell for 6s) full -CONFIG_ION_ADC_FULL_MV=25200 +# Consider 29.2V (4.2V/cell for 8s) full +CONFIG_ION_ADC_FULL_MV=28200 From 3e0f7c2663a5035549a8db76aa327d1ab51629b8 Mon Sep 17 00:00:00 2001 From: mooiweertje Date: Mon, 6 Oct 2025 23:18:28 +0200 Subject: [PATCH 07/34] esp_idf_version --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1d0075b..6ed097d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -57,7 +57,7 @@ jobs: - name: esp-idf build uses: espressif/esp-idf-ci-action@v1 with: - esp_idf_version: latest + esp_idf_version: v5.1 target: ${{ matrix.target }} path: '' From 47dc2907c0a924df7be8a8ad8b796bf67114ea04 Mon Sep 17 00:00:00 2001 From: mooiweertje Date: Fri, 7 Nov 2025 21:55:09 +0100 Subject: [PATCH 08/34] esp_idf_version_5.5.1 --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6ed097d..4066e8f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -57,7 +57,7 @@ jobs: - name: esp-idf build uses: espressif/esp-idf-ci-action@v1 with: - esp_idf_version: v5.1 + esp_idf_version: v5.5.1 target: ${{ matrix.target }} path: '' From 805d1a88e49dd974456f2f29d1a421cd33d04791 Mon Sep 17 00:00:00 2001 From: mooiweertje Date: Tue, 11 Nov 2025 15:41:07 +0100 Subject: [PATCH 09/34] ADCOptimization --- main/bat.cpp | 28 +++++++++++++--------------- sdkconfig.supermini | 4 ++-- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/main/bat.cpp b/main/bat.cpp index ebc61a9..e262a30 100644 --- a/main/bat.cpp +++ b/main/bat.cpp @@ -20,9 +20,11 @@ static adc_cali_handle_t adc1_cali_handle = NULL; 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; + +// static size_t historyIndex = 0; +// static size_t historySize = 0; static void adc_calibration_init(adc_unit_t unit, adc_atten_t atten) { esp_err_t ret = ESP_FAIL; @@ -130,11 +132,12 @@ 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++; - } + + history += batMv; + uint32_t avg = history >> 7; + history -= avg; + + batPercentage = batMvToPercentage(avg); } uint32_t getBatMv() { @@ -142,17 +145,12 @@ uint32_t getBatMv() { } uint8_t getBatPercentage() { - if(historySize == 0) { + if(batPercentage == 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/sdkconfig.supermini b/sdkconfig.supermini index 7b1ba63..d4c77a6 100644 --- a/sdkconfig.supermini +++ b/sdkconfig.supermini @@ -24,7 +24,7 @@ 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 @@ -32,7 +32,7 @@ CONFIG_ESP32C3_DEFAULT_CPU_FREQ_160=y CONFIG_ION_ADC=y # ADC channel 1 is pin 4 -CONFIG_ION_ADC_CHAN=4 +CONFIG_ION_ADC_CHAN=2 # The scale of the divider, times 1000, from APM power module CONFIG_ION_DIVIDER_SCALE=10829 From 6e0e2c4396a25f03862f36d1f8aec8e26251e04b Mon Sep 17 00:00:00 2001 From: mooiweertje Date: Sat, 15 Nov 2025 07:55:34 +0100 Subject: [PATCH 10/34] spiffsToLittlefs --- .github/workflows/main.yml | 4 ++-- .gitmodules | 3 +++ .vscode/c_cpp_properties.json | 2 +- components/littlefs | 1 + main/CMakeLists.txt | 4 +++- main/main.cpp | 2 +- main/storage.cpp | 32 ++++++++++++++++++-------------- main/storage.h | 2 +- main/trip.cpp | 2 +- partitions.csv | 10 +++++----- sdkconfig.defaults | 2 +- sdkconfig.supermini | 6 +++--- 12 files changed, 40 insertions(+), 30 deletions(-) create mode 160000 components/littlefs diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4066e8f..a1d037c 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: diff --git a/.gitmodules b/.gitmodules index 8664b7b..dab1ef7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "components/esp32-button"] path = components/esp32-button url = https://github.com/void-spark/esp32-button.git +[submodule "components/littlefs"] + path = components/littlefs + url = https://github.com/joltwallet/esp_littlefs.git diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 3e21a85..72c25a2 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -98,7 +98,7 @@ "${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/littlefs/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/components/littlefs b/components/littlefs new file mode 160000 index 0000000..8274371 --- /dev/null +++ b/components/littlefs @@ -0,0 +1 @@ +Subproject commit 8274371dc5912196f66ac3e71dbb6291760cb8b0 diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index ab0e6c1..d98875c 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 littlefs esp_timer nvs_flash esp_adc +) \ No newline at end of file diff --git a/main/main.cpp b/main/main.cpp index d319148..20f855a 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -229,7 +229,7 @@ static void my_task(void *pvParameter) { adc_init(); #endif - init_spiffs(); + init_littlefs(); initUart(); diff --git a/main/storage.cpp b/main/storage.cpp index 8c9570d..8a6c2b9 100644 --- a/main/storage.cpp +++ b/main/storage.cpp @@ -1,27 +1,31 @@ #include #include "esp_log.h" -#include "esp_spiffs.h" +#include "esp_littlefs.h" #include "storage.h" static const char *TAG = "storage"; -#define CALIBRATION_FILE "/spiffs/calibration.bin" +#define CALIBRATION_FILE "/littlefs/calibration.bin" -void init_spiffs() { - ESP_LOGI(TAG, "Initializing SPIFFS"); +void init_littlefs() { + ESP_LOGI(TAG, "Initializing LittleFS"); - 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; + esp_vfs_littlefs_conf_t conf = { + .base_path = "/littlefs", + .partition_label = "littlefs", // moet overeenkomen met je partitions.csv + .partition = NULL, + .format_if_mount_failed = true, + .read_only = false, + .dont_mount = false, + .grow_on_mount = true + }; - ESP_ERROR_CHECK(esp_vfs_spiffs_register(&spiffs_conf)); + ESP_ERROR_CHECK(esp_vfs_littlefs_register(&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)); + esp_err_t ret = esp_littlefs_info(conf.partition_label, &total, &used); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to get LittleFS partition information (%s)", esp_err_to_name(ret)); } else { ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used); } @@ -80,4 +84,4 @@ bool writeData(const char * path, void * source, size_t size) { fclose(fp); return true; -} +} \ No newline at end of file diff --git a/main/storage.h b/main/storage.h index 65c3a42..0734089 100644 --- a/main/storage.h +++ b/main/storage.h @@ -2,7 +2,7 @@ #include -void init_spiffs(); +void init_littlefs(); // Is a calibration file stored? bool calibrationFileExists(); diff --git a/main/trip.cpp b/main/trip.cpp index 2e555dd..50e7da7 100644 --- a/main/trip.cpp +++ b/main/trip.cpp @@ -1,7 +1,7 @@ #include "storage.h" #include "trip.h" -#define DISTANCE_FILE "/spiffs/distance.bin" +#define DISTANCE_FILE "/littleFS/distance.bin" struct distancesStruct { // Trip-1 in 10m increments diff --git a/partitions.csv b/partitions.csv index 7fbc28b..6f1bbda 100644 --- a/partitions.csv +++ b/partitions.csv @@ -1,5 +1,5 @@ -# Name, Type, SubType, Offset, Size, Flags -nvs, data, nvs, , 0x4000, -phy_init, data, phy, , 0x1000, -factory, app, factory, , 1M, -spiffs, data, spiffs, , 1M, +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, , 0x4000, +phy_init, data, phy, , 0x1000, +factory, app, factory, , 1M, +littlefs, data, littlefs, , 1M, \ No newline at end of file diff --git a/sdkconfig.defaults b/sdkconfig.defaults index 6d5b1e8..260dfe5 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 for littlefs CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" diff --git a/sdkconfig.supermini b/sdkconfig.supermini index 7b1ba63..0f2b1db 100644 --- a/sdkconfig.supermini +++ b/sdkconfig.supermini @@ -24,15 +24,15 @@ 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_ESP32C3_DEFAULT_CPU_FREQ_160=y CONFIG_ION_ADC=y -# ADC channel 1 is pin 4 -CONFIG_ION_ADC_CHAN=4 +# 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 From 6b8eb18df3778bac6262f44e3bc82185fb494d05 Mon Sep 17 00:00:00 2001 From: mooiweertje Date: Sat, 15 Nov 2025 20:23:16 +0100 Subject: [PATCH 11/34] heartBeat --- main/main.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/main/main.cpp b/main/main.cpp index d319148..e6643a2 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -64,6 +64,8 @@ static const int MEASURE_BAT_BIT = BIT7; static EventGroupHandle_t controlEventGroup; +volatile bool myTaskAlive = false; + enum messageHandlingResult { // We got a handoff back, so we get to send the next message CONTROL_TO_US, @@ -74,9 +76,17 @@ enum messageHandlingResult { }; static TimerHandle_t measureBatTimer; +TimerHandle_t healthCheckTimer ; static void measureBatTimerCallback(TimerHandle_t xTimer) { xEventGroupSetBits(controlEventGroup, MEASURE_BAT_BIT); } +static void checkMyTaskHealth(TimerHandle_t xTimer) { + if (!myTaskAlive) { + esp_restart(); + } + myTaskAlive = false; // Reset voor volgende check +} + static messageHandlingResult handleMotorMessage(ion_state * state) { messageType message = {}; readResult result; @@ -249,8 +259,10 @@ static void my_task(void *pvParameter) { initMotor(); measureBatTimer = xTimerCreate("measureBatTimer", (100 / portTICK_PERIOD_MS), pdTRUE, (void *)0, measureBatTimerCallback); - xTimerStart(measureBatTimer, 0); + + healthCheckTimer = xTimerCreate("healthCheckTimer", 60000 / portTICK_PERIOD_MS, pdTRUE, NULL, checkMyTaskHealth); + xTimerStart(healthCheckTimer, 0); ion_state state = { .state = IDLE, @@ -266,6 +278,8 @@ static void my_task(void *pvParameter) { while(true) { + myTaskAlive = true; // sign of life + // TODO: // More use of timeouts // See if we really need 8k stack (copying message structure a lot I guess) From 9e922f8c66a6321bbb2b7d8722ef312c5d87c7c5 Mon Sep 17 00:00:00 2001 From: void-spark <81029971+void-spark@users.noreply.github.com> Date: Sun, 16 Nov 2025 16:46:55 +0100 Subject: [PATCH 12/34] Add a bit of comments on the new adc code --- main/bat.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/main/bat.cpp b/main/bat.cpp index e262a30..f700a53 100644 --- a/main/bat.cpp +++ b/main/bat.cpp @@ -19,13 +19,9 @@ 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 uint32_t history; static uint8_t batPercentage; -// static size_t historyIndex = 0; -// static size_t historySize = 0; - static void adc_calibration_init(adc_unit_t unit, adc_atten_t atten) { esp_err_t ret = ESP_FAIL; @@ -133,6 +129,11 @@ static uint8_t batMvToPercentage(uint32_t batMv) { void measureBat() { batMv = measureBatMv(); + // 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; From e9c3b554291e86b5a2e05b1e773daa3123bd2e19 Mon Sep 17 00:00:00 2001 From: mooiweertje Date: Wed, 19 Nov 2025 14:38:27 +0100 Subject: [PATCH 13/34] spiffsToLittlefs --- main/trip.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/trip.cpp b/main/trip.cpp index 50e7da7..3fc266c 100644 --- a/main/trip.cpp +++ b/main/trip.cpp @@ -1,7 +1,7 @@ #include "storage.h" #include "trip.h" -#define DISTANCE_FILE "/littleFS/distance.bin" +#define DISTANCE_FILE "/littlefs/distance.bin" struct distancesStruct { // Trip-1 in 10m increments From 0a0e7fe9015aefceccb74c4eb81fae3c3218b648 Mon Sep 17 00:00:00 2001 From: mooiweertje Date: Wed, 17 Dec 2025 18:26:29 +0100 Subject: [PATCH 14/34] heartBeat --- main/Kconfig.projbuild | 4 ++++ main/main.cpp | 13 ++++++++++--- sdkconfig.supermini | 3 +++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index 085e674..e7b4f6c 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 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/main.cpp b/main/main.cpp index e6643a2..20848a5 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -64,8 +64,6 @@ static const int MEASURE_BAT_BIT = BIT7; static EventGroupHandle_t controlEventGroup; -volatile bool myTaskAlive = false; - enum messageHandlingResult { // We got a handoff back, so we get to send the next message CONTROL_TO_US, @@ -76,16 +74,21 @@ enum messageHandlingResult { }; static TimerHandle_t measureBatTimer; -TimerHandle_t healthCheckTimer ; 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) { esp_restart(); } myTaskAlive = false; // Reset voor volgende check } +#endif + static messageHandlingResult handleMotorMessage(ion_state * state) { messageType message = {}; @@ -261,8 +264,10 @@ 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, @@ -278,7 +283,9 @@ static void my_task(void *pvParameter) { while(true) { +#if CONFIG_ION_KEEPALIVE myTaskAlive = true; // sign of life +#endif // TODO: // More use of timeouts diff --git a/sdkconfig.supermini b/sdkconfig.supermini index 7b1ba63..249563c 100644 --- a/sdkconfig.supermini +++ b/sdkconfig.supermini @@ -42,3 +42,6 @@ CONFIG_ION_ADC_EMPTY_MV=18000 # Consider 25.2V (4.2V/cell for 6s) full CONFIG_ION_ADC_FULL_MV=25200 + +# keepalive +CONFIG_ION_KEEPALIVE=y From f2dfaf1ee097c4b3e854ac0ad17e04b3a4a87fe2 Mon Sep 17 00:00:00 2001 From: mooiweertje Date: Mon, 6 Oct 2025 23:18:28 +0200 Subject: [PATCH 15/34] esp_idf_version --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5c13586..f4a5de0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -35,7 +35,7 @@ jobs: - name: esp-idf build uses: espressif/esp-idf-ci-action@v1 with: - esp_idf_version: latest + esp_idf_version: v5.1 target: ${{ matrix.target }} path: '' From c5deb27ddbe89e9d8093215e24071ac6b427a338 Mon Sep 17 00:00:00 2001 From: mooiweertje Date: Tue, 14 Oct 2025 15:37:02 +0200 Subject: [PATCH 16/34] LiFe8S --- .github/workflows/main.yml | 2 +- sdkconfig.supermini | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f4a5de0..88d994e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -14,7 +14,7 @@ jobs: strategy: matrix: target: [esp32c3] - display: [cu3] + display: [cu2, cu3] custom: [supermini] steps: - name: Checkout repo diff --git a/sdkconfig.supermini b/sdkconfig.supermini index c2623d4..f82d3a3 100644 --- a/sdkconfig.supermini +++ b/sdkconfig.supermini @@ -20,13 +20,13 @@ CONFIG_ION_UART=1 CONFIG_ION_RXD=20 CONFIG_ION_TXD=21 -CONFIG_ION_RELAY=n +CONFIG_ION_RELAY=y CONFIG_ION_RELAY_PIN=0 -CONFIG_ION_RELAY_PIN_INVERTED=y +CONFIG_ION_RELAY_PIN_INVERTED=n -CONFIG_ION_LIGHT=n +CONFIG_ION_LIGHT=y CONFIG_ION_LIGHT_PIN=1 -CONFIG_ION_LIGHT_PIN_INVERTED=y +CONFIG_ION_LIGHT_PIN_INVERTED=n CONFIG_ESP32C3_DEFAULT_CPU_FREQ_160=y @@ -37,8 +37,9 @@ CONFIG_ION_ADC_CHAN=4 # The scale of the divider, times 1000, from APM power module CONFIG_ION_DIVIDER_SCALE=10829 -# Consider 22V (3.0V/cell for 8s LifePo4) empty +# Consider 20V (2.5V/cell for 8s) empty CONFIG_ION_ADC_EMPTY_MV=21000 -# Consider 29.2V (4.2V/cell for 8s) full -CONFIG_ION_ADC_FULL_MV=28200 +# Consider 28V (3.5V/cell for 8s) full +CONFIG_ION_ADC_FULL_MV=27000 + From 5aeae5266f4e1e66c26ac5ae8a96a7d8fffefe2a Mon Sep 17 00:00:00 2001 From: mooiweertje Date: Fri, 7 Nov 2025 21:55:09 +0100 Subject: [PATCH 17/34] esp_idf_version_5.5.1 --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 88d994e..6846e47 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -35,7 +35,7 @@ jobs: - name: esp-idf build uses: espressif/esp-idf-ci-action@v1 with: - esp_idf_version: v5.1 + esp_idf_version: v5.5.1 target: ${{ matrix.target }} path: '' From 06250fea4d60782dee4caa2455145ffb5073421f Mon Sep 17 00:00:00 2001 From: mooiweertje Date: Thu, 6 Nov 2025 17:25:29 +0100 Subject: [PATCH 18/34] quickMotorOff --- main/states/motor_on.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/states/motor_on.cpp b/main/states/motor_on.cpp index 0e84440..6eb88ff 100644 --- a/main/states/motor_on.cpp +++ b/main/states/motor_on.cpp @@ -35,7 +35,7 @@ void handleMotorOnState(ion_state * state, bool modeShortPress, bool lightLongPr state->step++; } - if(now -lastMoving > 10 * 1000 * 1000 ) { + if(now -lastMoving > 3 * 1000 * 1000 ) { toTurnMotorOffState(state); return; } From 7117dd5f028e5b241a033580c73a356c3edbd773 Mon Sep 17 00:00:00 2001 From: mooiweertje Date: Tue, 11 Nov 2025 15:41:07 +0100 Subject: [PATCH 19/34] ADCOptimization --- main/bat.cpp | 28 +++++++++++++--------------- sdkconfig.supermini | 2 +- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/main/bat.cpp b/main/bat.cpp index ebc61a9..e262a30 100644 --- a/main/bat.cpp +++ b/main/bat.cpp @@ -20,9 +20,11 @@ static adc_cali_handle_t adc1_cali_handle = NULL; 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; + +// static size_t historyIndex = 0; +// static size_t historySize = 0; static void adc_calibration_init(adc_unit_t unit, adc_atten_t atten) { esp_err_t ret = ESP_FAIL; @@ -130,11 +132,12 @@ 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++; - } + + history += batMv; + uint32_t avg = history >> 7; + history -= avg; + + batPercentage = batMvToPercentage(avg); } uint32_t getBatMv() { @@ -142,17 +145,12 @@ uint32_t getBatMv() { } uint8_t getBatPercentage() { - if(historySize == 0) { + if(batPercentage == 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/sdkconfig.supermini b/sdkconfig.supermini index f82d3a3..68347ab 100644 --- a/sdkconfig.supermini +++ b/sdkconfig.supermini @@ -32,7 +32,7 @@ CONFIG_ESP32C3_DEFAULT_CPU_FREQ_160=y CONFIG_ION_ADC=y # ADC channel 1 is pin 4 -CONFIG_ION_ADC_CHAN=4 +CONFIG_ION_ADC_CHAN=2 # The scale of the divider, times 1000, from APM power module CONFIG_ION_DIVIDER_SCALE=10829 From 7107f083f0326688f2c142ebd1e65a24bd508bd0 Mon Sep 17 00:00:00 2001 From: mooiweertje Date: Sat, 15 Nov 2025 13:26:39 +0100 Subject: [PATCH 20/34] AmpADC1 --- main/Kconfig.projbuild | 13 ++++++- main/bat.cpp | 57 +++++++++++++++++++++++------- main/bat.h | 10 +++++- main/charge.cpp | 65 +++++++++++++++++++++++++++++++++++ main/charge.h | 17 +++++++++ main/display.cpp | 10 ++++-- main/main.cpp | 39 +++++++++++++-------- main/states/motor_off.cpp | 2 ++ main/states/turn_motor_on.cpp | 9 +++-- sdkconfig.supermini | 18 +++++++++- 10 files changed, 206 insertions(+), 34 deletions(-) create mode 100644 main/charge.cpp create mode 100644 main/charge.h diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index 085e674..c38534c 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -87,4 +87,15 @@ menu "Sparta Ion Config" int "The actual battery voltage in mv for full (=100%). For example 42000mv for a 10s battery" default 42000 -endmenu + config ION_CURR_ADC + bool "Enable ADC for current measurement" + default n + + config ION_CURR_ADC_CHAN + int "ADC channel to use for current measurement" + default 6 + + config ION_BAT_CHARGE + int "Full battery charge in mAh." + default 10000 +endmenu \ No newline at end of file diff --git a/main/bat.cpp b/main/bat.cpp index e262a30..928ddf5 100644 --- a/main/bat.cpp +++ b/main/bat.cpp @@ -7,6 +7,8 @@ static const char *TAG = "bat"; +static uint32_t chargeFullMah = (CONFIG_ION_BAT_CHARGE * 3); + // 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. @@ -16,13 +18,20 @@ static bool cali_enable = false; static adc_oneshot_unit_handle_t adc1_handle = NULL; static adc_cali_handle_t adc1_cali_handle = NULL; -// Use a fake value of 27.6v when we don't have ADC. +// Batterij en stroomwaarden +// Use a fake value of CONFIG_ION_ADC_FULL_MVv when we don't have ADC. static uint32_t batMv = 27600; +static uint32_t batMa = 0; +static uint32_t historyMa = 0; // We try to measure every 100ms, so 100 points gives us 10 seconds history. 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 size_t historyIndex = 0; // static size_t historySize = 0; @@ -87,8 +96,12 @@ 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)); + // Current Channel + ESP_ERROR_CHECK(adc_oneshot_config_channel(adc1_handle, (adc_channel_t)CONFIG_ION_CURR_ADC_CHAN, &config)); + adc_calibration_init(ADC_UNIT_1, ADC_ATTEN); } @@ -108,20 +121,30 @@ uint32_t measureBatMv() { return (adcVoltageMv * CONFIG_ION_DIVIDER_SCALE) / 1000; } -static uint8_t batMvToPercentage(uint32_t batMv) { +uint32_t measureCurrentMv() { + int adc_raw = 0; + ESP_ERROR_CHECK(adc_oneshot_read(adc1_handle, (adc_channel_t)CONFIG_ION_CURR_ADC_CHAN, &adc_raw)); + int adcCurrentMv = 0; + if (cali_enable) { + ESP_ERROR_CHECK(adc_cali_raw_to_voltage(adc1_cali_handle, adc_raw, &adcCurrentMv)); + } else { + adcCurrentMv = (adc_raw * 3550) / (1 << SOC_ADC_RTC_MAX_BITWIDTH); + } + historyMa += adcCurrentMv; + uint32_t avg = historyMa >> 5; + historyMa -= avg; - // Get lower/upper limit from configuration - uint32_t emptyMv = CONFIG_ION_ADC_EMPTY_MV; - uint32_t fullMv = CONFIG_ION_ADC_FULL_MV; + return avg; +} + +static uint8_t batMvToPercentage(uint32_t batMv) { // 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; @@ -133,11 +156,15 @@ static uint8_t batMvToPercentage(uint32_t batMv) { void measureBat() { batMv = measureBatMv(); - history += batMv; - uint32_t avg = history >> 7; - history -= avg; + history += batMv; + uint32_t avg = history >> 7; + history -= avg; - batPercentage = batMvToPercentage(avg); + batPercentage = batMvToPercentage(avg); +} + +void measureCurrent() { + batMa = measureCurrentMv(); } uint32_t getBatMv() { @@ -145,7 +172,7 @@ uint32_t getBatMv() { } uint8_t getBatPercentage() { - if(batPercentage == 0) { + if(batMv == 0) { // Use a fake value of 50% when we don't have ADC. return 50; } @@ -153,6 +180,10 @@ uint8_t getBatPercentage() { return batPercentage; } +uint32_t getBatMa() { + return batMa; +} + void adc_teardown() { // Tear Down diff --git a/main/bat.h b/main/bat.h index 3e94de6..18dafcb 100644 --- a/main/bat.h +++ b/main/bat.h @@ -1,7 +1,15 @@ #pragma once +// Initialisatie en teardown void adc_init(); +void adc_teardown(); + +// Voltage measurement void measureBat(); uint32_t getBatMv(); uint8_t getBatPercentage(); -void adc_teardown(); + +// Current Measurement +void measureCurrent(); +uint32_t getBatMa(); +uint32_t getBatMah(); \ No newline at end of file diff --git a/main/charge.cpp b/main/charge.cpp new file mode 100644 index 0000000..2d44b1c --- /dev/null +++ b/main/charge.cpp @@ -0,0 +1,65 @@ +#include "sdkconfig.h" +#include "storage.h" +#include "charge.h" + +#define CHARGE_FILE "/littlefs/charge.bin" +// note that the Ah is not real Ah but a relative Ah measurement based on ADC and timer. + +struct chargeStruct { + + uint32_t mv; + + uint32_t mah; +}; + +static chargeStruct charge; + +static uint32_t chargeFullMah = (CONFIG_ION_BAT_CHARGE * 2400); // increase * 2400 to conform to the relative current measurement. + +bool full = true; + + +uint8_t getChargePercentage() { + uint8_t percentageUsed = (uint8_t) (((float)charge.mah / (float)chargeFullMah) * 100.0f); + if(percentageUsed > 100) percentageUsed = 100; + return 100 - percentageUsed; //percentage left +} + +uint32_t getMv() { + return charge.mv; +} + +uint32_t getMah() { + return charge.mah; +} + +void chargeUpdate(uint32_t mv, uint32_t ma) { + /* + uint32_t diff = (charge.mv > mv) ? (charge.mv - mv) : (mv - charge.mv); + if (full) { + charge.mah = chargeFullMah; + } + */ + charge.mv = mv; + charge.mah += ma; + /* + if(charge.mah > chargeFullMah) { + charge.mah = 0; + } + */ +} + +void loadCharge() { + if(fileExists(CHARGE_FILE)) { + readData(CHARGE_FILE, &charge, sizeof(charge)); + } +} + +void saveCharge() { + writeData(CHARGE_FILE, &charge, sizeof(charge)); +} + +void resetCharge() { + charge.mv = 0; + charge.mah = 0; +} diff --git a/main/charge.h b/main/charge.h new file mode 100644 index 0000000..25b48a7 --- /dev/null +++ b/main/charge.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +uint8_t getChargePercentage(); + +uint32_t getMv(); + +uint32_t getMah(); + +void chargeUpdate(uint32_t mv, uint32_t mah); + +void loadCharge(); + +void saveCharge(); + +void resetCharge(); diff --git a/main/display.cpp b/main/display.cpp index 6f61d9f..ea16ddc 100644 --- a/main/display.cpp +++ b/main/display.cpp @@ -3,6 +3,7 @@ #include "freertos/timers.h" #include "states/states.h" #include "trip.h" +#include "charge.h" #include "relays.h" #include "bat.h" #include "cu2.h" @@ -41,7 +42,12 @@ void stopDisplayUpdates() { static void displayUpdate(ion_state * state) { #if CONFIG_ION_CU2 uint16_t numTop = digits(state->speed, 3, 2); - uint32_t numBottom = digits(getTrip1() / 100, 5, 1); + // uint16_t numTop = digits(getChargePercentage(), 3, 2); + //uint16_t numTop = digits(getBatMv(), 3, 2); + // uint32_t numBottom = digits(getTrip1() / 100, 5, 1); + // uint32_t numBottom = digits(getBatMa(), 5, 1); + uint32_t numBottom = digits(getMah() / 1000, 5, 1); + uint8_t batPercentage = getChargePercentage(); displayUpdateCu2(false, // setDefault (assist_level)state->level, // assistLevel BLNK_SOLID, // assistBlink @@ -55,7 +61,7 @@ static void displayUpdate(ion_state * state) { BLNK_SOLID, // top BLNK_SOLID, // bottom false, // miles - getBatPercentage(), // batPercentage + batPercentage, // batPercentage numTop, // topVal numBottom); // bottomVal #elif CONFIG_ION_CU3 diff --git a/main/main.cpp b/main/main.cpp index d319148..f62991c 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -31,6 +31,7 @@ #include "motor.h" #include "relays.h" #include "trip.h" +#include "charge.h" #include "states/states.h" #include "storage.h" @@ -53,14 +54,14 @@ static const char *TAG = "app"; #define CHARGE_PIN ((gpio_num_t)CONFIG_ION_CHARGE_PIN) #endif -static const int BUTTON_MODE_SHORT_PRESS_BIT = BIT0; -static const int BUTTON_MODE_LONG_PRESS_BIT = BIT1; -static const int BUTTON_LIGHT_SHORT_PRESS_BIT = BIT2; -static const int BUTTON_LIGHT_LONG_PRESS_BIT = BIT3; -static const int IGNORE_HELD_BIT = BIT4; -static const int WAKEUP_BIT = BIT5; -static const int CALIBRATE_BIT = BIT6; -static const int MEASURE_BAT_BIT = BIT7; +static const int BUTTON_MODE_SHORT_PRESS_BIT = BIT0; +static const int BUTTON_MODE_LONG_PRESS_BIT = BIT1; +static const int BUTTON_LIGHT_SHORT_PRESS_BIT = BIT2; +static const int BUTTON_LIGHT_LONG_PRESS_BIT = BIT3; +static const int IGNORE_HELD_BIT = BIT4; +static const int WAKEUP_BIT = BIT5; +static const int CALIBRATE_BIT = BIT6; +static const int MEASURE_BAT_BIT = BIT7; static EventGroupHandle_t controlEventGroup; @@ -234,6 +235,7 @@ static void my_task(void *pvParameter) { initUart(); loadDistances(); + loadCharge(); #if CONFIG_ION_CU2 initCu2(controlEventGroup, @@ -285,12 +287,12 @@ static void my_task(void *pvParameter) { #endif EventBits_t buttonBits = xEventGroupWaitBits(controlEventGroup, BUTTON_MODE_SHORT_PRESS_BIT | BUTTON_MODE_LONG_PRESS_BIT | BUTTON_LIGHT_SHORT_PRESS_BIT | BUTTON_LIGHT_LONG_PRESS_BIT | WAKEUP_BIT | CALIBRATE_BIT, true, false, 0); - const bool modeShortPress = (buttonBits & BUTTON_MODE_SHORT_PRESS_BIT) != 0; - const bool modeLongPress = (buttonBits & BUTTON_MODE_LONG_PRESS_BIT) != 0; + const bool modeShortPress = (buttonBits & BUTTON_MODE_SHORT_PRESS_BIT) != 0; + const bool modeLongPress = (buttonBits & BUTTON_MODE_LONG_PRESS_BIT) != 0; const bool lightShortPress = (buttonBits & BUTTON_LIGHT_SHORT_PRESS_BIT) != 0; - const bool lightLongPress = (buttonBits & BUTTON_LIGHT_LONG_PRESS_BIT) != 0; - const bool wakeup = (buttonBits & WAKEUP_BIT) != 0; - const bool calibrate = (buttonBits & CALIBRATE_BIT) != 0; + const bool lightLongPress = (buttonBits & BUTTON_LIGHT_LONG_PRESS_BIT) != 0; + const bool wakeup = (buttonBits & WAKEUP_BIT) != 0; + const bool calibrate = (buttonBits & CALIBRATE_BIT) != 0; if(lightShortPress) { toggleLight(); @@ -299,6 +301,7 @@ static void my_task(void *pvParameter) { if(modeLongPress) { resetTrip1(0); + resetCharge(); requestDisplayUpdate(); } @@ -307,7 +310,15 @@ static void my_task(void *pvParameter) { EventBits_t bits = xEventGroupWaitBits(controlEventGroup, bitsToCheck, false, false, 0); if((bits & MEASURE_BAT_BIT) != 0) { xEventGroupClearBits(controlEventGroup, MEASURE_BAT_BIT); + + // Batterij meten measureBat(); + +#if CONFIG_ION_CURR_ADC + // Stroom meten tegelijk + measureCurrent(); + chargeUpdate(getBatMv(), getBatMa()); +#endif } else #endif if(handleDisplayUpdate(&state)) { @@ -381,4 +392,4 @@ extern "C" void app_main() { } } #endif -} +} \ No newline at end of file diff --git a/main/states/motor_off.cpp b/main/states/motor_off.cpp index 364e2e0..e429e3f 100644 --- a/main/states/motor_off.cpp +++ b/main/states/motor_off.cpp @@ -4,6 +4,7 @@ #include "esp_log.h" #include "blink.h" #include "trip.h" +#include "charge.h" #include "states.h" void toMotorOffState(ion_state * state) { @@ -11,6 +12,7 @@ void toMotorOffState(ion_state * state) { queueBlink(4, 100, 300); saveDistances(); + saveCharge(); state->state = MOTOR_OFF; state->step = 0; diff --git a/main/states/turn_motor_on.cpp b/main/states/turn_motor_on.cpp index 03e0f25..64f7056 100644 --- a/main/states/turn_motor_on.cpp +++ b/main/states/turn_motor_on.cpp @@ -11,6 +11,8 @@ #include "cu3.h" #include "motor.h" #include "states.h" +#include "trip.h" +#include "charge.h" static const char *TAG = "turn_motor_on_state"; @@ -56,7 +58,8 @@ void handleTurnMotorOnState(ion_state * state) { readResult result = exchange(cmdReq(MSG_DISPLAY, MSG_BMS, CMD_BUTTON_POLL, payload, sizeof(payload)), &message, 225 / portTICK_PERIOD_MS ); } else if(state->step == 1) { // Update display - displayUpdateCu2(false, ASS_OFF, BLNK_SOLID, BLNK_OFF, BLNK_SOLID, BLNK_OFF, BLNK_OFF, BLNK_SOLID, BLNK_OFF, BLNK_SOLID, BLNK_SOLID, BLNK_SOLID, true, 25, 0xccc, 0xccccc); + // assistLevel assistBlink wrench total trip light bars comma km top bottom miles batPercentage topVal bottomVal + displayUpdateCu2(false, ASS_OFF, BLNK_SOLID, BLNK_OFF, BLNK_OFF, BLNK_OFF, BLNK_OFF, BLNK_SOLID, BLNK_OFF, BLNK_SOLID, BLNK_SOLID, BLNK_SOLID, false, getChargePercentage(), 0xccc, 0xa0a0a); } else if(state->step == 2) { // Unknown command which is always the same and always sent to the // display at this point. @@ -69,7 +72,9 @@ void handleTurnMotorOnState(ion_state * state) { startButtonCheck(); } else if(state->step == 4) { // Set default display, which is shown if the display isn't updated for a bit (?) - displayUpdateCu2(true, ASS_OFF, BLNK_SOLID, BLNK_OFF, BLNK_SOLID, BLNK_OFF, BLNK_OFF, BLNK_SOLID, BLNK_OFF, BLNK_SOLID, BLNK_OFF, BLNK_SOLID, false, 10, 0xccc, 0xccccc); + // setDefault assistLevel assistBlink wrench total trip light bars comma km top bottom miles batPercentage topVal bottomVal + // displayUpdateCu2(f, ASS_OFF, BLNK_SOLID, BLNK_OFF, BLNK_SOLID, BLNK_OFF, BLNK_OFF, BLNK_SOLID, BLNK_OFF, BLNK_SOLID, BLNK_SOLID, BLNK_SOLID, true, 100, 0xccc, 0xccccc); + displayUpdateCu2(true, ASS_OFF, BLNK_SOLID, BLNK_OFF, BLNK_SOLID, BLNK_OFF, BLNK_OFF, BLNK_SOLID, BLNK_OFF, BLNK_SOLID, BLNK_OFF, BLNK_SOLID, false, getChargePercentage(), 0xccc, digits(getTotal(), 5, 1)); } else #else const uint8_t nextStep = 0; diff --git a/sdkconfig.supermini b/sdkconfig.supermini index 68347ab..73386d4 100644 --- a/sdkconfig.supermini +++ b/sdkconfig.supermini @@ -30,8 +30,16 @@ 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 +# --- Voltage measuring --- +# ADC channel 1 is pin 2 CONFIG_ION_ADC_CHAN=2 # The scale of the divider, times 1000, from APM power module @@ -43,3 +51,11 @@ CONFIG_ION_ADC_EMPTY_MV=21000 # Consider 28V (3.5V/cell for 8s) 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 From 3355c3163bdcfd7f4dc167f1ac345658d97d0cd1 Mon Sep 17 00:00:00 2001 From: mooiweertje Date: Sat, 15 Nov 2025 19:53:16 +0100 Subject: [PATCH 21/34] heartBeat --- main/main.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/main/main.cpp b/main/main.cpp index f62991c..7dc0571 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -65,6 +65,8 @@ static const int MEASURE_BAT_BIT = BIT7; static EventGroupHandle_t controlEventGroup; +volatile bool myTaskAlive = false; + enum messageHandlingResult { // We got a handoff back, so we get to send the next message CONTROL_TO_US, @@ -75,9 +77,17 @@ enum messageHandlingResult { }; static TimerHandle_t measureBatTimer; +TimerHandle_t healthCheckTimer ; static void measureBatTimerCallback(TimerHandle_t xTimer) { xEventGroupSetBits(controlEventGroup, MEASURE_BAT_BIT); } +static void checkMyTaskHealth(TimerHandle_t xTimer) { + if (!myTaskAlive) { + esp_restart(); + } + myTaskAlive = false; // Reset voor volgende check +} + static messageHandlingResult handleMotorMessage(ion_state * state) { messageType message = {}; readResult result; @@ -251,8 +261,10 @@ static void my_task(void *pvParameter) { initMotor(); measureBatTimer = xTimerCreate("measureBatTimer", (100 / portTICK_PERIOD_MS), pdTRUE, (void *)0, measureBatTimerCallback); - xTimerStart(measureBatTimer, 0); + + healthCheckTimer = xTimerCreate("healthCheckTimer", 60000 / portTICK_PERIOD_MS, pdTRUE, NULL, checkMyTaskHealth); + xTimerStart(healthCheckTimer, 0); ion_state state = { .state = IDLE, @@ -268,6 +280,8 @@ static void my_task(void *pvParameter) { while(true) { + myTaskAlive = true; // Teken van leven + // TODO: // More use of timeouts // See if we really need 8k stack (copying message structure a lot I guess) From b1ce64042a9820efe799a6d2b348c358a9348233 Mon Sep 17 00:00:00 2001 From: mooiweertje Date: Fri, 21 Nov 2025 16:39:02 +0100 Subject: [PATCH 22/34] AmpADC2 --- main/charge.cpp | 30 +++++++++++++++--------------- main/display.cpp | 4 ++-- main/main.cpp | 6 ++++-- main/states/calibrate.cpp | 6 ++++++ main/states/motor_on.cpp | 2 +- main/states/turn_motor_on.cpp | 4 ++-- sdkconfig.supermini | 2 +- 7 files changed, 31 insertions(+), 23 deletions(-) diff --git a/main/charge.cpp b/main/charge.cpp index 2d44b1c..77397f0 100644 --- a/main/charge.cpp +++ b/main/charge.cpp @@ -3,10 +3,13 @@ #include "charge.h" #define CHARGE_FILE "/littlefs/charge.bin" -// note that the Ah is not real Ah but a relative Ah measurement based on ADC and timer. + +// note that Ah is not real Ah but a relative Ah measurement based on ADC and timer. struct chargeStruct { + uint8_t percentage; + uint32_t mv; uint32_t mah; @@ -14,15 +17,22 @@ struct chargeStruct { static chargeStruct charge; -static uint32_t chargeFullMah = (CONFIG_ION_BAT_CHARGE * 2400); // increase * 2400 to conform to the relative current measurement. +static uint32_t chargeFullMah = (CONFIG_ION_BAT_CHARGE * 1800); // increase * 1800 to conform to the relative current measurement. bool full = true; - uint8_t getChargePercentage() { + uint8_t percentageUsed = (uint8_t) (((float)charge.mah / (float)chargeFullMah) * 100.0f); if(percentageUsed > 100) percentageUsed = 100; - return 100 - percentageUsed; //percentage left + uint8_t percentage = 100 - percentageUsed; //percentage left; + + if(percentage != charge.percentage){ + charge.percentage = percentage; + saveCharge(); + } + + return charge.percentage; } uint32_t getMv() { @@ -34,19 +44,8 @@ uint32_t getMah() { } void chargeUpdate(uint32_t mv, uint32_t ma) { - /* - uint32_t diff = (charge.mv > mv) ? (charge.mv - mv) : (mv - charge.mv); - if (full) { - charge.mah = chargeFullMah; - } - */ charge.mv = mv; charge.mah += ma; - /* - if(charge.mah > chargeFullMah) { - charge.mah = 0; - } - */ } void loadCharge() { @@ -60,6 +59,7 @@ void saveCharge() { } void resetCharge() { + charge.percentage = 100; charge.mv = 0; charge.mah = 0; } diff --git a/main/display.cpp b/main/display.cpp index ea16ddc..6192098 100644 --- a/main/display.cpp +++ b/main/display.cpp @@ -44,9 +44,9 @@ static void displayUpdate(ion_state * state) { uint16_t numTop = digits(state->speed, 3, 2); // uint16_t numTop = digits(getChargePercentage(), 3, 2); //uint16_t numTop = digits(getBatMv(), 3, 2); - // uint32_t numBottom = digits(getTrip1() / 100, 5, 1); + uint32_t numBottom = digits(getTrip1() / 100, 5, 1); // uint32_t numBottom = digits(getBatMa(), 5, 1); - uint32_t numBottom = digits(getMah() / 1000, 5, 1); + // uint32_t numBottom = digits(getMah() / 1000, 5, 1); uint8_t batPercentage = getChargePercentage(); displayUpdateCu2(false, // setDefault (assist_level)state->level, // assistLevel diff --git a/main/main.cpp b/main/main.cpp index 7dc0571..a9816cb 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -65,7 +65,7 @@ static const int MEASURE_BAT_BIT = BIT7; static EventGroupHandle_t controlEventGroup; -volatile bool myTaskAlive = false; +volatile bool myTaskAlive = true; enum messageHandlingResult { // We got a handoff back, so we get to send the next message @@ -83,6 +83,8 @@ static void measureBatTimerCallback(TimerHandle_t xTimer) { xEventGroupSetBits(c static void checkMyTaskHealth(TimerHandle_t xTimer) { if (!myTaskAlive) { + saveDistances(); + saveCharge(); esp_restart(); } myTaskAlive = false; // Reset voor volgende check @@ -280,7 +282,7 @@ static void my_task(void *pvParameter) { while(true) { - myTaskAlive = true; // Teken van leven + myTaskAlive = true; // sign of life // TODO: // More use of timeouts diff --git a/main/states/calibrate.cpp b/main/states/calibrate.cpp index 50147c1..ed6313c 100644 --- a/main/states/calibrate.cpp +++ b/main/states/calibrate.cpp @@ -2,6 +2,8 @@ #include "freertos/event_groups.h" #include "esp_log.h" #include "blink.h" +#include "display.h" +#include "cu2.h" #include "cmds.h" #include "bow.h" #include "states.h" @@ -22,6 +24,10 @@ void handleCalibrateState(ion_state * state) { // >> cal: almost directly after cal cmd (35) // XXX handoffs later DP(CU3) pings motor, and starts to include it in handoffs // Motor does get data 2a +#if CONFIG_ION_CU2 + displayUpdateCu2(false, ASS_OFF, BLNK_SOLID, BLNK_OFF, BLNK_OFF, BLNK_OFF, BLNK_OFF, BLNK_SOLID, BLNK_OFF, BLNK_SOLID, BLNK_SOLID, BLNK_SOLID, false, 0, 0xccc, 0xa0a0a); + requestDisplayUpdate(); +#endif if(state->step == 0) { exchange(cmdReq(MSG_MOTOR, MSG_BMS, CMD_CALIBRATE)); } else if (state->step == 1) { diff --git a/main/states/motor_on.cpp b/main/states/motor_on.cpp index 6eb88ff..0e84440 100644 --- a/main/states/motor_on.cpp +++ b/main/states/motor_on.cpp @@ -35,7 +35,7 @@ void handleMotorOnState(ion_state * state, bool modeShortPress, bool lightLongPr state->step++; } - if(now -lastMoving > 3 * 1000 * 1000 ) { + if(now -lastMoving > 10 * 1000 * 1000 ) { toTurnMotorOffState(state); return; } diff --git a/main/states/turn_motor_on.cpp b/main/states/turn_motor_on.cpp index 64f7056..b943df1 100644 --- a/main/states/turn_motor_on.cpp +++ b/main/states/turn_motor_on.cpp @@ -59,7 +59,7 @@ void handleTurnMotorOnState(ion_state * state) { } else if(state->step == 1) { // Update display // assistLevel assistBlink wrench total trip light bars comma km top bottom miles batPercentage topVal bottomVal - displayUpdateCu2(false, ASS_OFF, BLNK_SOLID, BLNK_OFF, BLNK_OFF, BLNK_OFF, BLNK_OFF, BLNK_SOLID, BLNK_OFF, BLNK_SOLID, BLNK_SOLID, BLNK_SOLID, false, getChargePercentage(), 0xccc, 0xa0a0a); + displayUpdateCu2(false, ASS_OFF, BLNK_SOLID, BLNK_OFF, BLNK_OFF, BLNK_OFF, BLNK_OFF, BLNK_SOLID, BLNK_OFF, BLNK_SOLID, BLNK_SOLID, BLNK_SOLID, false, getChargePercentage(), 0xccc, digits(getTotal() / 100, 5, 1)); } else if(state->step == 2) { // Unknown command which is always the same and always sent to the // display at this point. @@ -74,7 +74,7 @@ void handleTurnMotorOnState(ion_state * state) { // Set default display, which is shown if the display isn't updated for a bit (?) // setDefault assistLevel assistBlink wrench total trip light bars comma km top bottom miles batPercentage topVal bottomVal // displayUpdateCu2(f, ASS_OFF, BLNK_SOLID, BLNK_OFF, BLNK_SOLID, BLNK_OFF, BLNK_OFF, BLNK_SOLID, BLNK_OFF, BLNK_SOLID, BLNK_SOLID, BLNK_SOLID, true, 100, 0xccc, 0xccccc); - displayUpdateCu2(true, ASS_OFF, BLNK_SOLID, BLNK_OFF, BLNK_SOLID, BLNK_OFF, BLNK_OFF, BLNK_SOLID, BLNK_OFF, BLNK_SOLID, BLNK_OFF, BLNK_SOLID, false, getChargePercentage(), 0xccc, digits(getTotal(), 5, 1)); + displayUpdateCu2(true, ASS_OFF, BLNK_SOLID, BLNK_OFF, BLNK_SOLID, BLNK_OFF, BLNK_OFF, BLNK_SOLID, BLNK_OFF, BLNK_SOLID, BLNK_OFF, BLNK_SOLID, false, getChargePercentage(), 0xccc, digits(getTotal() / 100, 5, 1)); } else #else const uint8_t nextStep = 0; diff --git a/sdkconfig.supermini b/sdkconfig.supermini index 73386d4..2b71a8f 100644 --- a/sdkconfig.supermini +++ b/sdkconfig.supermini @@ -58,4 +58,4 @@ CONFIG_ION_CURR_ADC=y CONFIG_ION_CURR_ADC_CHAN=1 # Full battery charge in mAh. -CONFIG_ION_BAT_CHARGE=15000 +CONFIG_ION_BAT_CHARGE=20000 From c7b2d8ca0c245e45d7b0c1da4b1b14ea0b29d831 Mon Sep 17 00:00:00 2001 From: mooiweertje Date: Fri, 21 Nov 2025 16:50:21 +0100 Subject: [PATCH 23/34] AmpADC2 --- main/states/calibrate.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/main/states/calibrate.cpp b/main/states/calibrate.cpp index ed6313c..56858c6 100644 --- a/main/states/calibrate.cpp +++ b/main/states/calibrate.cpp @@ -27,6 +27,7 @@ void handleCalibrateState(ion_state * state) { #if CONFIG_ION_CU2 displayUpdateCu2(false, ASS_OFF, BLNK_SOLID, BLNK_OFF, BLNK_OFF, BLNK_OFF, BLNK_OFF, BLNK_SOLID, BLNK_OFF, BLNK_SOLID, BLNK_SOLID, BLNK_SOLID, false, 0, 0xccc, 0xa0a0a); requestDisplayUpdate(); + vTaskDelay(pdMS_TO_TICKS(1000)); #endif if(state->step == 0) { exchange(cmdReq(MSG_MOTOR, MSG_BMS, CMD_CALIBRATE)); From 79452553e43e6f278679dd75f457052e2fc313e2 Mon Sep 17 00:00:00 2001 From: mooiweertje Date: Sat, 15 Nov 2025 20:23:16 +0100 Subject: [PATCH 24/34] heartBeat --- main/main.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/main/main.cpp b/main/main.cpp index a9816cb..9956db3 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -83,7 +83,7 @@ static void measureBatTimerCallback(TimerHandle_t xTimer) { xEventGroupSetBits(c static void checkMyTaskHealth(TimerHandle_t xTimer) { if (!myTaskAlive) { - saveDistances(); + saveDistances(); saveCharge(); esp_restart(); } @@ -265,8 +265,8 @@ static void my_task(void *pvParameter) { measureBatTimer = xTimerCreate("measureBatTimer", (100 / portTICK_PERIOD_MS), pdTRUE, (void *)0, measureBatTimerCallback); xTimerStart(measureBatTimer, 0); - healthCheckTimer = xTimerCreate("healthCheckTimer", 60000 / portTICK_PERIOD_MS, pdTRUE, NULL, checkMyTaskHealth); - xTimerStart(healthCheckTimer, 0); + healthCheckTimer = xTimerCreate("healthCheckTimer", 60000 / portTICK_PERIOD_MS, pdTRUE, NULL, checkMyTaskHealth); + xTimerStart(healthCheckTimer, 0); ion_state state = { .state = IDLE, From b42612185ecfef65b8c4f114f4aaa8351fde7bcf Mon Sep 17 00:00:00 2001 From: void-spark <81029971+void-spark@users.noreply.github.com> Date: Sun, 16 Nov 2025 16:46:55 +0100 Subject: [PATCH 25/34] Add a bit of comments on the new adc code --- main/bat.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/main/bat.cpp b/main/bat.cpp index 928ddf5..87ab8b0 100644 --- a/main/bat.cpp +++ b/main/bat.cpp @@ -24,17 +24,12 @@ static uint32_t batMv = 27600; static uint32_t batMa = 0; static uint32_t historyMa = 0; -// We try to measure every 100ms, so 100 points gives us 10 seconds history. 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 size_t historyIndex = 0; -// static size_t historySize = 0; - static void adc_calibration_init(adc_unit_t unit, adc_atten_t atten) { esp_err_t ret = ESP_FAIL; @@ -156,9 +151,20 @@ static uint8_t batMvToPercentage(uint32_t batMv) { void measureBat() { batMv = measureBatMv(); +<<<<<<< HEAD history += batMv; uint32_t avg = history >> 7; history -= avg; +======= + // 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; +>>>>>>> 9e922f8 (Add a bit of comments on the new adc code) batPercentage = batMvToPercentage(avg); } From ecd2030904395d41b63eab9566a77a50618fe238 Mon Sep 17 00:00:00 2001 From: mooiweertje Date: Wed, 17 Dec 2025 18:26:29 +0100 Subject: [PATCH 26/34] heartBeat --- main/Kconfig.projbuild | 8 +++++++- main/main.cpp | 13 ++++++++++--- sdkconfig.supermini | 4 ++++ 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index c38534c..c8b7b0a 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -98,4 +98,10 @@ menu "Sparta Ion Config" config ION_BAT_CHARGE int "Full battery charge in mAh." default 10000 -endmenu \ No newline at end of file + + config 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/main.cpp b/main/main.cpp index 9956db3..3fb7354 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -65,8 +65,6 @@ static const int MEASURE_BAT_BIT = BIT7; static EventGroupHandle_t controlEventGroup; -volatile bool myTaskAlive = true; - enum messageHandlingResult { // We got a handoff back, so we get to send the next message CONTROL_TO_US, @@ -77,10 +75,13 @@ enum messageHandlingResult { }; static TimerHandle_t measureBatTimer; -TimerHandle_t healthCheckTimer ; 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) { saveDistances(); @@ -89,6 +90,8 @@ static void checkMyTaskHealth(TimerHandle_t xTimer) { } myTaskAlive = false; // Reset voor volgende check } +#endif + static messageHandlingResult handleMotorMessage(ion_state * state) { messageType message = {}; @@ -265,8 +268,10 @@ 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, @@ -282,7 +287,9 @@ static void my_task(void *pvParameter) { while(true) { +#if CONFIG_ION_KEEPALIVE myTaskAlive = true; // sign of life +#endif // TODO: // More use of timeouts diff --git a/sdkconfig.supermini b/sdkconfig.supermini index 2b71a8f..2a6ad0a 100644 --- a/sdkconfig.supermini +++ b/sdkconfig.supermini @@ -59,3 +59,7 @@ CONFIG_ION_CURR_ADC_CHAN=1 # Full battery charge in mAh. CONFIG_ION_BAT_CHARGE=20000 + +# keepalive +CONFIG_ION_KEEPALIVE=y + From dd0bbd17278917c3964c2d29283e60bf6a065739 Mon Sep 17 00:00:00 2001 From: mooiweertje Date: Thu, 18 Dec 2025 10:57:18 +0100 Subject: [PATCH 27/34] Tesla config split --- .github/workflows/main.yml | 13 +++-- sdkconfig.Tesla | 65 +++++++++++++++++++++++++ sdkconfig.supermini => sdkconfig.TeslaS | 4 ++ sdkconfig.TeslaTour | 65 +++++++++++++++++++++++++ 4 files changed, 144 insertions(+), 3 deletions(-) create mode 100644 sdkconfig.Tesla rename sdkconfig.supermini => sdkconfig.TeslaS (92%) create mode 100644 sdkconfig.TeslaTour diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6846e47..49d36ee 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -13,9 +13,16 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - target: [esp32c3] - display: [cu2, cu3] - custom: [supermini] + include: + - target: esp32c3 + display: cu2 + custom: Tesla + - target: esp32c3 + display: cu2 + custom: TeslaS + - target: esp32c3 + display: cu3 + custom: TeslaTour steps: - name: Checkout repo uses: actions/checkout@v3 diff --git a/sdkconfig.Tesla b/sdkconfig.Tesla new file mode 100644 index 0000000..bc720d6 --- /dev/null +++ b/sdkconfig.Tesla @@ -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_LIGHT=y +CONFIG_ION_LIGHT_PIN=3 +CONFIG_ION_LIGHT_PIN_INVERTED=n + +CONFIG_ION_RELAY=y +CONFIG_ION_RELAY_PIN=0 +CONFIG_ION_RELAY_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 22V (2.5V/cell for 8s LiFePo4) empty +CONFIG_ION_ADC_EMPTY_MV=21000 + +# Consider 27V (3.5V/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.supermini b/sdkconfig.TeslaS similarity index 92% rename from sdkconfig.supermini rename to sdkconfig.TeslaS index 2a6ad0a..b2a1af5 100644 --- a/sdkconfig.supermini +++ b/sdkconfig.TeslaS @@ -61,5 +61,9 @@ CONFIG_ION_CURR_ADC_CHAN=1 CONFIG_ION_BAT_CHARGE=20000 # keepalive +<<<<<<< HEAD:sdkconfig.supermini CONFIG_ION_KEEPALIVE=y +======= +CONFIG_ION_KEEPALIVE=n +>>>>>>> 8796a39 (Tesla config split):sdkconfig.TeslaS diff --git a/sdkconfig.TeslaTour b/sdkconfig.TeslaTour new file mode 100644 index 0000000..bc720d6 --- /dev/null +++ b/sdkconfig.TeslaTour @@ -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_LIGHT=y +CONFIG_ION_LIGHT_PIN=3 +CONFIG_ION_LIGHT_PIN_INVERTED=n + +CONFIG_ION_RELAY=y +CONFIG_ION_RELAY_PIN=0 +CONFIG_ION_RELAY_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 22V (2.5V/cell for 8s LiFePo4) empty +CONFIG_ION_ADC_EMPTY_MV=21000 + +# Consider 27V (3.5V/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 e542e69e545a88fc6355a705865e5ad34750cc89 Mon Sep 17 00:00:00 2001 From: mooiweertje Date: Mon, 22 Dec 2025 15:08:57 +0100 Subject: [PATCH 28/34] backToSpiffsMergeFixes --- .github/workflows/main.yml | 4 ++-- main/Kconfig.projbuild | 1 - main/bat.cpp | 6 ------ main/charge.cpp | 2 -- main/main.cpp | 2 +- main/storage.cpp | 2 +- main/storage.h | 6 +++++- main/trip.cpp | 2 -- sdkconfig.Tesla | 14 +++++++------- sdkconfig.TeslaS | 16 ++++++---------- sdkconfig.TeslaTour | 14 +++++++------- sdkconfig.defaults | 2 +- 12 files changed, 30 insertions(+), 41 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 49d36ee..202df80 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: diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index c8b7b0a..1344c98 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -104,4 +104,3 @@ menu "Sparta Ion Config" default n endmenu - diff --git a/main/bat.cpp b/main/bat.cpp index 87ab8b0..cd9c50a 100644 --- a/main/bat.cpp +++ b/main/bat.cpp @@ -151,11 +151,6 @@ static uint8_t batMvToPercentage(uint32_t batMv) { void measureBat() { batMv = measureBatMv(); -<<<<<<< HEAD - history += batMv; - uint32_t avg = history >> 7; - history -= avg; -======= // 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. @@ -164,7 +159,6 @@ void measureBat() { history += batMv; uint32_t avg = history >> 7; history -= avg; ->>>>>>> 9e922f8 (Add a bit of comments on the new adc code) batPercentage = batMvToPercentage(avg); } diff --git a/main/charge.cpp b/main/charge.cpp index 77397f0..fac7ae0 100644 --- a/main/charge.cpp +++ b/main/charge.cpp @@ -2,8 +2,6 @@ #include "storage.h" #include "charge.h" -#define CHARGE_FILE "/littlefs/charge.bin" - // note that Ah is not real Ah but a relative Ah measurement based on ADC and timer. struct chargeStruct { diff --git a/main/main.cpp b/main/main.cpp index 3fb7354..0412dfc 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -245,7 +245,7 @@ static void my_task(void *pvParameter) { adc_init(); #endif - init_spiffs(); + init_fs(); initUart(); diff --git a/main/storage.cpp b/main/storage.cpp index 8c9570d..29b4ec8 100644 --- a/main/storage.cpp +++ b/main/storage.cpp @@ -7,7 +7,7 @@ static const char *TAG = "storage"; #define CALIBRATION_FILE "/spiffs/calibration.bin" -void init_spiffs() { +void init_fs() { ESP_LOGI(TAG, "Initializing SPIFFS"); esp_vfs_spiffs_conf_t spiffs_conf = {}; diff --git a/main/storage.h b/main/storage.h index 65c3a42..4e075b2 100644 --- a/main/storage.h +++ b/main/storage.h @@ -2,7 +2,11 @@ #include -void init_spiffs(); +#define DISTANCE_FILE "/spiffs/distance.bin" + +#define CHARGE_FILE "/spiffs/charge.bin" + +void init_fs(); // Is a calibration file stored? bool calibrationFileExists(); diff --git a/main/trip.cpp b/main/trip.cpp index 2e555dd..23a2e91 100644 --- a/main/trip.cpp +++ b/main/trip.cpp @@ -1,8 +1,6 @@ #include "storage.h" #include "trip.h" -#define DISTANCE_FILE "/spiffs/distance.bin" - struct distancesStruct { // Trip-1 in 10m increments uint32_t trip1; diff --git a/sdkconfig.Tesla b/sdkconfig.Tesla index bc720d6..c6ffe83 100644 --- a/sdkconfig.Tesla +++ b/sdkconfig.Tesla @@ -20,14 +20,14 @@ CONFIG_ION_UART=1 CONFIG_ION_RXD=20 CONFIG_ION_TXD=21 -CONFIG_ION_LIGHT=y -CONFIG_ION_LIGHT_PIN=3 -CONFIG_ION_LIGHT_PIN_INVERTED=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 --- @@ -46,10 +46,10 @@ CONFIG_ION_ADC_CHAN=2 # The scale of the divider, times 1000, from APM power module CONFIG_ION_DIVIDER_SCALE=10829 -# Consider 22V (2.5V/cell for 8s LiFePo4) empty -CONFIG_ION_ADC_EMPTY_MV=21000 +# Consider 23V (2,875V/cell for 8s LiFePo4) empty +CONFIG_ION_ADC_EMPTY_MV=23000 -# Consider 27V (3.5V/cell for 8s LiFePo4) full +# Consider 27V (3,375V/cell for 8s LiFePo4) full CONFIG_ION_ADC_FULL_MV=27000 # --- Current measuring --- diff --git a/sdkconfig.TeslaS b/sdkconfig.TeslaS index b2a1af5..a0369f3 100644 --- a/sdkconfig.TeslaS +++ b/sdkconfig.TeslaS @@ -25,7 +25,7 @@ CONFIG_ION_RELAY_PIN=0 CONFIG_ION_RELAY_PIN_INVERTED=n CONFIG_ION_LIGHT=y -CONFIG_ION_LIGHT_PIN=1 +CONFIG_ION_LIGHT_PIN=3 CONFIG_ION_LIGHT_PIN_INVERTED=n CONFIG_ESP32C3_DEFAULT_CPU_FREQ_160=y @@ -37,6 +37,7 @@ CONFIG_ESP32C3_DEFAULT_CPU_FREQ_160=y # 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 @@ -45,10 +46,10 @@ CONFIG_ION_ADC_CHAN=2 # The scale of the divider, times 1000, from APM power module CONFIG_ION_DIVIDER_SCALE=10829 -# Consider 20V (2.5V/cell for 8s) empty -CONFIG_ION_ADC_EMPTY_MV=21000 +# Consider 23V (2,875V/cell for 8s LiFePo4) empty +CONFIG_ION_ADC_EMPTY_MV=23000 -# Consider 28V (3.5V/cell for 8s) full +# Consider 27V (3,375V/cell for 8s LiFePo4) full CONFIG_ION_ADC_FULL_MV=27000 # --- Current measuring --- @@ -61,9 +62,4 @@ CONFIG_ION_CURR_ADC_CHAN=1 CONFIG_ION_BAT_CHARGE=20000 # keepalive -<<<<<<< HEAD:sdkconfig.supermini -CONFIG_ION_KEEPALIVE=y - -======= -CONFIG_ION_KEEPALIVE=n ->>>>>>> 8796a39 (Tesla config split):sdkconfig.TeslaS +CONFIG_ION_KEEPALIVE=y \ No newline at end of file diff --git a/sdkconfig.TeslaTour b/sdkconfig.TeslaTour index bc720d6..c6ffe83 100644 --- a/sdkconfig.TeslaTour +++ b/sdkconfig.TeslaTour @@ -20,14 +20,14 @@ CONFIG_ION_UART=1 CONFIG_ION_RXD=20 CONFIG_ION_TXD=21 -CONFIG_ION_LIGHT=y -CONFIG_ION_LIGHT_PIN=3 -CONFIG_ION_LIGHT_PIN_INVERTED=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 --- @@ -46,10 +46,10 @@ CONFIG_ION_ADC_CHAN=2 # The scale of the divider, times 1000, from APM power module CONFIG_ION_DIVIDER_SCALE=10829 -# Consider 22V (2.5V/cell for 8s LiFePo4) empty -CONFIG_ION_ADC_EMPTY_MV=21000 +# Consider 23V (2,875V/cell for 8s LiFePo4) empty +CONFIG_ION_ADC_EMPTY_MV=23000 -# Consider 27V (3.5V/cell for 8s LiFePo4) full +# Consider 27V (3,375V/cell for 8s LiFePo4) full CONFIG_ION_ADC_FULL_MV=27000 # --- Current measuring --- 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" From 41bde3053839a755901247e46c17b71d741b758e Mon Sep 17 00:00:00 2001 From: mooiweertje Date: Mon, 22 Dec 2025 20:11:36 +0100 Subject: [PATCH 29/34] littlefs --- .gitmodules | 3 +++ .vscode/c_cpp_properties.json | 2 +- components/esp_littlefs | 1 + main/CMakeLists.txt | 4 +++- main/storage.cpp | 28 +++++++++++++++------------- main/storage.h | 6 ++++-- partitions.csv | 2 +- sdkconfig.defaults.esp32c3 | 1 - 8 files changed, 28 insertions(+), 19 deletions(-) create mode 160000 components/esp_littlefs diff --git a/.gitmodules b/.gitmodules index 8664b7b..678fb42 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "components/esp32-button"] path = components/esp32-button url = https://github.com/void-spark/esp32-button.git +[submodule "components/esp_littlefs"] + path = components/esp_littlefs + url = https://github.com/joltwallet/esp_littlefs.git diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 3e21a85..72c25a2 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -98,7 +98,7 @@ "${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/littlefs/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/components/esp_littlefs b/components/esp_littlefs new file mode 160000 index 0000000..2b0faf0 --- /dev/null +++ b/components/esp_littlefs @@ -0,0 +1 @@ +Subproject commit 2b0faf0c77c1e7b991af27c19f9c863ac21d09ac diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index ab0e6c1..79aac17 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_littlefs esp_timer nvs_flash esp_adc +) \ No newline at end of file diff --git a/main/storage.cpp b/main/storage.cpp index 29b4ec8..07f34f9 100644 --- a/main/storage.cpp +++ b/main/storage.cpp @@ -1,27 +1,29 @@ #include #include "esp_log.h" -#include "esp_spiffs.h" +#include "esp_littlefs.h" #include "storage.h" static const char *TAG = "storage"; -#define CALIBRATION_FILE "/spiffs/calibration.bin" - void init_fs() { - ESP_LOGI(TAG, "Initializing SPIFFS"); + ESP_LOGI(TAG, "Initializing LittleFS"); - 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; + esp_vfs_littlefs_conf_t conf = { + .base_path = "/littlefs", + .partition_label = "littlefs", // moet overeenkomen met je partitions.csv + .partition = NULL, + .format_if_mount_failed = true, + .read_only = false, + .dont_mount = false, + .grow_on_mount = true + }; - ESP_ERROR_CHECK(esp_vfs_spiffs_register(&spiffs_conf)); + ESP_ERROR_CHECK(esp_vfs_littlefs_register(&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)); + esp_err_t ret = esp_littlefs_info(conf.partition_label, &total, &used); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to get LittleFS partition information (%s)", esp_err_to_name(ret)); } else { ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used); } diff --git a/main/storage.h b/main/storage.h index 4e075b2..331be9c 100644 --- a/main/storage.h +++ b/main/storage.h @@ -2,9 +2,11 @@ #include -#define DISTANCE_FILE "/spiffs/distance.bin" +#define CALIBRATION_FILE "/littlefs/calibration.bin" -#define CHARGE_FILE "/spiffs/charge.bin" +#define DISTANCE_FILE "/littlefs/distance.bin" + +#define CHARGE_FILE "/littlefs/charge.bin" void init_fs(); diff --git a/partitions.csv b/partitions.csv index 7fbc28b..1898d0a 100644 --- a/partitions.csv +++ b/partitions.csv @@ -2,4 +2,4 @@ nvs, data, nvs, , 0x4000, phy_init, data, phy, , 0x1000, factory, app, factory, , 1M, -spiffs, data, spiffs, , 1M, +littlefs, data, littlefs, , 1M, \ No newline at end of file diff --git a/sdkconfig.defaults.esp32c3 b/sdkconfig.defaults.esp32c3 index 1a90b98..e5bd942 100644 --- a/sdkconfig.defaults.esp32c3 +++ b/sdkconfig.defaults.esp32c3 @@ -11,7 +11,6 @@ # 7(--) - RST - RST # 8(--) - VCC - 3.3V - CONFIG_ION_BUTTON=n CONFIG_ION_LED_PIN=3 CONFIG_ION_UART=1 From e8ac218d957679ff0a16b267b3bf5c9965141bd4 Mon Sep 17 00:00:00 2001 From: mooiweertje Date: Tue, 23 Dec 2025 13:20:31 +0100 Subject: [PATCH 30/34] heartBeat --- main/Kconfig.projbuild | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index e7b4f6c..b942e2e 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -87,7 +87,7 @@ menu "Sparta Ion Config" int "The actual battery voltage in mv for full (=100%). For example 42000mv for a 10s battery" default 42000 - config CONFIG_ION_KEEPALIVE + config ION_KEEPALIVE bool "Enable keepalive heartbeat. Will reset the ESP32 when main loop is stuck for more than a minute." default n From 872689eddfeb68f241b69685af8df98c62c7e692 Mon Sep 17 00:00:00 2001 From: mooiweertje Date: Fri, 26 Dec 2025 12:39:54 +0100 Subject: [PATCH 31/34] littlefs --- main/storage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/storage.cpp b/main/storage.cpp index 07f34f9..ef5c0bc 100644 --- a/main/storage.cpp +++ b/main/storage.cpp @@ -82,4 +82,4 @@ bool writeData(const char * path, void * source, size_t size) { fclose(fp); return true; -} +} \ No newline at end of file From a0bc9e871024d3f8367c3b3a092d1fa28befa558 Mon Sep 17 00:00:00 2001 From: mooiweertje Date: Sun, 28 Dec 2025 17:19:31 +0100 Subject: [PATCH 32/34] NVS --- .github/workflows/main.yml | 9 +- .gitmodules | 3 - .vscode/c_cpp_properties.json | 1 - components/esp_littlefs | 1 - main/CMakeLists.txt | 2 +- main/charge.cpp | 62 +++---- main/charge.h | 14 +- main/main.cpp | 25 +-- main/states/motor_off.cpp | 6 +- main/storage.cpp | 169 +++++++++++------- main/storage.h | 47 ++--- main/trip.cpp | 47 ++--- main/trip.h | 19 +- partitions.csv | 3 +- sdkconfig.tesla.10Ah | 65 +++++++ sdkconfig.Tesla => sdkconfig.tesla.15Ah | 0 sdkconfig.TeslaS => sdkconfig.tesla.20Ah | 2 +- ...nfig.TeslaTour => sdkconfig.teslatour.15Ah | 0 18 files changed, 273 insertions(+), 202 deletions(-) delete mode 160000 components/esp_littlefs create mode 100644 sdkconfig.tesla.10Ah rename sdkconfig.Tesla => sdkconfig.tesla.15Ah (100%) rename sdkconfig.TeslaS => sdkconfig.tesla.20Ah (100%) rename sdkconfig.TeslaTour => sdkconfig.teslatour.15Ah (100%) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 202df80..f6b57b3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -16,13 +16,16 @@ jobs: include: - target: esp32c3 display: cu2 - custom: Tesla + custom: tesla.10Ah - target: esp32c3 display: cu2 - custom: TeslaS + custom: tesla.15Ah + - target: esp32c3 + display: cu2 + custom: tesla.20Ah - target: esp32c3 display: cu3 - custom: TeslaTour + custom: teslatour.15Ah steps: - name: Checkout repo uses: actions/checkout@v3 diff --git a/.gitmodules b/.gitmodules index 678fb42..8664b7b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,6 +10,3 @@ [submodule "components/esp32-button"] path = components/esp32-button url = https://github.com/void-spark/esp32-button.git -[submodule "components/esp_littlefs"] - path = components/esp_littlefs - url = https://github.com/joltwallet/esp_littlefs.git diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 72c25a2..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/littlefs/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/components/esp_littlefs b/components/esp_littlefs deleted file mode 160000 index 2b0faf0..0000000 --- a/components/esp_littlefs +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2b0faf0c77c1e7b991af27c19f9c863ac21d09ac diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 79aac17..99d4c7c 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,4 +1,4 @@ idf_component_register(SRC_DIRS "." "states" INCLUDE_DIRS "." - REQUIRES esp32-button driver esp_littlefs esp_timer nvs_flash esp_adc + REQUIRES esp32-button driver esp_timer nvs_flash esp_adc ) \ No newline at end of file diff --git a/main/charge.cpp b/main/charge.cpp index fac7ae0..a5fa5fc 100644 --- a/main/charge.cpp +++ b/main/charge.cpp @@ -2,18 +2,7 @@ #include "storage.h" #include "charge.h" -// note that Ah is not real Ah but a relative Ah measurement based on ADC and timer. - -struct chargeStruct { - - uint8_t percentage; - - uint32_t mv; - - uint32_t mah; -}; - -static chargeStruct charge; +static struct batData *bat; static uint32_t chargeFullMah = (CONFIG_ION_BAT_CHARGE * 1800); // increase * 1800 to conform to the relative current measurement. @@ -21,43 +10,46 @@ bool full = true; uint8_t getChargePercentage() { - uint8_t percentageUsed = (uint8_t) (((float)charge.mah / (float)chargeFullMah) * 100.0f); - if(percentageUsed > 100) percentageUsed = 100; - uint8_t percentage = 100 - percentageUsed; //percentage left; - - if(percentage != charge.percentage){ - charge.percentage = percentage; - saveCharge(); - } - - return charge.percentage; + uint8_t percentageUsed = (uint8_t)(((float)bat->mah / (float)chargeFullMah) * 100.0f); + if (percentageUsed > 100) percentageUsed = 100; + + uint8_t percentage = 100 - percentageUsed; + + if (percentage != bat->percentage) { + bat->percentage = percentage; + batDataSave(); + } + + return bat->percentage; } uint32_t getMv() { - return charge.mv; + return bat->mv; } uint32_t getMah() { - return charge.mah; + return bat->mah; } void chargeUpdate(uint32_t mv, uint32_t ma) { - charge.mv = mv; - charge.mah += ma; + bat->mv = mv; + bat->mah += ma; } void loadCharge() { - if(fileExists(CHARGE_FILE)) { - readData(CHARGE_FILE, &charge, sizeof(charge)); - } -} + bat = batDataGet(); -void saveCharge() { - writeData(CHARGE_FILE, &charge, sizeof(charge)); + if (!batDataLoad()) { + // Defaults als er nog geen data in NVS staat + bat->percentage = 100; + bat->mv = 0; + bat->mah = 0; + } } void resetCharge() { - charge.percentage = 100; - charge.mv = 0; - charge.mah = 0; + bat->percentage = 100; + bat->mv = 0; + bat->mah = 0; + batDataSave(); } diff --git a/main/charge.h b/main/charge.h index 25b48a7..e246ea2 100644 --- a/main/charge.h +++ b/main/charge.h @@ -1,17 +1,15 @@ #pragma once -#include +#include -uint8_t getChargePercentage(); +uint8_t getChargePercentage(void); -uint32_t getMv(); +uint32_t getMv(void); -uint32_t getMah(); +uint32_t getMah(void); void chargeUpdate(uint32_t mv, uint32_t mah); -void loadCharge(); +void loadCharge(void); -void saveCharge(); - -void resetCharge(); +void resetCharge(void); diff --git a/main/main.cpp b/main/main.cpp index 0412dfc..b1a336f 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -84,8 +84,8 @@ TimerHandle_t healthCheckTimer ; static void checkMyTaskHealth(TimerHandle_t xTimer) { if (!myTaskAlive) { - saveDistances(); - saveCharge(); + batDataSave(); + batDataSave(); esp_restart(); } myTaskAlive = false; // Reset voor volgende check @@ -150,21 +150,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 @@ -177,7 +164,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; } @@ -245,7 +232,7 @@ static void my_task(void *pvParameter) { adc_init(); #endif - init_fs(); + storageInit(); initUart(); diff --git a/main/states/motor_off.cpp b/main/states/motor_off.cpp index e429e3f..32e65f2 100644 --- a/main/states/motor_off.cpp +++ b/main/states/motor_off.cpp @@ -3,16 +3,14 @@ #include "freertos/event_groups.h" #include "esp_log.h" #include "blink.h" -#include "trip.h" -#include "charge.h" +#include "storage.h" #include "states.h" void toMotorOffState(ion_state * state) { queueBlink(4, 100, 300); - saveDistances(); - saveCharge(); + batDataSave(); state->state = MOTOR_OFF; state->step = 0; diff --git a/main/storage.cpp b/main/storage.cpp index ef5c0bc..83983d3 100644 --- a/main/storage.cpp +++ b/main/storage.cpp @@ -1,85 +1,132 @@ -#include -#include "esp_log.h" -#include "esp_littlefs.h" #include "storage.h" +#include "nvs_flash.h" +#include "nvs.h" +#include "esp_log.h" +#include // <-- toegevoegd voor memcpy() static const char *TAG = "storage"; -void init_fs() { - ESP_LOGI(TAG, "Initializing LittleFS"); - - esp_vfs_littlefs_conf_t conf = { - .base_path = "/littlefs", - .partition_label = "littlefs", // moet overeenkomen met je partitions.csv - .partition = NULL, - .format_if_mount_failed = true, - .read_only = false, - .dont_mount = false, - .grow_on_mount = true - }; +#define NVS_NAMESPACE "storage" +#define NVS_KEY_BATDATA "batdata" +#define NVS_KEY_CALIB "calibration" - ESP_ERROR_CHECK(esp_vfs_littlefs_register(&conf)); +// Centrale instantie van batData +static struct batData bat; - size_t total = 0, used = 0; - esp_err_t ret = esp_littlefs_info(conf.partition_label, &total, &used); - if (ret != ESP_OK) { - ESP_LOGE(TAG, "Failed to get LittleFS 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); - } +// ----------------------------------------------------------------------------- +// Initialisatie +// ----------------------------------------------------------------------------- + +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; -bool readCalibrationData(uint8_t * target) { - return readData(CALIBRATION_FILE, target, 10); + bat.percentage = 0; + bat.mv = 0; + bat.mah = 0; + + 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); } \ No newline at end of file diff --git a/main/storage.h b/main/storage.h index 331be9c..495e03c 100644 --- a/main/storage.h +++ b/main/storage.h @@ -1,24 +1,25 @@ #pragma once - -#include - -#define CALIBRATION_FILE "/littlefs/calibration.bin" - -#define DISTANCE_FILE "/littlefs/distance.bin" - -#define CHARGE_FILE "/littlefs/charge.bin" - -void init_fs(); - -// Is a calibration file stored? -bool calibrationFileExists(); - -// Reads calibration data to the given buffer, data is 10 bytes long -bool readCalibrationData(uint8_t * target); - -// 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); +#include +#include + +struct batData { + uint32_t trip1; + uint32_t trip2; + uint32_t total; + + uint8_t percentage; + uint32_t mv; + uint32_t mah; +}; + +// Init NVS + defaults +void storageInit(void); + +// batData API +struct batData *batDataGet(void); +bool batDataLoad(void); +bool batDataSave(void); + +// calibration API +uint8_t *calibrationLoad(void); +bool calibrationSave(uint8_t *source); diff --git a/main/trip.cpp b/main/trip.cpp index 23a2e91..0f4dfcc 100644 --- a/main/trip.cpp +++ b/main/trip.cpp @@ -1,60 +1,49 @@ #include "storage.h" #include "trip.h" -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..b75bd52 100644 --- a/main/trip.h +++ b/main/trip.h @@ -1,24 +1,21 @@ #pragma once -#include +#include -// To reset on long press mode button +// Reset Trip-1 (bijv. op long-press van mode-knop) void resetTrip1(uint32_t distance); // Trip-1 in 10m increments -uint32_t getTrip1(); +uint32_t getTrip1(void); // Trip-2 in 10m increments -uint32_t getTrip2(); +uint32_t getTrip2(void); // Total in 10m increments -uint32_t getTotal(); +uint32_t getTotal(void); -// Distance update from the motor, distance since motor power on in 10m increments +// Distance update vanuit de motor (afstand sinds motor power-on) void distanceUpdate(uint32_t distance); -// Load distances from flash -void loadDistances(); - -// Write distances to flash -void saveDistances(); +// Laad trip-data uit NVS (via batData) +void loadDistances(void); diff --git a/partitions.csv b/partitions.csv index 1898d0a..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, -littlefs, data, littlefs, , 1M, \ No newline at end of file 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 b/sdkconfig.tesla.15Ah similarity index 100% rename from sdkconfig.Tesla rename to sdkconfig.tesla.15Ah diff --git a/sdkconfig.TeslaS b/sdkconfig.tesla.20Ah similarity index 100% rename from sdkconfig.TeslaS rename to sdkconfig.tesla.20Ah index 1f88132..33b4881 100644 --- a/sdkconfig.TeslaS +++ b/sdkconfig.tesla.20Ah @@ -37,8 +37,8 @@ CONFIG_ESP32C3_DEFAULT_CPU_FREQ_160=y # 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 diff --git a/sdkconfig.TeslaTour b/sdkconfig.teslatour.15Ah similarity index 100% rename from sdkconfig.TeslaTour rename to sdkconfig.teslatour.15Ah From b814799f1fa46b51c8d6dd02e7780c639833c5fb Mon Sep 17 00:00:00 2001 From: mooiweertje Date: Fri, 31 Jul 2026 05:55:40 +0200 Subject: [PATCH 33/34] tinycleanup --- main/main.cpp | 1 - sdkconfig.defaults.esp32c3 | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/main/main.cpp b/main/main.cpp index b1a336f..dfff9f8 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -85,7 +85,6 @@ TimerHandle_t healthCheckTimer ; static void checkMyTaskHealth(TimerHandle_t xTimer) { if (!myTaskAlive) { batDataSave(); - batDataSave(); esp_restart(); } myTaskAlive = false; // Reset voor volgende check diff --git a/sdkconfig.defaults.esp32c3 b/sdkconfig.defaults.esp32c3 index e5bd942..1a90b98 100644 --- a/sdkconfig.defaults.esp32c3 +++ b/sdkconfig.defaults.esp32c3 @@ -11,6 +11,7 @@ # 7(--) - RST - RST # 8(--) - VCC - 3.3V + CONFIG_ION_BUTTON=n CONFIG_ION_LED_PIN=3 CONFIG_ION_UART=1 From 733679f6c7ec215269351f0b23460c08ed32e359 Mon Sep 17 00:00:00 2001 From: mooiweertje Date: Fri, 31 Jul 2026 11:55:45 +0200 Subject: [PATCH 34/34] bigShot --- .gitattributes | 1 + .github/workflows/main.yml | 46 +----- .gitignore | 2 + .gitmodules | 6 - CMakeLists.txt | 6 +- buildscripts | 1 - components/esp32-button | 1 - components/esp32_button/CMakeLists.txt | 14 ++ components/esp32_button/Kconfig | 30 ++++ components/esp32_button/LICENSE | 21 +++ components/esp32_button/README.md | 44 ++++++ components/esp32_button/component.mk | 2 + components/esp32_button/include/button.h | 43 +++++ components/esp32_button/src/button.c | 145 +++++++++++++++++ components/mqtt_helper | 1 - components/wifi_helper | 1 - main/CMakeLists.txt | 2 +- main/bat.cpp | 23 +-- main/bow.cpp | 11 ++ main/calibration.cpp | 51 ++++++ main/calibration.h | 5 + main/ctrl_event_group.cpp | 22 +++ main/ctrl_event_group.h | 24 +++ main/cu2.cpp | 43 ++--- main/cu2.h | 9 +- main/main.cpp | 191 +++++------------------ main/msg_handling.cpp | 91 +++++++++++ main/msg_handling.h | 14 ++ main/states/turn_motor_on.cpp | 2 +- main/storage.cpp | 42 ++--- main/storage.h | 5 +- main/trip.cpp | 2 +- sdkconfig.10Ah | 65 ++++++++ sdkconfig.15Ah | 65 ++++++++ sdkconfig.20Ah | 65 ++++++++ sdkconfig.defaults | 28 ++-- sdkconfig.supermini | 56 +++++++ 37 files changed, 875 insertions(+), 305 deletions(-) delete mode 160000 buildscripts delete mode 160000 components/esp32-button create mode 100644 components/esp32_button/CMakeLists.txt create mode 100644 components/esp32_button/Kconfig create mode 100644 components/esp32_button/LICENSE create mode 100644 components/esp32_button/README.md create mode 100644 components/esp32_button/component.mk create mode 100644 components/esp32_button/include/button.h create mode 100644 components/esp32_button/src/button.c delete mode 160000 components/mqtt_helper delete mode 160000 components/wifi_helper create mode 100644 main/calibration.cpp create mode 100644 main/calibration.h create mode 100644 main/ctrl_event_group.cpp create mode 100644 main/ctrl_event_group.h create mode 100644 main/msg_handling.cpp create mode 100644 main/msg_handling.h create mode 100644 sdkconfig.10Ah create mode 100644 sdkconfig.15Ah create mode 100644 sdkconfig.20Ah create mode 100644 sdkconfig.supermini diff --git a/.gitattributes b/.gitattributes index 62d6803..26b14a9 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5,6 +5,7 @@ *.sh text eol=lf *.json text eol=lf sdkconfig text eol=lf +sdkconfig.* text eol=lf .gitattributes text eol=lf .gitignore text eol=lf .gitmodules text eol=lf diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3cc0808..17fc131 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,50 +15,14 @@ jobs: matrix: target: [esp32, esp32c3] display: [cu2, cu3] - custom: [std] - include: - - target: esp32 - display: cu3 - custom: mike - - target: esp32 - display: cu3 - custom: wroom2xrelay - - target: esp32 - display: cu2 - custom: gpu7990 - - target: esp32 - display: cu3 - custom: gpu7990 - - target: esp32 - display: cu2 - custom: dvjcodec - - target: esp32 - display: cu3 - custom: dvjcodec - - target: esp32c3 - display: cu2 - custom: tesla.10Ah - - target: esp32c3 - display: cu2 - custom: tesla.15Ah - - target: esp32c3 - display: cu2 - custom: tesla.20Ah - - target: esp32c3 - display: cu3 - custom: teslatour.15Ah + battery: [10Ah, 15Ah, 20Ah] steps: - name: Checkout repo uses: actions/checkout@v3 - with: - submodules: 'recursive' - + # We should be able to use SDKCONFIG_DEFAULTS, but it seems to be broken, this also works. # The echo makes sure there is a newline inbetween the original, and added part - - run: echo >> sdkconfig.defaults.${{ matrix.target }}; cat sdkconfig.${{ matrix.display }} >> sdkconfig.defaults.${{ matrix.target }} - - - if: ${{ matrix.custom != 'std' }} - run: echo >> sdkconfig.defaults.${{ matrix.target }}; cat sdkconfig.${{ matrix.custom }} >> sdkconfig.defaults.${{ matrix.target }} + - run: echo >> sdkconfig.defaults.${{ matrix.target }}; cat sdkconfig.${{ matrix.display }} >> sdkconfig.defaults.${{ matrix.target }}; cat sdkconfig.${{ matrix.battery }} >> sdkconfig.defaults.${{ matrix.target }} # Show used config - run: cat sdkconfig.defaults.${{ matrix.target }} @@ -68,11 +32,11 @@ jobs: with: esp_idf_version: v5.5.1 target: ${{ matrix.target }} - path: '' + path: . - uses: actions/upload-artifact@v4 with: - name: firmware-${{ matrix.target }}-${{ matrix.display }}-${{ matrix.custom }} + name: firmware-${{ matrix.target }}-${{ matrix.display }}-${{ matrix.battery }} path: | ${{ github.workspace }}/build/bootloader/bootloader.bin ${{ github.workspace }}/build/partition_table/partition-table.bin diff --git a/.gitignore b/.gitignore index e801888..0568bda 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ sdkconfig *.pyc sdk ipch/ +.idea +*.iml \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 8664b7b..81d4189 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,12 +1,6 @@ [submodule "buildscripts"] path = buildscripts url = https://github.com/void-spark/esp32_win_buildscripts.git -[submodule "components/wifi_helper"] - path = components/wifi_helper - url = https://github.com/void-spark/esp32_wifi_helper.git -[submodule "components/mqtt_helper"] - path = components/mqtt_helper - url = https://github.com/void-spark/esp32_mqtt_helper.git [submodule "components/esp32-button"] path = components/esp32-button url = https://github.com/void-spark/esp32-button.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 42f08d2..e8dec5b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,8 @@ # The following lines of boilerplate have to be in your project's CMakeLists # in this exact order for cmake to work correctly -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.22) include($ENV{IDF_PATH}/tools/cmake/project.cmake) -project(ion1-nowifi) +# "Trim" the build. Include the minimal set of components, main, and anything it depends on. +idf_build_set_property(MINIMAL_BUILD ON) +project(ion1) \ No newline at end of file diff --git a/buildscripts b/buildscripts deleted file mode 160000 index 2985cad..0000000 --- a/buildscripts +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2985cada1d357b46a32e7cf47a8243b680302650 diff --git a/components/esp32-button b/components/esp32-button deleted file mode 160000 index 3497a93..0000000 --- a/components/esp32-button +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3497a9308e1a44edae18fa14619001e7cf67a7e0 diff --git a/components/esp32_button/CMakeLists.txt b/components/esp32_button/CMakeLists.txt new file mode 100644 index 0000000..32d2278 --- /dev/null +++ b/components/esp32_button/CMakeLists.txt @@ -0,0 +1,14 @@ +if(IDF_VERSION_MAJOR GREATER_EQUAL 6) + idf_component_register(SRC_DIRS src + REQUIRES log driver esp_timer esp_driver_gpio + INCLUDE_DIRS include) +elseif(IDF_VERSION_MAJOR GREATER_EQUAL 4) + idf_component_register(SRC_DIRS src + REQUIRES log driver esp_timer + INCLUDE_DIRS include) +else() + set(COMPONENT_SRCDIRS src) + set(COMPONENT_ADD_INCLUDEDIRS include) + set(COMPONENT_REQUIRES log driver esp_timer) + register_component() +endif() diff --git a/components/esp32_button/Kconfig b/components/esp32_button/Kconfig new file mode 100644 index 0000000..2b93ba3 --- /dev/null +++ b/components/esp32_button/Kconfig @@ -0,0 +1,30 @@ +menu "ESP32 Button" + +config ESP32_BUTTON_LONG_PRESS_DURATION_MS + int "Button long press duration in ms" + default 2000 + help + Defines how long a button has to be pressed to trigger BUTTON_HELD events. + +config ESP32_BUTTON_LONG_PRESS_REPEAT_MS + int "Button long press repetition in ms" + default 50 + help + Defines in which interval BUTTON_HELD events are generated while a button is long pressed. + +config ESP32_BUTTON_QUEUE_SIZE + int "Size of the button event queue" + default 4 + help + Defines how many button events the queue can store. If the queue is full because the events + are not retreived from the queue quickly enough, the button task has to wait and does not + process any button presses during this time. + +config ESP32_BUTTON_TASK_STACK_SIZE + int "Button update task stack size" + default 1024 + help + Configure the stack size of the button task in bytes. Reducing this value to 2048 should not + cause any problems. + +endmenu \ No newline at end of file diff --git a/components/esp32_button/LICENSE b/components/esp32_button/LICENSE new file mode 100644 index 0000000..652c2dc --- /dev/null +++ b/components/esp32_button/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Craft Metrics Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/components/esp32_button/README.md b/components/esp32_button/README.md new file mode 100644 index 0000000..6ce6563 --- /dev/null +++ b/components/esp32_button/README.md @@ -0,0 +1,44 @@ +# Button press detector + +This implements a version of [THE ULTIMATE DEBOUNCER(TM) from hackaday](https://hackaday.com/2015/12/10/embed-with-elliot-debounce-your-noisy-buttons-part-ii/ +). + +It can monitor multiple pins, and sends button events over a queue for your application to process. + +## Available input GPIO pins + +Only the following pins can be used as inputs on the ESP32: + +0-19, 21-23, 25-27, 32-39 + + +## Example Usage + +```c +button_event_t ev; +QueueHandle_t button_events = button_init(BIT64(BUTTON_1) | BIT64(BUTTON_2)); +while (true) { + if (xQueueReceive(button_events, &ev, 1000/portTICK_PERIOD_MS)) { + if ((ev.pin == BUTTON_1) && (ev.event == BUTTON_DOWN)) { + // ... + } + if ((ev.pin == BUTTON_2) && (ev.event == BUTTON_DOWN)) { + // ... + } + } +} +``` + +## Event Types + +### BUTTON_DOWN + +Triggered when the button is first considered pressed. + +### BUTTON_UP + +Triggered when the button is considered released. In most cases you can use either the UP or DOWN event for your application, and ignore the other. + +### BUTTON_HELD + +Triggered starting after 2 seconds of long holding a button and then every 50ms thereafter. \ No newline at end of file diff --git a/components/esp32_button/component.mk b/components/esp32_button/component.mk new file mode 100644 index 0000000..e853332 --- /dev/null +++ b/components/esp32_button/component.mk @@ -0,0 +1,2 @@ +COMPONENT_ADD_INCLUDEDIRS = include +COMPONENT_SRCDIRS = src \ No newline at end of file diff --git a/components/esp32_button/include/button.h b/components/esp32_button/include/button.h new file mode 100644 index 0000000..428ef97 --- /dev/null +++ b/components/esp32_button/include/button.h @@ -0,0 +1,43 @@ +#ifndef ESP32_BUTTON_H +#define ESP32_BUTTON_H + +#include "freertos/queue.h" +#include "driver/gpio.h" + +#ifndef CONFIG_ESP32_BUTTON_LONG_PRESS_DURATION_MS +#define CONFIG_ESP32_BUTTON_LONG_PRESS_DURATION_MS (2000) +#endif + +#ifndef CONFIG_ESP32_BUTTON_LONG_PRESS_REPEAT_MS +#define CONFIG_ESP32_BUTTON_LONG_PRESS_REPEAT_MS (50) +#endif + +#ifndef CONFIG_ESP32_BUTTON_QUEUE_SIZE +#define CONFIG_ESP32_BUTTON_QUEUE_SIZE (4) +#endif + +#ifndef CONFIG_ESP32_BUTTON_TASK_STACK_SIZE +#define CONFIG_ESP32_BUTTON_TASK_STACK_SIZE 3072 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define BUTTON_DOWN (1) +#define BUTTON_UP (2) +#define BUTTON_HELD (3) + +typedef struct { + uint8_t pin; + uint8_t event; +} button_event_t; + +QueueHandle_t button_init(unsigned long long pin_select); +QueueHandle_t pulled_button_init(unsigned long long pin_select, gpio_pull_mode_t pull_mode); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/components/esp32_button/src/button.c b/components/esp32_button/src/button.c new file mode 100644 index 0000000..87fd2b1 --- /dev/null +++ b/components/esp32_button/src/button.c @@ -0,0 +1,145 @@ +#include +#include +#include +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "driver/gpio.h" +#include "esp_timer.h" +#include "esp_log.h" +#include "esp_timer.h" + +#include "button.h" + +#define TAG "BUTTON" + +typedef struct { + uint8_t pin; + bool inverted; + uint16_t history; + uint32_t down_time; + uint32_t next_long_time; +} debounce_t; + +static int pin_count = -1; +static debounce_t * debounce; +static QueueHandle_t queue; + +static void update_button(debounce_t *d) { + d->history = (d->history << 1) | gpio_get_level(d->pin); +} + +#define MASK 0b1111000000111111 +static bool button_rose(debounce_t *d) { + if ((d->history & MASK) == 0b0000000000111111) { + d->history = 0xffff; + return 1; + } + return 0; +} +static bool button_fell(debounce_t *d) { + if ((d->history & MASK) == 0b1111000000000000) { + d->history = 0x0000; + return 1; + } + return 0; +} +static bool button_down(debounce_t *d) { + if (d->inverted) return button_fell(d); + return button_rose(d); +} +static bool button_up(debounce_t *d) { + if (d->inverted) return button_rose(d); + return button_fell(d); +} + +static uint32_t millis() { + return esp_timer_get_time() / 1000; +} + +static void send_event(debounce_t db, int ev) { + button_event_t event = { + .pin = db.pin, + .event = ev, + }; + xQueueSend(queue, &event, portMAX_DELAY); +} + +static void button_task(void *pvParameter) +{ + for (;;) { + for (int idx=0; idx= debounce[idx].next_long_time) { + ESP_LOGD(TAG, "%d LONG", debounce[idx].pin); + debounce[idx].next_long_time = debounce[idx].next_long_time + CONFIG_ESP32_BUTTON_LONG_PRESS_REPEAT_MS; + send_event(debounce[idx], BUTTON_HELD); + } else if (button_down(&debounce[idx]) && debounce[idx].down_time == 0) { + debounce[idx].down_time = millis(); + ESP_LOGD(TAG, "%d DOWN", debounce[idx].pin); + debounce[idx].next_long_time = debounce[idx].down_time + CONFIG_ESP32_BUTTON_LONG_PRESS_DURATION_MS; + send_event(debounce[idx], BUTTON_DOWN); + } + } + vTaskDelay(10/portTICK_PERIOD_MS); + } +} + +QueueHandle_t button_init(unsigned long long pin_select) { + return pulled_button_init(pin_select, GPIO_FLOATING); +} + + +QueueHandle_t pulled_button_init(unsigned long long pin_select, gpio_pull_mode_t pull_mode) +{ + if (pin_count != -1) { + ESP_LOGI(TAG, "Already initialized"); + return NULL; + } + + // Configure the pins + gpio_config_t io_conf; + io_conf.intr_type = GPIO_INTR_POSEDGE; + io_conf.mode = GPIO_MODE_INPUT; + io_conf.pull_up_en = (pull_mode == GPIO_PULLUP_ONLY || pull_mode == GPIO_PULLUP_PULLDOWN); + io_conf.pull_down_en = (pull_mode == GPIO_PULLDOWN_ONLY || pull_mode == GPIO_PULLUP_PULLDOWN); + io_conf.pin_bit_mask = pin_select; + io_conf.intr_type = GPIO_INTR_DISABLE; + gpio_config(&io_conf); + + // Scan the pin map to determine number of pins + pin_count = 0; + for (int pin=0; pin<=39; pin++) { + if ((1ULL< 0, will do the same, but will stop and return MSG_NO_REPLY on the 'attempts'th timeout. + * + * Returns: + * MSG_OK if we received a response. + * MSG_NO_REPLY (only for attempts > 0) if after the given attempts, no valid response was received. + */ readResult exchange(const messageType& outMessage, messageType *inMessage, const TickType_t timeout, const uint32_t attempts) { writeMessage(outMessage); uint32_t count = 1; diff --git a/main/calibration.cpp b/main/calibration.cpp new file mode 100644 index 0000000..21c777e --- /dev/null +++ b/main/calibration.cpp @@ -0,0 +1,51 @@ +#include "cmds.h" +#include "storage.h" +#include "calibration.h" +#include + +#define CAL_SIZE 10 + +bool handleCalibrationMessage(const messageType& message) { + if(message.type == MSG_CMD_REQ && message.payloadSize == 4 && message.command == CMD_GET_DATA && message.payload[1] == 0x38 && message.payload[3] == 0x3a) { + // GET DATA 38 and 3a + + // Fallback calibration (10 bytes) + // This is from a test on an old sparta. It is probably not very good for most, + // but it's a better starting point than just 0s. + // I wonder what happens if we use status code 1 instead? Will the motor auto calibrate? + // Structure here: + // 0x94: Value is signed integer, size 2, more values follow. + // 0x38: ID + // 0x4b15: the actual first value: 19221 + // 0x28: Value is float, size 4, no values follow. + // 0x3a: ID + // 0x3e917950: Second value: 0.284128666 + static const uint8_t fallback[CAL_SIZE] = { 0x94, 0x38, 0x4b, 0x15, 0x28, 0x3a, 0x3e, 0x91, 0x79, 0x50 }; + + static uint8_t payload[CAL_SIZE + 1] = {}; + + // First byte is status byte. 0x00 is Ok, 0x01 is not found. + payload[0] = 0x00; + + // Try reading calibration from NVS. + if(!calibrationLoad(payload + 1, CAL_SIZE)) { + // Failed to load calibration, use fallback instead. + memcpy(payload + 1, fallback, CAL_SIZE); + } + + writeMessage(cmdResp(message.source, MSG_BMS, message.command, payload, sizeof(payload))); + return true; + } else if(message.type == MSG_CMD_REQ && message.payloadSize == CAL_SIZE && message.command == CMD_PUT_DATA && message.payload[1] == 0x38 && message.payload[5] == 0x3a) { + // PUT DATA 38 and 3a + + if(!calibrationSave(message.payload, CAL_SIZE)) { + return true; + } + + uint8_t payload[] = {0x00}; + writeMessage(cmdResp(message.source, MSG_BMS, message.command, payload, sizeof(payload))); + return true; + } + + return false; +} \ No newline at end of file diff --git a/main/calibration.h b/main/calibration.h new file mode 100644 index 0000000..8987d4f --- /dev/null +++ b/main/calibration.h @@ -0,0 +1,5 @@ +#pragma once +#include +#include + +bool handleCalibrationMessage(const messageType& message); \ No newline at end of file diff --git a/main/ctrl_event_group.cpp b/main/ctrl_event_group.cpp new file mode 100644 index 0000000..f01521a --- /dev/null +++ b/main/ctrl_event_group.cpp @@ -0,0 +1,22 @@ +#include "ctrl_event_group.h" + +static EventGroupHandle_t controlEventGroup; + +void initControlEventGroup() { + controlEventGroup = xEventGroupCreate(); +} + +void setControlBits(const EventBits_t uxBitsToSet) { + xEventGroupSetBits(controlEventGroup, uxBitsToSet); +} + +void clearControlBits(const EventBits_t uxBitsToClear) { + xEventGroupClearBits(controlEventGroup, uxBitsToClear); +} + +EventBits_t waitControlBits(const EventBits_t uxBitsToWaitFor, + const BaseType_t xClearOnExit, + const BaseType_t xWaitForAllBits, + TickType_t xTicksToWait) { + return xEventGroupWaitBits(controlEventGroup, uxBitsToWaitFor, xClearOnExit, xWaitForAllBits, xTicksToWait); +} \ No newline at end of file diff --git a/main/ctrl_event_group.h b/main/ctrl_event_group.h new file mode 100644 index 0000000..649f486 --- /dev/null +++ b/main/ctrl_event_group.h @@ -0,0 +1,24 @@ +#pragma once + +#include "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" + +static const int BUTTON_MODE_SHORT_PRESS_BIT = BIT0; +static const int BUTTON_MODE_LONG_PRESS_BIT = BIT1; +static const int BUTTON_LIGHT_SHORT_PRESS_BIT = BIT2; +static const int BUTTON_LIGHT_LONG_PRESS_BIT = BIT3; +static const int IGNORE_HELD_BIT = BIT4; +static const int WAKEUP_BIT = BIT5; +static const int CALIBRATE_BIT = BIT6; +static const int MEASURE_BAT_BIT = BIT7; + +void initControlEventGroup(); + +void setControlBits(const EventBits_t uxBitsToSet); + +void clearControlBits(const EventBits_t uxBitsToSet); + +EventBits_t waitControlBits(const EventBits_t uxBitsToWaitFor, + const BaseType_t xClearOnExit, + const BaseType_t xWaitForAllBits, + TickType_t xTicksToWait); \ No newline at end of file diff --git a/main/cu2.cpp b/main/cu2.cpp index 0a375d3..59773a3 100644 --- a/main/cu2.cpp +++ b/main/cu2.cpp @@ -4,6 +4,7 @@ #include "freertos/event_groups.h" #include "bow.h" #include "cmds.h" +#include "ctrl_event_group.h" #include "cu2.h" @@ -15,29 +16,13 @@ static const int CHECK_BUTTON_BIT = BIT0; static TimerHandle_t buttonCheckTimer; static EventGroupHandle_t displayEventGroupHandle; -static EventGroupHandle_t _eventGroupHandle; - -static int _buttonModeShortPressBit; -static int _buttonModeLongPressBit; -static int _buttonLightShortPressBit; -static int _buttonLightLongPressBit; -static int _ignoreHeldBit; - -static void buttonCheckTimerCallback(TimerHandle_t xTimer) { xEventGroupSetBits(displayEventGroupHandle, CHECK_BUTTON_BIT); } - -void initCu2(EventGroupHandle_t eventGroupHandle, - const int buttonModeShortPressBit, - const int buttonModeLongPressBit, - const int buttonLightShortPressBit, - const int buttonLightLongPressBit, - const int ignoreHeldBit) { + +static void buttonCheckTimerCallback(TimerHandle_t xTimer) { + xEventGroupSetBits(displayEventGroupHandle, CHECK_BUTTON_BIT); +} + +void initCu2() { displayEventGroupHandle = xEventGroupCreate(); - _eventGroupHandle = eventGroupHandle; - _buttonModeShortPressBit = buttonModeShortPressBit; - _buttonModeLongPressBit = buttonModeLongPressBit; - _buttonLightShortPressBit = buttonLightShortPressBit; - _buttonLightLongPressBit = buttonLightLongPressBit; - _ignoreHeldBit = ignoreHeldBit; buttonCheckTimer = xTimerCreate("buttonCheckTimer", (100 / portTICK_PERIOD_MS), pdTRUE, (void *)0, buttonCheckTimerCallback); } @@ -50,8 +35,8 @@ void buttonCheck() { static bool ignoreFirst = false; - EventBits_t bits = xEventGroupWaitBits(_eventGroupHandle, _ignoreHeldBit, true, false, 0); - if((bits & _ignoreHeldBit) != 0) { + EventBits_t bits = waitControlBits(IGNORE_HELD_BIT, true, false, 0); + if((bits & IGNORE_HELD_BIT) != 0) { ignoreFirst = true; } @@ -75,18 +60,18 @@ void buttonCheck() { } if(heldMode == LONG_PRESS_UPDATES) { - xEventGroupSetBits(_eventGroupHandle, _buttonModeLongPressBit); + setControlBits(BUTTON_MODE_LONG_PRESS_BIT); } if(heldLight == LONG_PRESS_UPDATES) { - xEventGroupSetBits(_eventGroupHandle, _buttonLightLongPressBit); + setControlBits(BUTTON_LIGHT_LONG_PRESS_BIT); } if(heldMode > 0 && !pressMode) { if(ignoreFirst) { ignoreFirst = false; } else if(heldMode < LONG_PRESS_UPDATES) { - xEventGroupSetBits(_eventGroupHandle, _buttonModeShortPressBit); + setControlBits(BUTTON_MODE_SHORT_PRESS_BIT); } heldMode = 0; } @@ -95,7 +80,7 @@ void buttonCheck() { if(ignoreFirst) { ignoreFirst = false; } else if(heldLight < LONG_PRESS_UPDATES) { - xEventGroupSetBits(_eventGroupHandle, _buttonLightShortPressBit); + setControlBits(BUTTON_LIGHT_SHORT_PRESS_BIT); } heldLight = 0; } @@ -109,7 +94,7 @@ void startButtonCheck() { xTimerStart(buttonCheckTimer, 0); } void stopButtonCheck() { xTimerStop(buttonCheckTimer, 0); } void ignorePress() { - xEventGroupSetBits(_eventGroupHandle, _ignoreHeldBit); + setControlBits(IGNORE_HELD_BIT); } uint32_t digits(uint32_t value, size_t digits, size_t atleast) { diff --git a/main/cu2.h b/main/cu2.h index bf8712b..4d25128 100644 --- a/main/cu2.h +++ b/main/cu2.h @@ -1,18 +1,11 @@ #pragma once #include -#include "freertos/FreeRTOS.h" -#include "freertos/event_groups.h" enum assist_level { ASS_OFF = 0, ASS_ECO, ASS_NORMAL, ASS_POWER }; enum blink_speed { BLNK_OFF = 0, BLNK_FAST, BLNK_SLOW, BLNK_SOLID }; -void initCu2(EventGroupHandle_t eventGroupHandle, - const int buttonModeShortPressBit, - const int buttonModeLongPressBit, - const int buttonLightShortPressBit, - const int buttonLightLongPressBit, - const int ignoreHeldBit); +void initCu2(); void buttonCheck(); diff --git a/main/main.cpp b/main/main.cpp index dfff9f8..5b10689 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -31,8 +31,11 @@ #include "motor.h" #include "relays.h" #include "trip.h" +#include "calibration.h" #include "charge.h" #include "states/states.h" +#include "ctrl_event_group.h" +#include "msg_handling.h" #include "storage.h" static const char *TAG = "app"; @@ -54,29 +57,11 @@ static const char *TAG = "app"; #define CHARGE_PIN ((gpio_num_t)CONFIG_ION_CHARGE_PIN) #endif -static const int BUTTON_MODE_SHORT_PRESS_BIT = BIT0; -static const int BUTTON_MODE_LONG_PRESS_BIT = BIT1; -static const int BUTTON_LIGHT_SHORT_PRESS_BIT = BIT2; -static const int BUTTON_LIGHT_LONG_PRESS_BIT = BIT3; -static const int IGNORE_HELD_BIT = BIT4; -static const int WAKEUP_BIT = BIT5; -static const int CALIBRATE_BIT = BIT6; -static const int MEASURE_BAT_BIT = BIT7; - -static EventGroupHandle_t controlEventGroup; - -enum messageHandlingResult { - // We got a handoff back, so we get to send the next message - CONTROL_TO_US, - // We had to reply to a message, so sender is next to send a message. - CONTROL_TO_SENDER, - // We did not get a timely reply to a handoff message. - HANDOFF_TIMEOUT -}; - static TimerHandle_t measureBatTimer; -static void measureBatTimerCallback(TimerHandle_t xTimer) { xEventGroupSetBits(controlEventGroup, MEASURE_BAT_BIT); } +static void measureBatTimerCallback(TimerHandle_t xTimer) { + setControlBits(MEASURE_BAT_BIT); +} #if CONFIG_ION_KEEPALIVE volatile bool myTaskAlive = false; @@ -91,110 +76,30 @@ static void checkMyTaskHealth(TimerHandle_t xTimer) { } #endif - -static messageHandlingResult handleMotorMessage(ion_state * state) { - messageType message = {}; - readResult result; - do { - // Replies to handoff should be a lot quicker then 250ms - result = readMessage(&message, 250 / portTICK_PERIOD_MS); - if(result == MSG_TIMEOUT) { - // Most likely the motor turned off, after we told it to (XHP, Toprun doesn't seem to stop) - return HANDOFF_TIMEOUT; - } - } while(result != MSG_OK || message.target != MSG_BMS); - - if(message.type == MSG_HANDOFF) { - // Handoff back to us - return CONTROL_TO_US; - } else if(message.type == MSG_PING_REQ) { - // ESP_LOGI(TAG, "|PING"); - writeMessage(pingResp(message.source, MSG_BMS)); - return CONTROL_TO_SENDER; - } else if(message.type == MSG_CMD_REQ && message.payloadSize == 0 && message.command == 0x01) { - // MYSTERY BATTERY COMMAND 01 - uint8_t payload[] = {0x02, 0x02}; - writeMessage(cmdResp(message.source, MSG_BMS, message.command, payload, sizeof(payload))); - return CONTROL_TO_SENDER; - } else if(message.type == MSG_CMD_REQ && message.payloadSize == 0 && message.command == CMD_BAT_STATUS_MOTOR_OFF) { - state->motorOffAck = true; - writeMessage(cmdResp(message.source, MSG_BMS, message.command)); - return CONTROL_TO_SENDER; - } else if(message.type == MSG_CMD_REQ && message.payloadSize == 1 && message.command == CMD_BAT_STATUS_ASSIST) { - writeMessage(cmdResp(message.source, MSG_BMS, message.command)); - return CONTROL_TO_SENDER; - } else if(message.type == MSG_CMD_REQ && message.payloadSize == 0 && message.command == CMD_BAT_WAKEUP) { - xEventGroupSetBits(controlEventGroup, WAKEUP_BIT); - writeMessage(cmdResp(message.source, MSG_BMS, message.command)); - return CONTROL_TO_SENDER; - } else if(message.type == MSG_CMD_REQ && message.payloadSize == 1 && message.command == CMD_BAT_CALIBRATE) { - xEventGroupSetBits(controlEventGroup, CALIBRATE_BIT); - writeMessage(cmdResp(message.source, MSG_BMS, message.command)); - return CONTROL_TO_SENDER; - } else if(message.type == MSG_CMD_REQ && message.payloadSize == 1 && message.command == CMD_BAT_SET_LIGHT) { - setLight(message.payload[0]); - writeMessage(cmdResp(message.source, MSG_BMS, message.command)); - return CONTROL_TO_SENDER; - } else if(message.type == MSG_CMD_REQ && message.payloadSize == 1 && message.command == CMD_BAT_SET_ASSIST_LEVEL) { - state->level = message.payload[0]; - writeMessage(cmdResp(message.source, MSG_BMS, message.command)); - return CONTROL_TO_SENDER; -#if CONFIG_ION_CU3 - } else if(handleCu3Message(message)) { - return CONTROL_TO_SENDER; -#endif - } else if(message.type == MSG_CMD_REQ && message.payloadSize == 2 && message.command == CMD_GET_DATA && message.payload[1] == 0x2a) { - // GET DATA 002a 00:2a(Unknown) - uint8_t payload[] = {0x00, message.payload[0], message.payload[1], 0x01}; - 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) { - 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 - state->speed = toUint16(message.payload, 2); - distanceUpdate(toUint32(message.payload, 6)); - - uint8_t payload[] = {0x00}; - writeMessage(cmdResp(message.source, MSG_BMS, message.command, payload, sizeof(payload))); - requestDisplayUpdate(); - 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(!calibrationSave(message.payload)) { - return CONTROL_TO_SENDER; - } - - uint8_t payload[] = {0x00}; - writeMessage(cmdResp(message.source, MSG_BMS, message.command, payload, sizeof(payload))); - return CONTROL_TO_SENDER; - } - - ESP_LOGI(TAG, "Unexpected: Tgt:%d, Src:%d, Type:%d, Command:%d", message.target, message.source, message.type, message.command); - ESP_LOG_BUFFER_HEX(TAG, message.payload, message.payloadSize); - - return CONTROL_TO_SENDER; -} - -static bool handoff(ion_state * state) { +static void doHandoff(ion_state * state) { #if CONFIG_ION_CU3 - uint8_t handoffTarget = MSG_DISPLAY; + writeMessage(handoffMsg(MSG_DISPLAY)); #else - uint8_t handoffTarget = MSG_MOTOR; + writeMessage(handoffMsg(MSG_MOTOR)); #endif - writeMessage(handoffMsg(handoffTarget)); while(true) { // Keep handling responses, and subsequent incoming messages, until someone hands off back to us. - messageHandlingResult result = handleMotorMessage(state); + messageHandlingResult result = handleMessage(state); if(result == CONTROL_TO_US) { - return true; - } - if(result == HANDOFF_TIMEOUT) { - return false; + return; + } else if(result == HANDOFF_TIMEOUT) { + // Timeout, assume motor turned off. CU3 will keep chatting, so no timeout then. + // Could also have been a CRC error at some point, in that case we might have to start things back up. +#if CONFIG_ION_CU2 + stopButtonCheck(); +#endif + if(state->state == MOTOR_OFF) { + state->doHandoffs = false; + toIdleState(state); + } + return; } } } @@ -239,13 +144,7 @@ static void my_task(void *pvParameter) { loadCharge(); #if CONFIG_ION_CU2 - initCu2(controlEventGroup, - BUTTON_MODE_SHORT_PRESS_BIT, - BUTTON_MODE_LONG_PRESS_BIT, - BUTTON_LIGHT_SHORT_PRESS_BIT, - BUTTON_LIGHT_LONG_PRESS_BIT, - IGNORE_HELD_BIT - ); + initCu2(); #endif initDisplay(); @@ -295,13 +194,13 @@ static void my_task(void *pvParameter) { } #endif - EventBits_t buttonBits = xEventGroupWaitBits(controlEventGroup, BUTTON_MODE_SHORT_PRESS_BIT | BUTTON_MODE_LONG_PRESS_BIT | BUTTON_LIGHT_SHORT_PRESS_BIT | BUTTON_LIGHT_LONG_PRESS_BIT | WAKEUP_BIT | CALIBRATE_BIT, true, false, 0); - const bool modeShortPress = (buttonBits & BUTTON_MODE_SHORT_PRESS_BIT) != 0; - const bool modeLongPress = (buttonBits & BUTTON_MODE_LONG_PRESS_BIT) != 0; + EventBits_t buttonBits = waitControlBits(BUTTON_MODE_SHORT_PRESS_BIT | BUTTON_MODE_LONG_PRESS_BIT | BUTTON_LIGHT_SHORT_PRESS_BIT | BUTTON_LIGHT_LONG_PRESS_BIT | WAKEUP_BIT | CALIBRATE_BIT, true, false, 0); + const bool modeShortPress = (buttonBits & BUTTON_MODE_SHORT_PRESS_BIT) != 0; + const bool modeLongPress = (buttonBits & BUTTON_MODE_LONG_PRESS_BIT) != 0; const bool lightShortPress = (buttonBits & BUTTON_LIGHT_SHORT_PRESS_BIT) != 0; - const bool lightLongPress = (buttonBits & BUTTON_LIGHT_LONG_PRESS_BIT) != 0; - const bool wakeup = (buttonBits & WAKEUP_BIT) != 0; - const bool calibrate = (buttonBits & CALIBRATE_BIT) != 0; + const bool lightLongPress = (buttonBits & BUTTON_LIGHT_LONG_PRESS_BIT) != 0; + const bool wakeup = (buttonBits & WAKEUP_BIT) != 0; + const bool calibrate = (buttonBits & CALIBRATE_BIT) != 0; if(lightShortPress) { toggleLight(); @@ -316,18 +215,10 @@ static void my_task(void *pvParameter) { #if CONFIG_ION_ADC EventBits_t bitsToCheck = MEASURE_BAT_BIT; - EventBits_t bits = xEventGroupWaitBits(controlEventGroup, bitsToCheck, false, false, 0); + EventBits_t bits = waitControlBits(bitsToCheck, false, false, 0); if((bits & MEASURE_BAT_BIT) != 0) { - xEventGroupClearBits(controlEventGroup, MEASURE_BAT_BIT); - - // Batterij meten + clearControlBits(MEASURE_BAT_BIT); measureBat(); - -#if CONFIG_ION_CURR_ADC - // Stroom meten tegelijk - measureCurrent(); - chargeUpdate(getBatMv(), getBatMa()); -#endif } else #endif if(handleDisplayUpdate(&state)) { @@ -353,17 +244,7 @@ static void my_task(void *pvParameter) { } if(state.doHandoffs) { - if(!handoff(&state)) { - // Timeout, assume motor turned off. CU3 will keep chatting, so no timeout then. - // Could also have been a CRC error at some point, in that case we might have to start things back up. -#if CONFIG_ION_CU2 - stopButtonCheck(); -#endif - if(state.state == MOTOR_OFF) { - state.doHandoffs = false; - toIdleState(&state); - } - } + doHandoff(&state); } } @@ -380,7 +261,7 @@ extern "C" void app_main() { } ESP_ERROR_CHECK(ret); - controlEventGroup = xEventGroupCreate(); + initControlEventGroup(); xTaskCreatePinnedToCore(my_task, "my_task", 4096 * 2, NULL, 5, NULL, SECOND_CPU); @@ -392,13 +273,13 @@ extern "C" void app_main() { if(xQueueReceive(button_events, &ev, 1000 / portTICK_PERIOD_MS)) { if((ev.pin == BUTTON || ev.pin == BUTTON_EXT) && (ev.event == BUTTON_UP)) { held = false; - xEventGroupSetBits(controlEventGroup, BUTTON_MODE_SHORT_PRESS_BIT); + setControlBits(BUTTON_MODE_SHORT_PRESS_BIT); } if(!held && (ev.pin == BUTTON || ev.pin == BUTTON_EXT) && (ev.event == BUTTON_HELD)) { - xEventGroupSetBits(controlEventGroup, BUTTON_LIGHT_LONG_PRESS_BIT); + setControlBits(BUTTON_LIGHT_LONG_PRESS_BIT); held = true; } } } #endif -} \ No newline at end of file +} diff --git a/main/msg_handling.cpp b/main/msg_handling.cpp new file mode 100644 index 0000000..7c535db --- /dev/null +++ b/main/msg_handling.cpp @@ -0,0 +1,91 @@ +#include "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" +#include "esp_log.h" +#include "bytes.h" +#include "bow.h" +#include "cmds.h" +#include "ctrl_event_group.h" +#include "cu3.h" +#include "calibration.h" +#include "trip.h" +#include "display.h" +#include "relays.h" +#include "msg_handling.h" + +static const char *TAG = "msg_handling"; + +messageHandlingResult handleMessage(ion_state * state) { + messageType message = {}; + readResult result; + do { + // Replies to handoff should be a lot quicker then 250ms + result = readMessage(&message, 250 / portTICK_PERIOD_MS); + if(result == MSG_TIMEOUT) { + // Most likely the motor turned off, after we told it to (XHP, Toprun doesn't seem to stop) + // Or if we're handing of to CU3, it might not be there. + return HANDOFF_TIMEOUT; + } + } while(result != MSG_OK || message.target != MSG_BMS); + + if(message.type == MSG_HANDOFF) { + // Handoff back to us + return CONTROL_TO_US; + } else if(message.type == MSG_PING_REQ) { + // ESP_LOGI(TAG, "|PING"); + writeMessage(pingResp(message.source, MSG_BMS)); + return CONTROL_TO_SENDER; + } else if(message.type == MSG_CMD_REQ && message.payloadSize == 0 && message.command == 0x01) { + // MYSTERY BATTERY COMMAND 01 + uint8_t payload[] = {0x02, 0x02}; + writeMessage(cmdResp(message.source, MSG_BMS, message.command, payload, sizeof(payload))); + return CONTROL_TO_SENDER; + } else if(message.type == MSG_CMD_REQ && message.payloadSize == 0 && message.command == CMD_BAT_STATUS_MOTOR_OFF) { + state->motorOffAck = true; + writeMessage(cmdResp(message.source, MSG_BMS, message.command)); + return CONTROL_TO_SENDER; + } else if(message.type == MSG_CMD_REQ && message.payloadSize == 1 && message.command == CMD_BAT_STATUS_ASSIST) { + writeMessage(cmdResp(message.source, MSG_BMS, message.command)); + return CONTROL_TO_SENDER; + } else if(message.type == MSG_CMD_REQ && message.payloadSize == 0 && message.command == CMD_BAT_WAKEUP) { + setControlBits(WAKEUP_BIT); + writeMessage(cmdResp(message.source, MSG_BMS, message.command)); + return CONTROL_TO_SENDER; + } else if(message.type == MSG_CMD_REQ && message.payloadSize == 1 && message.command == CMD_BAT_CALIBRATE) { + setControlBits(CALIBRATE_BIT); + writeMessage(cmdResp(message.source, MSG_BMS, message.command)); + return CONTROL_TO_SENDER; + } else if(message.type == MSG_CMD_REQ && message.payloadSize == 1 && message.command == CMD_BAT_SET_LIGHT) { + setLight(message.payload[0]); + writeMessage(cmdResp(message.source, MSG_BMS, message.command)); + return CONTROL_TO_SENDER; + } else if(message.type == MSG_CMD_REQ && message.payloadSize == 1 && message.command == CMD_BAT_SET_ASSIST_LEVEL) { + state->level = message.payload[0]; + writeMessage(cmdResp(message.source, MSG_BMS, message.command)); + return CONTROL_TO_SENDER; +#if CONFIG_ION_CU3 + } else if(handleCu3Message(message)) { + return CONTROL_TO_SENDER; +#endif + } else if(handleCalibrationMessage(message)) { + return CONTROL_TO_SENDER; + } else if(message.type == MSG_CMD_REQ && message.payloadSize == 2 && message.command == CMD_GET_DATA && message.payload[1] == 0x2a) { + // GET DATA 002a 00:2a(Unknown) + uint8_t payload[] = {0x00, message.payload[0], message.payload[1], 0x01}; + writeMessage(cmdResp(message.source, MSG_BMS, message.command, payload, sizeof(payload))); + 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 + state->speed = toUint16(message.payload, 2); + distanceUpdate(toUint32(message.payload, 6)); + + uint8_t payload[] = {0x00}; + writeMessage(cmdResp(message.source, MSG_BMS, message.command, payload, sizeof(payload))); + requestDisplayUpdate(); + return CONTROL_TO_SENDER; + } + + ESP_LOGI(TAG, "Unexpected: Tgt:%d, Src:%d, Type:%d, Command:%d", message.target, message.source, message.type, message.command); + ESP_LOG_BUFFER_HEX(TAG, message.payload, message.payloadSize); + + return CONTROL_TO_SENDER; +} \ No newline at end of file diff --git a/main/msg_handling.h b/main/msg_handling.h new file mode 100644 index 0000000..cc66155 --- /dev/null +++ b/main/msg_handling.h @@ -0,0 +1,14 @@ +#pragma once + +#include "states/states.h" + +enum messageHandlingResult { + // We got a handoff back, so we get to send the next message + CONTROL_TO_US, + // We had to reply to a message, so sender is next to send a message. + CONTROL_TO_SENDER, + // We did not get a timely reply to a handoff message. + HANDOFF_TIMEOUT +}; + +messageHandlingResult handleMessage(ion_state * state); \ No newline at end of file diff --git a/main/states/turn_motor_on.cpp b/main/states/turn_motor_on.cpp index b943df1..5a5a41b 100644 --- a/main/states/turn_motor_on.cpp +++ b/main/states/turn_motor_on.cpp @@ -72,7 +72,7 @@ void handleTurnMotorOnState(ion_state * state) { startButtonCheck(); } else if(state->step == 4) { // Set default display, which is shown if the display isn't updated for a bit (?) - // setDefault assistLevel assistBlink wrench total trip light bars comma km top bottom miles batPercentage topVal bottomVal + // setDefault assistLevel assistBlink wrench total trip light bars comma km top bottom miles batPercentage topVal bottomVal // displayUpdateCu2(f, ASS_OFF, BLNK_SOLID, BLNK_OFF, BLNK_SOLID, BLNK_OFF, BLNK_OFF, BLNK_SOLID, BLNK_OFF, BLNK_SOLID, BLNK_SOLID, BLNK_SOLID, true, 100, 0xccc, 0xccccc); displayUpdateCu2(true, ASS_OFF, BLNK_SOLID, BLNK_OFF, BLNK_SOLID, BLNK_OFF, BLNK_OFF, BLNK_SOLID, BLNK_OFF, BLNK_SOLID, BLNK_OFF, BLNK_SOLID, false, getChargePercentage(), 0xccc, digits(getTotal() / 100, 5, 1)); } else diff --git a/main/storage.cpp b/main/storage.cpp index 83983d3..5c92d42 100644 --- a/main/storage.cpp +++ b/main/storage.cpp @@ -1,6 +1,5 @@ #include "storage.h" #include "nvs_flash.h" -#include "nvs.h" #include "esp_log.h" #include // <-- toegevoegd voor memcpy() @@ -82,44 +81,25 @@ bool batDataSave(void) // 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 +bool calibrationLoad(void *out_value, size_t length) { 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 - } + if (nvs_open(NVS_NAMESPACE, NVS_READONLY, &handle) != ESP_OK) { + return false; } - // Geen calibratie ? fallback - memcpy(payload + 1, fallback, 10); - return payload; + esp_err_t err = nvs_get_blob(handle, NVS_KEY_CALIB, out_value, &length); + nvs_close(handle); + + return (err == ESP_OK); } -bool calibrationSave(uint8_t *source) -{ +bool calibrationSave(const void *value, size_t length) { nvs_handle_t handle; - if (nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle) != ESP_OK) + 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); + esp_err_t err = nvs_set_blob(handle, NVS_KEY_CALIB, value, length); if (err != ESP_OK) { nvs_close(handle); return false; diff --git a/main/storage.h b/main/storage.h index 495e03c..a0646c2 100644 --- a/main/storage.h +++ b/main/storage.h @@ -1,6 +1,7 @@ #pragma once #include #include +#include struct batData { uint32_t trip1; @@ -21,5 +22,5 @@ bool batDataLoad(void); bool batDataSave(void); // calibration API -uint8_t *calibrationLoad(void); -bool calibrationSave(uint8_t *source); +bool calibrationLoad(void *out_value, size_t length); +bool calibrationSave(const void *value, size_t length); diff --git a/main/trip.cpp b/main/trip.cpp index 0f4dfcc..48cea6a 100644 --- a/main/trip.cpp +++ b/main/trip.cpp @@ -24,7 +24,7 @@ uint32_t getTotal() { void distanceUpdate(uint32_t distance) { if (distance < lastDistance) { - // Motor is opnieuw opgestart → teller terug naar 0 + // Motor is opnieuw opgestart ? teller terug naar 0 lastDistance = 0; } diff --git a/sdkconfig.10Ah b/sdkconfig.10Ah new file mode 100644 index 0000000..6c4d2c7 --- /dev/null +++ b/sdkconfig.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.15Ah b/sdkconfig.15Ah new file mode 100644 index 0000000..c6ffe83 --- /dev/null +++ b/sdkconfig.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.20Ah b/sdkconfig.20Ah new file mode 100644 index 0000000..33b4881 --- /dev/null +++ b/sdkconfig.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.defaults b/sdkconfig.defaults index 9a07c8f..8fc81f3 100644 --- a/sdkconfig.defaults +++ b/sdkconfig.defaults @@ -1,14 +1,14 @@ -CONFIG_ION_CU2=n -CONFIG_ION_CU3=y - -# My chips are 4MB flash, once you use OTA this settings matters -CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y - -# Set up partitions -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" - -CONFIG_FREERTOS_HZ=1000 - -CONFIG_LOG_COLORS= -#CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y +CONFIG_ION_CU2=n +CONFIG_ION_CU3=y + +# My chips are 4MB flash, once you use OTA this settings matters +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y + +# Set up partitions +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" + +CONFIG_FREERTOS_HZ=1000 + +CONFIG_LOG_COLORS= +#CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y diff --git a/sdkconfig.supermini b/sdkconfig.supermini new file mode 100644 index 0000000..bd0d1ba --- /dev/null +++ b/sdkconfig.supermini @@ -0,0 +1,56 @@ +# 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 + +# keepalive +CONFIG_ION_KEEPALIVE=y