diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5ffb7db..17fc131 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,44 +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: supermini - - target: esp32c3 - display: cu3 - custom: supermini + 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 }} @@ -60,13 +30,13 @@ jobs: - name: esp-idf build uses: espressif/esp-idf-ci-action@v1 with: - esp_idf_version: latest + 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/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 917c004..c6eeaca 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -5,6 +5,8 @@ "includePath": [ "${workspaceFolder}/build/config", "${workspaceFolder}/components/esp32-button/include", + "${workspaceFolder}/components/mqtt_helper/include", + "${workspaceFolder}/components/wifi_helper/include", "${workspaceFolder}/main", "${workspaceFolder}/sdk/idf/components/app_trace/include", "${workspaceFolder}/sdk/idf/components/app_update/include", 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 d9154bb..0000000 --- a/components/esp32-button +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d9154bb0d36562981811ee754e96601cd517bb4f 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<> 5; + historyMa -= avg; + + return avg; +} + static uint8_t batMvToPercentage(uint32_t batMv) { // Calculate the percentage uint32_t percentage = (batMv < emptyMv) ? 0 : ((batMv - emptyMv) * 100) / (fullMv - emptyMv); @@ -138,6 +163,10 @@ void measureBat() { haveMeasurement = true; } +void measureCurrent() { + batMa = measureCurrentMv(); +} + uint32_t getBatMv() { if(!haveMeasurement) { // Use a fake value of 27.6v when we don't have ADC. @@ -156,6 +185,10 @@ uint8_t getBatPercentage() { return batPercentage; } +uint32_t getBatMa() { + return batMa; +} + void adc_teardown() { ESP_ERROR_CHECK(adc_oneshot_del_unit(adc1_handle)); if (cali_enable) { 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/calibration.cpp b/main/calibration.cpp index 1701999..21c777e 100644 --- a/main/calibration.cpp +++ b/main/calibration.cpp @@ -4,7 +4,6 @@ #include #define CAL_SIZE 10 -#define CAL_NVS_KEY_CALIB "calibration" 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) { @@ -29,7 +28,7 @@ bool handleCalibrationMessage(const messageType& message) { payload[0] = 0x00; // Try reading calibration from NVS. - if(!dataLoad(CAL_NVS_KEY_CALIB, payload + 1, CAL_SIZE)) { + if(!calibrationLoad(payload + 1, CAL_SIZE)) { // Failed to load calibration, use fallback instead. memcpy(payload + 1, fallback, CAL_SIZE); } @@ -39,7 +38,7 @@ bool handleCalibrationMessage(const messageType& message) { } 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(!dataSave(CAL_NVS_KEY_CALIB, message.payload, CAL_SIZE)) { + if(!calibrationSave(message.payload, CAL_SIZE)) { return true; } diff --git a/main/charge.cpp b/main/charge.cpp new file mode 100644 index 0000000..a5fa5fc --- /dev/null +++ b/main/charge.cpp @@ -0,0 +1,55 @@ +#include "sdkconfig.h" +#include "storage.h" +#include "charge.h" + +static struct batData *bat; + +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)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 bat->mv; +} + +uint32_t getMah() { + return bat->mah; +} + +void chargeUpdate(uint32_t mv, uint32_t ma) { + bat->mv = mv; + bat->mah += ma; +} + +void loadCharge() { + bat = batDataGet(); + + if (!batDataLoad()) { + // Defaults als er nog geen data in NVS staat + bat->percentage = 100; + bat->mv = 0; + bat->mah = 0; + } +} + +void resetCharge() { + bat->percentage = 100; + bat->mv = 0; + bat->mah = 0; + batDataSave(); +} diff --git a/main/charge.h b/main/charge.h new file mode 100644 index 0000000..e246ea2 --- /dev/null +++ b/main/charge.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +uint8_t getChargePercentage(void); + +uint32_t getMv(void); + +uint32_t getMah(void); + +void chargeUpdate(uint32_t mv, uint32_t mah); + +void loadCharge(void); + +void resetCharge(void); diff --git a/main/display.cpp b/main/display.cpp index 6f61d9f..6192098 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); + // 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 b66d4df..5b10689 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -32,9 +32,11 @@ #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"; @@ -67,7 +69,7 @@ TimerHandle_t healthCheckTimer ; static void checkMyTaskHealth(TimerHandle_t xTimer) { if (!myTaskAlive) { - saveDistances(); + batDataSave(); esp_restart(); } myTaskAlive = false; // Reset voor volgende check @@ -134,9 +136,12 @@ static void my_task(void *pvParameter) { adc_init(); #endif + storageInit(); + initUart(); loadDistances(); + loadCharge(); #if CONFIG_ION_CU2 initCu2(); @@ -204,6 +209,7 @@ static void my_task(void *pvParameter) { if(modeLongPress) { resetTrip1(0); + resetCharge(); requestDisplayUpdate(); } diff --git a/main/states/calibrate.cpp b/main/states/calibrate.cpp index 50147c1..56858c6 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,11 @@ 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(); + vTaskDelay(pdMS_TO_TICKS(1000)); +#endif if(state->step == 0) { exchange(cmdReq(MSG_MOTOR, MSG_BMS, CMD_CALIBRATE)); } else if (state->step == 1) { diff --git a/main/states/motor_off.cpp b/main/states/motor_off.cpp index 364e2e0..32e65f2 100644 --- a/main/states/motor_off.cpp +++ b/main/states/motor_off.cpp @@ -3,14 +3,14 @@ #include "freertos/event_groups.h" #include "esp_log.h" #include "blink.h" -#include "trip.h" +#include "storage.h" #include "states.h" void toMotorOffState(ion_state * state) { queueBlink(4, 100, 300); - saveDistances(); + batDataSave(); state->state = MOTOR_OFF; state->step = 0; diff --git a/main/states/turn_motor_on.cpp b/main/states/turn_motor_on.cpp index 03e0f25..5a5a41b 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, 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. @@ -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() / 100, 5, 1)); } else #else const uint8_t nextStep = 0; diff --git a/main/storage.cpp b/main/storage.cpp index cc84ce2..5c92d42 100644 --- a/main/storage.cpp +++ b/main/storage.cpp @@ -1,30 +1,105 @@ #include "storage.h" #include "nvs_flash.h" #include "esp_log.h" +#include // <-- toegevoegd voor memcpy() static const char *TAG = "storage"; #define NVS_NAMESPACE "storage" +#define NVS_KEY_BATDATA "batdata" +#define NVS_KEY_CALIB "calibration" -bool dataLoad(const char *key, void *out_value, size_t length) { +// Centrale instantie van batData +static struct batData bat; + +// ----------------------------------------------------------------------------- +// 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(); + } + + // Defaults voor batData + bat.trip1 = 0; + bat.trip2 = 0; + bat.total = 0; + + bat.percentage = 0; + bat.mv = 0; + bat.mah = 0; + + ESP_LOGI(TAG, "Storage initialized"); +} + +// ----------------------------------------------------------------------------- +// batData API +// ----------------------------------------------------------------------------- + +struct batData *batDataGet(void) +{ + return &bat; +} + +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 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; + } + + err = nvs_commit(handle); + nvs_close(handle); + + return (err == ESP_OK); +} + +// ----------------------------------------------------------------------------- +// Calibration API +// ----------------------------------------------------------------------------- + +bool calibrationLoad(void *out_value, size_t length) { nvs_handle_t handle; if (nvs_open(NVS_NAMESPACE, NVS_READONLY, &handle) != ESP_OK) { return false; } - esp_err_t err = nvs_get_blob(handle, key, out_value, &length); + esp_err_t err = nvs_get_blob(handle, NVS_KEY_CALIB, out_value, &length); nvs_close(handle); return (err == ESP_OK); } -bool dataSave(const char *key, const void *value, size_t length) { +bool calibrationSave(const void *value, size_t length) { nvs_handle_t handle; if (nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle) != ESP_OK) { return false; } - esp_err_t err = nvs_set_blob(handle, key, value, length); + 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 1635389..a0646c2 100644 --- a/main/storage.h +++ b/main/storage.h @@ -1,6 +1,26 @@ #pragma once #include #include +#include -bool dataLoad(const char *key, void *out_value, size_t length); -bool dataSave(const char *key, const void *value, size_t length); \ No newline at end of file +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 +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 648a413..48cea6a 100644 --- a/main/trip.cpp +++ b/main/trip.cpp @@ -1,47 +1,49 @@ #include "storage.h" #include "trip.h" -static struct tripData data; +static struct batData *bat; static uint32_t lastDistance = 0; void resetTrip1(uint32_t distance) { - data.trip1 = distance; + bat->trip1 = distance; } uint32_t getTrip1() { - return data.trip1; + return bat->trip1; } uint32_t getTrip2() { - return data.trip2; + return bat->trip2; } uint32_t getTotal() { - return data.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; - data.trip1 += delta; - data.trip2 += delta; - data.total += delta; + bat->trip1 += delta; + bat->trip2 += delta; + bat->total += delta; lastDistance = distance; } void loadDistances() { - dataLoad(TRIP_NVS_KEY_TRIPDATA, &data, sizeof(data)); -} + bat = batDataGet(); -void saveDistances() { - dataSave(TRIP_NVS_KEY_TRIPDATA, &data, sizeof(data)); + 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 5d3e5bd..b75bd52 100644 --- a/main/trip.h +++ b/main/trip.h @@ -2,31 +2,20 @@ #include -#define TRIP_NVS_KEY_TRIPDATA "tripdata" - -struct tripData { - uint32_t trip1; - uint32_t trip2; - uint32_t total; -}; - -// 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/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