From ca33040cfeb8faa552ccbdc9e509861453337b55 Mon Sep 17 00:00:00 2001 From: Jonny Date: Fri, 15 Aug 2025 21:49:57 +0100 Subject: [PATCH 001/245] Remove hardcoded cell counts from custom-BMS batteries --- Software/src/battery/DALY-BMS.cpp | 6 +++++- Software/src/battery/ORION-BMS.cpp | 5 ++--- Software/src/battery/ORION-BMS.h | 1 - Software/src/battery/SIMPBMS-BATTERY.cpp | 1 - Software/src/battery/SIMPBMS-BATTERY.h | 1 - 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Software/src/battery/DALY-BMS.cpp b/Software/src/battery/DALY-BMS.cpp index d40a5e8c..9eb2e577 100644 --- a/Software/src/battery/DALY-BMS.cpp +++ b/Software/src/battery/DALY-BMS.cpp @@ -16,6 +16,7 @@ static uint32_t remaining_capacity_mAh = 0; static uint16_t cellvoltages_mV[48] = {0}; static uint16_t cellvoltage_min_mV = 0; static uint16_t cellvoltage_max_mV = 0; +static uint16_t cell_count = 0; static uint16_t SOC = 0; static bool has_fault = false; @@ -54,6 +55,9 @@ void DalyBms::update_values() { datalayer.battery.status.cell_min_voltage_mV = cellvoltage_min_mV; datalayer.battery.status.cell_max_voltage_mV = cellvoltage_max_mV; + // Use the received value from the BMS, to avoid needing to configure it + datalayer.battery.info.number_of_cells = cell_count; + datalayer.battery.status.temperature_min_dC = temperature_min_dC; datalayer.battery.status.temperature_max_dC = temperature_max_dC; @@ -63,7 +67,6 @@ void DalyBms::update_values() { void DalyBms::setup(void) { // Performs one time setup at startup strncpy(datalayer.system.info.battery_protocol, Name, 63); datalayer.system.info.battery_protocol[63] = '\0'; - datalayer.battery.info.number_of_cells = CELL_COUNT; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; @@ -138,6 +141,7 @@ void decode_packet(uint8_t command, uint8_t data[8]) { remaining_capacity_mAh = decode_uint32be(data, 4); break; case 0x94: + cell_count = data[0]; break; case 0x95: if (data[0] > 0 && data[0] <= 16) { diff --git a/Software/src/battery/ORION-BMS.cpp b/Software/src/battery/ORION-BMS.cpp index 00445cc1..bf23ded6 100644 --- a/Software/src/battery/ORION-BMS.cpp +++ b/Software/src/battery/ORION-BMS.cpp @@ -57,8 +57,8 @@ void OrionBms::update_values() { datalayer.battery.status.cell_min_voltage_mV = Minimum_Cell_Voltage; - //If user did not configure amount of cells correctly in the header file, update the value - if ((amount_of_detected_cells > NUMBER_OF_CELLS) && (amount_of_detected_cells < MAX_AMOUNT_CELLS)) { + //Use the reported number of cells to avoid needing to configure it + if (amount_of_detected_cells < MAX_AMOUNT_CELLS) { datalayer.battery.info.number_of_cells = amount_of_detected_cells; } } @@ -115,7 +115,6 @@ void OrionBms::transmit_can(unsigned long currentMillis) { void OrionBms::setup(void) { // Performs one time setup at startup strncpy(datalayer.system.info.battery_protocol, Name, 63); datalayer.system.info.battery_protocol[63] = '\0'; - datalayer.battery.info.number_of_cells = NUMBER_OF_CELLS; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; diff --git a/Software/src/battery/ORION-BMS.h b/Software/src/battery/ORION-BMS.h index 20f52481..d93d3ae3 100644 --- a/Software/src/battery/ORION-BMS.h +++ b/Software/src/battery/ORION-BMS.h @@ -18,7 +18,6 @@ class OrionBms : public CanBattery { private: /* Change the following to suit your battery */ - static const int NUMBER_OF_CELLS = 96; static const int MAX_PACK_VOLTAGE_DV = 5000; //5000 = 500.0V static const int MIN_PACK_VOLTAGE_DV = 1500; static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value diff --git a/Software/src/battery/SIMPBMS-BATTERY.cpp b/Software/src/battery/SIMPBMS-BATTERY.cpp index b8b871c2..502faf8c 100644 --- a/Software/src/battery/SIMPBMS-BATTERY.cpp +++ b/Software/src/battery/SIMPBMS-BATTERY.cpp @@ -93,7 +93,6 @@ void SimpBmsBattery::transmit_can(unsigned long currentMillis) { void SimpBmsBattery::setup(void) { // Performs one time setup at startup strncpy(datalayer.system.info.battery_protocol, Name, 63); datalayer.system.info.battery_protocol[63] = '\0'; - datalayer.battery.info.number_of_cells = CELL_COUNT; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; diff --git a/Software/src/battery/SIMPBMS-BATTERY.h b/Software/src/battery/SIMPBMS-BATTERY.h index 365a372f..914bd58f 100644 --- a/Software/src/battery/SIMPBMS-BATTERY.h +++ b/Software/src/battery/SIMPBMS-BATTERY.h @@ -22,7 +22,6 @@ class SimpBmsBattery : public CanBattery { static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value static const int MAX_CELL_DEVIATION_MV = 500; - static const int CELL_COUNT = 96; static const int SIMPBMS_MAX_CELLS = 128; From a7897f30e66b964a2efba4f8f91325c9414b6acf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sat, 16 Aug 2025 12:17:28 +0300 Subject: [PATCH 002/245] Update Software.ino --- Software/Software.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/Software.ino b/Software/Software.ino index d7e1b545..b3f8b66c 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -41,7 +41,7 @@ volatile unsigned long long bmsResetTimeOffset = 0; // The current software version, shown on webserver -const char* version_number = "9.0.RC2"; +const char* version_number = "9.0.RC2experimental"; // Interval timers volatile unsigned long currentMillis = 0; From 555cc16a11679e4ae6e5c7069690904fbabee02d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sat, 16 Aug 2025 13:31:54 +0300 Subject: [PATCH 003/245] Make cellmonitor page show amount of detected cells --- Software/src/devboard/webserver/cellmonitor_html.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/Software/src/devboard/webserver/cellmonitor_html.cpp b/Software/src/devboard/webserver/cellmonitor_html.cpp index 0555f134..af82a0ed 100644 --- a/Software/src/devboard/webserver/cellmonitor_html.cpp +++ b/Software/src/devboard/webserver/cellmonitor_html.cpp @@ -236,9 +236,7 @@ String cellmonitor_processor(const String& var) { content += "updateVoltageValues(data);"; content += "}"; content += "else {"; - content += - "document.getElementById('voltageValues').textContent = 'Cell information not yet fetched, or information not " - "available';"; + content += "document.getElementById('voltageValues').textContent = '" + String(datalayer.battery.info.number_of_cells) + " cells, voltages not yet fetched, or cell amount not available';"; content += "}"; if (battery2) { @@ -365,10 +363,7 @@ String cellmonitor_processor(const String& var) { content += "updateVoltageValues2(data2);"; content += "}"; content += "else {"; - content += - "document.getElementById('voltageValues2').textContent = 'Cell information not yet fetched, or information " - "not " - "available';"; + content += "document.getElementById('voltageValues2').textContent = '" + String(datalayer.battery2.info.number_of_cells) + " cells, voltages not yet fetched, or cell amount not available';"; content += "}"; } From dd1ee5139db4fef24ae673ce0752d995c4b556b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sat, 16 Aug 2025 13:32:18 +0300 Subject: [PATCH 004/245] Make RJXZS protocol handle reboots gracefully with default values --- Software/src/battery/RJXZS-BMS.cpp | 10 ++-------- Software/src/battery/RJXZS-BMS.h | 8 ++++---- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/Software/src/battery/RJXZS-BMS.cpp b/Software/src/battery/RJXZS-BMS.cpp index 3d3cffe5..61358e96 100644 --- a/Software/src/battery/RJXZS-BMS.cpp +++ b/Software/src/battery/RJXZS-BMS.cpp @@ -72,14 +72,8 @@ void RjxzsBms::update_values() { datalayer.battery.status.temperature_max_dC = max_temp; - // The cellvoltages[] array can contain 0s inside it - populated_cellvoltages = 0; - for (int i = 0; i < MAX_AMOUNT_CELLS; ++i) { - if (cellvoltages[i] > 0) { // We have a measurement available - datalayer.battery.status.cell_voltages_mV[populated_cellvoltages] = cellvoltages[i]; - populated_cellvoltages++; - } - } + //Map all cell voltages to the global array + memcpy(datalayer.battery.status.cell_voltages_mV, cellvoltages, MAX_AMOUNT_CELLS * sizeof(uint16_t)); datalayer.battery.info.number_of_cells = populated_cellvoltages; // 1-192S diff --git a/Software/src/battery/RJXZS-BMS.h b/Software/src/battery/RJXZS-BMS.h index 50fad1e0..d6f4c53b 100644 --- a/Software/src/battery/RJXZS-BMS.h +++ b/Software/src/battery/RJXZS-BMS.h @@ -41,7 +41,7 @@ class RjxzsBms : public CanBattery { uint8_t mux = 0; bool setup_completed = false; - uint16_t total_voltage = 0; + uint16_t total_voltage = 3700; int16_t total_current = 0; uint16_t total_power = 0; uint16_t battery_usage_capacity = 0; @@ -83,9 +83,9 @@ class RjxzsBms : public CanBattery { uint16_t low_voltage_power_outage_delayed = 0; uint16_t num_of_triggering_protection_cells = 0; uint16_t balanced_reference_voltage = 0; - uint16_t minimum_cell_voltage = 0; - uint16_t maximum_cell_voltage = 0; - uint16_t cellvoltages[MAX_AMOUNT_CELLS]; + uint16_t minimum_cell_voltage = 3300; + uint16_t maximum_cell_voltage = 3300; + uint16_t cellvoltages[MAX_AMOUNT_CELLS] = {0}; uint8_t populated_cellvoltages = 0; uint16_t accumulated_total_capacity_high = 0; uint16_t accumulated_total_capacity_low = 0; From 9a898b72e2358f54a2f7df0515677e2b1f8e2c34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sat, 16 Aug 2025 16:02:30 +0300 Subject: [PATCH 005/245] Update text for cellvoltages not read --- .../devboard/webserver/cellmonitor_html.cpp | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/Software/src/devboard/webserver/cellmonitor_html.cpp b/Software/src/devboard/webserver/cellmonitor_html.cpp index af82a0ed..69404138 100644 --- a/Software/src/devboard/webserver/cellmonitor_html.cpp +++ b/Software/src/devboard/webserver/cellmonitor_html.cpp @@ -236,7 +236,14 @@ String cellmonitor_processor(const String& var) { content += "updateVoltageValues(data);"; content += "}"; content += "else {"; - content += "document.getElementById('voltageValues').textContent = '" + String(datalayer.battery.info.number_of_cells) + " cells, voltages not yet fetched, or cell amount not available';"; + if (datalayer.battery.info.number_of_cells > 0) { + content += "document.getElementById('voltageValues').textContent = '" + + String(datalayer.battery.info.number_of_cells) + " cells configured, but cellvoltages not yet read';"; + } else { + content += + "document.getElementById('voltageValues').textContent = 'Amount of cells unknown. Cellvoltages not yet " + "read';"; + } content += "}"; if (battery2) { @@ -363,7 +370,15 @@ String cellmonitor_processor(const String& var) { content += "updateVoltageValues2(data2);"; content += "}"; content += "else {"; - content += "document.getElementById('voltageValues2').textContent = '" + String(datalayer.battery2.info.number_of_cells) + " cells, voltages not yet fetched, or cell amount not available';"; + if (datalayer.battery2.info.number_of_cells > 0) { + content += "document.getElementById('voltageValues2').textContent = '" + + String(datalayer.battery2.info.number_of_cells) + + " cells configured, but cellvoltages not yet read';"; + } else { + content += + "document.getElementById('voltageValues2').textContent = 'Amount of cells unknown. Cellvoltages not yet " + "read';"; + } content += "}"; } From f4418672ba459f721b41ee6f0d1e3b14e00a7d76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sat, 16 Aug 2025 18:11:52 +0300 Subject: [PATCH 006/245] Working implementationN --- .../src/lib/adafruit-Adafruit_NeoPixel/esp.c | 235 +++++++++++------- 1 file changed, 146 insertions(+), 89 deletions(-) diff --git a/Software/src/lib/adafruit-Adafruit_NeoPixel/esp.c b/Software/src/lib/adafruit-Adafruit_NeoPixel/esp.c index 86a2002a..ce6cf1ae 100644 --- a/Software/src/lib/adafruit-Adafruit_NeoPixel/esp.c +++ b/Software/src/lib/adafruit-Adafruit_NeoPixel/esp.c @@ -1,100 +1,157 @@ -// Implements the RMT peripheral on Espressif SoCs -// Copyright (c) 2020 Lucian Copeland for Adafruit Industries - -/* Uses code from Espressif RGB LED Strip demo and drivers - * Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - #include -#include "driver/rmt.h" +#include "driver/rmt_tx.h" -// This code is adapted from the ESP-IDF v3.4 RMT "led_strip" example, altered -// to work with the Arduino version of the ESP-IDF (3.2) +// WS2812 timing parameters (in nanoseconds) +#define WS2812_T0H_NS 350 +#define WS2812_T0L_NS 1000 +#define WS2812_T1H_NS 900 +#define WS2812_T1L_NS 350 +#define WS2812_RESET_US 280 // Recommended reset time -#define WS2812_T0H_NS (400) -#define WS2812_T0L_NS (850) -#define WS2812_T1H_NS (800) -#define WS2812_T1L_NS (450) +static rmt_channel_handle_t led_chan = NULL; +static rmt_encoder_handle_t led_encoder = NULL; -static uint32_t t0h_ticks = 0; -static uint32_t t1h_ticks = 0; -static uint32_t t0l_ticks = 0; -static uint32_t t1l_ticks = 0; +typedef struct { + rmt_encoder_t base; + rmt_encoder_t *bytes_encoder; + rmt_encoder_t *copy_encoder; + int state; + rmt_symbol_word_t reset_code; +} rmt_led_strip_encoder_t; -static void IRAM_ATTR ws2812_rmt_adapter(const void* src, rmt_item32_t* dest, size_t src_size, size_t wanted_num, - size_t* translated_size, size_t* item_num) { - if (src == NULL || dest == NULL) { - *translated_size = 0; - *item_num = 0; - return; - } - const rmt_item32_t bit0 = {{{t0h_ticks, 1, t0l_ticks, 0}}}; //Logical 0 - const rmt_item32_t bit1 = {{{t1h_ticks, 1, t1l_ticks, 0}}}; //Logical 1 - size_t size = 0; - size_t num = 0; - uint8_t* psrc = (uint8_t*)src; - rmt_item32_t* pdest = dest; - while (size < src_size && num < wanted_num) { - for (int i = 0; i < 8; i++) { - // MSB first - if (*psrc & (1 << (7 - i))) { - pdest->val = bit1.val; - } else { - pdest->val = bit0.val; - } - num++; - pdest++; +// Encoder function prototypes +static size_t IRAM_ATTR rmt_encode_led_strip(rmt_encoder_t *encoder, rmt_channel_handle_t channel, + const void *primary_data, size_t data_size, + rmt_encode_state_t *ret_state); +static esp_err_t rmt_del_led_strip_encoder(rmt_encoder_t *encoder); +static esp_err_t rmt_led_strip_encoder_reset(rmt_encoder_t *encoder); + +// Encoder implementation +static size_t IRAM_ATTR rmt_encode_led_strip(rmt_encoder_t *encoder, rmt_channel_handle_t channel, + const void *primary_data, size_t data_size, + rmt_encode_state_t *ret_state) { + rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base); + rmt_encoder_handle_t bytes_encoder = led_encoder->bytes_encoder; + rmt_encoder_handle_t copy_encoder = led_encoder->copy_encoder; + rmt_encode_state_t session_state = 0; + rmt_encode_state_t state = 0; + size_t encoded_symbols = 0; + + switch (led_encoder->state) { + case 0: // send RGB data + encoded_symbols += bytes_encoder->encode(bytes_encoder, channel, primary_data, data_size, &session_state); + if (session_state & RMT_ENCODING_COMPLETE) { + led_encoder->state = 1; + } + if (session_state & RMT_ENCODING_MEM_FULL) { + state |= RMT_ENCODING_MEM_FULL; + goto out; + } + // fall-through + case 1: // send reset code + encoded_symbols += copy_encoder->encode(copy_encoder, channel, &led_encoder->reset_code, + sizeof(led_encoder->reset_code), &session_state); + if (session_state & RMT_ENCODING_COMPLETE) { + led_encoder->state = 0; + state |= RMT_ENCODING_COMPLETE; + } + if (session_state & RMT_ENCODING_MEM_FULL) { + state |= RMT_ENCODING_MEM_FULL; + goto out; + } } - size++; - psrc++; - } - *translated_size = size; - *item_num = num; +out: + *ret_state = state; + return encoded_symbols; } -static bool rmt_initialized = false; +static esp_err_t rmt_del_led_strip_encoder(rmt_encoder_t *encoder) { + rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base); + rmt_del_encoder(led_encoder->bytes_encoder); + rmt_del_encoder(led_encoder->copy_encoder); + free(led_encoder); + return ESP_OK; +} + +static esp_err_t rmt_led_strip_encoder_reset(rmt_encoder_t *encoder) { + rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base); + rmt_encoder_reset(led_encoder->bytes_encoder); + rmt_encoder_reset(led_encoder->copy_encoder); + led_encoder->state = 0; + return ESP_OK; +} + +static esp_err_t rmt_new_led_strip_encoder(rmt_encoder_handle_t *ret_encoder) { + esp_err_t ret = ESP_OK; + rmt_led_strip_encoder_t *led_encoder = calloc(1, sizeof(rmt_led_strip_encoder_t)); + if (!led_encoder) return ESP_ERR_NO_MEM; + + led_encoder->base.encode = rmt_encode_led_strip; + led_encoder->base.del = rmt_del_led_strip_encoder; + led_encoder->base.reset = rmt_led_strip_encoder_reset; + + // Bytes encoder configuration + rmt_bytes_encoder_config_t bytes_encoder_config = { + .bit0 = { + .level0 = 1, + .duration0 = (WS2812_T0H_NS + 50) / 100, // T0H in ticks (100ns resolution) + .level1 = 0, + .duration1 = (WS2812_T0L_NS + 50) / 100, // T0L in ticks + }, + .bit1 = { + .level0 = 1, + .duration0 = (WS2812_T1H_NS + 50) / 100, // T1H in ticks + .level1 = 0, + .duration1 = (WS2812_T1L_NS + 50) / 100, // T1L in ticks + }, + .flags.msb_first = 1 // WS2812 transfer bit order + }; + if ((ret = rmt_new_bytes_encoder(&bytes_encoder_config, &led_encoder->bytes_encoder)) != ESP_OK) { + goto err; + } + + rmt_copy_encoder_config_t copy_encoder_config = {}; + if ((ret = rmt_new_copy_encoder(©_encoder_config, &led_encoder->copy_encoder)) != ESP_OK) { + goto err; + } + + // Reset code (280us of low signal) + led_encoder->reset_code = (rmt_symbol_word_t) { + .level0 = 0, + .duration0 = (WS2812_RESET_US * 1000) / 100, // Convert µs to ticks + .level1 = 0, + .duration1 = 0, + }; + + *ret_encoder = &led_encoder->base; + return ESP_OK; + +err: + if (led_encoder) { + if (led_encoder->bytes_encoder) rmt_del_encoder(led_encoder->bytes_encoder); + if (led_encoder->copy_encoder) rmt_del_encoder(led_encoder->copy_encoder); + free(led_encoder); + } + return ret; +} void espShow(uint8_t pin, uint8_t* pixels, uint32_t numBytes) { - if (rmt_initialized == false) { - // Reserve channel - rmt_channel_t channel = 0; + if (led_chan == NULL) { + rmt_tx_channel_config_t tx_chan_config = { + .clk_src = RMT_CLK_SRC_DEFAULT, + .gpio_num = pin, + .mem_block_symbols = 64, + .resolution_hz = 10 * 1000 * 1000, // 10MHz, 1 tick = 100ns + .trans_queue_depth = 4, + }; + ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_chan_config, &led_chan)); + ESP_ERROR_CHECK(rmt_new_led_strip_encoder(&led_encoder)); + ESP_ERROR_CHECK(rmt_enable(led_chan)); + } - rmt_config_t config = RMT_DEFAULT_CONFIG_TX(pin, channel); - config.clk_div = 2; - - rmt_config(&config); - rmt_driver_install(config.channel, 0, 0); - - // Convert NS timings to ticks - uint32_t counter_clk_hz = 0; - - rmt_get_counter_clock(channel, &counter_clk_hz); - - // NS to tick converter - float ratio = (float)counter_clk_hz / 1e9; - - t0h_ticks = (uint32_t)(ratio * WS2812_T0H_NS); - t0l_ticks = (uint32_t)(ratio * WS2812_T0L_NS); - t1h_ticks = (uint32_t)(ratio * WS2812_T1H_NS); - t1l_ticks = (uint32_t)(ratio * WS2812_T1L_NS); - - // Initialize automatic timing translator - rmt_translator_init(0, ws2812_rmt_adapter); - rmt_initialized = true; - } - - // Write and wait to finish - rmt_write_sample(0, pixels, (size_t)numBytes, false); -} + rmt_transmit_config_t tx_config = { + .loop_count = 0, + }; + ESP_ERROR_CHECK(rmt_transmit(led_chan, led_encoder, pixels, numBytes, &tx_config)); + ESP_ERROR_CHECK(rmt_tx_wait_all_done(led_chan, portMAX_DELAY)); +} \ No newline at end of file From 6136c929962f8d365d35733f9bd5b3e9454ededb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sat, 16 Aug 2025 18:16:13 +0300 Subject: [PATCH 007/245] Further improve implementation --- .../src/lib/adafruit-Adafruit_NeoPixel/esp.c | 34 ++++++++----------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/Software/src/lib/adafruit-Adafruit_NeoPixel/esp.c b/Software/src/lib/adafruit-Adafruit_NeoPixel/esp.c index ce6cf1ae..bcf9b0fa 100644 --- a/Software/src/lib/adafruit-Adafruit_NeoPixel/esp.c +++ b/Software/src/lib/adafruit-Adafruit_NeoPixel/esp.c @@ -1,12 +1,12 @@ #include #include "driver/rmt_tx.h" - +// This code is adapted for the ESP-IDF v5.x RMT driver API // WS2812 timing parameters (in nanoseconds) #define WS2812_T0H_NS 350 #define WS2812_T0L_NS 1000 #define WS2812_T1H_NS 900 #define WS2812_T1L_NS 350 -#define WS2812_RESET_US 280 // Recommended reset time +#define WS2812_RESET_US 280 static rmt_channel_handle_t led_chan = NULL; static rmt_encoder_handle_t led_encoder = NULL; @@ -19,17 +19,15 @@ typedef struct { rmt_symbol_word_t reset_code; } rmt_led_strip_encoder_t; -// Encoder function prototypes -static size_t IRAM_ATTR rmt_encode_led_strip(rmt_encoder_t *encoder, rmt_channel_handle_t channel, - const void *primary_data, size_t data_size, - rmt_encode_state_t *ret_state); +static size_t rmt_encode_led_strip(rmt_encoder_t *encoder, rmt_channel_handle_t channel, + const void *primary_data, size_t data_size, + rmt_encode_state_t *ret_state); static esp_err_t rmt_del_led_strip_encoder(rmt_encoder_t *encoder); static esp_err_t rmt_led_strip_encoder_reset(rmt_encoder_t *encoder); -// Encoder implementation -static size_t IRAM_ATTR rmt_encode_led_strip(rmt_encoder_t *encoder, rmt_channel_handle_t channel, - const void *primary_data, size_t data_size, - rmt_encode_state_t *ret_state) { +static size_t IRAM_ATTR rmt_encode_led_strip(rmt_encoder_t *encoder, rmt_channel_handle_t channel, + const void *primary_data, size_t data_size, + rmt_encode_state_t *ret_state) { rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base); rmt_encoder_handle_t bytes_encoder = led_encoder->bytes_encoder; rmt_encoder_handle_t copy_encoder = led_encoder->copy_encoder; @@ -90,21 +88,20 @@ static esp_err_t rmt_new_led_strip_encoder(rmt_encoder_handle_t *ret_encoder) { led_encoder->base.del = rmt_del_led_strip_encoder; led_encoder->base.reset = rmt_led_strip_encoder_reset; - // Bytes encoder configuration rmt_bytes_encoder_config_t bytes_encoder_config = { .bit0 = { .level0 = 1, - .duration0 = (WS2812_T0H_NS + 50) / 100, // T0H in ticks (100ns resolution) + .duration0 = (WS2812_T0H_NS + 50) / 100, .level1 = 0, - .duration1 = (WS2812_T0L_NS + 50) / 100, // T0L in ticks + .duration1 = (WS2812_T0L_NS + 50) / 100, }, .bit1 = { .level0 = 1, - .duration0 = (WS2812_T1H_NS + 50) / 100, // T1H in ticks + .duration0 = (WS2812_T1H_NS + 50) / 100, .level1 = 0, - .duration1 = (WS2812_T1L_NS + 50) / 100, // T1L in ticks + .duration1 = (WS2812_T1L_NS + 50) / 100, }, - .flags.msb_first = 1 // WS2812 transfer bit order + .flags.msb_first = 1 }; if ((ret = rmt_new_bytes_encoder(&bytes_encoder_config, &led_encoder->bytes_encoder)) != ESP_OK) { goto err; @@ -115,10 +112,9 @@ static esp_err_t rmt_new_led_strip_encoder(rmt_encoder_handle_t *ret_encoder) { goto err; } - // Reset code (280us of low signal) led_encoder->reset_code = (rmt_symbol_word_t) { .level0 = 0, - .duration0 = (WS2812_RESET_US * 1000) / 100, // Convert µs to ticks + .duration0 = (WS2812_RESET_US * 1000) / 100, .level1 = 0, .duration1 = 0, }; @@ -141,7 +137,7 @@ void espShow(uint8_t pin, uint8_t* pixels, uint32_t numBytes) { .clk_src = RMT_CLK_SRC_DEFAULT, .gpio_num = pin, .mem_block_symbols = 64, - .resolution_hz = 10 * 1000 * 1000, // 10MHz, 1 tick = 100ns + .resolution_hz = 10 * 1000 * 1000, .trans_queue_depth = 4, }; ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_chan_config, &led_chan)); From 77706ce200a3af1953f49286fae04389eab503f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 17 Aug 2025 00:14:19 +0300 Subject: [PATCH 008/245] Optimize Adafruit library for maximum CPU usage reduction --- Software/src/devboard/utils/led_handler.cpp | 9 +- Software/src/devboard/utils/led_handler.h | 8 +- .../Adafruit_NeoPixel.cpp | 370 +----------------- .../Adafruit_NeoPixel.h | 149 +------ .../src/lib/adafruit-Adafruit_NeoPixel/esp.c | 2 +- 5 files changed, 34 insertions(+), 504 deletions(-) diff --git a/Software/src/devboard/utils/led_handler.cpp b/Software/src/devboard/utils/led_handler.cpp index 9c217a3d..73fefb4d 100644 --- a/Software/src/devboard/utils/led_handler.cpp +++ b/Software/src/devboard/utils/led_handler.cpp @@ -24,7 +24,6 @@ bool led_init(void) { } led = new LED(datalayer.battery.status.led_mode, esp32hal->LED_PIN(), esp32hal->LED_MAX_BRIGHTNESS()); - led->init(); return true; } @@ -57,20 +56,20 @@ void LED::exe(void) { switch (get_event_level()) { case EVENT_LEVEL_INFO: color = led_color::GREEN; - pixels.setPixelColor(0, COLOR_GREEN(brightness)); // Green pulsing LED + pixels.setPixelColor(COLOR_GREEN(brightness)); // Green pulsing LED break; case EVENT_LEVEL_WARNING: color = led_color::YELLOW; - pixels.setPixelColor(0, COLOR_YELLOW(brightness)); // Yellow pulsing LED + pixels.setPixelColor(COLOR_YELLOW(brightness)); // Yellow pulsing LED break; case EVENT_LEVEL_DEBUG: case EVENT_LEVEL_UPDATE: color = led_color::BLUE; - pixels.setPixelColor(0, COLOR_BLUE(brightness)); // Blue pulsing LED + pixels.setPixelColor(COLOR_BLUE(brightness)); // Blue pulsing LED break; case EVENT_LEVEL_ERROR: color = led_color::RED; - pixels.setPixelColor(0, COLOR_RED(esp32hal->LED_MAX_BRIGHTNESS())); // Red LED full brightness + pixels.setPixelColor(COLOR_RED(esp32hal->LED_MAX_BRIGHTNESS())); // Red LED full brightness break; default: break; diff --git a/Software/src/devboard/utils/led_handler.h b/Software/src/devboard/utils/led_handler.h index 60c18d8c..4d8e01ee 100644 --- a/Software/src/devboard/utils/led_handler.h +++ b/Software/src/devboard/utils/led_handler.h @@ -9,16 +9,12 @@ class LED { led_color color = led_color::GREEN; LED(gpio_num_t pin, uint8_t maxBrightness) - : pixels(1, pin, NEO_GRB), - max_brightness(maxBrightness), - brightness(maxBrightness), - mode(led_mode_enum::CLASSIC) {} + : pixels(pin), max_brightness(maxBrightness), brightness(maxBrightness), mode(led_mode_enum::CLASSIC) {} LED(led_mode_enum mode, gpio_num_t pin, uint8_t maxBrightness) - : pixels(1, pin, NEO_GRB), max_brightness(maxBrightness), brightness(maxBrightness), mode(mode) {} + : pixels(pin), max_brightness(maxBrightness), brightness(maxBrightness), mode(mode) {} void exe(void); - void init(void) { pixels.begin(); } private: Adafruit_NeoPixel pixels; diff --git a/Software/src/lib/adafruit-Adafruit_NeoPixel/Adafruit_NeoPixel.cpp b/Software/src/lib/adafruit-Adafruit_NeoPixel/Adafruit_NeoPixel.cpp index 06e964a0..540890bb 100644 --- a/Software/src/lib/adafruit-Adafruit_NeoPixel/Adafruit_NeoPixel.cpp +++ b/Software/src/lib/adafruit-Adafruit_NeoPixel/Adafruit_NeoPixel.cpp @@ -1,368 +1,38 @@ -/*! - * @file Adafruit_NeoPixel.cpp - * - * @mainpage Arduino Library for driving Adafruit NeoPixel addressable LEDs, - * FLORA RGB Smart Pixels and compatible devicess -- WS2811, WS2812, WS2812B, - * SK6812, etc. - * - * @section intro_sec Introduction - * - * This is the documentation for Adafruit's NeoPixel library for the - * Arduino platform, allowing a broad range of microcontroller boards - * (most AVR boards, many ARM devices, ESP8266 and ESP32, among others) - * to control Adafruit NeoPixels, FLORA RGB Smart Pixels and compatible - * devices -- WS2811, WS2812, WS2812B, SK6812, etc. - * - * Adafruit invests time and resources providing this open source code, - * please support Adafruit and open-source hardware by purchasing products - * from Adafruit! - * - * @section author Author - * - * Written by Phil "Paint Your Dragon" Burgess for Adafruit Industries, - * with contributions by PJRC, Michael Miller and other members of the - * open source community. - * - * @section license License - * - * This file is part of the Adafruit_NeoPixel library. - * - * Adafruit_NeoPixel is free software: you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * Adafruit_NeoPixel is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with NeoPixel. If not, see - * . - * - */ +/*Based on the Adafruit Neopixel library, which has been heavily modified to support only 1x RGB LED for lowest possible CPU usage*/ #include "Adafruit_NeoPixel.h" -/*! - @brief NeoPixel constructor when length, pin and pixel type are known - at compile-time. - @param n Number of NeoPixels in strand. - @param p Arduino pin number which will drive the NeoPixel data in. - @param t Pixel type -- add together NEO_* constants defined in - Adafruit_NeoPixel.h, for example NEO_GRB+NEO_KHZ800 for - NeoPixels expecting an 800 KHz (vs 400 KHz) data stream - with color bytes expressed in green, red, blue order per - pixel. - @return Adafruit_NeoPixel object. Call the begin() function before use. -*/ -Adafruit_NeoPixel::Adafruit_NeoPixel(uint16_t n, int16_t p, neoPixelType t) - : begun(false), brightness(0), pixels(NULL), endTime(0) { - updateType(t); - updateLength(n); +extern "C" void espShow(uint16_t pin, uint8_t *pixels, uint8_t numBytes); + +Adafruit_NeoPixel::Adafruit_NeoPixel(int16_t p) : pixels(NULL) { + updateLength(); setPin(p); } -/*! - @brief "Empty" NeoPixel constructor when length, pin and/or pixel type - are not known at compile-time, and must be initialized later with - updateType(), updateLength() and setPin(). - @return Adafruit_NeoPixel object. Call the begin() function before use. - @note This function is deprecated, here only for old projects that - may still be calling it. New projects should instead use the - 'new' keyword with the first constructor syntax (length, pin, - type). -*/ -Adafruit_NeoPixel::Adafruit_NeoPixel() - : - begun(false), numLEDs(0), numBytes(0), pin(-1), brightness(0), - pixels(NULL), rOffset(1), gOffset(0), bOffset(2), wOffset(1), endTime(0) { -} - -/*! - @brief Deallocate Adafruit_NeoPixel object, set data pin back to INPUT. -*/ -Adafruit_NeoPixel::~Adafruit_NeoPixel() { +void Adafruit_NeoPixel::updateLength(void) { free(pixels); - if (pin >= 0) - pinMode(pin, INPUT); + pixels = (uint8_t *)malloc(numBytes); + if (pixels) memset(pixels, 0, numBytes); } -/*! - @brief Configure NeoPixel pin for output. -*/ -void Adafruit_NeoPixel::begin(void) { - if (pin >= 0) { - pinMode(pin, OUTPUT); - digitalWrite(pin, LOW); - } - begun = true; -} - -/*! - @brief Change the length of a previously-declared Adafruit_NeoPixel - strip object. Old data is deallocated and new data is cleared. - Pin number and pixel format are unchanged. - @param n New length of strip, in pixels. - @note This function is deprecated, here only for old projects that - may still be calling it. New projects should instead use the - 'new' keyword with the first constructor syntax (length, pin, - type). -*/ -void Adafruit_NeoPixel::updateLength(uint16_t n) { - free(pixels); // Free existing data (if any) - - // Allocate new data -- note: ALL PIXELS ARE CLEARED - numBytes = n * ((wOffset == rOffset) ? 3 : 4); - if ((pixels = (uint8_t *)malloc(numBytes))) { - memset(pixels, 0, numBytes); - numLEDs = n; - } else { - numLEDs = numBytes = 0; - } -} - -/*! - @brief Change the pixel format of a previously-declared - Adafruit_NeoPixel strip object. If format changes from one of - the RGB variants to an RGBW variant (or RGBW to RGB), the old - data will be deallocated and new data is cleared. Otherwise, - the old data will remain in RAM and is not reordered to the - new format, so it's advisable to follow up with clear(). - @param t Pixel type -- add together NEO_* constants defined in - Adafruit_NeoPixel.h, for example NEO_GRB+NEO_KHZ800 for - NeoPixels expecting an 800 KHz (vs 400 KHz) data stream - with color bytes expressed in green, red, blue order per - pixel. - @note This function is deprecated, here only for old projects that - may still be calling it. New projects should instead use the - 'new' keyword with the first constructor syntax - (length, pin, type). -*/ -void Adafruit_NeoPixel::updateType(neoPixelType t) { - bool oldThreeBytesPerPixel = (wOffset == rOffset); // false if RGBW - - wOffset = (t >> 6) & 0b11; // See notes in header file - rOffset = (t >> 4) & 0b11; // regarding R/G/B/W offsets - gOffset = (t >> 2) & 0b11; - bOffset = t & 0b11; - - // If bytes-per-pixel has changed (and pixel data was previously - // allocated), re-allocate to new size. Will clear any data. - if (pixels) { - bool newThreeBytesPerPixel = (wOffset == rOffset); - if (newThreeBytesPerPixel != oldThreeBytesPerPixel) - updateLength(numLEDs); - } -} - -extern "C" void espShow(uint16_t pin, uint8_t *pixels, uint32_t numBytes); - -/*! - @brief Transmit pixel data in RAM to NeoPixels. - @note On most architectures, interrupts are temporarily disabled in - order to achieve the correct NeoPixel signal timing. This means - that the Arduino millis() and micros() functions, which require - interrupts, will lose small intervals of time whenever this - function is called (about 30 microseconds per RGB pixel, 40 for - RGBW pixels). There's no easy fix for this, but a few - specialized alternative or companion libraries exist that use - very device-specific peripherals to work around it. -*/ -void Adafruit_NeoPixel::show(void) { - - if (!pixels) - return; - - // Data latch = 300+ microsecond pause in the output stream. Rather than - // put a delay at the end of the function, the ending time is noted and - // the function will simply hold off (if needed) on issuing the - // subsequent round of data until the latch time has elapsed. This - // allows the mainline code to start generating the next frame of data - // rather than stalling for the latch. - while (!canShow()) - ; - // endTime is a private member (rather than global var) so that multiple - // instances on different pins can be quickly issued in succession (each - // instance doesn't delay the next). - - // In order to make this code runtime-configurable to work with any pin, - // SBI/CBI instructions are eschewed in favor of full PORT writes via the - // OUT or ST instructions. It relies on two facts: that peripheral - // functions (such as PWM) take precedence on output pins, so our PORT- - // wide writes won't interfere, and that interrupts are globally disabled - // while data is being issued to the LEDs, so no other code will be - // accessing the PORT. The code takes an initial 'snapshot' of the PORT - // state, computes 'pin high' and 'pin low' values, and writes these back - // to the PORT register as needed. - - // NRF52 may use PWM + DMA (if available), may not need to disable interrupt - // ESP32 may not disable interrupts because espShow() uses RMT which tries to acquire locks - - espShow(pin, pixels, numBytes); - - endTime = micros(); // Save EOD time for latch on next call -} - -/*! - @brief Set/change the NeoPixel output pin number. Previous pin, - if any, is set to INPUT and the new pin is set to OUTPUT. - @param p Arduino pin number (-1 = no pin). -*/ void Adafruit_NeoPixel::setPin(int16_t p) { - if (begun && (pin >= 0)) - pinMode(pin, INPUT); // Disable existing out pin + if (pin >= 0) pinMode(pin, INPUT); pin = p; - if (begun) { + if ((p >= 0)) { pinMode(p, OUTPUT); digitalWrite(p, LOW); } } -/*! - @brief Set a pixel's color using separate red, green and blue - components. If using RGBW pixels, white will be set to 0. - @param n Pixel index, starting from 0. - @param r Red brightness, 0 = minimum (off), 255 = maximum. - @param g Green brightness, 0 = minimum (off), 255 = maximum. - @param b Blue brightness, 0 = minimum (off), 255 = maximum. -*/ -void Adafruit_NeoPixel::setPixelColor(uint16_t n, uint8_t r, uint8_t g, - uint8_t b) { - - if (n < numLEDs) { - if (brightness) { // See notes in setBrightness() - r = (r * brightness) >> 8; - g = (g * brightness) >> 8; - b = (b * brightness) >> 8; - } - uint8_t *p; - if (wOffset == rOffset) { // Is an RGB-type strip - p = &pixels[n * 3]; // 3 bytes per pixel - } else { // Is a WRGB-type strip - p = &pixels[n * 4]; // 4 bytes per pixel - p[wOffset] = 0; // But only R,G,B passed -- set W to 0 - } - p[rOffset] = r; // R,G,B always stored - p[gOffset] = g; - p[bOffset] = b; - } +void Adafruit_NeoPixel::show(void) { + if (!pixels) return; + espShow(pin, pixels, numBytes); } -/*! - @brief Set a pixel's color using separate red, green, blue and white - components (for RGBW NeoPixels only). - @param n Pixel index, starting from 0. - @param r Red brightness, 0 = minimum (off), 255 = maximum. - @param g Green brightness, 0 = minimum (off), 255 = maximum. - @param b Blue brightness, 0 = minimum (off), 255 = maximum. - @param w White brightness, 0 = minimum (off), 255 = maximum, ignored - if using RGB pixels. -*/ -void Adafruit_NeoPixel::setPixelColor(uint16_t n, uint8_t r, uint8_t g, - uint8_t b, uint8_t w) { - - if (n < numLEDs) { - if (brightness) { // See notes in setBrightness() - r = (r * brightness) >> 8; - g = (g * brightness) >> 8; - b = (b * brightness) >> 8; - w = (w * brightness) >> 8; - } - uint8_t *p; - if (wOffset == rOffset) { // Is an RGB-type strip - p = &pixels[n * 3]; // 3 bytes per pixel (ignore W) - } else { // Is a WRGB-type strip - p = &pixels[n * 4]; // 4 bytes per pixel - p[wOffset] = w; // Store W - } - p[rOffset] = r; // Store R,G,B - p[gOffset] = g; - p[bOffset] = b; - } -} - -/*! - @brief Set a pixel's color using a 32-bit 'packed' RGB or RGBW value. - @param n Pixel index, starting from 0. - @param c 32-bit color value. Most significant byte is white (for RGBW - pixels) or ignored (for RGB pixels), next is red, then green, - and least significant byte is blue. -*/ -void Adafruit_NeoPixel::setPixelColor(uint16_t n, uint32_t c) { - if (n < numLEDs) { - uint8_t *p, r = (uint8_t)(c >> 16), g = (uint8_t)(c >> 8), b = (uint8_t)c; - if (brightness) { // See notes in setBrightness() - r = (r * brightness) >> 8; - g = (g * brightness) >> 8; - b = (b * brightness) >> 8; - } - if (wOffset == rOffset) { - p = &pixels[n * 3]; - } else { - p = &pixels[n * 4]; - uint8_t w = (uint8_t)(c >> 24); - p[wOffset] = brightness ? ((w * brightness) >> 8) : w; - } - p[rOffset] = r; - p[gOffset] = g; - p[bOffset] = b; - } -} - -/*! - @brief Adjust output brightness. Does not immediately affect what's - currently displayed on the LEDs. The next call to show() will - refresh the LEDs at this level. - @param b Brightness setting, 0=minimum (off), 255=brightest. - @note This was intended for one-time use in one's setup() function, - not as an animation effect in itself. Because of the way this - library "pre-multiplies" LED colors in RAM, changing the - brightness is often a "lossy" operation -- what you write to - pixels isn't necessary the same as what you'll read back. - Repeated brightness changes using this function exacerbate the - problem. Smart programs therefore treat the strip as a - write-only resource, maintaining their own state to render each - frame of an animation, not relying on read-modify-write. -*/ -void Adafruit_NeoPixel::setBrightness(uint8_t b) { - // Stored brightness value is different than what's passed. - // This simplifies the actual scaling math later, allowing a fast - // 8x8-bit multiply and taking the MSB. 'brightness' is a uint8_t, - // adding 1 here may (intentionally) roll over...so 0 = max brightness - // (color values are interpreted literally; no scaling), 1 = min - // brightness (off), 255 = just below max brightness. - uint8_t newBrightness = b + 1; - if (newBrightness != brightness) { // Compare against prior value - // Brightness has changed -- re-scale existing data in RAM, - // This process is potentially "lossy," especially when increasing - // brightness. The tight timing in the WS2811/WS2812 code means there - // aren't enough free cycles to perform this scaling on the fly as data - // is issued. So we make a pass through the existing color data in RAM - // and scale it (subsequent graphics commands also work at this - // brightness level). If there's a significant step up in brightness, - // the limited number of steps (quantization) in the old data will be - // quite visible in the re-scaled version. For a non-destructive - // change, you'll need to re-render the full strip data. C'est la vie. - uint8_t c, *ptr = pixels, - oldBrightness = brightness - 1; // De-wrap old brightness value - uint16_t scale; - if (oldBrightness == 0) - scale = 0; // Avoid /0 - else if (b == 255) - scale = 65535 / oldBrightness; - else - scale = (((uint16_t)newBrightness << 8) - 1) / oldBrightness; - for (uint16_t i = 0; i < numBytes; i++) { - c = *ptr; - *ptr++ = (c * scale) >> 8; - } - brightness = newBrightness; - } -} - -/*! - @brief Fill the whole NeoPixel strip with 0 / black / off. -*/ -void Adafruit_NeoPixel::clear(void) { memset(pixels, 0, numBytes); } +void Adafruit_NeoPixel::setPixelColor(uint32_t c) { + uint8_t *p = pixels; + uint8_t r = (uint8_t)(c >> 16), g = (uint8_t)(c >> 8), b = (uint8_t)c; + p[rOffset] = r; + p[gOffset] = g; + p[bOffset] = b; +} \ No newline at end of file diff --git a/Software/src/lib/adafruit-Adafruit_NeoPixel/Adafruit_NeoPixel.h b/Software/src/lib/adafruit-Adafruit_NeoPixel/Adafruit_NeoPixel.h index 541145ed..d4f4c1fc 100644 --- a/Software/src/lib/adafruit-Adafruit_NeoPixel/Adafruit_NeoPixel.h +++ b/Software/src/lib/adafruit-Adafruit_NeoPixel/Adafruit_NeoPixel.h @@ -38,52 +38,6 @@ #include -// The order of primary colors in the NeoPixel data stream can vary among -// device types, manufacturers and even different revisions of the same -// item. The third parameter to the Adafruit_NeoPixel constructor encodes -// the per-pixel byte offsets of the red, green and blue primaries (plus -// white, if present) in the data stream -- the following #defines provide -// an easier-to-use named version for each permutation. e.g. NEO_GRB -// indicates a NeoPixel-compatible device expecting three bytes per pixel, -// with the first byte transmitted containing the green value, second -// containing red and third containing blue. The in-memory representation -// of a chain of NeoPixels is the same as the data-stream order; no -// re-ordering of bytes is required when issuing data to the chain. -// Most of these values won't exist in real-world devices, but it's done -// this way so we're ready for it (also, if using the WS2811 driver IC, -// one might have their pixels set up in any weird permutation). - -// Bits 5,4 of this value are the offset (0-3) from the first byte of a -// pixel to the location of the red color byte. Bits 3,2 are the green -// offset and 1,0 are the blue offset. If it is an RGBW-type device -// (supporting a white primary in addition to R,G,B), bits 7,6 are the -// offset to the white byte...otherwise, bits 7,6 are set to the same value -// as 5,4 (red) to indicate an RGB (not RGBW) device. -// i.e. binary representation: -// 0bWWRRGGBB for RGBW devices -// 0bRRRRGGBB for RGB - -// RGB NeoPixel permutations; white and red offsets are always same -// Offset: W R G B -#define NEO_RGB ((0 << 6) | (0 << 4) | (1 << 2) | (2)) ///< Transmit as R,G,B -#define NEO_RBG ((0 << 6) | (0 << 4) | (2 << 2) | (1)) ///< Transmit as R,B,G -#define NEO_GRB ((1 << 6) | (1 << 4) | (0 << 2) | (2)) ///< Transmit as G,R,B -#define NEO_GBR ((2 << 6) | (2 << 4) | (0 << 2) | (1)) ///< Transmit as G,B,R -#define NEO_BRG ((1 << 6) | (1 << 4) | (2 << 2) | (0)) ///< Transmit as B,R,G -#define NEO_BGR ((2 << 6) | (2 << 4) | (1 << 2) | (0)) ///< Transmit as B,G,R - -// Add NEO_KHZ400 to the color order value to indicate a 400 KHz device. -// All but the earliest v1 NeoPixels expect an 800 KHz data stream, this is -// the default if unspecified. Because flash space is very limited on ATtiny -// devices (e.g. Trinket, Gemma), v1 NeoPixels aren't handled by default on -// those chips, though it can be enabled by removing the ifndef/endif below, -// but code will be bigger. Conversely, can disable the NEO_KHZ400 line on -// other MCUs to remove v1 support and save a little space. - -#define NEO_KHZ400 0x0100 ///< 400 KHz data transmission - -typedef uint16_t neoPixelType; ///< 3rd arg to Adafruit_NeoPixel constructor - /*! @brief Class that stores state and functions for interacting with Adafruit NeoPixels and compatible devices. @@ -92,112 +46,23 @@ class Adafruit_NeoPixel { public: // Constructor: number of LEDs, pin number, LED type - Adafruit_NeoPixel(uint16_t n, int16_t pin = 6, - neoPixelType type = NEO_GRB); + Adafruit_NeoPixel(int16_t pin = -1); Adafruit_NeoPixel(void); - ~Adafruit_NeoPixel(); - void begin(void); void show(void); void setPin(int16_t p); - void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b); - void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w); - void setPixelColor(uint16_t n, uint32_t c); - void setBrightness(uint8_t); - void clear(void); - void updateLength(uint16_t n); - void updateType(neoPixelType t); - /*! - @brief Check whether a call to show() will start sending data - immediately or will 'block' for a required interval. NeoPixels - require a short quiet time (about 300 microseconds) after the - last bit is received before the data 'latches' and new data can - start being received. Usually one's sketch is implicitly using - this time to generate a new frame of animation...but if it - finishes very quickly, this function could be used to see if - there's some idle time available for some low-priority - concurrent task. - @return 1 or true if show() will start sending immediately, 0 or false - if show() would block (meaning some idle time is available). - */ - bool canShow(void) { - // It's normal and possible for endTime to exceed micros() if the - // 32-bit clock counter has rolled over (about every 70 minutes). - // Since both are uint32_t, a negative delta correctly maps back to - // positive space, and it would seem like the subtraction below would - // suffice. But a problem arises if code invokes show() very - // infrequently...the micros() counter may roll over MULTIPLE times in - // that interval, the delta calculation is no longer correct and the - // next update may stall for a very long time. The check below resets - // the latch counter if a rollover has occurred. This can cause an - // extra delay of up to 300 microseconds in the rare case where a - // show() call happens precisely around the rollover, but that's - // neither likely nor especially harmful, vs. other code that might - // stall for 30+ minutes, or having to document and frequently remind - // and/or provide tech support explaining an unintuitive need for - // show() calls at least once an hour. - uint32_t now = micros(); - if (endTime > now) { - endTime = now; - } - return (now - endTime) >= 300L; - } - - /*! - @brief Retrieve the pin number used for NeoPixel data output. - @return Arduino pin number (-1 if not set). - */ - int16_t getPin(void) const { return pin; }; - /*! - @brief Return the number of pixels in an Adafruit_NeoPixel strip object. - @return Pixel count (0 if not set). - */ - uint16_t numPixels(void) const { return numLEDs; } - - /*! - @brief Convert separate red, green and blue values into a single - "packed" 32-bit RGB color. - @param r Red brightness, 0 to 255. - @param g Green brightness, 0 to 255. - @param b Blue brightness, 0 to 255. - @return 32-bit packed RGB value, which can then be assigned to a - variable for later use or passed to the setPixelColor() - function. Packed RGB format is predictable, regardless of - LED strand color order. - */ - static uint32_t Color(uint8_t r, uint8_t g, uint8_t b) { - return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; - } - /*! - @brief Convert separate red, green, blue and white values into a - single "packed" 32-bit WRGB color. - @param r Red brightness, 0 to 255. - @param g Green brightness, 0 to 255. - @param b Blue brightness, 0 to 255. - @param w White brightness, 0 to 255. - @return 32-bit packed WRGB value, which can then be assigned to a - variable for later use or passed to the setPixelColor() - function. Packed WRGB format is predictable, regardless of - LED strand color order. - */ - static uint32_t Color(uint8_t r, uint8_t g, uint8_t b, uint8_t w) { - return ((uint32_t)w << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; - } + void setPixelColor(uint32_t c); + void updateLength(void); private: protected: - bool begun; ///< true if begin() previously called - uint16_t numLEDs; ///< Number of RGB LEDs in strip - uint16_t numBytes; ///< Size of 'pixels' buffer below + uint8_t numBytes = 3; ///< Size of 'pixels' buffer below int16_t pin; ///< Output pin number (-1 if not yet set) - uint8_t brightness; ///< Strip brightness 0-255 (stored as +1) uint8_t *pixels; ///< Holds LED color values (3 or 4 bytes each) - uint8_t rOffset; ///< Red index within each 3- or 4-byte pixel - uint8_t gOffset; ///< Index of green byte - uint8_t bOffset; ///< Index of blue byte - uint8_t wOffset; ///< Index of white (==rOffset if no white) - uint32_t endTime; ///< Latch timing reference + uint8_t rOffset = 0b01; ///< Red index within each 3- or 4-byte pixel + uint8_t gOffset = 0b00; ///< Index of green byte + uint8_t bOffset = 0b10; ///< Index of blue byte }; #endif // ADAFRUIT_NEOPIXEL_H diff --git a/Software/src/lib/adafruit-Adafruit_NeoPixel/esp.c b/Software/src/lib/adafruit-Adafruit_NeoPixel/esp.c index bcf9b0fa..ec08e7d5 100644 --- a/Software/src/lib/adafruit-Adafruit_NeoPixel/esp.c +++ b/Software/src/lib/adafruit-Adafruit_NeoPixel/esp.c @@ -131,7 +131,7 @@ err: return ret; } -void espShow(uint8_t pin, uint8_t* pixels, uint32_t numBytes) { +void espShow(uint8_t pin, uint8_t* pixels, uint8_t numBytes) { if (led_chan == NULL) { rmt_tx_channel_config_t tx_chan_config = { .clk_src = RMT_CLK_SRC_DEFAULT, From ce7547f71eac0baed59153c2d14208ecfd83d2c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 17 Aug 2025 00:19:45 +0300 Subject: [PATCH 009/245] Optimize Neopixel library for maximum performance --- Software/src/devboard/utils/led_handler.cpp | 9 +- Software/src/devboard/utils/led_handler.h | 8 +- .../Adafruit_NeoPixel.cpp | 370 +----------------- .../Adafruit_NeoPixel.h | 149 +------ 4 files changed, 33 insertions(+), 503 deletions(-) diff --git a/Software/src/devboard/utils/led_handler.cpp b/Software/src/devboard/utils/led_handler.cpp index 9c217a3d..73fefb4d 100644 --- a/Software/src/devboard/utils/led_handler.cpp +++ b/Software/src/devboard/utils/led_handler.cpp @@ -24,7 +24,6 @@ bool led_init(void) { } led = new LED(datalayer.battery.status.led_mode, esp32hal->LED_PIN(), esp32hal->LED_MAX_BRIGHTNESS()); - led->init(); return true; } @@ -57,20 +56,20 @@ void LED::exe(void) { switch (get_event_level()) { case EVENT_LEVEL_INFO: color = led_color::GREEN; - pixels.setPixelColor(0, COLOR_GREEN(brightness)); // Green pulsing LED + pixels.setPixelColor(COLOR_GREEN(brightness)); // Green pulsing LED break; case EVENT_LEVEL_WARNING: color = led_color::YELLOW; - pixels.setPixelColor(0, COLOR_YELLOW(brightness)); // Yellow pulsing LED + pixels.setPixelColor(COLOR_YELLOW(brightness)); // Yellow pulsing LED break; case EVENT_LEVEL_DEBUG: case EVENT_LEVEL_UPDATE: color = led_color::BLUE; - pixels.setPixelColor(0, COLOR_BLUE(brightness)); // Blue pulsing LED + pixels.setPixelColor(COLOR_BLUE(brightness)); // Blue pulsing LED break; case EVENT_LEVEL_ERROR: color = led_color::RED; - pixels.setPixelColor(0, COLOR_RED(esp32hal->LED_MAX_BRIGHTNESS())); // Red LED full brightness + pixels.setPixelColor(COLOR_RED(esp32hal->LED_MAX_BRIGHTNESS())); // Red LED full brightness break; default: break; diff --git a/Software/src/devboard/utils/led_handler.h b/Software/src/devboard/utils/led_handler.h index 60c18d8c..4d8e01ee 100644 --- a/Software/src/devboard/utils/led_handler.h +++ b/Software/src/devboard/utils/led_handler.h @@ -9,16 +9,12 @@ class LED { led_color color = led_color::GREEN; LED(gpio_num_t pin, uint8_t maxBrightness) - : pixels(1, pin, NEO_GRB), - max_brightness(maxBrightness), - brightness(maxBrightness), - mode(led_mode_enum::CLASSIC) {} + : pixels(pin), max_brightness(maxBrightness), brightness(maxBrightness), mode(led_mode_enum::CLASSIC) {} LED(led_mode_enum mode, gpio_num_t pin, uint8_t maxBrightness) - : pixels(1, pin, NEO_GRB), max_brightness(maxBrightness), brightness(maxBrightness), mode(mode) {} + : pixels(pin), max_brightness(maxBrightness), brightness(maxBrightness), mode(mode) {} void exe(void); - void init(void) { pixels.begin(); } private: Adafruit_NeoPixel pixels; diff --git a/Software/src/lib/adafruit-Adafruit_NeoPixel/Adafruit_NeoPixel.cpp b/Software/src/lib/adafruit-Adafruit_NeoPixel/Adafruit_NeoPixel.cpp index 06e964a0..540890bb 100644 --- a/Software/src/lib/adafruit-Adafruit_NeoPixel/Adafruit_NeoPixel.cpp +++ b/Software/src/lib/adafruit-Adafruit_NeoPixel/Adafruit_NeoPixel.cpp @@ -1,368 +1,38 @@ -/*! - * @file Adafruit_NeoPixel.cpp - * - * @mainpage Arduino Library for driving Adafruit NeoPixel addressable LEDs, - * FLORA RGB Smart Pixels and compatible devicess -- WS2811, WS2812, WS2812B, - * SK6812, etc. - * - * @section intro_sec Introduction - * - * This is the documentation for Adafruit's NeoPixel library for the - * Arduino platform, allowing a broad range of microcontroller boards - * (most AVR boards, many ARM devices, ESP8266 and ESP32, among others) - * to control Adafruit NeoPixels, FLORA RGB Smart Pixels and compatible - * devices -- WS2811, WS2812, WS2812B, SK6812, etc. - * - * Adafruit invests time and resources providing this open source code, - * please support Adafruit and open-source hardware by purchasing products - * from Adafruit! - * - * @section author Author - * - * Written by Phil "Paint Your Dragon" Burgess for Adafruit Industries, - * with contributions by PJRC, Michael Miller and other members of the - * open source community. - * - * @section license License - * - * This file is part of the Adafruit_NeoPixel library. - * - * Adafruit_NeoPixel is free software: you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * Adafruit_NeoPixel is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with NeoPixel. If not, see - * . - * - */ +/*Based on the Adafruit Neopixel library, which has been heavily modified to support only 1x RGB LED for lowest possible CPU usage*/ #include "Adafruit_NeoPixel.h" -/*! - @brief NeoPixel constructor when length, pin and pixel type are known - at compile-time. - @param n Number of NeoPixels in strand. - @param p Arduino pin number which will drive the NeoPixel data in. - @param t Pixel type -- add together NEO_* constants defined in - Adafruit_NeoPixel.h, for example NEO_GRB+NEO_KHZ800 for - NeoPixels expecting an 800 KHz (vs 400 KHz) data stream - with color bytes expressed in green, red, blue order per - pixel. - @return Adafruit_NeoPixel object. Call the begin() function before use. -*/ -Adafruit_NeoPixel::Adafruit_NeoPixel(uint16_t n, int16_t p, neoPixelType t) - : begun(false), brightness(0), pixels(NULL), endTime(0) { - updateType(t); - updateLength(n); +extern "C" void espShow(uint16_t pin, uint8_t *pixels, uint8_t numBytes); + +Adafruit_NeoPixel::Adafruit_NeoPixel(int16_t p) : pixels(NULL) { + updateLength(); setPin(p); } -/*! - @brief "Empty" NeoPixel constructor when length, pin and/or pixel type - are not known at compile-time, and must be initialized later with - updateType(), updateLength() and setPin(). - @return Adafruit_NeoPixel object. Call the begin() function before use. - @note This function is deprecated, here only for old projects that - may still be calling it. New projects should instead use the - 'new' keyword with the first constructor syntax (length, pin, - type). -*/ -Adafruit_NeoPixel::Adafruit_NeoPixel() - : - begun(false), numLEDs(0), numBytes(0), pin(-1), brightness(0), - pixels(NULL), rOffset(1), gOffset(0), bOffset(2), wOffset(1), endTime(0) { -} - -/*! - @brief Deallocate Adafruit_NeoPixel object, set data pin back to INPUT. -*/ -Adafruit_NeoPixel::~Adafruit_NeoPixel() { +void Adafruit_NeoPixel::updateLength(void) { free(pixels); - if (pin >= 0) - pinMode(pin, INPUT); + pixels = (uint8_t *)malloc(numBytes); + if (pixels) memset(pixels, 0, numBytes); } -/*! - @brief Configure NeoPixel pin for output. -*/ -void Adafruit_NeoPixel::begin(void) { - if (pin >= 0) { - pinMode(pin, OUTPUT); - digitalWrite(pin, LOW); - } - begun = true; -} - -/*! - @brief Change the length of a previously-declared Adafruit_NeoPixel - strip object. Old data is deallocated and new data is cleared. - Pin number and pixel format are unchanged. - @param n New length of strip, in pixels. - @note This function is deprecated, here only for old projects that - may still be calling it. New projects should instead use the - 'new' keyword with the first constructor syntax (length, pin, - type). -*/ -void Adafruit_NeoPixel::updateLength(uint16_t n) { - free(pixels); // Free existing data (if any) - - // Allocate new data -- note: ALL PIXELS ARE CLEARED - numBytes = n * ((wOffset == rOffset) ? 3 : 4); - if ((pixels = (uint8_t *)malloc(numBytes))) { - memset(pixels, 0, numBytes); - numLEDs = n; - } else { - numLEDs = numBytes = 0; - } -} - -/*! - @brief Change the pixel format of a previously-declared - Adafruit_NeoPixel strip object. If format changes from one of - the RGB variants to an RGBW variant (or RGBW to RGB), the old - data will be deallocated and new data is cleared. Otherwise, - the old data will remain in RAM and is not reordered to the - new format, so it's advisable to follow up with clear(). - @param t Pixel type -- add together NEO_* constants defined in - Adafruit_NeoPixel.h, for example NEO_GRB+NEO_KHZ800 for - NeoPixels expecting an 800 KHz (vs 400 KHz) data stream - with color bytes expressed in green, red, blue order per - pixel. - @note This function is deprecated, here only for old projects that - may still be calling it. New projects should instead use the - 'new' keyword with the first constructor syntax - (length, pin, type). -*/ -void Adafruit_NeoPixel::updateType(neoPixelType t) { - bool oldThreeBytesPerPixel = (wOffset == rOffset); // false if RGBW - - wOffset = (t >> 6) & 0b11; // See notes in header file - rOffset = (t >> 4) & 0b11; // regarding R/G/B/W offsets - gOffset = (t >> 2) & 0b11; - bOffset = t & 0b11; - - // If bytes-per-pixel has changed (and pixel data was previously - // allocated), re-allocate to new size. Will clear any data. - if (pixels) { - bool newThreeBytesPerPixel = (wOffset == rOffset); - if (newThreeBytesPerPixel != oldThreeBytesPerPixel) - updateLength(numLEDs); - } -} - -extern "C" void espShow(uint16_t pin, uint8_t *pixels, uint32_t numBytes); - -/*! - @brief Transmit pixel data in RAM to NeoPixels. - @note On most architectures, interrupts are temporarily disabled in - order to achieve the correct NeoPixel signal timing. This means - that the Arduino millis() and micros() functions, which require - interrupts, will lose small intervals of time whenever this - function is called (about 30 microseconds per RGB pixel, 40 for - RGBW pixels). There's no easy fix for this, but a few - specialized alternative or companion libraries exist that use - very device-specific peripherals to work around it. -*/ -void Adafruit_NeoPixel::show(void) { - - if (!pixels) - return; - - // Data latch = 300+ microsecond pause in the output stream. Rather than - // put a delay at the end of the function, the ending time is noted and - // the function will simply hold off (if needed) on issuing the - // subsequent round of data until the latch time has elapsed. This - // allows the mainline code to start generating the next frame of data - // rather than stalling for the latch. - while (!canShow()) - ; - // endTime is a private member (rather than global var) so that multiple - // instances on different pins can be quickly issued in succession (each - // instance doesn't delay the next). - - // In order to make this code runtime-configurable to work with any pin, - // SBI/CBI instructions are eschewed in favor of full PORT writes via the - // OUT or ST instructions. It relies on two facts: that peripheral - // functions (such as PWM) take precedence on output pins, so our PORT- - // wide writes won't interfere, and that interrupts are globally disabled - // while data is being issued to the LEDs, so no other code will be - // accessing the PORT. The code takes an initial 'snapshot' of the PORT - // state, computes 'pin high' and 'pin low' values, and writes these back - // to the PORT register as needed. - - // NRF52 may use PWM + DMA (if available), may not need to disable interrupt - // ESP32 may not disable interrupts because espShow() uses RMT which tries to acquire locks - - espShow(pin, pixels, numBytes); - - endTime = micros(); // Save EOD time for latch on next call -} - -/*! - @brief Set/change the NeoPixel output pin number. Previous pin, - if any, is set to INPUT and the new pin is set to OUTPUT. - @param p Arduino pin number (-1 = no pin). -*/ void Adafruit_NeoPixel::setPin(int16_t p) { - if (begun && (pin >= 0)) - pinMode(pin, INPUT); // Disable existing out pin + if (pin >= 0) pinMode(pin, INPUT); pin = p; - if (begun) { + if ((p >= 0)) { pinMode(p, OUTPUT); digitalWrite(p, LOW); } } -/*! - @brief Set a pixel's color using separate red, green and blue - components. If using RGBW pixels, white will be set to 0. - @param n Pixel index, starting from 0. - @param r Red brightness, 0 = minimum (off), 255 = maximum. - @param g Green brightness, 0 = minimum (off), 255 = maximum. - @param b Blue brightness, 0 = minimum (off), 255 = maximum. -*/ -void Adafruit_NeoPixel::setPixelColor(uint16_t n, uint8_t r, uint8_t g, - uint8_t b) { - - if (n < numLEDs) { - if (brightness) { // See notes in setBrightness() - r = (r * brightness) >> 8; - g = (g * brightness) >> 8; - b = (b * brightness) >> 8; - } - uint8_t *p; - if (wOffset == rOffset) { // Is an RGB-type strip - p = &pixels[n * 3]; // 3 bytes per pixel - } else { // Is a WRGB-type strip - p = &pixels[n * 4]; // 4 bytes per pixel - p[wOffset] = 0; // But only R,G,B passed -- set W to 0 - } - p[rOffset] = r; // R,G,B always stored - p[gOffset] = g; - p[bOffset] = b; - } +void Adafruit_NeoPixel::show(void) { + if (!pixels) return; + espShow(pin, pixels, numBytes); } -/*! - @brief Set a pixel's color using separate red, green, blue and white - components (for RGBW NeoPixels only). - @param n Pixel index, starting from 0. - @param r Red brightness, 0 = minimum (off), 255 = maximum. - @param g Green brightness, 0 = minimum (off), 255 = maximum. - @param b Blue brightness, 0 = minimum (off), 255 = maximum. - @param w White brightness, 0 = minimum (off), 255 = maximum, ignored - if using RGB pixels. -*/ -void Adafruit_NeoPixel::setPixelColor(uint16_t n, uint8_t r, uint8_t g, - uint8_t b, uint8_t w) { - - if (n < numLEDs) { - if (brightness) { // See notes in setBrightness() - r = (r * brightness) >> 8; - g = (g * brightness) >> 8; - b = (b * brightness) >> 8; - w = (w * brightness) >> 8; - } - uint8_t *p; - if (wOffset == rOffset) { // Is an RGB-type strip - p = &pixels[n * 3]; // 3 bytes per pixel (ignore W) - } else { // Is a WRGB-type strip - p = &pixels[n * 4]; // 4 bytes per pixel - p[wOffset] = w; // Store W - } - p[rOffset] = r; // Store R,G,B - p[gOffset] = g; - p[bOffset] = b; - } -} - -/*! - @brief Set a pixel's color using a 32-bit 'packed' RGB or RGBW value. - @param n Pixel index, starting from 0. - @param c 32-bit color value. Most significant byte is white (for RGBW - pixels) or ignored (for RGB pixels), next is red, then green, - and least significant byte is blue. -*/ -void Adafruit_NeoPixel::setPixelColor(uint16_t n, uint32_t c) { - if (n < numLEDs) { - uint8_t *p, r = (uint8_t)(c >> 16), g = (uint8_t)(c >> 8), b = (uint8_t)c; - if (brightness) { // See notes in setBrightness() - r = (r * brightness) >> 8; - g = (g * brightness) >> 8; - b = (b * brightness) >> 8; - } - if (wOffset == rOffset) { - p = &pixels[n * 3]; - } else { - p = &pixels[n * 4]; - uint8_t w = (uint8_t)(c >> 24); - p[wOffset] = brightness ? ((w * brightness) >> 8) : w; - } - p[rOffset] = r; - p[gOffset] = g; - p[bOffset] = b; - } -} - -/*! - @brief Adjust output brightness. Does not immediately affect what's - currently displayed on the LEDs. The next call to show() will - refresh the LEDs at this level. - @param b Brightness setting, 0=minimum (off), 255=brightest. - @note This was intended for one-time use in one's setup() function, - not as an animation effect in itself. Because of the way this - library "pre-multiplies" LED colors in RAM, changing the - brightness is often a "lossy" operation -- what you write to - pixels isn't necessary the same as what you'll read back. - Repeated brightness changes using this function exacerbate the - problem. Smart programs therefore treat the strip as a - write-only resource, maintaining their own state to render each - frame of an animation, not relying on read-modify-write. -*/ -void Adafruit_NeoPixel::setBrightness(uint8_t b) { - // Stored brightness value is different than what's passed. - // This simplifies the actual scaling math later, allowing a fast - // 8x8-bit multiply and taking the MSB. 'brightness' is a uint8_t, - // adding 1 here may (intentionally) roll over...so 0 = max brightness - // (color values are interpreted literally; no scaling), 1 = min - // brightness (off), 255 = just below max brightness. - uint8_t newBrightness = b + 1; - if (newBrightness != brightness) { // Compare against prior value - // Brightness has changed -- re-scale existing data in RAM, - // This process is potentially "lossy," especially when increasing - // brightness. The tight timing in the WS2811/WS2812 code means there - // aren't enough free cycles to perform this scaling on the fly as data - // is issued. So we make a pass through the existing color data in RAM - // and scale it (subsequent graphics commands also work at this - // brightness level). If there's a significant step up in brightness, - // the limited number of steps (quantization) in the old data will be - // quite visible in the re-scaled version. For a non-destructive - // change, you'll need to re-render the full strip data. C'est la vie. - uint8_t c, *ptr = pixels, - oldBrightness = brightness - 1; // De-wrap old brightness value - uint16_t scale; - if (oldBrightness == 0) - scale = 0; // Avoid /0 - else if (b == 255) - scale = 65535 / oldBrightness; - else - scale = (((uint16_t)newBrightness << 8) - 1) / oldBrightness; - for (uint16_t i = 0; i < numBytes; i++) { - c = *ptr; - *ptr++ = (c * scale) >> 8; - } - brightness = newBrightness; - } -} - -/*! - @brief Fill the whole NeoPixel strip with 0 / black / off. -*/ -void Adafruit_NeoPixel::clear(void) { memset(pixels, 0, numBytes); } +void Adafruit_NeoPixel::setPixelColor(uint32_t c) { + uint8_t *p = pixels; + uint8_t r = (uint8_t)(c >> 16), g = (uint8_t)(c >> 8), b = (uint8_t)c; + p[rOffset] = r; + p[gOffset] = g; + p[bOffset] = b; +} \ No newline at end of file diff --git a/Software/src/lib/adafruit-Adafruit_NeoPixel/Adafruit_NeoPixel.h b/Software/src/lib/adafruit-Adafruit_NeoPixel/Adafruit_NeoPixel.h index 541145ed..d4f4c1fc 100644 --- a/Software/src/lib/adafruit-Adafruit_NeoPixel/Adafruit_NeoPixel.h +++ b/Software/src/lib/adafruit-Adafruit_NeoPixel/Adafruit_NeoPixel.h @@ -38,52 +38,6 @@ #include -// The order of primary colors in the NeoPixel data stream can vary among -// device types, manufacturers and even different revisions of the same -// item. The third parameter to the Adafruit_NeoPixel constructor encodes -// the per-pixel byte offsets of the red, green and blue primaries (plus -// white, if present) in the data stream -- the following #defines provide -// an easier-to-use named version for each permutation. e.g. NEO_GRB -// indicates a NeoPixel-compatible device expecting three bytes per pixel, -// with the first byte transmitted containing the green value, second -// containing red and third containing blue. The in-memory representation -// of a chain of NeoPixels is the same as the data-stream order; no -// re-ordering of bytes is required when issuing data to the chain. -// Most of these values won't exist in real-world devices, but it's done -// this way so we're ready for it (also, if using the WS2811 driver IC, -// one might have their pixels set up in any weird permutation). - -// Bits 5,4 of this value are the offset (0-3) from the first byte of a -// pixel to the location of the red color byte. Bits 3,2 are the green -// offset and 1,0 are the blue offset. If it is an RGBW-type device -// (supporting a white primary in addition to R,G,B), bits 7,6 are the -// offset to the white byte...otherwise, bits 7,6 are set to the same value -// as 5,4 (red) to indicate an RGB (not RGBW) device. -// i.e. binary representation: -// 0bWWRRGGBB for RGBW devices -// 0bRRRRGGBB for RGB - -// RGB NeoPixel permutations; white and red offsets are always same -// Offset: W R G B -#define NEO_RGB ((0 << 6) | (0 << 4) | (1 << 2) | (2)) ///< Transmit as R,G,B -#define NEO_RBG ((0 << 6) | (0 << 4) | (2 << 2) | (1)) ///< Transmit as R,B,G -#define NEO_GRB ((1 << 6) | (1 << 4) | (0 << 2) | (2)) ///< Transmit as G,R,B -#define NEO_GBR ((2 << 6) | (2 << 4) | (0 << 2) | (1)) ///< Transmit as G,B,R -#define NEO_BRG ((1 << 6) | (1 << 4) | (2 << 2) | (0)) ///< Transmit as B,R,G -#define NEO_BGR ((2 << 6) | (2 << 4) | (1 << 2) | (0)) ///< Transmit as B,G,R - -// Add NEO_KHZ400 to the color order value to indicate a 400 KHz device. -// All but the earliest v1 NeoPixels expect an 800 KHz data stream, this is -// the default if unspecified. Because flash space is very limited on ATtiny -// devices (e.g. Trinket, Gemma), v1 NeoPixels aren't handled by default on -// those chips, though it can be enabled by removing the ifndef/endif below, -// but code will be bigger. Conversely, can disable the NEO_KHZ400 line on -// other MCUs to remove v1 support and save a little space. - -#define NEO_KHZ400 0x0100 ///< 400 KHz data transmission - -typedef uint16_t neoPixelType; ///< 3rd arg to Adafruit_NeoPixel constructor - /*! @brief Class that stores state and functions for interacting with Adafruit NeoPixels and compatible devices. @@ -92,112 +46,23 @@ class Adafruit_NeoPixel { public: // Constructor: number of LEDs, pin number, LED type - Adafruit_NeoPixel(uint16_t n, int16_t pin = 6, - neoPixelType type = NEO_GRB); + Adafruit_NeoPixel(int16_t pin = -1); Adafruit_NeoPixel(void); - ~Adafruit_NeoPixel(); - void begin(void); void show(void); void setPin(int16_t p); - void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b); - void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w); - void setPixelColor(uint16_t n, uint32_t c); - void setBrightness(uint8_t); - void clear(void); - void updateLength(uint16_t n); - void updateType(neoPixelType t); - /*! - @brief Check whether a call to show() will start sending data - immediately or will 'block' for a required interval. NeoPixels - require a short quiet time (about 300 microseconds) after the - last bit is received before the data 'latches' and new data can - start being received. Usually one's sketch is implicitly using - this time to generate a new frame of animation...but if it - finishes very quickly, this function could be used to see if - there's some idle time available for some low-priority - concurrent task. - @return 1 or true if show() will start sending immediately, 0 or false - if show() would block (meaning some idle time is available). - */ - bool canShow(void) { - // It's normal and possible for endTime to exceed micros() if the - // 32-bit clock counter has rolled over (about every 70 minutes). - // Since both are uint32_t, a negative delta correctly maps back to - // positive space, and it would seem like the subtraction below would - // suffice. But a problem arises if code invokes show() very - // infrequently...the micros() counter may roll over MULTIPLE times in - // that interval, the delta calculation is no longer correct and the - // next update may stall for a very long time. The check below resets - // the latch counter if a rollover has occurred. This can cause an - // extra delay of up to 300 microseconds in the rare case where a - // show() call happens precisely around the rollover, but that's - // neither likely nor especially harmful, vs. other code that might - // stall for 30+ minutes, or having to document and frequently remind - // and/or provide tech support explaining an unintuitive need for - // show() calls at least once an hour. - uint32_t now = micros(); - if (endTime > now) { - endTime = now; - } - return (now - endTime) >= 300L; - } - - /*! - @brief Retrieve the pin number used for NeoPixel data output. - @return Arduino pin number (-1 if not set). - */ - int16_t getPin(void) const { return pin; }; - /*! - @brief Return the number of pixels in an Adafruit_NeoPixel strip object. - @return Pixel count (0 if not set). - */ - uint16_t numPixels(void) const { return numLEDs; } - - /*! - @brief Convert separate red, green and blue values into a single - "packed" 32-bit RGB color. - @param r Red brightness, 0 to 255. - @param g Green brightness, 0 to 255. - @param b Blue brightness, 0 to 255. - @return 32-bit packed RGB value, which can then be assigned to a - variable for later use or passed to the setPixelColor() - function. Packed RGB format is predictable, regardless of - LED strand color order. - */ - static uint32_t Color(uint8_t r, uint8_t g, uint8_t b) { - return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; - } - /*! - @brief Convert separate red, green, blue and white values into a - single "packed" 32-bit WRGB color. - @param r Red brightness, 0 to 255. - @param g Green brightness, 0 to 255. - @param b Blue brightness, 0 to 255. - @param w White brightness, 0 to 255. - @return 32-bit packed WRGB value, which can then be assigned to a - variable for later use or passed to the setPixelColor() - function. Packed WRGB format is predictable, regardless of - LED strand color order. - */ - static uint32_t Color(uint8_t r, uint8_t g, uint8_t b, uint8_t w) { - return ((uint32_t)w << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; - } + void setPixelColor(uint32_t c); + void updateLength(void); private: protected: - bool begun; ///< true if begin() previously called - uint16_t numLEDs; ///< Number of RGB LEDs in strip - uint16_t numBytes; ///< Size of 'pixels' buffer below + uint8_t numBytes = 3; ///< Size of 'pixels' buffer below int16_t pin; ///< Output pin number (-1 if not yet set) - uint8_t brightness; ///< Strip brightness 0-255 (stored as +1) uint8_t *pixels; ///< Holds LED color values (3 or 4 bytes each) - uint8_t rOffset; ///< Red index within each 3- or 4-byte pixel - uint8_t gOffset; ///< Index of green byte - uint8_t bOffset; ///< Index of blue byte - uint8_t wOffset; ///< Index of white (==rOffset if no white) - uint32_t endTime; ///< Latch timing reference + uint8_t rOffset = 0b01; ///< Red index within each 3- or 4-byte pixel + uint8_t gOffset = 0b00; ///< Index of green byte + uint8_t bOffset = 0b10; ///< Index of blue byte }; #endif // ADAFRUIT_NEOPIXEL_H From ec9ac18fa4abdf139fd37f70ba76a7eaa9a998fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 17 Aug 2025 11:02:24 +0300 Subject: [PATCH 010/245] Add 4 temperature measurements to More Battery Info page --- Software/src/battery/NISSAN-LEAF-BATTERY.cpp | 6 ++++++ Software/src/battery/NISSAN-LEAF-BATTERY.h | 8 ++++---- Software/src/battery/NISSAN-LEAF-HTML.h | 4 ++++ Software/src/datalayer/datalayer_extended.h | 6 ++++++ 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp index fd61a6f9..be9900d9 100644 --- a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp +++ b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp @@ -188,6 +188,10 @@ void NissanLeafBattery:: datalayer_nissan->HeatingStart = battery_Heating_Start; datalayer_nissan->HeaterSendRequest = battery_Batt_Heater_Mail_Send_Request; datalayer_nissan->battery_HX = battery_HX; + datalayer_nissan->temperature1 = ((Temp_fromRAW_to_F(battery_temp_raw_1) - 320) * 5) / 9; //Convert from F to C + datalayer_nissan->temperature2 = ((Temp_fromRAW_to_F(battery_temp_raw_2) - 320) * 5) / 9; //Convert from F to C + datalayer_nissan->temperature3 = ((Temp_fromRAW_to_F(battery_temp_raw_3) - 320) * 5) / 9; //Convert from F to C + datalayer_nissan->temperature4 = ((Temp_fromRAW_to_F(battery_temp_raw_4) - 320) * 5) / 9; //Convert from F to C datalayer_nissan->CryptoChallenge = incomingChallenge; datalayer_nissan->SolvedChallengeMSB = ((solvedChallenge[7] << 24) | (solvedChallenge[6] << 16) | (solvedChallenge[5] << 8) | solvedChallenge[4]); @@ -799,6 +803,8 @@ bool NissanLeafBattery::is_message_corrupt(CAN_frame rx_frame) { uint16_t Temp_fromRAW_to_F(uint16_t temperature) { //This function feels horrible, but apparently works well if (temperature == 1021) { return 10; + } else if (temperature == 65535) { //Value unavailable, sensor does not exist + return 718; //0*C final calculation } else if (temperature >= 589) { return static_cast(1620 - temperature * 1.81); } else if (temperature >= 569) { diff --git a/Software/src/battery/NISSAN-LEAF-BATTERY.h b/Software/src/battery/NISSAN-LEAF-BATTERY.h index 503e6151..034b7b78 100644 --- a/Software/src/battery/NISSAN-LEAF-BATTERY.h +++ b/Software/src/battery/NISSAN-LEAF-BATTERY.h @@ -198,11 +198,11 @@ class NissanLeafBattery : public CanBattery { uint16_t battery_min_max_voltage[2]; //contains cell min[0] and max[1] values in mV uint16_t battery_HX = 0; //Internal resistance uint16_t battery_insulation = 0; //Insulation resistance - uint16_t battery_temp_raw_1 = 0; + uint16_t battery_temp_raw_1 = 718; uint8_t battery_temp_raw_2_highnibble = 0; - uint16_t battery_temp_raw_2 = 0; - uint16_t battery_temp_raw_3 = 0; - uint16_t battery_temp_raw_4 = 0; + uint16_t battery_temp_raw_2 = 718; + uint16_t battery_temp_raw_3 = 718; //This measurement not available on 2013+ + uint16_t battery_temp_raw_4 = 718; uint16_t battery_temp_raw_max = 0; uint16_t battery_temp_raw_min = 0; int16_t battery_temp_polled_max = 0; diff --git a/Software/src/battery/NISSAN-LEAF-HTML.h b/Software/src/battery/NISSAN-LEAF-HTML.h index 184cfee6..3fbbd8b9 100644 --- a/Software/src/battery/NISSAN-LEAF-HTML.h +++ b/Software/src/battery/NISSAN-LEAF-HTML.h @@ -54,6 +54,10 @@ class NissanLeafHtmlRenderer : public BatteryHtmlRenderer { content += "

Heating stopped: " + String(datalayer_extended.nissanleaf.HeatingStop) + "

"; content += "

Heating started: " + String(datalayer_extended.nissanleaf.HeatingStart) + "

"; content += "

Heating requested: " + String(datalayer_extended.nissanleaf.HeaterSendRequest) + "

"; + content += "

Temperature 1: " + String(datalayer_extended.nissanleaf.temperature1) + " °C

"; + content += "

Temperature 2: " + String(datalayer_extended.nissanleaf.temperature2) + " °C

"; + content += "

Temperature 3: " + String(datalayer_extended.nissanleaf.temperature3) + " °C

"; + content += "

Temperature 4: " + String(datalayer_extended.nissanleaf.temperature4) + " °C

"; content += "

CryptoChallenge: " + String(datalayer_extended.nissanleaf.CryptoChallenge) + "

"; content += "

SolvedChallenge: " + String(datalayer_extended.nissanleaf.SolvedChallengeMSB) + String(datalayer_extended.nissanleaf.SolvedChallengeLSB) + "

"; diff --git a/Software/src/datalayer/datalayer_extended.h b/Software/src/datalayer/datalayer_extended.h index 141c6547..165588d4 100644 --- a/Software/src/datalayer/datalayer_extended.h +++ b/Software/src/datalayer/datalayer_extended.h @@ -625,6 +625,12 @@ struct DATALAYER_INFO_NISSAN_LEAF { /** int16_t */ /** Insulation resistance, most likely kOhm */ uint16_t Insulation = 0; + /** int16_t */ + /** Temperature sensoros 1-4 */ + int16_t temperature1 = 0; + int16_t temperature2 = 0; + int16_t temperature3 = 0; // This sensor not available on 2013+ packs + int16_t temperature4 = 0; /** uint8_t */ /** battery_FAIL status */ uint8_t RelayCutRequest = 0; From 38c88e78f3154c93e3811f204ea4a3b744930a37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 17 Aug 2025 12:40:15 +0300 Subject: [PATCH 011/245] Update CPU temperature setpoint Raised the CPU temperature warning from 80 to 87deg to cut down on false positive warnings --- Software/src/devboard/safety/safety.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/src/devboard/safety/safety.cpp b/Software/src/devboard/safety/safety.cpp index 36137fba..a07d3fa9 100644 --- a/Software/src/devboard/safety/safety.cpp +++ b/Software/src/devboard/safety/safety.cpp @@ -26,7 +26,7 @@ void update_machineryprotection() { /* Check if the ESP32 CPU running the Battery-Emulator is too hot. We start with a warning, you can start to see Wifi issues if it becomes too hot If the chip starts to approach the design limit, we perform a graceful shutdown */ - if (datalayer.system.info.CPU_temperature > 80.0f) { + if (datalayer.system.info.CPU_temperature > 87.0f) { set_event(EVENT_CPU_OVERHEATING, 0); } else { clear_event(EVENT_CPU_OVERHEATING); From 2536639272d32d3c0ee3ed898ad12470cc5f8ef2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 17 Aug 2025 12:43:23 +0300 Subject: [PATCH 012/245] Increase setpoint in tests --- test/safety_tests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/safety_tests.cpp b/test/safety_tests.cpp index d6f5b55f..edaf6fce 100644 --- a/test/safety_tests.cpp +++ b/test/safety_tests.cpp @@ -6,7 +6,7 @@ TEST(SafetyTests, ShouldSetEventWhenTemperatureTooHigh) { init_events(); - datalayer.system.info.CPU_temperature = 82; + datalayer.system.info.CPU_temperature = 88; update_machineryprotection(); auto event_pointer = get_event_pointer(EVENT_CPU_OVERHEATING); From 98ba854ae803b8944e6a5c5c91d2998fb9748d62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 17 Aug 2025 14:23:12 +0300 Subject: [PATCH 013/245] Add decimals to temp value --- Software/src/battery/NISSAN-LEAF-HTML.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Software/src/battery/NISSAN-LEAF-HTML.h b/Software/src/battery/NISSAN-LEAF-HTML.h index 3fbbd8b9..7e3c7c42 100644 --- a/Software/src/battery/NISSAN-LEAF-HTML.h +++ b/Software/src/battery/NISSAN-LEAF-HTML.h @@ -54,10 +54,11 @@ class NissanLeafHtmlRenderer : public BatteryHtmlRenderer { content += "

Heating stopped: " + String(datalayer_extended.nissanleaf.HeatingStop) + "

"; content += "

Heating started: " + String(datalayer_extended.nissanleaf.HeatingStart) + "

"; content += "

Heating requested: " + String(datalayer_extended.nissanleaf.HeaterSendRequest) + "

"; - content += "

Temperature 1: " + String(datalayer_extended.nissanleaf.temperature1) + " °C

"; - content += "

Temperature 2: " + String(datalayer_extended.nissanleaf.temperature2) + " °C

"; - content += "

Temperature 3: " + String(datalayer_extended.nissanleaf.temperature3) + " °C

"; - content += "

Temperature 4: " + String(datalayer_extended.nissanleaf.temperature4) + " °C

"; + content += "

Temperature 1: " + String(datalayer_extended.nissanleaf.temperature1 / 10.0) + " °C

"; + content += "

Temperature 2: " + String(datalayer_extended.nissanleaf.temperature2 / 10.0) + " °C

"; + content += "

Temperature 3: " + String(datalayer_extended.nissanleaf.temperature3 / 10.0) + + " °C (Not on 2013+)

"; + content += "

Temperature 4: " + String(datalayer_extended.nissanleaf.temperature4 / 10.0) + " °C

"; content += "

CryptoChallenge: " + String(datalayer_extended.nissanleaf.CryptoChallenge) + "

"; content += "

SolvedChallenge: " + String(datalayer_extended.nissanleaf.SolvedChallengeMSB) + String(datalayer_extended.nissanleaf.SolvedChallengeLSB) + "

"; From be96cd98ef3422992e10ffae81e08c6db181895e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 17 Aug 2025 14:29:58 +0300 Subject: [PATCH 014/245] Hide temp3 if not on ZE0 --- Software/src/battery/NISSAN-LEAF-HTML.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Software/src/battery/NISSAN-LEAF-HTML.h b/Software/src/battery/NISSAN-LEAF-HTML.h index 7e3c7c42..56bf1b92 100644 --- a/Software/src/battery/NISSAN-LEAF-HTML.h +++ b/Software/src/battery/NISSAN-LEAF-HTML.h @@ -56,8 +56,9 @@ class NissanLeafHtmlRenderer : public BatteryHtmlRenderer { content += "

Heating requested: " + String(datalayer_extended.nissanleaf.HeaterSendRequest) + "

"; content += "

Temperature 1: " + String(datalayer_extended.nissanleaf.temperature1 / 10.0) + " °C

"; content += "

Temperature 2: " + String(datalayer_extended.nissanleaf.temperature2 / 10.0) + " °C

"; - content += "

Temperature 3: " + String(datalayer_extended.nissanleaf.temperature3 / 10.0) + - " °C (Not on 2013+)

"; + if (datalayer_extended.nissanleaf.LEAF_gen == 0) { + content += "

Temperature 3: " + String(datalayer_extended.nissanleaf.temperature3 / 10.0) + " °C

"; + } content += "

Temperature 4: " + String(datalayer_extended.nissanleaf.temperature4 / 10.0) + " °C

"; content += "

CryptoChallenge: " + String(datalayer_extended.nissanleaf.CryptoChallenge) + "

"; content += "

SolvedChallenge: " + String(datalayer_extended.nissanleaf.SolvedChallengeMSB) + From 8444855f24822dc5d1fcea0ece291a4638e307dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 17 Aug 2025 20:02:11 +0300 Subject: [PATCH 015/245] Add new box for contactor status --- Software/src/devboard/webserver/webserver.cpp | 166 ++++++++++-------- 1 file changed, 94 insertions(+), 72 deletions(-) diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 174efc78..3d4b6975 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -824,6 +824,28 @@ String processor(const String& var) { content += "button:hover { background-color: #3A4A52; }"; content += "h2 { font-size: 1.2em; margin: 0.3em 0 0.5em 0; }"; content += "h4 { margin: 0.6em 0; line-height: 1.2; }"; + //content += ".tooltip { position: relative; display: inline-block; }"; + content += ".tooltip .tooltiptext {"; + content += " visibility: hidden;"; + content += " width: 200px;"; + content += " background-color: #3A4A52;"; // Matching your button hover color + content += " color: white;"; + content += " text-align: center;"; + content += " border-radius: 6px;"; + content += " padding: 8px;"; + content += " position: absolute;"; + content += " z-index: 1;"; + content += " bottom: 125%;"; + content += " left: 50%;"; + content += " margin-left: -100px;"; + content += " opacity: 0;"; + content += " transition: opacity 0.3s;"; + content += " font-size: 0.9em;"; + content += " font-weight: normal;"; + content += " line-height: 1.4;"; + content += "}"; + content += ".tooltip:hover .tooltiptext { visibility: visible; opacity: 1; }"; + content += ".tooltip-icon { color: #505E67; cursor: help; }"; // Matching your button color content += ""; // Compact header @@ -1103,58 +1125,6 @@ String processor(const String& var) { } } - content += "

Battery allows contactor closing: "; - if (datalayer.system.status.battery_allows_contactor_closing == true) { - content += ""; - } else { - content += ""; - } - - content += " Inverter allows contactor closing: "; - if (datalayer.system.status.inverter_allows_contactor_closing == true) { - content += "

"; - } else { - content += "

"; - } - if (emulator_pause_status == NORMAL) - content += "

Power status: " + String(get_emulator_pause_status().c_str()) + "

"; - else - content += "

Power status: " + String(get_emulator_pause_status().c_str()) + "

"; - - if (contactor_control_enabled) { - content += "

Contactors controlled by emulator, state: "; - if (datalayer.system.status.contactors_engaged) { - content += "ON"; - } else { - content += "OFF"; - } - content += "

"; - if (contactor_control_enabled_double_battery) { - if (pwm_contactor_control) { - content += "

Cont. Neg.: "; - if (datalayer.system.status.contactors_battery2_engaged) { - content += "Economized"; - content += " Cont. Pos.: "; - content += "Economized"; - } else { - content += ""; - content += " Cont. Pos.: "; - content += ""; - } - } else if ( - esp32hal->SECOND_BATTERY_CONTACTORS_PIN() != - GPIO_NUM_NC) { // No PWM_CONTACTOR_CONTROL , we can read the pin and see feedback. Helpful if channel overloaded - content += "

Cont. Neg.: "; - if (digitalRead(esp32hal->SECOND_BATTERY_CONTACTORS_PIN()) == HIGH) { - content += ""; - } else { - content += ""; - } - } //no PWM_CONTACTOR_CONTROL - content += "

"; - } - } - // Close the block content += ""; @@ -1253,26 +1223,6 @@ String processor(const String& var) { content += "

Battery charging!

"; } - content += "

Automatic contactor closing allowed:

"; - content += "

Battery: "; - if (datalayer.system.status.battery2_allowed_contactor_closing == true) { - content += ""; - } else { - content += ""; - } - - content += " Inverter: "; - if (datalayer.system.status.inverter_allows_contactor_closing == true) { - content += "

"; - } else { - content += ""; - } - - if (emulator_pause_status == NORMAL) - content += "

Power status: " + String(get_emulator_pause_status().c_str()) + "

"; - else - content += "

Power status: " + String(get_emulator_pause_status().c_str()) + "

"; - if (contactor_control_enabled) { content += "

Contactors controlled by emulator, state: "; if (datalayer.system.status.contactors_battery2_engaged) { @@ -1317,6 +1267,78 @@ String processor(const String& var) { content += ""; } } + // Block for Contactor status and component request status + // Start a new block with gray background color + content += "
"; + + if (emulator_pause_status == NORMAL) { + content += "

Power status: " + String(get_emulator_pause_status().c_str()) + "

"; + } else { + content += "

Power status: " + String(get_emulator_pause_status().c_str()) + "

"; + } + + content += "

Battery allows contactor closing: "; + if (datalayer.battery.status.bms_status == FAULT) { + content += ""; + } else { + content += ""; + } + content += " Inverter allows contactor closing: "; + if (datalayer.system.status.inverter_allows_contactor_closing == true) { + content += "

"; + } else { + content += "

"; + } + content += "

Secondary battery allowed to join "; + if (datalayer.system.status.battery2_allowed_contactor_closing == true) { + content += ""; + } else { + content += "✕ (voltage mismatch)"; + } + + if (!contactor_control_enabled) { + content += "
"; + content += "

Contactors not fully controlled via emulator [?]

"; + content += + "This means you are either running CAN controlled contactors OR manually " + "powering the contactors. Battery-Emulator will have limited amount of control over the contactors!"; + content += "
"; + } else { //contactor_control_enabled TRUE + content += "

Contactors controlled by emulator, state: "; + if (datalayer.system.status.contactors_engaged) { + content += "ON"; + } else { + content += "OFF"; + } + content += "

"; + if (contactor_control_enabled_double_battery) { + if (pwm_contactor_control) { + content += "

Cont. Neg.: "; + if (datalayer.system.status.contactors_battery2_engaged) { + content += "Economized"; + content += " Cont. Pos.: "; + content += "Economized"; + } else { + content += ""; + content += " Cont. Pos.: "; + content += ""; + } + } else if ( + esp32hal->SECOND_BATTERY_CONTACTORS_PIN() != + GPIO_NUM_NC) { // No PWM_CONTACTOR_CONTROL , we can read the pin and see feedback. Helpful if channel overloaded + content += "

Cont. Neg.: "; + if (digitalRead(esp32hal->SECOND_BATTERY_CONTACTORS_PIN()) == HIGH) { + content += ""; + } else { + content += ""; + } + } //no PWM_CONTACTOR_CONTROL + content += "

"; + } + } + + // Close the block + content += ""; if (charger) { // Start a new block with orange background color From 1645c5b337abef159fdc25c8c8969f93b097cb73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 17 Aug 2025 21:24:55 +0300 Subject: [PATCH 016/245] Further improve dialogue boxes and texts --- .../comm_contactorcontrol.cpp | 9 +++--- Software/src/datalayer/datalayer.h | 4 +-- Software/src/devboard/utils/events.cpp | 4 +-- Software/src/devboard/webserver/webserver.cpp | 28 ++++++++++++------- 4 files changed, 26 insertions(+), 19 deletions(-) diff --git a/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp b/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp index ec654d6e..c63dd4ae 100644 --- a/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp +++ b/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp @@ -192,7 +192,7 @@ void handle_contactors() { set(negPin, OFF, PWM_OFF_DUTY); set(posPin, OFF, PWM_OFF_DUTY); set_event(EVENT_ERROR_OPEN_CONTACTOR, 0); - datalayer.system.status.contactors_engaged = false; + datalayer.system.status.contactors_engaged = 2; return; // A fault scenario latches the contactor control. It is not possible to recover without a powercycle (and investigation why fault occured) } @@ -201,10 +201,9 @@ void handle_contactors() { set(prechargePin, OFF); set(negPin, OFF, PWM_OFF_DUTY); set(posPin, OFF, PWM_OFF_DUTY); - datalayer.system.status.contactors_engaged = false; + datalayer.system.status.contactors_engaged = 0; - if (datalayer.system.status.battery_allows_contactor_closing && - datalayer.system.status.inverter_allows_contactor_closing && + if (datalayer.system.status.inverter_allows_contactor_closing && !datalayer.system.settings.equipment_stop_active) { contactorStatus = START_PRECHARGE; } @@ -263,7 +262,7 @@ void handle_contactors() { set(posPin, ON, PWM_HOLD_DUTY); dbg_contactors("PRECHARGE_OFF"); contactorStatus = COMPLETED; - datalayer.system.status.contactors_engaged = true; + datalayer.system.status.contactors_engaged = 1; } break; default: diff --git a/Software/src/datalayer/datalayer.h b/Software/src/datalayer/datalayer.h index aca1668c..ad910865 100644 --- a/Software/src/datalayer/datalayer.h +++ b/Software/src/datalayer/datalayer.h @@ -310,8 +310,8 @@ struct DATALAYER_SYSTEM_STATUS_TYPE { /** True if the inverter allows for the contactors to close */ bool inverter_allows_contactor_closing = true; - /** True if the contactor controlled by battery-emulator is closed */ - bool contactors_engaged = false; + /** 0 if starting up, 1 if contactors engaged, 2 if the contactors controlled by battery-emulator is opened */ + uint8_t contactors_engaged = 0; /** True if the contactor controlled by battery-emulator is closed. Determined by check_interconnect_available(); if voltage is OK */ bool contactors_battery2_engaged = false; diff --git a/Software/src/devboard/utils/events.cpp b/Software/src/devboard/utils/events.cpp index b5ba4a8f..0f94f079 100644 --- a/Software/src/devboard/utils/events.cpp +++ b/Software/src/devboard/utils/events.cpp @@ -270,8 +270,8 @@ String get_event_message_string(EVENTS_ENUM_TYPE event) { case EVENT_INTERFACE_MISSING: return "Configuration trying to use CAN interface not baked into the software. Recompile software!"; case EVENT_ERROR_OPEN_CONTACTOR: - return "Too much time spent in error state. Opening contactors, not safe to continue charging. " - "Check other error code for reason!"; + return "Too much time spent in error state. Opening contactors, not safe to continue. " + "Check other active ERROR code for reason. Reboot emulator after problem is solved!"; case EVENT_MODBUS_INVERTER_MISSING: return "Modbus inverter has not sent any data. Inspect communication wiring!"; case EVENT_NO_ENABLE_DETECTED: diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 3d4b6975..3a4dd0d9 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -1277,7 +1277,7 @@ String processor(const String& var) { content += "

Power status: " + String(get_emulator_pause_status().c_str()) + "

"; } - content += "

Battery allows contactor closing: "; + content += "

Emulator allows contactor closing: "; if (datalayer.battery.status.bms_status == FAULT) { content += ""; } else { @@ -1289,11 +1289,13 @@ String processor(const String& var) { } else { content += "

"; } - content += "

Secondary battery allowed to join "; - if (datalayer.system.status.battery2_allowed_contactor_closing == true) { - content += ""; - } else { - content += "✕ (voltage mismatch)"; + if (battery2) { + content += "

Secondary battery allowed to join "; + if (datalayer.system.status.battery2_allowed_contactor_closing == true) { + content += ""; + } else { + content += "✕ (voltage mismatch)"; + } } if (!contactor_control_enabled) { @@ -1304,13 +1306,19 @@ String processor(const String& var) { "powering the contactors. Battery-Emulator will have limited amount of control over the contactors!"; content += ""; } else { //contactor_control_enabled TRUE - content += "

Contactors controlled by emulator, state: "; - if (datalayer.system.status.contactors_engaged) { + content += "

Contactors controlled by emulator, state: "; + if (datalayer.system.status.contactors_engaged == 0) { + content += "PRECHARGE"; + } else if (datalayer.system.status.contactors_engaged == 1) { content += "ON"; - } else { + } else if (datalayer.system.status.contactors_engaged == 2) { content += "OFF"; + content += " [!]"; + content += + "Emulator spent too much time in critical FAULT event. Investigate event " + "causing this via Events page. Reboot required to resume operation!"; } - content += "

"; + content += "

"; if (contactor_control_enabled_double_battery) { if (pwm_contactor_control) { content += "

Cont. Neg.: "; From 5888457926c76287a834885582ff29f9daee7030 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 17 Aug 2025 21:43:43 +0300 Subject: [PATCH 017/245] Improve secondary battery visualization --- .../comm_contactorcontrol.cpp | 2 +- Software/src/devboard/webserver/webserver.cpp | 54 ++----------------- 2 files changed, 5 insertions(+), 51 deletions(-) diff --git a/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp b/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp index c63dd4ae..6ea7798a 100644 --- a/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp +++ b/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp @@ -55,7 +55,7 @@ const int OFF = 0; #define OFF 1 #endif //NC_CONTACTORS -#define MAX_ALLOWED_FAULT_TICKS 1000 +#define MAX_ALLOWED_FAULT_TICKS 1000 //1000 = 10 seconds #define NEGATIVE_CONTACTOR_TIME_MS \ 500 // Time after negative contactor is turned on, to start precharge (not actual precharge time!) #define PRECHARGE_COMPLETED_TIME_MS \ diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 3a4dd0d9..6081ac12 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -1222,47 +1222,6 @@ String processor(const String& var) { } else { // > 0 content += "

Battery charging!

"; } - - if (contactor_control_enabled) { - content += "

Contactors controlled by emulator, state: "; - if (datalayer.system.status.contactors_battery2_engaged) { - content += "ON"; - } else { - content += "OFF"; - } - content += "

"; - - if (contactor_control_enabled_double_battery) { - content += "

Cont. Neg.: "; - if (pwm_contactor_control) { - if (datalayer.system.status.contactors_battery2_engaged) { - content += "Economized"; - content += " Cont. Pos.: "; - content += "Economized"; - } else { - content += ""; - content += " Cont. Pos.: "; - content += ""; - } - } else { // No PWM_CONTACTOR_CONTROL , we can read the pin and see feedback. Helpful if channel overloaded -#if defined(SECOND_POSITIVE_CONTACTOR_PIN) && defined(SECOND_NEGATIVE_CONTACTOR_PIN) - if (digitalRead(SECOND_NEGATIVE_CONTACTOR_PIN) == HIGH) { - content += ""; - } else { - content += ""; - } - - content += " Cont. Pos.: "; - if (digitalRead(SECOND_POSITIVE_CONTACTOR_PIN) == HIGH) { - content += ""; - } else { - content += ""; - } -#endif - } - content += "

"; - } - } content += ""; content += ""; } @@ -1320,25 +1279,20 @@ String processor(const String& var) { } content += "

"; if (contactor_control_enabled_double_battery) { + content += "

Secondary battery contactor, state: "; if (pwm_contactor_control) { - content += "

Cont. Neg.: "; if (datalayer.system.status.contactors_battery2_engaged) { content += "Economized"; - content += " Cont. Pos.: "; - content += "Economized"; } else { - content += ""; - content += " Cont. Pos.: "; - content += ""; + content += "OFF"; } } else if ( esp32hal->SECOND_BATTERY_CONTACTORS_PIN() != GPIO_NUM_NC) { // No PWM_CONTACTOR_CONTROL , we can read the pin and see feedback. Helpful if channel overloaded - content += "

Cont. Neg.: "; if (digitalRead(esp32hal->SECOND_BATTERY_CONTACTORS_PIN()) == HIGH) { - content += ""; + content += "ON"; } else { - content += ""; + content += "OFF"; } } //no PWM_CONTACTOR_CONTROL content += "

"; From 2f6b63afe874e3811852afa87e0886b5a018f3ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 17 Aug 2025 21:53:44 +0300 Subject: [PATCH 018/245] Add check if bat2 is configured --- Software/src/devboard/webserver/webserver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 6081ac12..c1225185 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -1278,7 +1278,7 @@ String processor(const String& var) { "causing this via Events page. Reboot required to resume operation!"; } content += ""; - if (contactor_control_enabled_double_battery) { + if (contactor_control_enabled_double_battery && battery2) { content += "

Secondary battery contactor, state: "; if (pwm_contactor_control) { if (datalayer.system.status.contactors_battery2_engaged) { From 24abab77463a8305b13713ffe3f1216938b7e7d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 17 Aug 2025 21:59:21 +0300 Subject: [PATCH 019/245] Fix startup voltage not triggering events --- Software/src/battery/KIA-HYUNDAI-64-BATTERY.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/src/battery/KIA-HYUNDAI-64-BATTERY.h b/Software/src/battery/KIA-HYUNDAI-64-BATTERY.h index 2a986255..1e991465 100644 --- a/Software/src/battery/KIA-HYUNDAI-64-BATTERY.h +++ b/Software/src/battery/KIA-HYUNDAI-64-BATTERY.h @@ -70,7 +70,7 @@ class KiaHyundai64Battery : public CanBattery { uint16_t CellVoltMin_mV = 3700; uint16_t allowedDischargePower = 0; uint16_t allowedChargePower = 0; - uint16_t batteryVoltage = 0; + uint16_t batteryVoltage = 3700; uint16_t inverterVoltageFrameHigh = 0; uint16_t inverterVoltage = 0; uint16_t cellvoltages_mv[98]; From df703536080a4301a2a7bcea316a3c73c9191903 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Aug 2025 01:42:22 +0000 Subject: [PATCH 020/245] Bump actions/checkout from 4 to 5 Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/compile-all-hardware.yml | 2 +- .github/workflows/compile-common-image-lilygo.yml | 2 +- .github/workflows/compile-common-image-stark.yml | 2 +- .github/workflows/release-assets.yml | 2 +- .github/workflows/run-pre-commit.yml | 2 +- .github/workflows/unit-tests.yml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/compile-all-hardware.yml b/.github/workflows/compile-all-hardware.yml index 8c3c7152..8e1895f7 100644 --- a/.github/workflows/compile-all-hardware.yml +++ b/.github/workflows/compile-all-hardware.yml @@ -69,7 +69,7 @@ jobs: steps: # First we clone the repo using the `checkout` action. - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 # Copy USER_SECRETS.TEMPLATE.h to USER_SECRETS.h - name: Copy USER_SECRETS.TEMPLATE.h to USER_SECRETS.h diff --git a/.github/workflows/compile-common-image-lilygo.yml b/.github/workflows/compile-common-image-lilygo.yml index 33c3e5ba..43c0cd44 100644 --- a/.github/workflows/compile-common-image-lilygo.yml +++ b/.github/workflows/compile-common-image-lilygo.yml @@ -28,7 +28,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 name: Checkout code - uses: actions/cache@v4 diff --git a/.github/workflows/compile-common-image-stark.yml b/.github/workflows/compile-common-image-stark.yml index 00b2c01c..8f763dc0 100644 --- a/.github/workflows/compile-common-image-stark.yml +++ b/.github/workflows/compile-common-image-stark.yml @@ -28,7 +28,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 name: Checkout code - uses: actions/cache@v4 diff --git a/.github/workflows/release-assets.yml b/.github/workflows/release-assets.yml index 272d1352..c87b029b 100644 --- a/.github/workflows/release-assets.yml +++ b/.github/workflows/release-assets.yml @@ -25,7 +25,7 @@ jobs: fi - name: Checkout code at tag - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: ref: refs/tags/${{ steps.vars.outputs.tag }} diff --git a/.github/workflows/run-pre-commit.yml b/.github/workflows/run-pre-commit.yml index e2ed0277..869545ba 100644 --- a/.github/workflows/run-pre-commit.yml +++ b/.github/workflows/run-pre-commit.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Run pre-commit uses: pre-commit/action@v3.0.1 diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 2e8d4e84..3b385bc8 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -9,7 +9,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Configure and build with CMake run: | From 1ff3ff43e4dbf81b414d4c3ff51dfba435e872a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Mon, 18 Aug 2025 20:24:48 +0300 Subject: [PATCH 021/245] Make the ESP-IDF v5.x RMT driver execute faster --- .../src/lib/adafruit-Adafruit_NeoPixel/esp.c | 42 +++++++++++++------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/Software/src/lib/adafruit-Adafruit_NeoPixel/esp.c b/Software/src/lib/adafruit-Adafruit_NeoPixel/esp.c index ec08e7d5..d056fda6 100644 --- a/Software/src/lib/adafruit-Adafruit_NeoPixel/esp.c +++ b/Software/src/lib/adafruit-Adafruit_NeoPixel/esp.c @@ -2,11 +2,15 @@ #include "driver/rmt_tx.h" // This code is adapted for the ESP-IDF v5.x RMT driver API // WS2812 timing parameters (in nanoseconds) -#define WS2812_T0H_NS 350 -#define WS2812_T0L_NS 1000 -#define WS2812_T1H_NS 900 -#define WS2812_T1L_NS 350 +#define WS2812_T0H_NS (400) +#define WS2812_T0L_NS (850) +#define WS2812_T1H_NS (800) +#define WS2812_T1L_NS (450) #define WS2812_RESET_US 280 +#define WS2812_T0H_TICKS (WS2812_T0H_NS / 25) +#define WS2812_T0L_TICKS (WS2812_T0L_NS / 25) +#define WS2812_T1H_TICKS (WS2812_T1H_NS / 25) +#define WS2812_T1L_TICKS (WS2812_T1L_NS / 25) static rmt_channel_handle_t led_chan = NULL; static rmt_encoder_handle_t led_encoder = NULL; @@ -131,23 +135,35 @@ err: return ret; } -void espShow(uint8_t pin, uint8_t* pixels, uint8_t numBytes) { - if (led_chan == NULL) { +void espShow(uint8_t pin, uint8_t* pixels, uint32_t numBytes) { + static bool initialized = false; + + if (!initialized) { rmt_tx_channel_config_t tx_chan_config = { .clk_src = RMT_CLK_SRC_DEFAULT, .gpio_num = pin, .mem_block_symbols = 64, - .resolution_hz = 10 * 1000 * 1000, + .resolution_hz = 40 * 1000 * 1000, // 40 MHz .trans_queue_depth = 4, }; ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_chan_config, &led_chan)); - ESP_ERROR_CHECK(rmt_new_led_strip_encoder(&led_encoder)); + + rmt_bytes_encoder_config_t bytes_encoder_config = { + .bit0 = { .level0 = 1, .duration0 = WS2812_T0H_NS / 25, + .level1 = 0, .duration1 = WS2812_T0L_NS / 25 }, + .bit1 = { .level0 = 1, .duration0 = WS2812_T1H_NS / 25, + .level1 = 0, .duration1 = WS2812_T1L_NS / 25 }, + .flags.msb_first = 1 + }; + ESP_ERROR_CHECK(rmt_new_bytes_encoder(&bytes_encoder_config, &led_encoder)); + ESP_ERROR_CHECK(rmt_enable(led_chan)); + initialized = true; } - rmt_transmit_config_t tx_config = { - .loop_count = 0, - }; + rmt_transmit_config_t tx_config = { .loop_count = 0 }; ESP_ERROR_CHECK(rmt_transmit(led_chan, led_encoder, pixels, numBytes, &tx_config)); - ESP_ERROR_CHECK(rmt_tx_wait_all_done(led_chan, portMAX_DELAY)); -} \ No newline at end of file + + // optional: only wait if strict timing required + // ESP_ERROR_CHECK(rmt_tx_wait_all_done(led_chan, portMAX_DELAY)); +} From 0d8bc06f0461ea064383ce0c76aa5912faf7dfcc Mon Sep 17 00:00:00 2001 From: Jonny Date: Fri, 15 Aug 2025 22:54:04 +0100 Subject: [PATCH 022/245] Make custom-BMS voltage limits configurable via settings --- Software/USER_SETTINGS.h | 6 + Software/src/battery/BATTERIES.cpp | 20 +++ Software/src/battery/BATTERIES.h | 5 + Software/src/battery/CELLPOWER-BMS.cpp | 9 +- Software/src/battery/CELLPOWER-BMS.h | 5 - Software/src/battery/DALY-BMS.cpp | 9 +- Software/src/battery/DALY-BMS.h | 5 - Software/src/battery/ORION-BMS.cpp | 9 +- Software/src/battery/ORION-BMS.h | 7 - Software/src/battery/PYLON-BATTERY.cpp | 9 +- Software/src/battery/PYLON-BATTERY.h | 5 - Software/src/battery/RJXZS-BMS.cpp | 9 +- Software/src/battery/RJXZS-BMS.h | 5 - Software/src/battery/SIMPBMS-BATTERY.cpp | 9 +- Software/src/battery/SIMPBMS-BATTERY.h | 7 - Software/src/communication/nvm/comm_nvm.cpp | 5 + .../src/devboard/webserver/settings_html.cpp | 123 +++++++++++++----- 17 files changed, 156 insertions(+), 91 deletions(-) diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index 59973c2a..a1d246fa 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -171,6 +171,12 @@ // 3000 = 300.0V, Target discharge voltage (Value can be tuned on the fly via webserver). Not used unless BATTERY_USE_VOLTAGE_LIMITS = true #define BATTERY_MAX_DISCHARGE_VOLTAGE 3000 +/* Pack/cell voltage limits for custom-BMS batteries (RJXZS, Daly, etc.) */ +//#define MAX_CUSTOM_PACK_VOLTAGE_DV 5000 // 5000 = 500.0V , Maximum pack voltage in decivolts +//#define MIN_CUSTOM_PACK_VOLTAGE_DV 2500 // 2500 = 250.0V , Minimum pack voltage in decivolts +//#define MAX_CUSTOM_CELL_VOLTAGE_MV 4250 // 4250 = 4.250V , Maximum cell voltage in millivolts (4250 = 4.250V) +//#define MIN_CUSTOM_CELL_VOLTAGE_MV 2650 // 2650 = 2.650V , Minimum cell voltage in millivolts (2650 = 2.650V) + /* LED settings. Optional customization for how the blinking pattern on the LED should behave. * CLASSIC - Slow up/down ramp. If CLASSIC, then a ramp up and ramp down will finish in LED_PERIOD_MS milliseconds * FLOW - Ramp up/down depending on flow of energy diff --git a/Software/src/battery/BATTERIES.cpp b/Software/src/battery/BATTERIES.cpp index f138c488..1aa07bc6 100644 --- a/Software/src/battery/BATTERIES.cpp +++ b/Software/src/battery/BATTERIES.cpp @@ -312,3 +312,23 @@ void setup_battery() { #endif } #endif + +/* User-selected voltages used for custom-BMS batteries (RJXZS etc.) */ +#if defined(MAX_CUSTOM_PACK_VOLTAGE_DV) && defined(MIN_CUSTOM_PACK_VOLTAGE_DV) && \ + defined(MAX_CUSTOM_CELL_VOLTAGE_MV) && defined(MIN_CUSTOM_CELL_VOLTAGE_MV) +// Use USER_SETTINGS.h values for cell/pack voltage defaults +uint16_t user_selected_max_pack_voltage_default_dV = MAX_CUSTOM_PACK_VOLTAGE_DV; +uint16_t user_selected_min_pack_voltage_default_dV = MIN_CUSTOM_PACK_VOLTAGE_DV; +uint16_t user_selected_max_cell_voltage_default_mV = MAX_CUSTOM_CELL_VOLTAGE_MV; +uint16_t user_selected_min_cell_voltage_default_mV = MIN_CUSTOM_CELL_VOLTAGE_MV; +#else +// Use 0V for user selected cell/pack voltage defaults (COMMON_IMAGE will replace with saved values from NVM) +uint16_t user_selected_max_pack_voltage_default_dV = 0; +uint16_t user_selected_min_pack_voltage_default_dV = 0; +uint16_t user_selected_max_cell_voltage_default_mV = 0; +uint16_t user_selected_min_cell_voltage_default_mV = 0; +#endif +uint16_t user_selected_max_pack_voltage_dV = user_selected_max_pack_voltage_default_dV; +uint16_t user_selected_min_pack_voltage_dV = user_selected_min_pack_voltage_default_dV; +uint16_t user_selected_max_cell_voltage_mV = user_selected_max_cell_voltage_default_mV; +uint16_t user_selected_min_cell_voltage_mV = user_selected_min_cell_voltage_default_mV; diff --git a/Software/src/battery/BATTERIES.h b/Software/src/battery/BATTERIES.h index 1f357a93..3e89149c 100644 --- a/Software/src/battery/BATTERIES.h +++ b/Software/src/battery/BATTERIES.h @@ -55,4 +55,9 @@ void setup_can_shunt(); void setup_battery(void); +extern uint16_t user_selected_max_pack_voltage_dV; +extern uint16_t user_selected_min_pack_voltage_dV; +extern uint16_t user_selected_max_cell_voltage_mV; +extern uint16_t user_selected_min_cell_voltage_mV; + #endif diff --git a/Software/src/battery/CELLPOWER-BMS.cpp b/Software/src/battery/CELLPOWER-BMS.cpp index 04407fc4..baa375d6 100644 --- a/Software/src/battery/CELLPOWER-BMS.cpp +++ b/Software/src/battery/CELLPOWER-BMS.cpp @@ -1,4 +1,5 @@ #include "CELLPOWER-BMS.h" +#include "../battery/BATTERIES.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../datalayer/datalayer_extended.h" //For "More battery info" webpage @@ -231,8 +232,8 @@ void CellPowerBms::setup(void) { // Performs one time setup at startup strncpy(datalayer.system.info.battery_protocol, Name, 63); datalayer.system.info.battery_protocol[63] = '\0'; datalayer.system.status.battery_allows_contactor_closing = true; - datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; - datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; - datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; - datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; + datalayer.battery.info.max_design_voltage_dV = user_selected_max_pack_voltage_dV; + datalayer.battery.info.min_design_voltage_dV = user_selected_min_pack_voltage_dV; + datalayer.battery.info.max_cell_voltage_mV = user_selected_max_cell_voltage_mV; + datalayer.battery.info.min_cell_voltage_mV = user_selected_min_cell_voltage_mV; } diff --git a/Software/src/battery/CELLPOWER-BMS.h b/Software/src/battery/CELLPOWER-BMS.h index 3a22c732..ba8be709 100644 --- a/Software/src/battery/CELLPOWER-BMS.h +++ b/Software/src/battery/CELLPOWER-BMS.h @@ -22,11 +22,6 @@ class CellPowerBms : public CanBattery { private: CellpowerHtmlRenderer renderer; - /* Tweak these according to your battery build */ - static const int MAX_PACK_VOLTAGE_DV = 5000; //5000 = 500.0V - static const int MIN_PACK_VOLTAGE_DV = 1500; - static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value - static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value unsigned long previousMillis1s = 0; // will store last time a 1s CAN Message was sent diff --git a/Software/src/battery/DALY-BMS.cpp b/Software/src/battery/DALY-BMS.cpp index 9eb2e577..49d0314a 100644 --- a/Software/src/battery/DALY-BMS.cpp +++ b/Software/src/battery/DALY-BMS.cpp @@ -1,6 +1,7 @@ #include "DALY-BMS.h" #include #include +#include "../battery/BATTERIES.h" #include "../datalayer/datalayer.h" #include "../devboard/hal/hal.h" #include "../devboard/utils/events.h" @@ -67,10 +68,10 @@ void DalyBms::update_values() { void DalyBms::setup(void) { // Performs one time setup at startup strncpy(datalayer.system.info.battery_protocol, Name, 63); datalayer.system.info.battery_protocol[63] = '\0'; - datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; - datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; - datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; - datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; + datalayer.battery.info.max_design_voltage_dV = user_selected_max_pack_voltage_dV; + datalayer.battery.info.min_design_voltage_dV = user_selected_min_pack_voltage_dV; + datalayer.battery.info.max_cell_voltage_mV = user_selected_max_cell_voltage_mV; + datalayer.battery.info.min_cell_voltage_mV = user_selected_min_cell_voltage_mV; datalayer.battery.info.total_capacity_Wh = BATTERY_WH_MAX; datalayer.system.status.battery_allows_contactor_closing = true; diff --git a/Software/src/battery/DALY-BMS.h b/Software/src/battery/DALY-BMS.h index 27ec018d..4b9c6998 100644 --- a/Software/src/battery/DALY-BMS.h +++ b/Software/src/battery/DALY-BMS.h @@ -17,11 +17,6 @@ class DalyBms : public RS485Battery { private: /* Tweak these according to your battery build */ - static const int CELL_COUNT = 14; - static const int MAX_PACK_VOLTAGE_DV = 580; //580 = 58.0V - static const int MIN_PACK_VOLTAGE_DV = 460; //480 = 48.0V - static const int MAX_CELL_VOLTAGE_MV = 4200; //Battery is put into emergency stop if one cell goes over this value - static const int MIN_CELL_VOLTAGE_MV = 3200; //Battery is put into emergency stop if one cell goes below this value static const int POWER_PER_PERCENT = 50; // below 20% and above 80% limit power to 50W * SOC (i.e. 150W at 3%, 500W at 10%, ...) static const int POWER_PER_DEGREE_C = 60; // max power added/removed per degree above/below 0°C diff --git a/Software/src/battery/ORION-BMS.cpp b/Software/src/battery/ORION-BMS.cpp index bf23ded6..e99d03db 100644 --- a/Software/src/battery/ORION-BMS.cpp +++ b/Software/src/battery/ORION-BMS.cpp @@ -1,4 +1,5 @@ #include "ORION-BMS.h" +#include "../battery/BATTERIES.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" @@ -115,9 +116,9 @@ void OrionBms::transmit_can(unsigned long currentMillis) { void OrionBms::setup(void) { // Performs one time setup at startup strncpy(datalayer.system.info.battery_protocol, Name, 63); datalayer.system.info.battery_protocol[63] = '\0'; - datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; - datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; - datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; - datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; + datalayer.battery.info.max_design_voltage_dV = user_selected_max_pack_voltage_dV; + datalayer.battery.info.min_design_voltage_dV = user_selected_min_pack_voltage_dV; + datalayer.battery.info.max_cell_voltage_mV = user_selected_max_cell_voltage_mV; + datalayer.battery.info.min_cell_voltage_mV = user_selected_min_cell_voltage_mV; datalayer.system.status.battery_allows_contactor_closing = true; } diff --git a/Software/src/battery/ORION-BMS.h b/Software/src/battery/ORION-BMS.h index d93d3ae3..eaf03c59 100644 --- a/Software/src/battery/ORION-BMS.h +++ b/Software/src/battery/ORION-BMS.h @@ -17,13 +17,6 @@ class OrionBms : public CanBattery { static constexpr const char* Name = "DIY battery with Orion BMS (Victron setting)"; private: - /* Change the following to suit your battery */ - static const int MAX_PACK_VOLTAGE_DV = 5000; //5000 = 500.0V - static const int MIN_PACK_VOLTAGE_DV = 1500; - static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value - static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value - static const int MAX_CELL_DEVIATION_MV = 150; - uint16_t cellvoltages[MAX_AMOUNT_CELLS]; //array with all the cellvoltages uint16_t Maximum_Cell_Voltage = 3700; uint16_t Minimum_Cell_Voltage = 3700; diff --git a/Software/src/battery/PYLON-BATTERY.cpp b/Software/src/battery/PYLON-BATTERY.cpp index c2a8ac40..e03b8393 100644 --- a/Software/src/battery/PYLON-BATTERY.cpp +++ b/Software/src/battery/PYLON-BATTERY.cpp @@ -1,4 +1,5 @@ #include "PYLON-BATTERY.h" +#include "../battery/BATTERIES.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" @@ -131,10 +132,10 @@ void PylonBattery::setup(void) { // Performs one time setup at startup strncpy(datalayer.system.info.battery_protocol, "Pylon compatible battery", 63); datalayer.system.info.battery_protocol[63] = '\0'; datalayer_battery->info.number_of_cells = 2; - datalayer_battery->info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; - datalayer_battery->info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; - datalayer_battery->info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; - datalayer_battery->info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; + datalayer_battery->info.max_design_voltage_dV = user_selected_max_pack_voltage_dV; + datalayer_battery->info.min_design_voltage_dV = user_selected_min_pack_voltage_dV; + datalayer_battery->info.max_cell_voltage_mV = user_selected_max_cell_voltage_mV; + datalayer_battery->info.min_cell_voltage_mV = user_selected_min_cell_voltage_mV; datalayer.battery2.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV; diff --git a/Software/src/battery/PYLON-BATTERY.h b/Software/src/battery/PYLON-BATTERY.h index 71963e83..bc88ba99 100644 --- a/Software/src/battery/PYLON-BATTERY.h +++ b/Software/src/battery/PYLON-BATTERY.h @@ -32,11 +32,6 @@ class PylonBattery : public CanBattery { static constexpr const char* Name = "Pylon compatible battery"; private: - /* Change the following to suit your battery */ - static const int MAX_PACK_VOLTAGE_DV = 5000; //5000 = 500.0V - static const int MIN_PACK_VOLTAGE_DV = 1500; - static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value - static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value static const int MAX_CELL_DEVIATION_MV = 150; DATALAYER_BATTERY_TYPE* datalayer_battery; diff --git a/Software/src/battery/RJXZS-BMS.cpp b/Software/src/battery/RJXZS-BMS.cpp index 61358e96..6bbb4fe8 100644 --- a/Software/src/battery/RJXZS-BMS.cpp +++ b/Software/src/battery/RJXZS-BMS.cpp @@ -1,4 +1,5 @@ #include "RJXZS-BMS.h" +#include "../battery/BATTERIES.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" @@ -511,9 +512,9 @@ void RjxzsBms::transmit_can(unsigned long currentMillis) { void RjxzsBms::setup(void) { // Performs one time setup at startup strncpy(datalayer.system.info.battery_protocol, Name, 63); datalayer.system.info.battery_protocol[63] = '\0'; - datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; - datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; - datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; - datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; + datalayer.battery.info.max_design_voltage_dV = user_selected_max_pack_voltage_dV; + datalayer.battery.info.min_design_voltage_dV = user_selected_min_pack_voltage_dV; + datalayer.battery.info.max_cell_voltage_mV = user_selected_max_cell_voltage_mV; + datalayer.battery.info.min_cell_voltage_mV = user_selected_min_cell_voltage_mV; datalayer.system.status.battery_allows_contactor_closing = true; } diff --git a/Software/src/battery/RJXZS-BMS.h b/Software/src/battery/RJXZS-BMS.h index d6f4c53b..d616f31a 100644 --- a/Software/src/battery/RJXZS-BMS.h +++ b/Software/src/battery/RJXZS-BMS.h @@ -20,11 +20,6 @@ class RjxzsBms : public CanBattery { private: /* Tweak these according to your battery build */ - static const int MAX_PACK_VOLTAGE_DV = 5000; //5000 = 500.0V - static const int MIN_PACK_VOLTAGE_DV = 1500; - static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value - static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value - static const int MAX_CELL_DEVIATION_MV = 250; static const int MAX_DISCHARGE_POWER_ALLOWED_W = 5000; static const int MAX_CHARGE_POWER_ALLOWED_W = 5000; static const int MAX_CHARGE_POWER_WHEN_TOPBALANCING_W = 500; diff --git a/Software/src/battery/SIMPBMS-BATTERY.cpp b/Software/src/battery/SIMPBMS-BATTERY.cpp index 502faf8c..bc58c580 100644 --- a/Software/src/battery/SIMPBMS-BATTERY.cpp +++ b/Software/src/battery/SIMPBMS-BATTERY.cpp @@ -1,4 +1,5 @@ #include "SIMPBMS-BATTERY.h" +#include "../battery/BATTERIES.h" #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" @@ -93,9 +94,9 @@ void SimpBmsBattery::transmit_can(unsigned long currentMillis) { void SimpBmsBattery::setup(void) { // Performs one time setup at startup strncpy(datalayer.system.info.battery_protocol, Name, 63); datalayer.system.info.battery_protocol[63] = '\0'; - datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; - datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; - datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; - datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; + datalayer.battery.info.max_design_voltage_dV = user_selected_max_pack_voltage_dV; + datalayer.battery.info.min_design_voltage_dV = user_selected_min_pack_voltage_dV; + datalayer.battery.info.max_cell_voltage_mV = user_selected_max_cell_voltage_mV; + datalayer.battery.info.min_cell_voltage_mV = user_selected_min_cell_voltage_mV; datalayer.system.status.battery_allows_contactor_closing = true; } diff --git a/Software/src/battery/SIMPBMS-BATTERY.h b/Software/src/battery/SIMPBMS-BATTERY.h index 914bd58f..9295c635 100644 --- a/Software/src/battery/SIMPBMS-BATTERY.h +++ b/Software/src/battery/SIMPBMS-BATTERY.h @@ -16,13 +16,6 @@ class SimpBmsBattery : public CanBattery { static constexpr const char* Name = "SIMPBMS battery"; private: - /* DEFAULT VALUES BMS will send configured */ - static const int MAX_PACK_VOLTAGE_DV = 5000; //5000 = 500.0V - static const int MIN_PACK_VOLTAGE_DV = 1500; - static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value - static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value - static const int MAX_CELL_DEVIATION_MV = 500; - static const int SIMPBMS_MAX_CELLS = 128; unsigned long previousMillis1000 = 0; // will store last time a 1s CAN Message was sent diff --git a/Software/src/communication/nvm/comm_nvm.cpp b/Software/src/communication/nvm/comm_nvm.cpp index a7ec7e2d..582316d8 100644 --- a/Software/src/communication/nvm/comm_nvm.cpp +++ b/Software/src/communication/nvm/comm_nvm.cpp @@ -1,4 +1,5 @@ #include "comm_nvm.h" +#include "../../battery/BATTERIES.h" #include "../../battery/Battery.h" #include "../../battery/Shunt.h" #include "../../charger/CanCharger.h" @@ -91,6 +92,10 @@ void init_stored_settings() { user_selected_inverter_protocol = (InverterProtocolType)settings.getUInt("INVTYPE", (int)InverterProtocolType::None); user_selected_charger_type = (ChargerType)settings.getUInt("CHGTYPE", (int)ChargerType::None); user_selected_shunt_type = (ShuntType)settings.getUInt("SHUNTTYPE", (int)ShuntType::None); + user_selected_max_pack_voltage_dV = settings.getUInt("BATTPVMAX", 0); + user_selected_min_pack_voltage_dV = settings.getUInt("BATTPVMIN", 0); + user_selected_max_cell_voltage_mV = settings.getUInt("BATTCVMAX", 0); + user_selected_min_cell_voltage_mV = settings.getUInt("BATTCVMIN", 0); auto readIf = [](const char* settingName) { auto batt1If = (comm_interface)settings.getUInt(settingName, (int)comm_interface::CanNative); diff --git a/Software/src/devboard/webserver/settings_html.cpp b/Software/src/devboard/webserver/settings_html.cpp index 8e866a48..4449bb3c 100644 --- a/Software/src/devboard/webserver/settings_html.cpp +++ b/Software/src/devboard/webserver/settings_html.cpp @@ -306,6 +306,22 @@ String settings_processor(const String& var, BatteryEmulatorSettingsStore& setti } } + if (var == "BATTPVMAX") { + return String(static_cast(settings.getUInt("BATTPVMAX", 0)) / 10.0, 1); + } + + if (var == "BATTPVMIN") { + return String(static_cast(settings.getUInt("BATTPVMIN", 0)) / 10.0, 1); + } + + if (var == "BATTCVMAX") { + return String(settings.getUInt("BATTCVMAX", 0)); + } + + if (var == "BATTCVMIN") { + return String(settings.getUInt("BATTCVMIN", 0)); + } + if (var == "BATTERY_WH_MAX") { return String(datalayer.battery.info.total_capacity_Wh); } @@ -593,23 +609,13 @@ const char* getCANInterfaceName(CAN_Interface interface) { function goToMainPage() { window.location.href = '/'; } - function toggleMqtt() { - var mqttEnabled = document.querySelector('input[name="MQTTENABLED"]').checked; - document.querySelectorAll('.mqtt-settings').forEach(function (el) { - el.style.display = mqttEnabled ? 'contents' : 'none'; - }); - } - - document.addEventListener('DOMContentLoaded', toggleMqtt); - - function toggleTopics() { - var topicsEnabled = document.querySelector('input[name="MQTTTOPICS"]').checked; - document.querySelectorAll('.mqtt-topics').forEach(function (el) { - el.style.display = topicsEnabled ? 'contents' : 'none'; - }); - } - - document.addEventListener('DOMContentLoaded', toggleTopics); + document.querySelectorAll('select,input').forEach(function(sel) { + function ch() { + sel.closest('form').setAttribute('data-' + sel.name?.toLowerCase(), sel.type=='checkbox'?sel.checked:sel.value); + } + sel.addEventListener('change', ch); + ch(); + }); )rawliteral" @@ -621,7 +627,7 @@ const char* getCANInterfaceName(CAN_Interface interface) { cursor: pointer; border-radius: 10px; } button:hover { background-color: #3A4A52; } h4 { margin: 0.6em 0; line-height: 1.2; } - select { max-width: 250px; } + select, input { max-width: 250px; box-sizing: border-box; } .hidden { display: none; } @@ -641,6 +647,32 @@ const char* getCANInterfaceName(CAN_Interface interface) { grid-column: span 2; } + form .if-battery, form .if-inverter, form .if-charger, form .if-shunt { display: contents; } + form[data-battery="0"] .if-battery { display: none; } + form[data-inverter="0"] .if-inverter { display: none; } + form[data-charger="0"] .if-charger { display: none; } + form[data-shunt="0"] .if-shunt { display: none; } + + form .if-cbms { display: none; } + form[data-battery="6"] .if-cbms, form[data-battery="11"] .if-cbms, form[data-battery="22"] .if-cbms, form[data-battery="23"] .if-cbms, form[data-battery="24"] .if-cbms, form[data-battery="31"] .if-cbms { + display: contents; + } + + form .if-dblbtr { display: none; } + form[data-dblbtr="true"] .if-dblbtr { + display: contents; + } + + form .if-mqtt { display: none; } + form[data-mqttenabled="true"] .if-mqtt { + display: contents; + } + + form .if-topics { display: none; } + form[data-mqtttopics="true"] .if-topics { + display: contents; + } + )rawliteral" @@ -654,13 +686,13 @@ const char* getCANInterfaceName(CAN_Interface interface) {
-
+ +
@@ -668,30 +700,51 @@ const char* getCANInterfaceName(CAN_Interface interface) { +
+ +
+ + + + + + + + + + + +
+
+
+
+
+
+
+
+
+
+
@@ -726,26 +783,26 @@ const char* getCANInterfaceName(CAN_Interface interface) { - + - + -
- - - - +
+ + + + - + -
+
- - - - + + + +
From 0e871e477afa5ed5f1573226591102b8e93f3f4b Mon Sep 17 00:00:00 2001 From: Jonny Date: Sat, 16 Aug 2025 09:49:57 +0100 Subject: [PATCH 023/245] Make reboots reliably reload the page --- Software/src/devboard/webserver/index_html.h | 3 +++ Software/src/devboard/webserver/settings_html.cpp | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Software/src/devboard/webserver/index_html.h b/Software/src/devboard/webserver/index_html.h index cd867244..8cd4d5b3 100644 --- a/Software/src/devboard/webserver/index_html.h +++ b/Software/src/devboard/webserver/index_html.h @@ -17,6 +17,9 @@ function reboot() { var xhr = new XMLHttpRequest(); xhr.open('GET', '/reboot', true); xhr.send(); + setTimeout(function() { + window.location = "/"; + }, 3000); } )rawliteral" diff --git a/Software/src/devboard/webserver/settings_html.cpp b/Software/src/devboard/webserver/settings_html.cpp index 4449bb3c..e9e52804 100644 --- a/Software/src/devboard/webserver/settings_html.cpp +++ b/Software/src/devboard/webserver/settings_html.cpp @@ -811,10 +811,10 @@ const char* getCANInterfaceName(CAN_Interface interface) {
-
+
-

Settings saved. Reboot to take the settings into use.

+

Settings saved. Reboot to take the settings into use.

From 2a8f8713e737853c63fc77a3ed173d8eb106d112 Mon Sep 17 00:00:00 2001 From: Jonny Date: Sat, 16 Aug 2025 10:08:07 +0100 Subject: [PATCH 024/245] Make new voltage limit settings actually save --- Software/src/devboard/webserver/webserver.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 174efc78..4949bd86 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -445,6 +445,18 @@ void init_webserver() { } else if (p->name() == "BATTCOMM") { auto type = static_cast(atoi(p->value().c_str())); settings.saveUInt("BATTCOMM", (int)type); + } else if (p->name() == "BATTPVMAX") { + auto type = p->value().toFloat() * 10.0; + settings.saveUInt("BATTPVMAX", (int)type); + } else if (p->name() == "BATTPVMIN") { + auto type = p->value().toFloat() * 10.0; + settings.saveUInt("BATTPVMIN", (int)type); + } else if (p->name() == "BATTCVMAX") { + auto type = atoi(p->value().c_str()); + settings.saveUInt("BATTCVMAX", type); + } else if (p->name() == "BATTCVMIN") { + auto type = atoi(p->value().c_str()); + settings.saveUInt("BATTCVMIN", type); } else if (p->name() == "charger") { auto type = static_cast(atoi(p->value().c_str())); settings.saveUInt("CHGTYPE", (int)type); From 7a2e2519cd436a231f648cee36076ab01e37d6ef Mon Sep 17 00:00:00 2001 From: Jonny Date: Sat, 16 Aug 2025 12:14:42 +0100 Subject: [PATCH 025/245] Move SOFAR_ID to new setting mechanism. --- Software/src/communication/nvm/comm_nvm.cpp | 5 +---- .../src/devboard/webserver/settings_html.cpp | 20 +++++++++++++------ Software/src/devboard/webserver/webserver.cpp | 7 +++---- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/Software/src/communication/nvm/comm_nvm.cpp b/Software/src/communication/nvm/comm_nvm.cpp index 582316d8..688d961c 100644 --- a/Software/src/communication/nvm/comm_nvm.cpp +++ b/Software/src/communication/nvm/comm_nvm.cpp @@ -193,10 +193,7 @@ void store_settings() { if (!settings.putUInt("TARGETDISCHVOLT", datalayer.battery.settings.max_user_set_discharge_voltage_dV)) { set_event(EVENT_PERSISTENT_SAVE_INFO, 11); } - if (!settings.putUInt("SOFAR_ID", datalayer.battery.settings.sofar_user_specified_battery_id)) { - set_event(EVENT_PERSISTENT_SAVE_INFO, 12); - } - if (!settings.putUInt("BMSRESETDUR", datalayer.battery.settings.sofar_user_specified_battery_id)) { + if (!settings.putUInt("BMSRESETDUR", datalayer.battery.settings.user_set_bms_reset_duration_ms)) { set_event(EVENT_PERSISTENT_SAVE_INFO, 13); } diff --git a/Software/src/devboard/webserver/settings_html.cpp b/Software/src/devboard/webserver/settings_html.cpp index e9e52804..31936a55 100644 --- a/Software/src/devboard/webserver/settings_html.cpp +++ b/Software/src/devboard/webserver/settings_html.cpp @@ -468,6 +468,10 @@ String settings_processor(const String& var, BatteryEmulatorSettingsStore& setti return String(datalayer.charger.charger_setpoint_HV_IDC, 1); } + if (var == "SOFAR_ID") { + return String(settings.getUInt("SOFAR_ID", 0)); + } + return String(); } @@ -527,10 +531,6 @@ const char* getCANInterfaceName(CAN_Interface interface) { function editPassword(){var value=prompt('Enter new password:');if(value!==null){var xhr=new XMLHttpRequest();xhr.onload=editComplete;xhr.onerror=editError;xhr.open('GET','/updatePassword?value='+encodeURIComponent(value),true);xhr.send();}} - function editSofarID(){var value=prompt('For double battery setups. Which battery ID should this emulator send? Remember to reboot after configuring this! Enter new value between (0-15):'); - if(value!==null){if(value>=0&&value<=15){var xhr=new XMLHttpRequest();xhr.onload=editComplete;xhr.onerror=editError;xhr.open('GET','/updateSofarID?value='+value,true);xhr.send();} - else {alert('Invalid value. Please enter a value between 0 and 15.');}}} - function editWh(){var value=prompt('How much energy the battery can store. Enter new Wh value (1-400000):'); if(value!==null){if(value>=1&&value<=400000){var xhr=new XMLHttpRequest();xhr.onload=editComplete;xhr.onerror=editError;xhr.open('GET','/updateBatterySize?value='+value,true);xhr.send();}else{ @@ -663,6 +663,11 @@ const char* getCANInterfaceName(CAN_Interface interface) { display: contents; } + form .if-sofar { display: none; } + form[data-inverter="17"] .if-sofar { + display: contents; + } + form .if-mqtt { display: none; } form[data-mqttenabled="true"] .if-mqtt { display: contents; @@ -726,6 +731,11 @@ const char* getCANInterfaceName(CAN_Interface interface) {
+
+ + +
+ @@ -826,8 +836,6 @@ const char* getCANInterfaceName(CAN_Interface interface) {

Battery interface: %BATTERY2INTF%

Inverter interface: %INVINTF%

- -

Battery ID: %INVBID%

Shunt interface: %SHUNTINTF%

diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 4949bd86..498ecc27 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -494,6 +494,9 @@ void init_webserver() { settings.saveString("MQTTDEVICENAME", p->value().c_str()); } else if (p->name() == "HADEVICEID") { settings.saveString("HADEVICEID", p->value().c_str()); + } else if (p->name() == "SOFAR_ID") { + auto type = atoi(p->value().c_str()); + settings.saveUInt("SOFAR_ID", type); } for (auto& boolSetting : boolSettings) { @@ -579,10 +582,6 @@ void init_webserver() { update_string_setting(route, [setter](String value) { setter(value.toInt()); }); }; - // Route for editing Sofar ID - update_int_setting("/updateSofarID", - [](int value) { datalayer.battery.settings.sofar_user_specified_battery_id = value; }); - // Route for editing Wh update_int_setting("/updateBatterySize", [](int value) { datalayer.battery.info.total_capacity_Wh = value; }); From 9816417b21afe6ed41fee938ab96696f63c49f38 Mon Sep 17 00:00:00 2001 From: Jonny Date: Sat, 16 Aug 2025 12:15:05 +0100 Subject: [PATCH 026/245] Pin Inverter enum values so they won't change in future --- Software/src/inverter/InverterProtocol.h | 42 ++++++++++++------------ 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/Software/src/inverter/InverterProtocol.h b/Software/src/inverter/InverterProtocol.h index 3e48ea70..7b9a3f5f 100644 --- a/Software/src/inverter/InverterProtocol.h +++ b/Software/src/inverter/InverterProtocol.h @@ -5,27 +5,27 @@ enum class InverterProtocolType { None = 0, - AforeCan, - BydCan, - BydModbus, - FerroampCan, - Foxess, - GrowattHv, - GrowattLv, - GrowattWit, - Kostal, - Pylon, - PylonLv, - Schneider, - SmaBydH, - SmaBydHvs, - SmaLv, - SmaTripower, - Sofar, - Solax, - Solxpow, - SolArkLv, - Sungrow, + AforeCan = 1, + BydCan = 2, + BydModbus = 3, + FerroampCan = 4, + Foxess = 5, + GrowattHv = 6, + GrowattLv = 7, + GrowattWit = 8, + Kostal = 9, + Pylon = 10, + PylonLv = 11, + Schneider = 12, + SmaBydH = 13, + SmaBydHvs = 14, + SmaLv = 15, + SmaTripower = 16, + Sofar = 17, + Solax = 18, + Solxpow = 19, + SolArkLv = 20, + Sungrow = 21, Highest }; From 2f1ff9950ce8843bcfbc3dcbd03c060ffc392499 Mon Sep 17 00:00:00 2001 From: Jonny Date: Sat, 16 Aug 2025 12:18:11 +0100 Subject: [PATCH 027/245] Add configurable cell/module/etc settings for Pylonish/Solax inverters to use --- Software/src/communication/nvm/comm_nvm.cpp | 7 ++ .../src/devboard/webserver/settings_html.cpp | 67 +++++++++++++++++++ Software/src/devboard/webserver/webserver.cpp | 20 +++++- Software/src/inverter/INVERTERS.cpp | 9 +++ Software/src/inverter/INVERTERS.h | 10 +++ 5 files changed, 112 insertions(+), 1 deletion(-) diff --git a/Software/src/communication/nvm/comm_nvm.cpp b/Software/src/communication/nvm/comm_nvm.cpp index 688d961c..91adc099 100644 --- a/Software/src/communication/nvm/comm_nvm.cpp +++ b/Software/src/communication/nvm/comm_nvm.cpp @@ -96,6 +96,13 @@ void init_stored_settings() { user_selected_min_pack_voltage_dV = settings.getUInt("BATTPVMIN", 0); user_selected_max_cell_voltage_mV = settings.getUInt("BATTCVMAX", 0); user_selected_min_cell_voltage_mV = settings.getUInt("BATTCVMIN", 0); + user_selected_inverter_cells = settings.getUInt("INVCELLS", 0); + user_selected_inverter_modules = settings.getUInt("INVMODULES", 0); + user_selected_inverter_cells_per_module = settings.getUInt("INVCELLSPER", 0); + user_selected_inverter_voltage_level = settings.getUInt("INVVLEVEL", 0); + user_selected_inverter_ah_capacity = settings.getUInt("INVAHCAPACITY", 0); + user_selected_inverter_battery_type = settings.getUInt("INVBTYPE", 0); + user_selected_inverter_ignore_contactors = settings.getBool("INVICNT", false); auto readIf = [](const char* settingName) { auto batt1If = (comm_interface)settings.getUInt(settingName, (int)comm_interface::CanNative); diff --git a/Software/src/devboard/webserver/settings_html.cpp b/Software/src/devboard/webserver/settings_html.cpp index 31936a55..7ca338ce 100644 --- a/Software/src/devboard/webserver/settings_html.cpp +++ b/Software/src/devboard/webserver/settings_html.cpp @@ -472,6 +472,34 @@ String settings_processor(const String& var, BatteryEmulatorSettingsStore& setti return String(settings.getUInt("SOFAR_ID", 0)); } + if (var == "INVCELLS") { + return String(settings.getUInt("INVCELLS", 0)); + } + + if (var == "INVMODULES") { + return String(settings.getUInt("INVMODULES", 0)); + } + + if (var == "INVCELLSPER") { + return String(settings.getUInt("INVCELLSPER", 0)); + } + + if (var == "INVVLEVEL") { + return String(settings.getUInt("INVVLEVEL", 0)); + } + + if (var == "INVCAPACITY") { + return String(settings.getUInt("INVCAPACITY", 0)); + } + + if (var == "INVBTYPE") { + return String(settings.getUInt("INVBTYPE", 0)); + } + + if (var == "INVICNT") { + return settings.getBool("INVICNT") ? "checked" : ""; + } + return String(); } @@ -668,6 +696,16 @@ const char* getCANInterfaceName(CAN_Interface interface) { display: contents; } + form .if-pylonish { display: none; } + form[data-inverter="4"] .if-pylonish, form[data-inverter="10"] .if-pylonish, form[data-inverter="19"] .if-pylonish { + display: contents; + } + + form .if-solax { display: none; } + form[data-inverter="18"] .if-solax { + display: contents; + } + form .if-mqtt { display: none; } form[data-mqttenabled="true"] .if-mqtt { display: contents; @@ -736,6 +774,35 @@ const char* getCANInterfaceName(CAN_Interface interface) {
+
+ + +
+ +
+ + +
+ +
+ + + + + + + + +
+ +
+ + + + + +
+ diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 498ecc27..cfa65a8a 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -414,7 +414,7 @@ void init_webserver() { const char* boolSettingNames[] = { "DBLBTR", "CNTCTRL", "CNTCTRLDBL", "PWMCNTCTRL", "PERBMSRESET", "REMBMSRESET", - "CANFDASCAN", "WIFIAPENABLED", "MQTTENABLED", "HADISC", "MQTTTOPICS", + "CANFDASCAN", "WIFIAPENABLED", "MQTTENABLED", "HADISC", "MQTTTOPICS", "INVICNT", }; // Handles the form POST from UI to save settings of the common image @@ -497,6 +497,24 @@ void init_webserver() { } else if (p->name() == "SOFAR_ID") { auto type = atoi(p->value().c_str()); settings.saveUInt("SOFAR_ID", type); + } else if (p->name() == "INVCELLS") { + auto type = atoi(p->value().c_str()); + settings.saveUInt("INVCELLS", type); + } else if (p->name() == "INVMODULES") { + auto type = atoi(p->value().c_str()); + settings.saveUInt("INVMODULES", type); + } else if (p->name() == "INVCELLSPER") { + auto type = atoi(p->value().c_str()); + settings.saveUInt("INVCELLSPER", type); + } else if (p->name() == "INVVLEVEL") { + auto type = atoi(p->value().c_str()); + settings.saveUInt("INVVLEVEL", type); + } else if (p->name() == "INVCAPACITY") { + auto type = atoi(p->value().c_str()); + settings.saveUInt("INVCAPACITY", type); + } else if (p->name() == "INVBTYPE") { + auto type = atoi(p->value().c_str()); + settings.saveUInt("INVBTYPE", (int)type); } for (auto& boolSetting : boolSettings) { diff --git a/Software/src/inverter/INVERTERS.cpp b/Software/src/inverter/INVERTERS.cpp index aa762e75..f9b80d88 100644 --- a/Software/src/inverter/INVERTERS.cpp +++ b/Software/src/inverter/INVERTERS.cpp @@ -90,6 +90,15 @@ extern const char* name_for_inverter_type(InverterProtocolType type) { #error "Compile time SELECTED_INVERTER_CLASS should not be defined with COMMON_IMAGE" #endif +// Some settings that can be used by inverters +uint16_t user_selected_inverter_cells = 0; +uint16_t user_selected_inverter_modules = 0; +uint16_t user_selected_inverter_cells_per_module = 0; +uint16_t user_selected_inverter_voltage_level = 0; +uint16_t user_selected_inverter_ah_capacity = 0; +uint16_t user_selected_inverter_battery_type = 0; +bool user_selected_inverter_ignore_contactors = false; + bool setup_inverter() { if (inverter) { return true; diff --git a/Software/src/inverter/INVERTERS.h b/Software/src/inverter/INVERTERS.h index c706c8a9..712b425a 100644 --- a/Software/src/inverter/INVERTERS.h +++ b/Software/src/inverter/INVERTERS.h @@ -36,4 +36,14 @@ extern InverterProtocol* inverter; // Call to initialize the build-time selected inverter. Safe to call even though inverter was not selected. bool setup_inverter(); +#ifdef COMMON_IMAGE +extern uint16_t user_selected_inverter_cells; +extern uint16_t user_selected_inverter_modules; +extern uint16_t user_selected_inverter_cells_per_module; +extern uint16_t user_selected_inverter_voltage_level; +extern uint16_t user_selected_inverter_ah_capacity; +extern uint16_t user_selected_inverter_battery_type; +extern bool user_selected_inverter_ignore_contactors; +#endif + #endif From efd6ca13490c5e2c534ddeee981fe470cc7b6647 Mon Sep 17 00:00:00 2001 From: Jonny Date: Sat, 16 Aug 2025 12:19:30 +0100 Subject: [PATCH 028/245] Make Pylon/Solxpow/Ferroamp inverters use the new settings in COMMON_IMAGE mode --- Software/src/inverter/FERROAMP-CAN.cpp | 36 ++++++++++++++++++++++++++ Software/src/inverter/FERROAMP-CAN.h | 1 + Software/src/inverter/INVERTERS.cpp | 20 +++++++------- Software/src/inverter/INVERTERS.h | 2 -- Software/src/inverter/PYLON-CAN.cpp | 36 ++++++++++++++++++++++++++ Software/src/inverter/PYLON-CAN.h | 1 + Software/src/inverter/SOLXPOW-CAN.cpp | 36 ++++++++++++++++++++++++++ Software/src/inverter/SOLXPOW-CAN.h | 1 + 8 files changed, 122 insertions(+), 11 deletions(-) diff --git a/Software/src/inverter/FERROAMP-CAN.cpp b/Software/src/inverter/FERROAMP-CAN.cpp index 0ecdf69e..cb9757d5 100644 --- a/Software/src/inverter/FERROAMP-CAN.cpp +++ b/Software/src/inverter/FERROAMP-CAN.cpp @@ -1,6 +1,7 @@ #include "FERROAMP-CAN.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" +#include "../inverter/INVERTERS.h" //#define SEND_0 //If defined, the messages will have ID ending with 0 (useful for some inverters) #define SEND_1 //If defined, the messages will have ID ending with 1 (useful for some inverters) @@ -364,3 +365,38 @@ void FerroampCanInverter::send_system_data() { //System equipment information transmit_can_frame(&PYLON_4291); #endif } + +bool FerroampCanInverter::setup() { + if (user_selected_inverter_cells > 0) { + PYLON_7320.data.u8[0] = user_selected_inverter_cells & 0xff; + PYLON_7320.data.u8[1] = (uint8_t)(user_selected_inverter_cells >> 8); + PYLON_7321.data.u8[0] = user_selected_inverter_cells & 0xff; + PYLON_7321.data.u8[1] = (uint8_t)(user_selected_inverter_cells >> 8); + } + + if (user_selected_inverter_modules > 0) { + PYLON_7320.data.u8[2] = user_selected_inverter_modules; + PYLON_7321.data.u8[2] = user_selected_inverter_modules; + } + + if (user_selected_inverter_cells_per_module > 0) { + PYLON_7320.data.u8[3] = user_selected_inverter_cells_per_module; + PYLON_7321.data.u8[3] = user_selected_inverter_cells_per_module; + } + + if (user_selected_inverter_voltage_level > 0) { + PYLON_7320.data.u8[4] = user_selected_inverter_voltage_level & 0xff; + PYLON_7320.data.u8[5] = (uint8_t)(user_selected_inverter_voltage_level >> 8); + PYLON_7321.data.u8[4] = user_selected_inverter_voltage_level & 0xff; + PYLON_7321.data.u8[5] = (uint8_t)(user_selected_inverter_voltage_level >> 8); + } + + if (user_selected_inverter_ah_capacity > 0) { + PYLON_7320.data.u8[6] = user_selected_inverter_ah_capacity & 0xff; + PYLON_7320.data.u8[7] = (uint8_t)(user_selected_inverter_ah_capacity >> 8); + PYLON_7321.data.u8[6] = user_selected_inverter_ah_capacity & 0xff; + PYLON_7321.data.u8[7] = (uint8_t)(user_selected_inverter_ah_capacity >> 8); + } + + return true; +} diff --git a/Software/src/inverter/FERROAMP-CAN.h b/Software/src/inverter/FERROAMP-CAN.h index f45dabc4..00ec78bd 100644 --- a/Software/src/inverter/FERROAMP-CAN.h +++ b/Software/src/inverter/FERROAMP-CAN.h @@ -10,6 +10,7 @@ class FerroampCanInverter : public CanInverterProtocol { public: const char* name() override { return Name; } + bool setup() override; void update_values(); void transmit_can(unsigned long currentMillis); void map_can_frame_to_variable(CAN_frame rx_frame); diff --git a/Software/src/inverter/INVERTERS.cpp b/Software/src/inverter/INVERTERS.cpp index f9b80d88..cbd64f4b 100644 --- a/Software/src/inverter/INVERTERS.cpp +++ b/Software/src/inverter/INVERTERS.cpp @@ -4,6 +4,17 @@ InverterProtocol* inverter = nullptr; InverterProtocolType user_selected_inverter_protocol = InverterProtocolType::BydModbus; +// Some user-configurable settings that can be used by inverters. These +// inverters should use sensible defaults if the corresponding user_selected +// value is zero. +uint16_t user_selected_inverter_cells = 0; +uint16_t user_selected_inverter_modules = 0; +uint16_t user_selected_inverter_cells_per_module = 0; +uint16_t user_selected_inverter_voltage_level = 0; +uint16_t user_selected_inverter_ah_capacity = 0; +uint16_t user_selected_inverter_battery_type = 0; +bool user_selected_inverter_ignore_contactors = false; + std::vector supported_inverter_protocols() { std::vector types; @@ -90,15 +101,6 @@ extern const char* name_for_inverter_type(InverterProtocolType type) { #error "Compile time SELECTED_INVERTER_CLASS should not be defined with COMMON_IMAGE" #endif -// Some settings that can be used by inverters -uint16_t user_selected_inverter_cells = 0; -uint16_t user_selected_inverter_modules = 0; -uint16_t user_selected_inverter_cells_per_module = 0; -uint16_t user_selected_inverter_voltage_level = 0; -uint16_t user_selected_inverter_ah_capacity = 0; -uint16_t user_selected_inverter_battery_type = 0; -bool user_selected_inverter_ignore_contactors = false; - bool setup_inverter() { if (inverter) { return true; diff --git a/Software/src/inverter/INVERTERS.h b/Software/src/inverter/INVERTERS.h index 712b425a..b8207f7d 100644 --- a/Software/src/inverter/INVERTERS.h +++ b/Software/src/inverter/INVERTERS.h @@ -36,7 +36,6 @@ extern InverterProtocol* inverter; // Call to initialize the build-time selected inverter. Safe to call even though inverter was not selected. bool setup_inverter(); -#ifdef COMMON_IMAGE extern uint16_t user_selected_inverter_cells; extern uint16_t user_selected_inverter_modules; extern uint16_t user_selected_inverter_cells_per_module; @@ -44,6 +43,5 @@ extern uint16_t user_selected_inverter_voltage_level; extern uint16_t user_selected_inverter_ah_capacity; extern uint16_t user_selected_inverter_battery_type; extern bool user_selected_inverter_ignore_contactors; -#endif #endif diff --git a/Software/src/inverter/PYLON-CAN.cpp b/Software/src/inverter/PYLON-CAN.cpp index 293e53cf..dabd778f 100644 --- a/Software/src/inverter/PYLON-CAN.cpp +++ b/Software/src/inverter/PYLON-CAN.cpp @@ -1,6 +1,7 @@ #include "PYLON-CAN.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" +#include "../inverter/INVERTERS.h" #define SEND_0 //If defined, the messages will have ID ending with 0 (useful for some inverters) //#define SEND_1 //If defined, the messages will have ID ending with 1 (useful for some inverters) @@ -351,3 +352,38 @@ void PylonInverter::send_system_data() { //System equipment information transmit_can_frame(&PYLON_4291); #endif } + +bool PylonInverter::setup() { + if (user_selected_inverter_cells > 0) { + PYLON_7320.data.u8[0] = user_selected_inverter_cells & 0xff; + PYLON_7320.data.u8[1] = (uint8_t)(user_selected_inverter_cells >> 8); + PYLON_7321.data.u8[0] = user_selected_inverter_cells & 0xff; + PYLON_7321.data.u8[1] = (uint8_t)(user_selected_inverter_cells >> 8); + } + + if (user_selected_inverter_modules > 0) { + PYLON_7320.data.u8[2] = user_selected_inverter_modules; + PYLON_7321.data.u8[2] = user_selected_inverter_modules; + } + + if (user_selected_inverter_cells_per_module > 0) { + PYLON_7320.data.u8[3] = user_selected_inverter_cells_per_module; + PYLON_7321.data.u8[3] = user_selected_inverter_cells_per_module; + } + + if (user_selected_inverter_voltage_level > 0) { + PYLON_7320.data.u8[4] = user_selected_inverter_voltage_level & 0xff; + PYLON_7320.data.u8[5] = (uint8_t)(user_selected_inverter_voltage_level >> 8); + PYLON_7321.data.u8[4] = user_selected_inverter_voltage_level & 0xff; + PYLON_7321.data.u8[5] = (uint8_t)(user_selected_inverter_voltage_level >> 8); + } + + if (user_selected_inverter_ah_capacity > 0) { + PYLON_7320.data.u8[6] = user_selected_inverter_ah_capacity & 0xff; + PYLON_7320.data.u8[7] = (uint8_t)(user_selected_inverter_ah_capacity >> 8); + PYLON_7321.data.u8[6] = user_selected_inverter_ah_capacity & 0xff; + PYLON_7321.data.u8[7] = (uint8_t)(user_selected_inverter_ah_capacity >> 8); + } + + return true; +} diff --git a/Software/src/inverter/PYLON-CAN.h b/Software/src/inverter/PYLON-CAN.h index 5c55b039..33a8b1b6 100644 --- a/Software/src/inverter/PYLON-CAN.h +++ b/Software/src/inverter/PYLON-CAN.h @@ -10,6 +10,7 @@ class PylonInverter : public CanInverterProtocol { public: const char* name() override { return Name; } + bool setup() override; void update_values(); void transmit_can(unsigned long currentMillis); void map_can_frame_to_variable(CAN_frame rx_frame); diff --git a/Software/src/inverter/SOLXPOW-CAN.cpp b/Software/src/inverter/SOLXPOW-CAN.cpp index d6f2cdcc..b29946f1 100644 --- a/Software/src/inverter/SOLXPOW-CAN.cpp +++ b/Software/src/inverter/SOLXPOW-CAN.cpp @@ -1,6 +1,7 @@ #include "SOLXPOW-CAN.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" +#include "../inverter/INVERTERS.h" #define SEND_0 //If defined, the messages will have ID ending with 0 (useful for some inverters) //#define SEND_1 //If defined, the messages will have ID ending with 1 (useful for some inverters) @@ -353,3 +354,38 @@ void SolxpowInverter::send_system_data() { //System equipment information transmit_can_frame(&SOLXPOW_4291); #endif } + +bool SolxpowInverter::setup() { + if (user_selected_inverter_cells > 0) { + SOLXPOW_7320.data.u8[0] = user_selected_inverter_cells & 0xff; + SOLXPOW_7320.data.u8[1] = (uint8_t)(user_selected_inverter_cells >> 8); + SOLXPOW_7321.data.u8[0] = user_selected_inverter_cells & 0xff; + SOLXPOW_7321.data.u8[1] = (uint8_t)(user_selected_inverter_cells >> 8); + } + + if (user_selected_inverter_modules > 0) { + SOLXPOW_7320.data.u8[2] = user_selected_inverter_modules; + SOLXPOW_7321.data.u8[2] = user_selected_inverter_modules; + } + + if (user_selected_inverter_cells_per_module > 0) { + SOLXPOW_7320.data.u8[3] = user_selected_inverter_cells_per_module; + SOLXPOW_7321.data.u8[3] = user_selected_inverter_cells_per_module; + } + + if (user_selected_inverter_voltage_level > 0) { + SOLXPOW_7320.data.u8[4] = user_selected_inverter_voltage_level & 0xff; + SOLXPOW_7320.data.u8[5] = (uint8_t)(user_selected_inverter_voltage_level >> 8); + SOLXPOW_7321.data.u8[4] = user_selected_inverter_voltage_level & 0xff; + SOLXPOW_7321.data.u8[5] = (uint8_t)(user_selected_inverter_voltage_level >> 8); + } + + if (user_selected_inverter_ah_capacity > 0) { + SOLXPOW_7320.data.u8[6] = user_selected_inverter_ah_capacity & 0xff; + SOLXPOW_7320.data.u8[7] = (uint8_t)(user_selected_inverter_ah_capacity >> 8); + SOLXPOW_7321.data.u8[6] = user_selected_inverter_ah_capacity & 0xff; + SOLXPOW_7321.data.u8[7] = (uint8_t)(user_selected_inverter_ah_capacity >> 8); + } + + return true; +} diff --git a/Software/src/inverter/SOLXPOW-CAN.h b/Software/src/inverter/SOLXPOW-CAN.h index 73bc7e13..f45970ee 100644 --- a/Software/src/inverter/SOLXPOW-CAN.h +++ b/Software/src/inverter/SOLXPOW-CAN.h @@ -10,6 +10,7 @@ class SolxpowInverter : public CanInverterProtocol { public: const char* name() override { return Name; } + bool setup() override; void update_values(); void transmit_can(unsigned long currentMillis); void map_can_frame_to_variable(CAN_frame rx_frame); From a718d99672bb174b6448e15be949de18dca47ff9 Mon Sep 17 00:00:00 2001 From: Jonny Date: Sat, 16 Aug 2025 12:20:30 +0100 Subject: [PATCH 029/245] Make Solax use type settings in COMMON_IMAGE mode, add ignore contactors feature --- Software/src/inverter/SOLAX-CAN.cpp | 59 +++++++++++++++++++++++------ Software/src/inverter/SOLAX-CAN.h | 12 ++++++ 2 files changed, 59 insertions(+), 12 deletions(-) diff --git a/Software/src/inverter/SOLAX-CAN.cpp b/Software/src/inverter/SOLAX-CAN.cpp index a3f77a3e..16df4f2a 100644 --- a/Software/src/inverter/SOLAX-CAN.cpp +++ b/Software/src/inverter/SOLAX-CAN.cpp @@ -4,11 +4,7 @@ #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" #include "../devboard/utils/logging.h" - -#define NUMBER_OF_MODULES 0 -#define BATTERY_TYPE 0x50 -// If you are having BattVoltFault issues, configure the above values according to wiki page -// https://github.com/dalathegreat/Battery-Emulator/wiki/Solax-inverters +#include "../inverter/INVERTERS.h" // __builtin_bswap64 needed to convert to ESP32 little endian format // Byte[4] defines the requested contactor state: 1 = Closed , 0 = Open @@ -18,7 +14,7 @@ void SolaxInverter:: update_values() { //This function maps all the values fetched from battery CAN to the correct CAN messages // If not receiveing any communication from the inverter, open contactors and return to battery announce state - if (millis() - LastFrameTime >= SolaxTimeout) { + if (millis() - LastFrameTime >= SolaxTimeout && !configured_ignore_contactors) { datalayer.system.status.inverter_allows_contactor_closing = false; STATE = BATTERY_ANNOUNCE; } @@ -73,8 +69,8 @@ void SolaxInverter:: //BMS_Status SOLAX_1875.data.u8[0] = (uint8_t)temperature_average; SOLAX_1875.data.u8[1] = (temperature_average >> 8); - SOLAX_1875.data.u8[2] = (uint8_t)NUMBER_OF_MODULES; // Number of slave batteries - SOLAX_1875.data.u8[4] = (uint8_t)0; // Contactor Status 0=off, 1=on. + SOLAX_1875.data.u8[2] = (uint8_t)configured_number_of_modules; // Number of slave batteries + SOLAX_1875.data.u8[4] = (uint8_t)0; // Contactor Status 0=off, 1=on. //BMS_PackTemps (strange name, since it has voltages?) SOLAX_1876.data.u8[0] = (int8_t)datalayer.battery.status.temperature_max_dC; @@ -88,8 +84,8 @@ void SolaxInverter:: SOLAX_1876.data.u8[7] = (datalayer.battery.status.cell_min_voltage_mV >> 8); //Unknown - SOLAX_1877.data.u8[4] = (uint8_t)BATTERY_TYPE; // Battery type (Default 0x50) - SOLAX_1877.data.u8[6] = (uint8_t)0x22; // Firmware version? + SOLAX_1877.data.u8[4] = (uint8_t)configured_battery_type; // Battery type (Default 0x50) + SOLAX_1877.data.u8[6] = (uint8_t)0x22; // Firmware version? SOLAX_1877.data.u8[7] = (uint8_t)0x02; // The above firmware version applies to:02 = Master BMS, 10 = S1, 20 = S2, 30 = S3, 40 = S4 @@ -129,6 +125,26 @@ void SolaxInverter::map_can_frame_to_variable(CAN_frame rx_frame) { if (rx_frame.ID == 0x1871 && rx_frame.data.u8[0] == (0x01) || rx_frame.ID == 0x1871 && rx_frame.data.u8[0] == (0x02)) { LastFrameTime = millis(); + + if (configured_ignore_contactors) { + // Skip the state machine since we're not going to open/close contactors, + // and the Solax would otherwise wait forever for us to do so. + + datalayer.system.status.inverter_allows_contactor_closing = true; + SOLAX_1875.data.u8[4] = (0x01); // Inform Inverter: Contactor 0=off, 1=on. + transmit_can_frame(&SOLAX_187E); + transmit_can_frame(&SOLAX_187A); + transmit_can_frame(&SOLAX_1872); + transmit_can_frame(&SOLAX_1873); + transmit_can_frame(&SOLAX_1874); + transmit_can_frame(&SOLAX_1875); + transmit_can_frame(&SOLAX_1876); + transmit_can_frame(&SOLAX_1877); + transmit_can_frame(&SOLAX_1878); + transmit_can_frame(&SOLAX_100A001); + return; + } + switch (STATE) { case (BATTERY_ANNOUNCE): #ifdef DEBUG_LOG @@ -208,7 +224,26 @@ void SolaxInverter::map_can_frame_to_variable(CAN_frame rx_frame) { } } -bool SolaxInverter::setup(void) { // Performs one time setup at startup - datalayer.system.status.inverter_allows_contactor_closing = false; // The inverter needs to allow first +bool SolaxInverter::setup(void) { // Performs one time setup at startup + // Use user selected values if nonzero, otherwise use defaults + if (user_selected_inverter_modules > 0) { + configured_number_of_modules = user_selected_inverter_modules; + } else { + configured_number_of_modules = NUMBER_OF_MODULES; + } + + if (user_selected_inverter_battery_type > 0) { + configured_battery_type = user_selected_inverter_battery_type; + } else { + configured_battery_type = BATTERY_TYPE; + } + + configured_ignore_contactors = user_selected_inverter_ignore_contactors; + + if (!configured_ignore_contactors) { + // Only prevent closing if we're not ignoring contactors + datalayer.system.status.inverter_allows_contactor_closing = false; // The inverter needs to allow first + } + return true; } diff --git a/Software/src/inverter/SOLAX-CAN.h b/Software/src/inverter/SOLAX-CAN.h index 0a3d8efa..6a67e8bf 100644 --- a/Software/src/inverter/SOLAX-CAN.h +++ b/Software/src/inverter/SOLAX-CAN.h @@ -17,6 +17,11 @@ class SolaxInverter : public CanInverterProtocol { static constexpr const char* Name = "SolaX Triple Power LFP over CAN bus"; private: + static const int NUMBER_OF_MODULES = 0; + static const int BATTERY_TYPE = 0x50; + // If you are having BattVoltFault issues, configure the above values according to wiki page + // https://github.com/dalathegreat/Battery-Emulator/wiki/Solax-inverters + // Timeout in milliseconds static const int SolaxTimeout = 2000; @@ -34,6 +39,13 @@ class SolaxInverter : public CanInverterProtocol { uint16_t capped_capacity_Wh; uint16_t capped_remaining_capacity_Wh; + int configured_number_of_modules = 0; + int configured_battery_type = 0; + // If true, the integration will ignore the inverter's requests to open the + // battery contactors. Useful for batteries that can't open contactors on + // request. + bool configured_ignore_contactors = false; + //CAN message translations from this amazing repository: https://github.com/rand12345/solax_can_bus CAN_frame SOLAX_1801 = {.FD = false, From 291f8584882166089c4f14486482e848c28e8d5e Mon Sep 17 00:00:00 2001 From: Fredrik Date: Tue, 19 Aug 2025 07:47:34 +0200 Subject: [PATCH 030/245] Default SOC method changed to measured --- Software/src/battery/BYD-ATTO-3-BATTERY.cpp | 6 ------ Software/src/battery/BYD-ATTO-3-BATTERY.h | 8 ++++++-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp index 97db58ad..db969772 100644 --- a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp +++ b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp @@ -4,12 +4,6 @@ #include "../datalayer/datalayer_extended.h" #include "../devboard/utils/events.h" -/* Notes -SOC% by default is now ESTIMATED. -If you have a crash-locked pack, See the Wiki for more info on how to attempt an unlock -After battery has been unlocked, you can remove the "USE_ESTIMATED_SOC" from the BYD-ATTO-3-BATTERY.h file -*/ - #define POLL_FOR_BATTERY_VOLTAGE 0x0008 #define POLL_FOR_BATTERY_CURRENT 0x0009 #define POLL_FOR_LOWEST_TEMP_CELL 0x002f diff --git a/Software/src/battery/BYD-ATTO-3-BATTERY.h b/Software/src/battery/BYD-ATTO-3-BATTERY.h index a34c32dd..3d409e84 100644 --- a/Software/src/battery/BYD-ATTO-3-BATTERY.h +++ b/Software/src/battery/BYD-ATTO-3-BATTERY.h @@ -7,8 +7,12 @@ #include "BYD-ATTO-3-HTML.h" #include "CanBattery.h" -#define USE_ESTIMATED_SOC // If enabled, SOC is estimated from pack voltage. Useful for locked packs. \ - // Comment out this only if you know your BMS is unlocked and able to send SOC% +/* Notes +SOC% by default is now MEASURED by BMS. +If you have a crash-locked pack, See the Wiki for more info on how to attempt an unlock. +Remove the comment from "USE_ESTIMATED_SOC" below if you still decide to use a locked battery and want to use estimated SOC. +*/ +//#define USE_ESTIMATED_SOC //Uncomment and configure this line, if you want to filter out a broken temperature sensor (1-10) //Make sure you understand risks associated with disabling. Values can be read via "More Battery info" From b2553fcabe22dfecb17673e362197eb3e7550ae2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 19 Aug 2025 09:40:08 +0300 Subject: [PATCH 031/245] Update acanfd library to v2.1.16 --- .../pierremolinaro-ACAN2517FD/ACAN2517FD.cpp | 293 ++++++++++-------- .../pierremolinaro-ACAN2517FD/ACAN2517FD.h | 182 ++++++----- .../ACAN2517FDFilters.h | 58 ++-- .../ACAN2517FDSettings.cpp | 241 ++++++-------- .../ACAN2517FDSettings.h | 182 +++++------ .../ACAN2517FD_ACANFDBuffer.h | 122 ++++++++ ...NFDMessage.h => ACAN2517FD_CANFDMessage.h} | 46 +-- .../{CANMessage.h => ACAN2517FD_CANMessage.h} | 12 +- ...actor.h => ACAN2517FD_DataBitRateFactor.h} | 10 +- .../pierremolinaro-ACAN2517FD/ACANFDBuffer.h | 119 ------- .../lib/pierremolinaro-ACAN2517FD/README.md | 0 .../library.properties | 9 + 12 files changed, 628 insertions(+), 646 deletions(-) create mode 100644 Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD_ACANFDBuffer.h rename Software/src/lib/pierremolinaro-ACAN2517FD/{CANFDMessage.h => ACAN2517FD_CANFDMessage.h} (65%) rename Software/src/lib/pierremolinaro-ACAN2517FD/{CANMessage.h => ACAN2517FD_CANMessage.h} (84%) rename Software/src/lib/pierremolinaro-ACAN2517FD/{ACANFD_DataBitRateFactor.h => ACAN2517FD_DataBitRateFactor.h} (73%) delete mode 100644 Software/src/lib/pierremolinaro-ACAN2517FD/ACANFDBuffer.h mode change 100644 => 100755 Software/src/lib/pierremolinaro-ACAN2517FD/README.md create mode 100644 Software/src/lib/pierremolinaro-ACAN2517FD/library.properties diff --git a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.cpp b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.cpp index ec2526c2..e3d9186d 100644 --- a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.cpp +++ b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.cpp @@ -1,20 +1,20 @@ -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // A CAN driver for MCP2517FD, CANFD mode // by Pierre Molinaro // https://github.com/pierremolinaro/acan2517FD // -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ #include "ACAN2517FD.h" -#include "../../system_settings.h" //Contains task priority +#include "../../system_settings.h" //Contains task priority (Battery-Emulator modified line) -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ static const uint8_t TXBWS = 0 ; -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // Note about ESP32 -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // // It appears that Arduino ESP32 interrupts are managed in a completely different way from "usual" Arduino: // - SPI.usingInterrupt is not implemented; @@ -43,7 +43,7 @@ static const uint8_t TXBWS = 0 ; // #endif // mSPI.endTransaction () ; // -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ #ifdef ARDUINO_ARCH_ESP32 static void myESP32Task (void * pData) { @@ -55,9 +55,9 @@ static const uint8_t TXBWS = 0 ; } #endif -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // ACAN2517FD register addresses -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ static const uint16_t CON_REGISTER = 0x000 ; static const uint16_t NBTCFG_REGISTER = 0x004 ; @@ -68,85 +68,85 @@ static const uint16_t TREC_REGISTER = 0x034 ; static const uint16_t BDIAG0_REGISTER = 0x038 ; static const uint16_t BDIAG1_REGISTER = 0x03C ; -//······················································································································ +//------------------------------------------------------------------------------ // TXQ REGISTERS -//······················································································································ +//------------------------------------------------------------------------------ static const uint16_t TXQCON_REGISTER = 0x050 ; static const uint16_t TXQSTA_REGISTER = 0x054 ; static const uint16_t TXQUA_REGISTER = 0x058 ; -//······················································································································ +//------------------------------------------------------------------------------ // INTERRUPT REGISTERS -//······················································································································ +//------------------------------------------------------------------------------ static const uint16_t INT_REGISTER = 0x01C ; -//······················································································································ +//------------------------------------------------------------------------------ // FIFO REGISTERS -//······················································································································ +//------------------------------------------------------------------------------ static uint16_t FIFOCON_REGISTER (const uint16_t inFIFOIndex) { // 1 ... 31 return 0x05C + 12 * (inFIFOIndex - 1) ; } -//······················································································································ +//------------------------------------------------------------------------------ static uint16_t FIFOSTA_REGISTER (const uint16_t inFIFOIndex) { // 1 ... 31 return 0x060 + 12 * (inFIFOIndex - 1) ; } -//······················································································································ +//------------------------------------------------------------------------------ static uint16_t FIFOUA_REGISTER (const uint16_t inFIFOIndex) { // 1 ... 31 return 0x064 + 12 * (inFIFOIndex - 1) ; } -//······················································································································ +//------------------------------------------------------------------------------ // FILTER REGISTERS -//······················································································································ +//------------------------------------------------------------------------------ static uint16_t FLTCON_REGISTER (const uint16_t inFilterIndex) { // 0 ... 31 (DS20005688B, page 58) return 0x1D0 + inFilterIndex ; } -//······················································································································ +//------------------------------------------------------------------------------ static uint16_t FLTOBJ_REGISTER (const uint16_t inFilterIndex) { // 0 ... 31 (DS20005688B, page 60) return 0x1F0 + 8 * inFilterIndex ; } -//······················································································································ +//------------------------------------------------------------------------------ static uint16_t MASK_REGISTER (const uint16_t inFilterIndex) { // 0 ... 31 (DS20005688B, page 61) return 0x1F4 + 8 * inFilterIndex ; } -//······················································································································ +//------------------------------------------------------------------------------ // OSCILLATOR REGISTER -//······················································································································ +//------------------------------------------------------------------------------ static const uint16_t OSC_REGISTER = 0xE00 ; -//······················································································································ +//------------------------------------------------------------------------------ // INPUT / OUPUT CONTROL REGISTER -//······················································································································ +//------------------------------------------------------------------------------ static const uint16_t IOCON_REGISTER_00_07 = 0xE04 ; static const uint16_t IOCON_REGISTER_08_15 = 0xE05 ; static const uint16_t IOCON_REGISTER_16_23 = 0xE06 ; static const uint16_t IOCON_REGISTER_24_31 = 0xE07 ; -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // RECEIVE FIFO INDEX -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ static const uint8_t RECEIVE_FIFO_INDEX = 1 ; static const uint8_t TRANSMIT_FIFO_INDEX = 2 ; -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // BYTE BUFFER UTILITY FUNCTIONS -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ static void enterU32InBufferAtIndex (const uint32_t inValue, uint8_t ioBuffer [], const uint8_t inIndex) { ioBuffer [inIndex + 0] = (uint8_t) inValue ; @@ -155,7 +155,7 @@ static void enterU32InBufferAtIndex (const uint32_t inValue, uint8_t ioBuffer [] ioBuffer [inIndex + 3] = (uint8_t) (inValue >> 24) ; } -//······················································································································ +//------------------------------------------------------------------------------ static uint32_t u32FromBufferAtIndex (uint8_t ioBuffer [], const uint8_t inIndex) { uint32_t result = (uint32_t) ioBuffer [inIndex + 0] ; @@ -165,7 +165,7 @@ static uint32_t u32FromBufferAtIndex (uint8_t ioBuffer [], const uint8_t inIndex return result ; } -//······················································································································ +//------------------------------------------------------------------------------ static uint16_t u16FromBufferAtIndex (uint8_t ioBuffer [], const uint8_t inIndex) { uint16_t result = (uint16_t) ioBuffer [inIndex + 0] ; @@ -173,15 +173,11 @@ static uint16_t u16FromBufferAtIndex (uint8_t ioBuffer [], const uint8_t inIndex return result ; } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ static inline void turnOffInterrupts () { #ifndef DISABLEMCP2517FDCOMPAT - #ifdef ARDUINO_ARCH_ESP32 - taskDISABLE_INTERRUPTS () ; - #else - noInterrupts () ; - #endif +turnOffInterrupts () ; #endif } @@ -209,7 +205,6 @@ mINT (inINT), mUsesTXQ (false), mHardwareTxFIFOFull (false), mRxInterruptEnabled (true), -mHasDataBitRate (false), mTransmitFIFOPayload (0), mTXQBufferPayload (0), mReceiveFIFOPayload (0), @@ -223,7 +218,7 @@ mDriverTransmitBuffer () { } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ uint32_t ACAN2517FD::begin (const ACAN2517FDSettings & inSettings, void (* inInterruptServiceRoutine) (void)) { @@ -234,7 +229,7 @@ uint32_t ACAN2517FD::begin (const ACAN2517FDSettings & inSettings, return begin (inSettings, inInterruptServiceRoutine, filters) ; } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ uint32_t ACAN2517FD::begin (const ACAN2517FDSettings & inSettings, void (* inInterruptServiceRoutine) (void), @@ -503,17 +498,14 @@ uint32_t ACAN2517FD::begin (const ACAN2517FDSettings & inSettings, // bits 11-8: TSEG2 - 1 // bits 7-4: unused // bits 3-0: SJW - 1 - mHasDataBitRate = inSettings.mDataBitRateFactor != ::DataBitRateFactor::x1 ; - if (mHasDataBitRate) { - data = inSettings.mBitRatePrescaler - 1 ; - data <<= 8 ; - data |= inSettings.mDataPhaseSegment1 - 1 ; - data <<= 8 ; - data |= inSettings.mDataPhaseSegment2 - 1 ; - data <<= 8 ; - data |= inSettings.mDataSJW - 1 ; - writeRegister32 (DBTCFG_REGISTER, data) ; - } + data = inSettings.mBitRatePrescaler - 1 ; + data <<= 8 ; + data |= inSettings.mDataPhaseSegment1 - 1 ; + data <<= 8 ; + data |= inSettings.mDataPhaseSegment2 - 1 ; + data <<= 8 ; + data |= inSettings.mDataSJW - 1 ; + writeRegister32 (DBTCFG_REGISTER, data) ; //----------------------------------- Request mode (CON_REGISTER + 3, DS20005688B, page 24) // bits 7-4: Transmit Bandwith Sharing Bits ---> 0 // bit 3: Abort All Pending Transmissions bit --> 0 @@ -531,14 +523,14 @@ uint32_t ACAN2517FD::begin (const ACAN2517FDSettings & inSettings, } } #ifdef ARDUINO_ARCH_ESP32 - xTaskCreate (myESP32Task, "ACAN2517Handler", 1024, this, TASK_ACAN2517FD_PRIORITY, &mESP32TaskHandle) ; + xTaskCreate (myESP32Task, "ACAN2517Handler", 1024, this, TASK_ACAN2517FD_PRIORITY, &mESP32TaskHandle) ; //(Battery-Emulator modified line) #endif if (mINT != 255) { // 255 means interrupt is not used #ifdef ARDUINO_ARCH_ESP32 attachInterrupt (itPin, inInterruptServiceRoutine, FALLING) ; #else - attachInterrupt (itPin, inInterruptServiceRoutine, LOW) ; // Thank to Flole998 mSPI.usingInterrupt (itPin) ; // usingInterrupt is not implemented in Arduino ESP32 + attachInterrupt (itPin, inInterruptServiceRoutine, LOW) ; // Thank to Flole998 #endif } // If you begin() multiple times without constructor, @@ -550,13 +542,13 @@ uint32_t ACAN2517FD::begin (const ACAN2517FDSettings & inSettings, return errorCode ; } -//······················································································································ +//------------------------------------------------------------------------------ // end method (resets the MCP2517FD, deallocate buffers, and detach interrupt pin) -//······················································································································ +//------------------------------------------------------------------------------ bool ACAN2517FD::end (void) { mSPI.beginTransaction (mSPISettings) ; - turnOffInterrupts () ; +turnOffInterrupts () ; //--- Detach interrupt pin if (mINT != 255) { // 255 means interrupt is not used const int8_t itPin = digitalPinToInterrupt (mINT) ; @@ -591,21 +583,25 @@ bool ACAN2517FD::end (void) { mDriverReceiveBuffer.initWithSize (0) ; mDriverTransmitBuffer.initWithSize (0) ; //--- - turnOnInterrupts () ; + #ifdef ARDUINO_ARCH_ESP32 + taskENABLE_INTERRUPTS () ; + #else + interrupts () ; + #endif mSPI.endTransaction () ; //--- return ok ; } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // SEND FRAME -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ bool ACAN2517FD::tryToSend (const CANFDMessage & inMessage) { bool ok = inMessage.isValid () ; if (ok) { mSPI.beginTransaction (mSPISettings) ; - turnOffInterrupts () ; + turnOffInterrupts () ; if (inMessage.idx == 0) { ok = inMessage.len <= mTransmitFIFOPayload ; if (ok) { @@ -623,7 +619,7 @@ bool ACAN2517FD::tryToSend (const CANFDMessage & inMessage) { return ok ; } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ bool ACAN2517FD::enterInTransmitBuffer (const CANFDMessage & inMessage) { bool result ; @@ -645,7 +641,7 @@ bool ACAN2517FD::enterInTransmitBuffer (const CANFDMessage & inMessage) { return result ; } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ static uint32_t lengthCodeForLength (const uint8_t inLength) { uint32_t result = inLength & 0x0F ; @@ -661,7 +657,7 @@ static uint32_t lengthCodeForLength (const uint8_t inLength) { return result ; } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ void ACAN2517FD::appendInControllerTxFIFO (const CANFDMessage & inMessage) { const uint16_t ramAddr = uint16_t (0x400 + readRegister32Assume_SPI_transaction (FIFOUA_REGISTER (TRANSMIT_FIFO_INDEX))) ; @@ -686,9 +682,7 @@ void ACAN2517FD::appendInControllerTxFIFO (const CANFDMessage & inMessage) { break ; case CANFDMessage::CANFD_WITH_BIT_RATE_SWITCH : flags |= 1 << 7 ; // Set FDF bit - if (mHasDataBitRate) { - flags |= 1 << 6 ; // Set BRS bit - } + flags |= 1 << 6 ; // Set BRS bit break ; } //--- Word count @@ -714,7 +708,7 @@ void ACAN2517FD::appendInControllerTxFIFO (const CANFDMessage & inMessage) { writeRegister8Assume_SPI_transaction (FIFOCON_REGISTER (TRANSMIT_FIFO_INDEX) + 1, data8); } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ bool ACAN2517FD::sendViaTXQ (const CANFDMessage & inMessage) { bool ok = mUsesTXQ ; @@ -751,9 +745,7 @@ bool ACAN2517FD::sendViaTXQ (const CANFDMessage & inMessage) { break ; case CANFDMessage::CANFD_WITH_BIT_RATE_SWITCH : flags |= 1 << 7 ; // Set FDF bit - if (mHasDataBitRate) { - flags |= 1 << 6 ; // Set BRS bit - } + flags |= 1 << 6 ; // Set BRS bit break ; } //--- Word count @@ -782,41 +774,56 @@ bool ACAN2517FD::sendViaTXQ (const CANFDMessage & inMessage) { return ok ; } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // RECEIVE FRAME -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ bool ACAN2517FD::available (void) { mSPI.beginTransaction (mSPISettings) ; turnOffInterrupts () ; const bool hasReceivedMessage = mDriverReceiveBuffer.count () > 0 ; - turnOnInterrupts(); + #ifdef ARDUINO_ARCH_ESP32 + taskENABLE_INTERRUPTS () ; + #else + interrupts () ; + #endif mSPI.endTransaction () ; return hasReceivedMessage ; } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ bool ACAN2517FD::receive (CANFDMessage & outMessage) { - mSPI.beginTransaction (mSPISettings) ; - turnOffInterrupts () ; const bool hasReceivedMessage = mDriverReceiveBuffer.remove (outMessage) ; //--- If receive interrupt is disabled, enable it (added in release 2.17) if (mINT == 255) { // No interrupt pin mRxInterruptEnabled = true ; isr_poll_core () ; // Perform polling }else if (!mRxInterruptEnabled) { + + mSPI.beginTransaction (mSPISettings) ; + #ifdef ARDUINO_ARCH_ESP32 + taskDISABLE_INTERRUPTS () ; + #else + noInterrupts () ; + #endif + mRxInterruptEnabled = true ; uint8_t data8 = readRegister8Assume_SPI_transaction (INT_REGISTER + 2) ; data8 |= (1 << 1) ; // Receive FIFO Interrupt Enable writeRegister8Assume_SPI_transaction (INT_REGISTER + 2, data8) ; + + #ifdef ARDUINO_ARCH_ESP32 + taskENABLE_INTERRUPTS () ; + #else + interrupts () ; + #endif + mSPI.endTransaction () ; } - turnOnInterrupts(); - mSPI.endTransaction () ; return hasReceivedMessage ; } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ bool ACAN2517FD::dispatchReceivedMessage (const tFilterMatchCallBack inFilterMatchCallBack) { CANFDMessage receivedMessage ; @@ -834,9 +841,9 @@ bool ACAN2517FD::dispatchReceivedMessage (const tFilterMatchCallBack inFilterMat return hasReceived ; } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // POLLING (ESP32) -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ #ifdef ARDUINO_ARCH_ESP32 void ACAN2517FD::poll (void) { @@ -846,22 +853,22 @@ bool ACAN2517FD::dispatchReceivedMessage (const tFilterMatchCallBack inFilterMat } #endif -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // POLLING (other than ESP32) -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ #ifndef ARDUINO_ARCH_ESP32 void ACAN2517FD::poll (void) { - turnOffInterrupts () ; + noInterrupts () ; isr_poll_core () ; - turnOnInterrupts () ; + interrupts () ; } #endif -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // INTERRUPT SERVICE ROUTINE (ESP32) // https://stackoverflow.com/questions/51750377/how-to-disable-interrupt-watchdog-in-esp32-or-increase-isr-time-limit -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ #ifdef ARDUINO_ARCH_ESP32 void ACAN2517FD::isr (void) { @@ -871,9 +878,9 @@ bool ACAN2517FD::dispatchReceivedMessage (const tFilterMatchCallBack inFilterMat } #endif -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // INTERRUPT SERVICE ROUTINE (other than ESP32) -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ #ifndef ARDUINO_ARCH_ESP32 void ACAN2517FD::isr (void) { @@ -881,9 +888,9 @@ bool ACAN2517FD::dispatchReceivedMessage (const tFilterMatchCallBack inFilterMat } #endif -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // INTERRUPT SERVICE ROUTINES (common) -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ void ACAN2517FD::isr_poll_core (void) { mSPI.beginTransaction (mSPISettings) ; @@ -933,7 +940,7 @@ void ACAN2517FD::isr_poll_core (void) { mSPI.endTransaction () ; } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ void ACAN2517FD::transmitInterrupt (void) { // Generated if hardware transmit FIFO is not full CANFDMessage message ; @@ -948,7 +955,7 @@ void ACAN2517FD::transmitInterrupt (void) { // Generated if hardware transmit FI } } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ void ACAN2517FD::receiveInterrupt (void) { const uint16_t ramAddress = uint16_t (0x400 + readRegister32Assume_SPI_transaction (FIFOUA_REGISTER (RECEIVE_FIFO_INDEX))) ; @@ -1007,9 +1014,9 @@ void ACAN2517FD::receiveInterrupt (void) { } } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // MCP2517FD REGISTER ACCESS, SECOND LEVEL FUNCTIONS (HANDLE CS, ASSUME WITHIN SPI TRANSACTION) -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ void ACAN2517FD::writeRegister32Assume_SPI_transaction (const uint16_t inRegisterAddress, const uint32_t inValue) { @@ -1027,7 +1034,7 @@ void ACAN2517FD::writeRegister32Assume_SPI_transaction (const uint16_t inRegiste deassertCS () ; } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ void ACAN2517FD::writeRegister8Assume_SPI_transaction (const uint16_t inRegisterAddress, const uint8_t inValue) { @@ -1042,7 +1049,7 @@ void ACAN2517FD::writeRegister8Assume_SPI_transaction (const uint16_t inRegister deassertCS () ; } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ uint32_t ACAN2517FD::readRegister32Assume_SPI_transaction (const uint16_t inRegisterAddress) { //--- Read word register via 6-byte buffer (speed enhancement, thanks to thomasfla) @@ -1061,7 +1068,7 @@ uint32_t ACAN2517FD::readRegister32Assume_SPI_transaction (const uint16_t inRegi return result ; } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ uint16_t ACAN2517FD::readRegister16Assume_SPI_transaction (const uint16_t inRegisterAddress) { //--- Read half-word register via 4-byte buffer (speed enhancement, thanks to thomasfla) @@ -1080,7 +1087,7 @@ uint16_t ACAN2517FD::readRegister16Assume_SPI_transaction (const uint16_t inRegi return result ; } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ uint8_t ACAN2517FD::readRegister8Assume_SPI_transaction (const uint16_t inRegisterAddress) { //--- Read byte register via 3-byte buffer (speed enhancement, thanks to thomasfla) @@ -1094,71 +1101,87 @@ uint8_t ACAN2517FD::readRegister8Assume_SPI_transaction (const uint16_t inRegist return buffer [2] ; } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // MCP2517FD REGISTER ACCESS, THIRD LEVEL FUNCTIONS (HANDLE CS AND SPI TRANSACTION) -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ void ACAN2517FD::writeRegister8 (const uint16_t inRegisterAddress, const uint8_t inValue) { mSPI.beginTransaction (mSPISettings) ; - turnOffInterrupts () ; +turnOffInterrupts () ; writeRegister8Assume_SPI_transaction (inRegisterAddress, inValue) ; - turnOnInterrupts () ; + turnOnInterrupts(); mSPI.endTransaction () ; } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ uint8_t ACAN2517FD::readRegister8 (const uint16_t inRegisterAddress) { mSPI.beginTransaction (mSPISettings) ; - turnOffInterrupts () ; +turnOffInterrupts () ; const uint8_t result = readRegister8Assume_SPI_transaction (inRegisterAddress) ; - turnOnInterrupts () ; + #ifdef ARDUINO_ARCH_ESP32 + taskENABLE_INTERRUPTS () ; + #else + interrupts () ; + #endif mSPI.endTransaction () ; return result ; } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ uint16_t ACAN2517FD::readRegister16 (const uint16_t inRegisterAddress) { mSPI.beginTransaction (mSPISettings) ; - turnOffInterrupts () ; +turnOffInterrupts () ; const uint16_t result = readRegister16Assume_SPI_transaction (inRegisterAddress) ; - turnOnInterrupts () ; + #ifdef ARDUINO_ARCH_ESP32 + taskENABLE_INTERRUPTS () ; + #else + interrupts () ; + #endif mSPI.endTransaction () ; return result ; } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ void ACAN2517FD::writeRegister32 (const uint16_t inRegisterAddress, const uint32_t inValue) { mSPI.beginTransaction (mSPISettings) ; - turnOffInterrupts () ; +turnOffInterrupts () ; writeRegister32Assume_SPI_transaction (inRegisterAddress, inValue) ; - turnOnInterrupts () ; + #ifdef ARDUINO_ARCH_ESP32 + taskENABLE_INTERRUPTS () ; + #else + interrupts () ; + #endif mSPI.endTransaction () ; } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ uint32_t ACAN2517FD::readRegister32 (const uint16_t inRegisterAddress) { mSPI.beginTransaction (mSPISettings) ; - turnOffInterrupts () ; +turnOffInterrupts () ; const uint32_t result = readRegister32Assume_SPI_transaction (inRegisterAddress) ; - turnOnInterrupts () ; + #ifdef ARDUINO_ARCH_ESP32 + taskENABLE_INTERRUPTS () ; + #else + interrupts () ; + #endif mSPI.endTransaction () ; return result ; } -//······················································································································ +//------------------------------------------------------------------------------ // Current MCP2517FD Operation Mode -//······················································································································ +//------------------------------------------------------------------------------ ACAN2517FDSettings::OperationMode ACAN2517FD::currentOperationMode (void) { const uint8_t mode = readRegister8 (CON_REGISTER + 2) >> 5 ; return ACAN2517FDSettings::OperationMode (mode) ; } -//······················································································································ +//------------------------------------------------------------------------------ bool ACAN2517FD::recoverFromRestrictedOperationMode (void) { bool recoveryDone = false ; @@ -1182,9 +1205,9 @@ bool ACAN2517FD::recoverFromRestrictedOperationMode (void) { return recoveryDone ; } -//······················································································································ +//------------------------------------------------------------------------------ // Set MCP2517FD Operation Mode -//······················································································································ +//------------------------------------------------------------------------------ void ACAN2517FD::setOperationMode (const ACAN2517FDSettings::OperationMode inOperationMode) { // bits 7-4: Transmit Bandwith Sharing Bits ---> 0 @@ -1192,22 +1215,26 @@ void ACAN2517FD::setOperationMode (const ACAN2517FDSettings::OperationMode inOpe writeRegister8 (CON_REGISTER + 3, uint8_t (inOperationMode)); } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ void ACAN2517FD::reset2517FD (void) { mSPI.beginTransaction (mSPISettings) ; // Check RESET is performed with 800 kHz clock - turnOffInterrupts () ; +turnOffInterrupts () ; assertCS () ; mSPI.transfer16 (0x00) ; // Reset instruction: 0x0000 deassertCS () ; - turnOnInterrupts () ; + #ifdef ARDUINO_ARCH_ESP32 + taskENABLE_INTERRUPTS () ; + #else + interrupts () ; + #endif mSPI.endTransaction () ; } -//······················································································································ +//------------------------------------------------------------------------------ // Sleep Mode to Configuration Mode // (returns true if MCP2517FD was in sleep mode) -//······················································································································ +//------------------------------------------------------------------------------ // The device exits Sleep mode due to a dominant edge on RXCAN or by enabling the oscillator (clearing OSC.OSCDIS). // The module will transition automatically to Configuration mode. @@ -1226,21 +1253,21 @@ bool ACAN2517FD::performSleepModeToConfigurationMode (void) { return inSleepMode ; } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ uint32_t ACAN2517FD::errorCounters (void) { return readRegister32 (TREC_REGISTER) ; } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ uint32_t ACAN2517FD::diagInfos (const int inIndex) { // thanks to Flole998 and turmary return readRegister32 (inIndex ? BDIAG1_REGISTER: BDIAG0_REGISTER) ; } -//······················································································································ +//------------------------------------------------------------------------------ // GPIO -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ void ACAN2517FD::gpioSetMode (const uint8_t inPin, const uint8_t inMode) { if (inPin <= 1) { @@ -1260,7 +1287,7 @@ void ACAN2517FD::gpioSetMode (const uint8_t inPin, const uint8_t inMode) { } } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ void ACAN2517FD::gpioWrite (const uint8_t inPin, const uint8_t inLevel) { if (inPin <= 1) { @@ -1274,14 +1301,14 @@ void ACAN2517FD::gpioWrite (const uint8_t inPin, const uint8_t inLevel) { } } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ bool ACAN2517FD::gpioRead (const uint8_t inPin) { const uint8_t value = readRegister8 (IOCON_REGISTER_16_23) ; return (value >> inPin) & 1 ; } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ void ACAN2517FD::configureGPIO0AsXSTBY (void) { uint8_t value = readRegister8 (IOCON_REGISTER_00_07) ; @@ -1289,4 +1316,4 @@ void ACAN2517FD::configureGPIO0AsXSTBY (void) { writeRegister8 (IOCON_REGISTER_00_07, value) ; } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ diff --git a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h index d9f85db7..779e7f7f 100644 --- a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h +++ b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h @@ -1,47 +1,36 @@ -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // A CAN driver for MCP2517FD, CANFD mode // by Pierre Molinaro // https://github.com/pierremolinaro/acan2517FD -// -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ #pragma once -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ -#include "ACAN2517FDSettings.h" -#include "ACANFDBuffer.h" -#include "CANMessage.h" -#include "ACAN2517FDFilters.h" +#include +#include +#include +#include #include -//---------------------------------------------------------------------------------------------------------------------- -// Settings -//---------------------------------------------------------------------------------------------------------------------- -// -// Enable this if you want to disable the MCP2517FD compatability mode. This can slightly increase performance when -// running on the MCP2518FD but you risk hitting issues mentioned in the MCP2517FD errata-sheet when using this option -// on the MCP2517FD. -// -#define DISABLEMCP2517FDCOMPAT - -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // ACAN2517FD class -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ class ACAN2517FD { -//······················································································································ -// CONSTRUCTOR -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // CONSTRUCTOR + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public: ACAN2517FD (const uint8_t inCS, // CS input of MCP2517FD SPIClass & inSPI, // Hardware SPI object const uint8_t inINT) ; // INT output of MCP2517FD -//······················································································································ -// begin method (returns 0 if no error) -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // begin method (returns 0 if no error) + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public: uint32_t begin (const ACAN2517FDSettings & inSettings, void (* inInterruptServiceRoutine) (void)) ; @@ -73,22 +62,22 @@ class ACAN2517FD { public: static const uint32_t kISRNotNullAndNoIntPin = uint32_t (1) << 19 ; public: static const uint32_t kInvalidTDCO = uint32_t (1) << 20 ; -//······················································································································ -// end method (resets the MCP2517FD, deallocate buffers, and detach interrupt pin) -// Return true if end method succeeds, and false otherwise -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // end method (resets the MCP2517FD, deallocate buffers, and detach interrupt pin) + // Return true if end method succeeds, and false otherwise + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public: bool end (void) ; -//······················································································································ -// Send a message -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send a message + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public: bool tryToSend (const CANFDMessage & inMessage) ; -//······················································································································ -// Receive a message -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Receive a message + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public: bool receive (CANFDMessage & outMessage) ; public: bool available (void) ; @@ -98,44 +87,44 @@ class ACAN2517FD { //--- Call back function array private: ACANFDCallBackRoutine * mCallBackFunctionArray = NULL ; -//······················································································································ -// Get error counters -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Get error counters + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public: uint32_t errorCounters (void) ; -//······················································································································ -// Get diagnostic information (thanks to Flole998 and turmary) -// inIndex == 0 returns BDIAG0_REGISTER -// inIndex != 0 returns BDIAG1_REGISTER -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Get diagnostic information (thanks to Flole998 and turmary) + // inIndex == 0 returns BDIAG0_REGISTER + // inIndex != 0 returns BDIAG1_REGISTER + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public: uint32_t diagInfos (const int inIndex = 1) ; -//······················································································································ -// Operation Mode -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Operation Mode + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public: ACAN2517FDSettings::OperationMode currentOperationMode (void) ; public: void setOperationMode (const ACAN2517FDSettings::OperationMode inMode) ; -//······················································································································ -// Recovery from Restricted Operation Mode -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Recovery from Restricted Operation Mode + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public: bool recoverFromRestrictedOperationMode (void) ; -//······················································································································ -// Sleep Mode to Configuration Mode -// (returns true if MCP2517FD was in sleep mode) -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Sleep Mode to Configuration Mode + // (returns true if MCP2517FD was in sleep mode) + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public: bool performSleepModeToConfigurationMode (void) ; -//······················································································································ -// Private properties -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Private properties + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #ifdef ARDUINO_ARCH_ESP32 private: TaskHandle_t mESP32TaskHandle = nullptr ; @@ -147,40 +136,51 @@ class ACAN2517FD { private: bool mUsesTXQ ; private: bool mHardwareTxFIFOFull ; private: bool mRxInterruptEnabled ; // Added in 2.1.7 - private: bool mHasDataBitRate ; private: uint8_t mTransmitFIFOPayload ; // in byte count private: uint8_t mTXQBufferPayload ; // in byte count private: uint8_t mReceiveFIFOPayload ; // in byte count private: uint8_t mTXBWS_RequestedMode ; private: uint8_t mHardwareReceiveBufferOverflowCount ; -//······················································································································ -// Receive buffer -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Receive buffer + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - private: ACANFDBuffer mDriverReceiveBuffer ; - public: uint32_t driverReceiveBufferPeakCount (void) const { return mDriverReceiveBuffer.peakCount () ; } + public: uint32_t driverReceiveBufferPeakCount (void) const { + return mDriverReceiveBuffer.peakCount () ; + } - public: uint8_t hardwareReceiveBufferOverflowCount (void) const { return mHardwareReceiveBufferOverflowCount ; } + public: uint8_t hardwareReceiveBufferOverflowCount (void) const { + return mHardwareReceiveBufferOverflowCount ; + } - public: void resetHardwareReceiveBufferOverflowCount (void) { mHardwareReceiveBufferOverflowCount = 0 ; } + public: void resetHardwareReceiveBufferOverflowCount (void) { + mHardwareReceiveBufferOverflowCount = 0 ; + } -//······················································································································ -// Transmit buffer -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Transmit buffer + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - private: ACANFDBuffer mDriverTransmitBuffer ; - public: uint32_t driverTransmitBufferSize (void) const { return mDriverTransmitBuffer.size () ; } + public: uint32_t driverTransmitBufferSize (void) const { + return mDriverTransmitBuffer.size () ; + } - public: uint32_t driverTransmitBufferCount (void) const { return mDriverTransmitBuffer.count () ; } + public: uint32_t driverTransmitBufferCount (void) const { + return mDriverTransmitBuffer.count () ; + } - public: uint32_t driverTransmitBufferPeakCount (void) const { return mDriverTransmitBuffer.peakCount () ; } + public: uint32_t driverTransmitBufferPeakCount (void) const { + return mDriverTransmitBuffer.peakCount () ; + } -//······················································································································ -// Private methods -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Private methods + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - private: void writeRegister32Assume_SPI_transaction (const uint16_t inRegisterAddress, const uint32_t inValue) ; private: void writeRegister8Assume_SPI_transaction (const uint16_t inRegisterAddress, const uint8_t inValue) ; @@ -202,15 +202,15 @@ class ACAN2517FD { private: bool enterInTransmitBuffer (const CANFDMessage & inMessage) ; private: void appendInControllerTxFIFO (const CANFDMessage & inMessage) ; -//······················································································································ -// Polling -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Polling + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public: void poll (void) ; -//······················································································································ -// Interrupt service routine -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Interrupt service routine + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public: void isr (void) ; public: void isr_poll_core (void) ; @@ -220,9 +220,9 @@ class ACAN2517FD { public: SemaphoreHandle_t mISRSemaphore ; #endif -//---------------------------------------------------------------------------------------------------------------------- -// Optimized CS handling (thanks to Flole998) -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Optimized CS handling (thanks to Flole998) + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #if defined(__AVR__) private: volatile uint8_t *cs_pin_reg; @@ -293,10 +293,8 @@ class ACAN2517FD { *(cs_pin_reg+8+2) = cs_pin_mask; } #elif defined(ARDUINO_ARCH_ESP8266) - // private: volatile uint32_t *cs_pin_reg; private: uint32_t cs_pin_mask; private: inline void initCS () { - // cs_pin_reg = (volatile uint32_t*)GPO; cs_pin_mask = 1 << mCS; pinMode(mCS, OUTPUT); } @@ -333,9 +331,9 @@ class ACAN2517FD { } #endif -//······················································································································ -// GPIO -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // GPIO + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public: void gpioSetMode (const uint8_t inPin, const uint8_t inMode) ; @@ -345,16 +343,16 @@ class ACAN2517FD { public: void configureGPIO0AsXSTBY (void) ; -//······················································································································ -// No copy -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // No copy + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - private: ACAN2517FD (const ACAN2517FD &) = delete ; private: ACAN2517FD & operator = (const ACAN2517FD &) = delete ; -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - } ; -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ diff --git a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDFilters.h b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDFilters.h index 69895acb..632d2f76 100644 --- a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDFilters.h +++ b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDFilters.h @@ -1,27 +1,27 @@ -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // An utility class for: // - ACAN2517FD CAN driver for MCP2517FD (CANFD mode) // by Pierre Molinaro // https://github.com/pierremolinaro/acan2517FD // -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ #ifndef ACAN2517FD_FILTERS_CLASS_DEFINED #define ACAN2517FD_FILTERS_CLASS_DEFINED -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ -#include "CANFDMessage.h" +#include -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // ACAN2517FDFilters class -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ class ACAN2517FDFilters { -//······················································································································ +//------------------------------------------------------------------------------ // EMBEDDED CLASS -//······················································································································ +//------------------------------------------------------------------------------ private: class Filter { public: Filter * mNextFilter ; @@ -43,9 +43,9 @@ class ACAN2517FDFilters { private: Filter & operator = (const Filter &) ; } ; -//······················································································································ +//------------------------------------------------------------------------------ // ENUMERATED TYPE -//······················································································································ +//------------------------------------------------------------------------------ public: typedef enum { kFiltersOk, @@ -58,15 +58,15 @@ class ACAN2517FDFilters { kInconsistencyBetweenMaskAndAcceptance } FilterStatus ; -//······················································································································ +//------------------------------------------------------------------------------ // CONSTRUCTOR -//······················································································································ +//------------------------------------------------------------------------------ public: ACAN2517FDFilters (void) {} -//······················································································································ +//------------------------------------------------------------------------------ // DESTRUCTOR -//······················································································································ +//------------------------------------------------------------------------------ public: ~ ACAN2517FDFilters (void) { while (mFirstFilter != NULL) { @@ -76,9 +76,9 @@ class ACAN2517FDFilters { } } -//······················································································································ +//------------------------------------------------------------------------------ // RECEIVE FILTERS -//······················································································································ +//------------------------------------------------------------------------------ public: void appendPassAllFilter (const ACANFDCallBackRoutine inCallBackRoutine) { // Accept any frame Filter * f = new Filter (0, 0, inCallBackRoutine) ; @@ -91,7 +91,7 @@ class ACAN2517FDFilters { mFilterCount += 1 ; } -//······················································································································ +//------------------------------------------------------------------------------ public: void appendFormatFilter (const tFrameFormat inFormat, // Accept any identifier const ACANFDCallBackRoutine inCallBackRoutine) { @@ -107,7 +107,7 @@ class ACAN2517FDFilters { mFilterCount += 1 ; } -//······················································································································ +//------------------------------------------------------------------------------ public: void appendFrameFilter (const tFrameFormat inFormat, const uint32_t inIdentifier, @@ -141,7 +141,7 @@ class ACAN2517FDFilters { mFilterCount += 1 ; } -//······················································································································ +//------------------------------------------------------------------------------ public: void appendFilter (const tFrameFormat inFormat, const uint32_t inMask, @@ -196,9 +196,9 @@ class ACAN2517FDFilters { mFilterCount += 1 ; } -//······················································································································ +//------------------------------------------------------------------------------ // ACCESSORS -//······················································································································ +//------------------------------------------------------------------------------ public: FilterStatus filterStatus (void) const { return mFilterStatus ; } @@ -206,9 +206,9 @@ class ACAN2517FDFilters { public: uint8_t filterCount (void) const { return mFilterCount ; } -//······················································································································ +//------------------------------------------------------------------------------ // PRIVATE PROPERTIES -//······················································································································ +//------------------------------------------------------------------------------ private: uint8_t mFilterCount = 0 ; private: Filter * mFirstFilter = NULL ; @@ -216,23 +216,23 @@ class ACAN2517FDFilters { private: FilterStatus mFilterStatus = kFiltersOk ; private: uint8_t mFilterErrorIndex = 0 ; -//······················································································································ +//------------------------------------------------------------------------------ // NO COPY -//······················································································································ +//------------------------------------------------------------------------------ private: ACAN2517FDFilters (const ACAN2517FDFilters &) = delete ; private: ACAN2517FDFilters & operator = (const ACAN2517FDFilters &) = delete ; -//······················································································································ +//------------------------------------------------------------------------------ // Friend -//······················································································································ +//------------------------------------------------------------------------------ friend class ACAN2517FD ; -//······················································································································ +//------------------------------------------------------------------------------ } ; -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ #endif diff --git a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDSettings.cpp b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDSettings.cpp index 0a97683a..4c5ccbd3 100644 --- a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDSettings.cpp +++ b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDSettings.cpp @@ -1,19 +1,19 @@ -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // A CAN driver for MCP2517FD (CANFD mode) // by Pierre Molinaro // https://github.com/pierremolinaro/acan2517FD // -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ -#include "ACAN2517FDSettings.h" +#include -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ #pragma GCC diagnostic error "-Wswitch-enum" -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // sysClock -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ uint32_t ACAN2517FDSettings::sysClock (const Oscillator inOscillator) { uint32_t sysClock = 40UL * 1000 * 1000 ; @@ -39,9 +39,9 @@ uint32_t ACAN2517FDSettings::sysClock (const Oscillator inOscillator) { return sysClock ; } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // CONSTRUCTOR -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ ACAN2517FDSettings::ACAN2517FDSettings (const Oscillator inOscillator, const uint32_t inDesiredArbitrationBitRate, @@ -51,148 +51,93 @@ mOscillator (inOscillator), mSysClock (sysClock (inOscillator)), mDesiredArbitrationBitRate (inDesiredArbitrationBitRate), mDataBitRateFactor (inDataBitRateFactor) { - if (inDataBitRateFactor == DataBitRateFactor::x1) { // Single bit rate - const uint32_t maxTQCount = MAX_ARBITRATION_PHASE_SEGMENT_1 + MAX_ARBITRATION_PHASE_SEGMENT_2 + 1 ; // Setting for slowest bit rate - uint32_t BRP = MAX_BRP ; - uint32_t smallestError = UINT32_MAX ; - uint32_t bestBRP = 1 ; // Setting for highest bit rate - uint32_t bestTQCount = 4 ; // Setting for highest bit rate - uint32_t TQCount = mSysClock / inDesiredArbitrationBitRate / BRP ; - //--- Loop for finding best BRP and best TQCount - while ((TQCount <= (MAX_ARBITRATION_PHASE_SEGMENT_1 + MAX_ARBITRATION_PHASE_SEGMENT_2 + 1)) && (BRP > 0)) { - //--- Compute error using TQCount - if ((TQCount >= 4) && (TQCount <= maxTQCount)) { - const uint32_t error = mSysClock - inDesiredArbitrationBitRate * TQCount * BRP ; // error is always >= 0 - if (error <= smallestError) { - smallestError = error ; - bestBRP = BRP ; - bestTQCount = TQCount ; - } +// First compute data bit rate + const uint32_t maxDataTQCount = MAX_DATA_PHASE_SEGMENT_1 + MAX_DATA_PHASE_SEGMENT_2 ; // Setting for slowest bit rate + const uint32_t desiredDataBitRate = inDesiredArbitrationBitRate * uint8_t (inDataBitRateFactor) ; + uint32_t smallestError = UINT32_MAX ; + uint32_t bestBRP = MAX_BRP ; // Setting for lowest bit rate + uint32_t bestDataTQCount = maxDataTQCount ; // Setting for lowest bit rate + uint32_t dataTQCount = 4 ; + uint32_t brp = mSysClock / desiredDataBitRate / dataTQCount ; +//--- Loop for finding best BRP and best TQCount + while ((dataTQCount <= maxDataTQCount) && (brp > 0)) { + //--- Compute error using brp + if (brp <= MAX_BRP) { + const uint32_t error = mSysClock - desiredDataBitRate * dataTQCount * brp ; // error is always >= 0 + if (error <= smallestError) { + smallestError = error ; + bestBRP = brp ; + bestDataTQCount = dataTQCount ; } - //--- Compute error using TQCount+1 - if ((TQCount >= 3) && (TQCount < maxTQCount)) { - const uint32_t error = inDesiredArbitrationBitRate * (TQCount + 1) * BRP - mSysClock ; // error is always >= 0 - if (error <= smallestError) { - smallestError = error ; - bestBRP = BRP ; - bestTQCount = TQCount + 1 ; - } + } + //--- Compute error using brp+1 + if (brp < MAX_BRP) { + const uint32_t error = desiredDataBitRate * dataTQCount * (brp + 1) - mSysClock ; // error is always >= 0 + if (error <= smallestError) { + smallestError = error ; + bestBRP = brp + 1 ; + bestDataTQCount = dataTQCount ; } - //--- Continue with next value of BRP - BRP -- ; - TQCount = (BRP == 0) ? (maxTQCount + 1) : (mSysClock / inDesiredArbitrationBitRate / BRP) ; } - //--- Compute PS2 (1 <= PS2 <= 128) - uint32_t PS2 = bestTQCount / 5 ; // For sampling point at 80% - if (PS2 == 0) { - PS2 = 1 ; - }else if (PS2 > MAX_ARBITRATION_PHASE_SEGMENT_2) { - PS2 = MAX_ARBITRATION_PHASE_SEGMENT_2 ; - } - //--- Compute PS1 (1 <= PS1 <= 256) - uint32_t PS1 = bestTQCount - PS2 - 1 /* Sync Seg */ ; - if (PS1 > MAX_ARBITRATION_PHASE_SEGMENT_1) { - PS2 += PS1 - MAX_ARBITRATION_PHASE_SEGMENT_1 ; - PS1 = MAX_ARBITRATION_PHASE_SEGMENT_1 ; - } - //--- - mBitRatePrescaler = (uint16_t) bestBRP ; - mArbitrationPhaseSegment1 = (uint16_t) PS1 ; - mArbitrationPhaseSegment2 = (uint8_t) PS2 ; - mArbitrationSJW = mArbitrationPhaseSegment2 ; // Always 1 <= SJW <= 128, and SJW <= mArbitrationPhaseSegment2 - //--- Final check of the nominal configuration - const uint32_t W = bestTQCount * mDesiredArbitrationBitRate * bestBRP ; - const uint64_t diff = (mSysClock > W) ? (mSysClock - W) : (W - mSysClock) ; - const uint64_t ppm = (uint64_t) (1000UL * 1000UL) ; // UL suffix is required for Arduino Uno - mArbitrationBitRateClosedToDesiredRate = (diff * ppm) <= (((uint64_t) W) * inTolerancePPM) ; - }else{ // Dual bit rate, first compute data bit rate - const uint32_t maxDataTQCount = MAX_DATA_PHASE_SEGMENT_1 + MAX_DATA_PHASE_SEGMENT_2 ; // Setting for slowest bit rate - const uint32_t desiredDataBitRate = inDesiredArbitrationBitRate * uint8_t (inDataBitRateFactor) ; - uint32_t smallestError = UINT32_MAX ; - uint32_t bestBRP = MAX_BRP ; // Setting for lowest bit rate - uint32_t bestDataTQCount = maxDataTQCount ; // Setting for lowest bit rate - uint32_t dataTQCount = 4 ; - uint32_t brp = mSysClock / desiredDataBitRate / dataTQCount ; - //--- Loop for finding best BRP and best TQCount - while ((dataTQCount <= maxDataTQCount) && (brp > 0)) { - //--- Compute error using brp - if (brp <= MAX_BRP) { - const uint32_t error = mSysClock - desiredDataBitRate * dataTQCount * brp ; // error is always >= 0 - if (error <= smallestError) { - smallestError = error ; - bestBRP = brp ; - bestDataTQCount = dataTQCount ; - } - } - //--- Compute error using brp+1 - if (brp < MAX_BRP) { - const uint32_t error = desiredDataBitRate * dataTQCount * (brp + 1) - mSysClock ; // error is always >= 0 - if (error <= smallestError) { - smallestError = error ; - bestBRP = brp + 1 ; - bestDataTQCount = dataTQCount ; - } - } - //--- Continue with next value of BRP - dataTQCount += 1 ; - brp = mSysClock / desiredDataBitRate / dataTQCount ; - } - //--- Compute data PS2 (1 <= PS2 <= 16) - uint32_t dataPS2 = bestDataTQCount / 5 ; // For sampling point at 80% - if (dataPS2 == 0) { - dataPS2 = 1 ; - } - //--- Compute data PS1 (1 <= PS1 <= 32) - uint32_t dataPS1 = bestDataTQCount - dataPS2 - 1 /* Sync Seg */ ; - if (dataPS1 > MAX_DATA_PHASE_SEGMENT_1) { - dataPS2 += dataPS1 - MAX_DATA_PHASE_SEGMENT_1 ; - dataPS1 = MAX_DATA_PHASE_SEGMENT_1 ; - } - //--- - if ((mDesiredArbitrationBitRate * uint32_t (inDataBitRateFactor)) <= (1000UL * 1000)) { - mTDCO = 0 ; - }else{ - const int TDCO = bestBRP * dataPS1 ; // According to DS20005678D, §3.4.8 Page 20 - mTDCO = (TDCO > 63) ? 63 : (int8_t) TDCO ; - } - mDataPhaseSegment1 = (uint8_t) dataPS1 ; - mDataPhaseSegment2 = (uint8_t) dataPS2 ; - mDataSJW = mDataPhaseSegment2 ; - const uint32_t arbitrationTQCount = bestDataTQCount * uint8_t (mDataBitRateFactor) ; - //--- Compute arbiration PS2 (1 <= PS2 <= 128) - uint32_t arbitrationPS2 = arbitrationTQCount / 5 ; // For sampling point at 80% - if (arbitrationPS2 == 0) { - arbitrationPS2 = 1 ; - } - //--- Compute PS1 (1 <= PS1 <= 256) - uint32_t arbitrationPS1 = arbitrationTQCount - arbitrationPS2 - 1 /* Sync Seg */ ; - if (arbitrationPS1 > MAX_ARBITRATION_PHASE_SEGMENT_1) { - arbitrationPS2 += arbitrationPS1 - MAX_ARBITRATION_PHASE_SEGMENT_1 ; - arbitrationPS1 = MAX_ARBITRATION_PHASE_SEGMENT_1 ; - } - //--- - mBitRatePrescaler = (uint16_t) bestBRP ; - mArbitrationPhaseSegment1 = (uint16_t) arbitrationPS1 ; - mArbitrationPhaseSegment2 = (uint8_t) arbitrationPS2 ; - mArbitrationSJW = mArbitrationPhaseSegment2 ; // Always 1 <= SJW <= 128, and SJW <= mArbitrationPhaseSegment2 - //--- Final check of the nominal configuration - const uint32_t W = arbitrationTQCount * mDesiredArbitrationBitRate * bestBRP ; - const uint64_t diff = (mSysClock > W) ? (mSysClock - W) : (W - mSysClock) ; - const uint64_t ppm = (uint64_t) (1000UL * 1000UL) ; // UL suffix is required for Arduino Uno - mArbitrationBitRateClosedToDesiredRate = (diff * ppm) <= (((uint64_t) W) * inTolerancePPM) ; + //--- Continue with next value of BRP + dataTQCount += 1 ; + brp = mSysClock / desiredDataBitRate / dataTQCount ; } +//--- Compute data PS2 (1 <= PS2 <= 16) + uint32_t dataPS2 = bestDataTQCount / 5 ; // For sampling point at 80% + if (dataPS2 == 0) { + dataPS2 = 1 ; + } +//--- Compute data PS1 (1 <= PS1 <= 32) + uint32_t dataPS1 = bestDataTQCount - dataPS2 - 1 /* Sync Seg */ ; + if (dataPS1 > MAX_DATA_PHASE_SEGMENT_1) { + dataPS2 += dataPS1 - MAX_DATA_PHASE_SEGMENT_1 ; + dataPS1 = MAX_DATA_PHASE_SEGMENT_1 ; + } +//--- + if ((mDesiredArbitrationBitRate * uint32_t (inDataBitRateFactor)) <= (1000UL * 1000)) { + mTDCO = 0 ; + }else{ + const int TDCO = bestBRP * dataPS1 ; // According to DS20005678D, §3.4.8 Page 20 + mTDCO = (TDCO > 63) ? 63 : (int8_t) TDCO ; + } + mDataPhaseSegment1 = (uint8_t) dataPS1 ; + mDataPhaseSegment2 = (uint8_t) dataPS2 ; + mDataSJW = mDataPhaseSegment2 ; + const uint32_t arbitrationTQCount = bestDataTQCount * uint8_t (mDataBitRateFactor) ; +//--- Compute arbitration PS2 (1 <= PS2 <= 128) + uint32_t arbitrationPS2 = arbitrationTQCount / 5 ; // For sampling point at 80% + if (arbitrationPS2 == 0) { + arbitrationPS2 = 1 ; + } +//--- Compute PS1 (1 <= PS1 <= 256) + uint32_t arbitrationPS1 = arbitrationTQCount - arbitrationPS2 - 1 /* Sync Seg */ ; + if (arbitrationPS1 > MAX_ARBITRATION_PHASE_SEGMENT_1) { + arbitrationPS2 += arbitrationPS1 - MAX_ARBITRATION_PHASE_SEGMENT_1 ; + arbitrationPS1 = MAX_ARBITRATION_PHASE_SEGMENT_1 ; + } +//--- + mBitRatePrescaler = (uint16_t) bestBRP ; + mArbitrationPhaseSegment1 = (uint16_t) arbitrationPS1 ; + mArbitrationPhaseSegment2 = (uint8_t) arbitrationPS2 ; + mArbitrationSJW = mArbitrationPhaseSegment2 ; // Always 1 <= SJW <= 128, and SJW <= mArbitrationPhaseSegment2 +//--- Final check of the nominal configuration + const uint32_t W = arbitrationTQCount * mDesiredArbitrationBitRate * bestBRP ; + const uint64_t diff = (mSysClock > W) ? (mSysClock - W) : (W - mSysClock) ; + const uint64_t ppm = (uint64_t) (1000UL * 1000UL) ; // UL suffix is required for Arduino Uno + mArbitrationBitRateClosedToDesiredRate = (diff * ppm) <= (((uint64_t) W) * inTolerancePPM) ; } ; -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // ACCESSORS -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ uint32_t ACAN2517FDSettings::actualArbitrationBitRate (void) const { const uint32_t arbitrationTQCount = 1 /* Sync Seg */ + mArbitrationPhaseSegment1 + mArbitrationPhaseSegment2 ; return mSysClock / mBitRatePrescaler / arbitrationTQCount ; } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ uint32_t ACAN2517FDSettings::actualDataBitRate (void) const { if (mDataBitRateFactor == DataBitRateFactor::x1) { @@ -203,14 +148,14 @@ uint32_t ACAN2517FDSettings::actualDataBitRate (void) const { } } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ bool ACAN2517FDSettings::exactArbitrationBitRate (void) const { const uint32_t TQCount = 1 /* Sync Seg */ + mArbitrationPhaseSegment1 + mArbitrationPhaseSegment2 ; return mSysClock == (mBitRatePrescaler * mDesiredArbitrationBitRate * TQCount) ; } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ bool ACAN2517FDSettings::exactDataBitRate (void) const { if (mDataBitRateFactor == DataBitRateFactor::x1) { @@ -221,7 +166,7 @@ bool ACAN2517FDSettings::exactDataBitRate (void) const { } } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ bool ACAN2517FDSettings::dataBitRateIsAMultipleOfArbitrationBitRate (void) const { bool result = mDataBitRateFactor == DataBitRateFactor::x1 ; @@ -233,7 +178,7 @@ bool ACAN2517FDSettings::dataBitRateIsAMultipleOfArbitrationBitRate (void) const return result ; } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ uint32_t ACAN2517FDSettings::ppmFromDesiredArbitrationBitRate (void) const { const uint32_t TQCount = 1 /* Sync Seg */ + mArbitrationPhaseSegment1 + mArbitrationPhaseSegment2 ; @@ -243,7 +188,7 @@ uint32_t ACAN2517FDSettings::ppmFromDesiredArbitrationBitRate (void) const { return (uint32_t) ((diff * ppm) / W) ; } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ uint32_t ACAN2517FDSettings::arbitrationSamplePointFromBitStart (void) const { const uint32_t nominalTQCount = 1 /* Sync Seg */ + mArbitrationPhaseSegment1 + mArbitrationPhaseSegment2 ; @@ -252,7 +197,7 @@ uint32_t ACAN2517FDSettings::arbitrationSamplePointFromBitStart (void) const { return (samplePoint * partPerCent) / nominalTQCount ; } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ uint32_t ACAN2517FDSettings::dataSamplePointFromBitStart (void) const { const uint32_t nominalTQCount = 1 /* Sync Seg */ + mDataPhaseSegment1 + mDataPhaseSegment2 ; @@ -261,7 +206,7 @@ uint32_t ACAN2517FDSettings::dataSamplePointFromBitStart (void) const { return (samplePoint * partPerCent) / nominalTQCount ; } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ uint32_t ACAN2517FDSettings::CANBitSettingConsistency (void) const { uint32_t errorCode = 0 ; // Means no error @@ -329,9 +274,9 @@ uint32_t ACAN2517FDSettings::CANBitSettingConsistency (void) const { return errorCode ; } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // RAM USAGE -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ uint32_t ACAN2517FDSettings::ramUsage (void) const { uint32_t result = 0 ; @@ -345,11 +290,11 @@ uint32_t ACAN2517FDSettings::ramUsage (void) const { return result ; } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ uint32_t ACAN2517FDSettings::objectSizeForPayload (const PayloadSize inPayload) { static const uint8_t kPayload [8] = {16, 20, 24, 28, 32, 40, 56, 72} ; return kPayload [inPayload] ; } -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ diff --git a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDSettings.h b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDSettings.h index ec86560c..0cc79802 100644 --- a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDSettings.h +++ b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDSettings.h @@ -1,25 +1,25 @@ -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // A CAN driver for MCP2517FD (CANFD mode) // by Pierre Molinaro // https://github.com/pierremolinaro/acan2517FD // -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ #pragma once -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ -#include "ACANFD_DataBitRateFactor.h" +#include -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // ACAN2517FDSettings class -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ class ACAN2517FDSettings { -//······················································································································ -// ENUMERATED TYPES -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // ENUMERATED TYPES + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public: typedef enum : uint8_t { OSC_4MHz, @@ -64,9 +64,9 @@ class ACAN2517FDSettings { PAYLOAD_64 = 7 } PayloadSize ; -//······················································································································ -// Deprecated enumeration (now use DataBitRateFactor declared in ACANFD_DataBitRateFactor.h) -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Deprecated enumeration (now use DataBitRateFactor declared in ACANFD_DataBitRateFactor.h) + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public : typedef enum : uint8_t { DATA_BITRATE_x1 = 1, @@ -81,18 +81,18 @@ class ACAN2517FDSettings { DATA_BITRATE_x10 = 10 } DataBitRateFactor_Deprecated ; -//······················································································································ -// CONSTRUCTOR -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // CONSTRUCTOR + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public: ACAN2517FDSettings (const Oscillator inOscillator, const uint32_t inDesiredArbitrationBitRate, const DataBitRateFactor inDataBitRateFactor, const uint32_t inTolerancePPM = 1000) ; -//······················································································································ -// DEPRECATED CONSTRUCTOR (for compatibility with version < 2.1.0) -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // DEPRECATED CONSTRUCTOR (for compatibility with version < 2.1.0) + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public: ACAN2517FDSettings (const Oscillator inOscillator, const uint32_t inDesiredArbitrationBitRate, @@ -101,9 +101,9 @@ class ACAN2517FDSettings { ACAN2517FDSettings (inOscillator, inDesiredArbitrationBitRate, DataBitRateFactor (inDataBitRateFactor), inTolerancePPM) { } -//······················································································································ -// CAN BIT TIMING -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // CAN BIT TIMING + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - private: Oscillator mOscillator ; private: uint32_t mSysClock ; // In Hz @@ -123,44 +123,44 @@ class ACAN2517FDSettings { //--- Transmitter Delay Compensation Offset public: int8_t mTDCO = 0 ; // -64 ... +63 -//······················································································································ -// MCP2517FD TXCAN pin is Open Drain ? -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // MCP2517FD TXCAN pin is Open Drain ? + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public: bool mTXCANIsOpenDrain = false ; // false --> Push/Pull Output, true --> Open Drain Output -//······················································································································ -// MCP2517FD INT pin is Open Drain ? -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // MCP2517FD INT pin is Open Drain ? + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public: bool mINTIsOpenDrain = false ; // false --> Push/Pull Output, true --> Open Drain Output -//······················································································································ -// ISO CRC Enable -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // ISO CRC Enable + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // false --> Do NOT include Stuff Bit Count in CRC Field and use CRC Initialization Vector with all zeros // true --> Include Stuff Bit Count in CRC Field and use Non-Zero CRC Initialization Vector according to ISO 11898-1:2015 public: bool mISOCRCEnabled = true ; -//······················································································································ -// CLKO pin function (default value is MCP2517FD power on setting) -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // CLKO pin function (default value is MCP2517FD power on setting) + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public: CLKOpin mCLKOPin = CLKO_DIVIDED_BY_10 ; -//······················································································································ -// Requested mode -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Requested mode + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public: OperationMode mRequestedMode = NormalFD ; -//······················································································································ -// TRANSMIT FIFO -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // TRANSMIT FIFO + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--- Driver transmit buffer size - public: uint16_t mDriverTransmitFIFOSize = 22 ; // >= 0 + public: uint16_t mDriverTransmitFIFOSize = 16 ; // >= 0 //--- Controller transmit FIFO size public: uint8_t mControllerTransmitFIFOSize = 1 ; // 1 ... 32 @@ -174,9 +174,9 @@ class ACAN2517FDSettings { //--- Controller transmit FIFO retransmission attempts public: RetransmissionAttempts mControllerTransmitFIFORetransmissionAttempts = UnlimitedNumber ; -//······················································································································ -// TXQ BUFFER -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // TXQ BUFFER + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--- TXQ buffer size (0 --> TXQ disabled) public: uint8_t mControllerTXQSize = 0 ; // 0 ... 32 @@ -191,9 +191,9 @@ class ACAN2517FDSettings { public: RetransmissionAttempts mControllerTXQBufferRetransmissionAttempts = UnlimitedNumber ; -//······················································································································ -// RECEIVE FIFO -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // RECEIVE FIFO + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //--- Driver receive buffer size public: uint16_t mDriverReceiveFIFOSize = 32 ; // > 0 @@ -204,15 +204,15 @@ class ACAN2517FDSettings { //--- Controller receive FIFO size public: uint8_t mControllerReceiveFIFOSize = 27 ; // 1 ... 32 -//······················································································································ -// SYSCLOCK frequency computation -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // SYSCLOCK frequency computation + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public: static uint32_t sysClock (const Oscillator inOscillator) ; -//······················································································································ -// Accessors -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Accessors + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public: Oscillator oscillator (void) const { return mOscillator ; } public: uint32_t sysClock (void) const { return mSysClock ; } @@ -222,61 +222,61 @@ class ACAN2517FDSettings { public: bool exactDataBitRate (void) const ; public: bool dataBitRateIsAMultipleOfArbitrationBitRate (void) const ; -//······················································································································ -// RAM USAGE -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // RAM USAGE + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public: uint32_t ramUsage (void) const ; public: static uint32_t objectSizeForPayload (const PayloadSize inPayload) ; -//······················································································································ -// Distance between actual bit rate and requested bit rate (in ppm, part-per-million) -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Distance between actual bit rate and requested bit rate (in ppm, part-per-million) + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public: uint32_t ppmFromDesiredArbitrationBitRate (void) const ; public: uint32_t ppmFromDesiredDataBitRate (void) const ; -//······················································································································ -// Distance of sample point from bit start (in ppc, part-per-cent, denoted by %) -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Distance of sample point from bit start (in ppc, part-per-cent, denoted by %) + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public: uint32_t arbitrationSamplePointFromBitStart (void) const ; public: uint32_t dataSamplePointFromBitStart (void) const ; -//······················································································································ -// Bit settings are consistent ? (returns 0 if ok) -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Bit settings are consistent ? (returns 0 if ok) + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public: uint32_t CANBitSettingConsistency (void) const ; -//······················································································································ -// Constants returned by CANBitSettingConsistency -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Constants returned by CANBitSettingConsistency + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public: static const uint32_t kBitRatePrescalerIsZero = ((uint32_t) 1) << 0 ; - public: static const uint32_t kBitRatePrescalerIsGreaterThan256 = ((uint32_t) 1) << 1 ; - public: static const uint32_t kArbitrationPhaseSegment1IsLowerThan2 = ((uint32_t) 1) << 2 ; - public: static const uint32_t kArbitrationPhaseSegment1IsGreaterThan256 = ((uint32_t) 1) << 3 ; - public: static const uint32_t kArbitrationPhaseSegment2IsZero = ((uint32_t) 1) << 4 ; - public: static const uint32_t kArbitrationPhaseSegment2IsGreaterThan128 = ((uint32_t) 1) << 5 ; - public: static const uint32_t kArbitrationSJWIsZero = ((uint32_t) 1) << 6 ; - public: static const uint32_t kArbitrationSJWIsGreaterThan128 = ((uint32_t) 1) << 7 ; - public: static const uint32_t kArbitrationSJWIsGreaterThanPhaseSegment1 = ((uint32_t) 1) << 8 ; - public: static const uint32_t kArbitrationSJWIsGreaterThanPhaseSegment2 = ((uint32_t) 1) << 9 ; - public: static const uint32_t kArbitrationTQCountNotDivisibleByDataBitRateFactor = ((uint32_t) 1) << 10 ; - public: static const uint32_t kDataPhaseSegment1IsLowerThan2 = ((uint32_t) 1) << 11 ; - public: static const uint32_t kDataPhaseSegment1IsGreaterThan32 = ((uint32_t) 1) << 12 ; - public: static const uint32_t kDataPhaseSegment2IsZero = ((uint32_t) 1) << 13 ; - public: static const uint32_t kDataPhaseSegment2IsGreaterThan16 = ((uint32_t) 1) << 14 ; - public: static const uint32_t kDataSJWIsZero = ((uint32_t) 1) << 15 ; - public: static const uint32_t kDataSJWIsGreaterThan16 = ((uint32_t) 1) << 16 ; - public: static const uint32_t kDataSJWIsGreaterThanPhaseSegment1 = ((uint32_t) 1) << 17 ; - public: static const uint32_t kDataSJWIsGreaterThanPhaseSegment2 = ((uint32_t) 1) << 18 ; + public: static const uint32_t kBitRatePrescalerIsZero = uint32_t (1) << 0 ; + public: static const uint32_t kBitRatePrescalerIsGreaterThan256 = uint32_t (1) << 1 ; + public: static const uint32_t kArbitrationPhaseSegment1IsLowerThan2 = uint32_t (1) << 2 ; + public: static const uint32_t kArbitrationPhaseSegment1IsGreaterThan256 = uint32_t (1) << 3 ; + public: static const uint32_t kArbitrationPhaseSegment2IsZero = uint32_t (1) << 4 ; + public: static const uint32_t kArbitrationPhaseSegment2IsGreaterThan128 = uint32_t (1) << 5 ; + public: static const uint32_t kArbitrationSJWIsZero = uint32_t (1) << 6 ; + public: static const uint32_t kArbitrationSJWIsGreaterThan128 = uint32_t (1) << 7 ; + public: static const uint32_t kArbitrationSJWIsGreaterThanPhaseSegment1 = uint32_t (1) << 8 ; + public: static const uint32_t kArbitrationSJWIsGreaterThanPhaseSegment2 = uint32_t (1) << 9 ; + public: static const uint32_t kArbitrationTQCountNotDivisibleByDataBitRateFactor = uint32_t (1) << 10 ; + public: static const uint32_t kDataPhaseSegment1IsLowerThan2 = uint32_t (1) << 11 ; + public: static const uint32_t kDataPhaseSegment1IsGreaterThan32 = uint32_t (1) << 12 ; + public: static const uint32_t kDataPhaseSegment2IsZero = uint32_t (1) << 13 ; + public: static const uint32_t kDataPhaseSegment2IsGreaterThan16 = uint32_t (1) << 14 ; + public: static const uint32_t kDataSJWIsZero = uint32_t (1) << 15 ; + public: static const uint32_t kDataSJWIsGreaterThan16 = uint32_t (1) << 16 ; + public: static const uint32_t kDataSJWIsGreaterThanPhaseSegment1 = uint32_t (1) << 17 ; + public: static const uint32_t kDataSJWIsGreaterThanPhaseSegment2 = uint32_t (1) << 18 ; -//······················································································································ -// Max values -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Max values + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public: static const uint16_t MAX_BRP = 256 ; @@ -288,9 +288,9 @@ class ACAN2517FDSettings { public: static const uint8_t MAX_DATA_PHASE_SEGMENT_2 = 16 ; public: static const uint8_t MAX_DATA_SJW = 16 ; -//······················································································································ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - } ; -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ diff --git a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD_ACANFDBuffer.h b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD_ACANFDBuffer.h new file mode 100644 index 00000000..cdbf59b4 --- /dev/null +++ b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD_ACANFDBuffer.h @@ -0,0 +1,122 @@ +//------------------------------------------------------------------------------ +// A CAN driver for MCP2517FD CAN Controller in CANFD mode +// by Pierre Molinaro +// https://github.com/pierremolinaro/acan2517FD +// +//------------------------------------------------------------------------------ + +#ifndef ACANFD_BUFFER_CLASS_DEFINED +#define ACANFD_BUFFER_CLASS_DEFINED + +//------------------------------------------------------------------------------ + +#include + +//------------------------------------------------------------------------------ + +class ACANFDBuffer { + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Default constructor + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + public: ACANFDBuffer (void) : + mBuffer (NULL), + mSize (0), + mReadIndex (0), + mCount (0), + mPeakCount (0) { + } + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Destructor + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + public: ~ ACANFDBuffer (void) { + delete [] mBuffer ; + } + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Private properties + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + private: CANFDMessage * mBuffer ; + private: uint32_t mSize ; + private: uint32_t mReadIndex ; + private: uint32_t mCount ; + private: uint32_t mPeakCount ; // > mSize if overflow did occur + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Accessors + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + public: inline uint32_t size (void) const { return mSize ; } + public: inline uint32_t count (void) const { return mCount ; } + public: inline bool isFull (void) const { return mCount == mSize ; } // Added in release 2.17 (thanks to Flole998) + public: inline uint32_t peakCount (void) const { return mPeakCount ; } + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // initWithSize + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + public: void initWithSize (const uint32_t inSize) { + delete [] mBuffer ; mBuffer = new CANFDMessage [inSize] ; + mSize = inSize ; + mReadIndex = 0 ; + mCount = 0 ; + mPeakCount = 0 ; + } + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // append + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + public: bool append (const CANFDMessage & inMessage) { + const bool ok = mCount < mSize ; + if (ok) { + uint32_t writeIndex = mReadIndex + mCount ; + if (writeIndex >= mSize) { + writeIndex -= mSize ; + } + mBuffer [writeIndex] = inMessage ; + mCount += 1 ; + if (mPeakCount < mCount) { + mPeakCount = mCount ; + } + }else{ + mPeakCount = mSize + 1 ; + } + return ok ; + } + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Remove + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + public: bool remove (CANFDMessage & outMessage) { + const bool ok = mCount > 0 ; + if (ok) { + outMessage = mBuffer [mReadIndex] ; + mCount -= 1 ; + mReadIndex += 1 ; + if (mReadIndex == mSize) { + mReadIndex = 0 ; + } + } + return ok ; + } + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // No copy + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + private: ACANFDBuffer (const ACANFDBuffer &) = delete ; + private: ACANFDBuffer & operator = (const ACANFDBuffer &) = delete ; + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +} ; + +//------------------------------------------------------------------------------ + +#endif diff --git a/Software/src/lib/pierremolinaro-ACAN2517FD/CANFDMessage.h b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD_CANFDMessage.h similarity index 65% rename from Software/src/lib/pierremolinaro-ACAN2517FD/CANFDMessage.h rename to Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD_CANFDMessage.h index 10cf7d3b..f72b5bb4 100644 --- a/Software/src/lib/pierremolinaro-ACAN2517FD/CANFDMessage.h +++ b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD_CANFDMessage.h @@ -1,21 +1,21 @@ -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // Generic CANFD Message // by Pierre Molinaro // // https://github.com/pierremolinaro/acan2517FD // -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ #ifndef GENERIC_CANFD_MESSAGE_DEFINED #define GENERIC_CANFD_MESSAGE_DEFINED -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ -#include "CANMessage.h" +#include -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // CANFDMessage class -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // Note that "len" field contains the actual length, not its encoding in CANFD frames // Valid values are: 0, 1, ..., 8, 12, 16, 20, 24, 32, 48, 64. // Having other values is an error that prevents frame to be sent by tryToSend @@ -23,9 +23,9 @@ class CANFDMessage { -//············································································· -// Constructors -//············································································· + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Constructors + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public : CANFDMessage (void) : id (0), // Frame identifier @@ -36,7 +36,7 @@ class CANFDMessage { data () { } -//············································································· + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public : CANFDMessage (const CANMessage & inMessage) : id (inMessage.id), // Frame identifier @@ -48,9 +48,9 @@ class CANFDMessage { data64 [0] = inMessage.data64 ; } -//············································································· -// Enumerated Type -//············································································· + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Enumerated Type + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public: typedef enum : uint8_t { CAN_REMOTE, @@ -59,9 +59,9 @@ class CANFDMessage { CANFD_WITH_BIT_RATE_SWITCH } Type ; -//············································································· -// Properties -//············································································· + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Properties + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public : uint32_t id ; // Frame identifier public : bool ext ; // false -> base frame, true -> extended frame @@ -80,9 +80,9 @@ class CANFDMessage { uint8_t data [64] ; } ; -//············································································· -// Methods -//············································································· + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Methods + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public: void pad (void) { uint8_t paddedLength = len ; @@ -107,7 +107,7 @@ class CANFDMessage { } } -//············································································· + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public: bool isValid (void) const { if ((type == CAN_REMOTE) || (type == CAN_DATA)) { // Remote frame @@ -121,14 +121,14 @@ class CANFDMessage { } } -//············································································· + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - } ; -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ typedef void (*ACANFDCallBackRoutine) (const CANFDMessage & inMessage) ; -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ #endif diff --git a/Software/src/lib/pierremolinaro-ACAN2517FD/CANMessage.h b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD_CANMessage.h similarity index 84% rename from Software/src/lib/pierremolinaro-ACAN2517FD/CANMessage.h rename to Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD_CANMessage.h index 6c687d7e..9e175a3a 100644 --- a/Software/src/lib/pierremolinaro-ACAN2517FD/CANMessage.h +++ b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD_CANMessage.h @@ -1,4 +1,4 @@ -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // Generic CAN Message // by Pierre Molinaro // @@ -8,16 +8,16 @@ // https://github.com/pierremolinaro/acan2517 // https://github.com/pierremolinaro/acan2517FD // -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ #ifndef GENERIC_CAN_MESSAGE_DEFINED #define GENERIC_CAN_MESSAGE_DEFINED -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ #include -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ class CANMessage { public : uint32_t id = 0 ; // Frame identifier @@ -38,12 +38,12 @@ class CANMessage { } ; } ; -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ typedef enum {kStandard, kExtended} tFrameFormat ; typedef enum {kData, kRemote} tFrameKind ; typedef void (*ACANCallBackRoutine) (const CANMessage & inMessage) ; -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ #endif diff --git a/Software/src/lib/pierremolinaro-ACAN2517FD/ACANFD_DataBitRateFactor.h b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD_DataBitRateFactor.h similarity index 73% rename from Software/src/lib/pierremolinaro-ACAN2517FD/ACANFD_DataBitRateFactor.h rename to Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD_DataBitRateFactor.h index 7177cae4..b3868638 100644 --- a/Software/src/lib/pierremolinaro-ACAN2517FD/ACANFD_DataBitRateFactor.h +++ b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD_DataBitRateFactor.h @@ -1,4 +1,4 @@ -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // A CANFD driver // by Pierre Molinaro @@ -6,16 +6,16 @@ // https://github.com/pierremolinaro/ACAN_T4 // https://github.com/pierremolinaro/ACAN2517FD // -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ #ifndef ACANFD_DATA_BIT_RATE_FACTOR_DEFINED #define ACANFD_DATA_BIT_RATE_FACTOR_DEFINED -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ #include -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ enum class DataBitRateFactor : uint8_t { x1 = 1, @@ -30,6 +30,6 @@ enum class DataBitRateFactor : uint8_t { x10 = 10 } ; -//---------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------ #endif diff --git a/Software/src/lib/pierremolinaro-ACAN2517FD/ACANFDBuffer.h b/Software/src/lib/pierremolinaro-ACAN2517FD/ACANFDBuffer.h deleted file mode 100644 index 75de5ef8..00000000 --- a/Software/src/lib/pierremolinaro-ACAN2517FD/ACANFDBuffer.h +++ /dev/null @@ -1,119 +0,0 @@ -//---------------------------------------------------------------------------------------------------------------------- -// A CAN driver for MCP2517FD CAN Controller in CANFD mode -// by Pierre Molinaro -// https://github.com/pierremolinaro/acan2517FD -// -//---------------------------------------------------------------------------------------------------------------------- - -#ifndef ACANFD_BUFFER_CLASS_DEFINED -#define ACANFD_BUFFER_CLASS_DEFINED - -//---------------------------------------------------------------------------------------------------------------------- - -#include "CANFDMessage.h" - -//---------------------------------------------------------------------------------------------------------------------- - -class ACANFDBuffer { - -//······················································································································ -// Default constructor -//······················································································································ - - public: ACANFDBuffer (void) : - mBuffer (NULL), - mSize (0), - mReadIndex (0), - mCount (0), - mPeakCount (0) { - } - -//······················································································································ -// Destructor -//······················································································································ - - public: ~ ACANFDBuffer (void) { - delete [] mBuffer ; - } - -//······················································································································ -// Private properties -//······················································································································ - - private: CANFDMessage * mBuffer ; - private: uint32_t mSize ; - private: uint32_t mReadIndex ; - private: uint32_t mCount ; - private: uint32_t mPeakCount ; // > mSize if overflow did occur - -//······················································································································ -// Accessors -//······················································································································ - - public: inline uint32_t size (void) const { return mSize ; } - public: inline uint32_t count (void) const { return mCount ; } - public: inline bool isFull (void) const { return mCount == mSize ; } // Added in release 2.17 (thanks to Flole998) - public: inline uint32_t peakCount (void) const { return mPeakCount ; } - -//······················································································································ -// initWithSize -//······················································································································ - - public: void initWithSize (const uint32_t inSize) { - delete [] mBuffer ; mBuffer = new CANFDMessage [inSize] ; - mSize = inSize ; - mReadIndex = 0 ; - mCount = 0 ; - mPeakCount = 0 ; - } - -//······················································································································ -// append -//······················································································································ - - public: bool append (const CANFDMessage & inMessage) { - const bool ok = mCount < mSize ; - if (ok) { - uint32_t writeIndex = mReadIndex + mCount ; - if (writeIndex >= mSize) { - writeIndex -= mSize ; - } - mBuffer [writeIndex] = inMessage ; - mCount += 1 ; - if (mPeakCount < mCount) { - mPeakCount = mCount ; - } - }else{ - mPeakCount = mSize + 1 ; - } - return ok ; - } - -//······················································································································ -// Remove -//······················································································································ - - public: bool remove (CANFDMessage & outMessage) { - const bool ok = mCount > 0 ; - if (ok) { - outMessage = mBuffer [mReadIndex] ; - mCount -= 1 ; - mReadIndex += 1 ; - if (mReadIndex == mSize) { - mReadIndex = 0 ; - } - } - return ok ; - } - -//······················································································································ -// No copy -//······················································································································ - - private: ACANFDBuffer (const ACANFDBuffer &) = delete ; - private: ACANFDBuffer & operator = (const ACANFDBuffer &) = delete ; -} ; - -//---------------------------------------------------------------------------------------------------------------------- - -#endif diff --git a/Software/src/lib/pierremolinaro-ACAN2517FD/README.md b/Software/src/lib/pierremolinaro-ACAN2517FD/README.md old mode 100644 new mode 100755 diff --git a/Software/src/lib/pierremolinaro-ACAN2517FD/library.properties b/Software/src/lib/pierremolinaro-ACAN2517FD/library.properties new file mode 100644 index 00000000..b249255d --- /dev/null +++ b/Software/src/lib/pierremolinaro-ACAN2517FD/library.properties @@ -0,0 +1,9 @@ +name=ACAN2517FD +version=2.1.16 +author=Pierre Molinaro +maintainer=Pierre Molinaro +sentence=Driver for MCP2517FD and MCP2518FD CAN Controller (CAN FD mode) +paragraph=This library is an Arduino CAN network driver for the MCP2517FD, the MCP2518FD and the MCP251863 CAN Controller, in CAN FD mode. Compatible with ACAN, ACAN2515, ACAN2517 libraries, with ACAN_T4 library from version 2.1.0. Default configuration sends and receives any frame – no default filter to provide. Reception filters (up to 32) can be easily defined. Compatible with ESP32 from version 1.1.0. +category=Communication +url=https://github.com/pierremolinaro/acan2517FD +architectures=* From 297c56b2899736cec287cd97f96d6fc703ac4be5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 19 Aug 2025 09:42:24 +0300 Subject: [PATCH 032/245] Update acanfd library to v2.1.16 --- .../pierremolinaro-ACAN2517FD/ACAN2517FD.cpp | 91 ++++++++++++------- 1 file changed, 57 insertions(+), 34 deletions(-) diff --git a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.cpp b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.cpp index e3d9186d..6dbba0c8 100644 --- a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.cpp +++ b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.cpp @@ -5,8 +5,7 @@ // //------------------------------------------------------------------------------ -#include "ACAN2517FD.h" -#include "../../system_settings.h" //Contains task priority (Battery-Emulator modified line) +#include //------------------------------------------------------------------------------ @@ -175,26 +174,6 @@ static uint16_t u16FromBufferAtIndex (uint8_t ioBuffer [], const uint8_t inIndex //------------------------------------------------------------------------------ -static inline void turnOffInterrupts () { - #ifndef DISABLEMCP2517FDCOMPAT -turnOffInterrupts () ; - #endif -} - -//---------------------------------------------------------------------------------------------------------------------- - -static inline void turnOnInterrupts() { - #ifndef DISABLEMCP2517FDCOMPAT - #ifdef ARDUINO_ARCH_ESP32 - taskENABLE_INTERRUPTS () ; - #else - interrupts () ; - #endif - #endif -} - -//---------------------------------------------------------------------------------------------------------------------- - ACAN2517FD::ACAN2517FD (const uint8_t inCS, // CS input of MCP2517FD SPIClass & inSPI, // Hardware SPI object const uint8_t inINT) : // INT output of MCP2517FD @@ -523,7 +502,7 @@ uint32_t ACAN2517FD::begin (const ACAN2517FDSettings & inSettings, } } #ifdef ARDUINO_ARCH_ESP32 - xTaskCreate (myESP32Task, "ACAN2517Handler", 1024, this, TASK_ACAN2517FD_PRIORITY, &mESP32TaskHandle) ; //(Battery-Emulator modified line) + xTaskCreate (myESP32Task, "ACAN2517Handler", 1024, this, 16, &mESP32TaskHandle) ; #endif if (mINT != 255) { // 255 means interrupt is not used #ifdef ARDUINO_ARCH_ESP32 @@ -548,7 +527,11 @@ uint32_t ACAN2517FD::begin (const ACAN2517FDSettings & inSettings, bool ACAN2517FD::end (void) { mSPI.beginTransaction (mSPISettings) ; -turnOffInterrupts () ; + #ifdef ARDUINO_ARCH_ESP32 + taskDISABLE_INTERRUPTS () ; + #else + noInterrupts () ; + #endif //--- Detach interrupt pin if (mINT != 255) { // 255 means interrupt is not used const int8_t itPin = digitalPinToInterrupt (mINT) ; @@ -601,7 +584,11 @@ bool ACAN2517FD::tryToSend (const CANFDMessage & inMessage) { bool ok = inMessage.isValid () ; if (ok) { mSPI.beginTransaction (mSPISettings) ; - turnOffInterrupts () ; + #ifdef ARDUINO_ARCH_ESP32 + taskDISABLE_INTERRUPTS () ; + #else + noInterrupts () ; + #endif if (inMessage.idx == 0) { ok = inMessage.len <= mTransmitFIFOPayload ; if (ok) { @@ -613,7 +600,11 @@ bool ACAN2517FD::tryToSend (const CANFDMessage & inMessage) { ok = sendViaTXQ (inMessage) ; } } - turnOnInterrupts(); + #ifdef ARDUINO_ARCH_ESP32 + taskENABLE_INTERRUPTS () ; + #else + interrupts () ; + #endif mSPI.endTransaction () ; } return ok ; @@ -780,7 +771,11 @@ bool ACAN2517FD::sendViaTXQ (const CANFDMessage & inMessage) { bool ACAN2517FD::available (void) { mSPI.beginTransaction (mSPISettings) ; - turnOffInterrupts () ; + #ifdef ARDUINO_ARCH_ESP32 + taskDISABLE_INTERRUPTS () ; + #else + noInterrupts () ; + #endif const bool hasReceivedMessage = mDriverReceiveBuffer.count () > 0 ; #ifdef ARDUINO_ARCH_ESP32 taskENABLE_INTERRUPTS () ; @@ -1107,9 +1102,17 @@ uint8_t ACAN2517FD::readRegister8Assume_SPI_transaction (const uint16_t inRegist void ACAN2517FD::writeRegister8 (const uint16_t inRegisterAddress, const uint8_t inValue) { mSPI.beginTransaction (mSPISettings) ; -turnOffInterrupts () ; + #ifdef ARDUINO_ARCH_ESP32 + taskDISABLE_INTERRUPTS () ; + #else + noInterrupts () ; + #endif writeRegister8Assume_SPI_transaction (inRegisterAddress, inValue) ; - turnOnInterrupts(); + #ifdef ARDUINO_ARCH_ESP32 + taskENABLE_INTERRUPTS () ; + #else + interrupts () ; + #endif mSPI.endTransaction () ; } @@ -1117,7 +1120,11 @@ turnOffInterrupts () ; uint8_t ACAN2517FD::readRegister8 (const uint16_t inRegisterAddress) { mSPI.beginTransaction (mSPISettings) ; -turnOffInterrupts () ; + #ifdef ARDUINO_ARCH_ESP32 + taskDISABLE_INTERRUPTS () ; + #else + noInterrupts () ; + #endif const uint8_t result = readRegister8Assume_SPI_transaction (inRegisterAddress) ; #ifdef ARDUINO_ARCH_ESP32 taskENABLE_INTERRUPTS () ; @@ -1132,7 +1139,11 @@ turnOffInterrupts () ; uint16_t ACAN2517FD::readRegister16 (const uint16_t inRegisterAddress) { mSPI.beginTransaction (mSPISettings) ; -turnOffInterrupts () ; + #ifdef ARDUINO_ARCH_ESP32 + taskDISABLE_INTERRUPTS () ; + #else + noInterrupts () ; + #endif const uint16_t result = readRegister16Assume_SPI_transaction (inRegisterAddress) ; #ifdef ARDUINO_ARCH_ESP32 taskENABLE_INTERRUPTS () ; @@ -1147,7 +1158,11 @@ turnOffInterrupts () ; void ACAN2517FD::writeRegister32 (const uint16_t inRegisterAddress, const uint32_t inValue) { mSPI.beginTransaction (mSPISettings) ; -turnOffInterrupts () ; + #ifdef ARDUINO_ARCH_ESP32 + taskDISABLE_INTERRUPTS () ; + #else + noInterrupts () ; + #endif writeRegister32Assume_SPI_transaction (inRegisterAddress, inValue) ; #ifdef ARDUINO_ARCH_ESP32 taskENABLE_INTERRUPTS () ; @@ -1161,7 +1176,11 @@ turnOffInterrupts () ; uint32_t ACAN2517FD::readRegister32 (const uint16_t inRegisterAddress) { mSPI.beginTransaction (mSPISettings) ; -turnOffInterrupts () ; + #ifdef ARDUINO_ARCH_ESP32 + taskDISABLE_INTERRUPTS () ; + #else + noInterrupts () ; + #endif const uint32_t result = readRegister32Assume_SPI_transaction (inRegisterAddress) ; #ifdef ARDUINO_ARCH_ESP32 taskENABLE_INTERRUPTS () ; @@ -1219,7 +1238,11 @@ void ACAN2517FD::setOperationMode (const ACAN2517FDSettings::OperationMode inOpe void ACAN2517FD::reset2517FD (void) { mSPI.beginTransaction (mSPISettings) ; // Check RESET is performed with 800 kHz clock -turnOffInterrupts () ; + #ifdef ARDUINO_ARCH_ESP32 + taskDISABLE_INTERRUPTS () ; + #else + noInterrupts () ; + #endif assertCS () ; mSPI.transfer16 (0x00) ; // Reset instruction: 0x0000 deassertCS () ; From 43fdb4a2f35ce412583850ff4a22f8a26dd29771 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 19 Aug 2025 09:53:20 +0300 Subject: [PATCH 033/245] Modify library with BE specific tweaks --- .../pierremolinaro-ACAN2517FD/ACAN2517FD.cpp | 140 ++++++------------ .../pierremolinaro-ACAN2517FD/ACAN2517FD.h | 18 ++- .../ACAN2517FDFilters.h | 2 +- .../ACAN2517FDSettings.cpp | 2 +- .../ACAN2517FDSettings.h | 2 +- .../ACAN2517FD_ACANFDBuffer.h | 2 +- .../ACAN2517FD_CANFDMessage.h | 2 +- 7 files changed, 64 insertions(+), 104 deletions(-) diff --git a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.cpp b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.cpp index 6dbba0c8..ce91c391 100644 --- a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.cpp +++ b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.cpp @@ -5,7 +5,8 @@ // //------------------------------------------------------------------------------ -#include +#include "ACAN2517FD.h" +#include "../../system_settings.h" //Contains task priority //Modded by Battery-Emulator //------------------------------------------------------------------------------ @@ -173,6 +174,29 @@ static uint16_t u16FromBufferAtIndex (uint8_t ioBuffer [], const uint8_t inIndex } //------------------------------------------------------------------------------ +static inline void turnOffInterrupts () { + #ifndef DISABLEMCP2517FDCOMPAT + #ifdef ARDUINO_ARCH_ESP32 + taskDISABLE_INTERRUPTS () ; + #else + noInterrupts () ; + #endif + #endif +} + +//---------------------------------------------------------------------------------------------------------------------- + +static inline void turnOnInterrupts() { + #ifndef DISABLEMCP2517FDCOMPAT + #ifdef ARDUINO_ARCH_ESP32 + taskENABLE_INTERRUPTS () ; + #else + interrupts () ; + #endif + #endif +} + +//---------------------------------------------------------------------------------------------------------------------- ACAN2517FD::ACAN2517FD (const uint8_t inCS, // CS input of MCP2517FD SPIClass & inSPI, // Hardware SPI object @@ -502,7 +526,7 @@ uint32_t ACAN2517FD::begin (const ACAN2517FDSettings & inSettings, } } #ifdef ARDUINO_ARCH_ESP32 - xTaskCreate (myESP32Task, "ACAN2517Handler", 1024, this, 16, &mESP32TaskHandle) ; + xTaskCreate (myESP32Task, "ACAN2517Handler", 1024, this, TASK_ACAN2517FD_PRIORITY, &mESP32TaskHandle) ; //Modded by Battery-Emulator #endif if (mINT != 255) { // 255 means interrupt is not used #ifdef ARDUINO_ARCH_ESP32 @@ -527,11 +551,7 @@ uint32_t ACAN2517FD::begin (const ACAN2517FDSettings & inSettings, bool ACAN2517FD::end (void) { mSPI.beginTransaction (mSPISettings) ; - #ifdef ARDUINO_ARCH_ESP32 - taskDISABLE_INTERRUPTS () ; - #else - noInterrupts () ; - #endif + turnOffInterrupts () ; //--- Detach interrupt pin if (mINT != 255) { // 255 means interrupt is not used const int8_t itPin = digitalPinToInterrupt (mINT) ; @@ -566,11 +586,7 @@ bool ACAN2517FD::end (void) { mDriverReceiveBuffer.initWithSize (0) ; mDriverTransmitBuffer.initWithSize (0) ; //--- - #ifdef ARDUINO_ARCH_ESP32 - taskENABLE_INTERRUPTS () ; - #else - interrupts () ; - #endif + turnOnInterrupts () ; mSPI.endTransaction () ; //--- return ok ; @@ -600,11 +616,7 @@ bool ACAN2517FD::tryToSend (const CANFDMessage & inMessage) { ok = sendViaTXQ (inMessage) ; } } - #ifdef ARDUINO_ARCH_ESP32 - taskENABLE_INTERRUPTS () ; - #else - interrupts () ; - #endif + turnOnInterrupts () ; mSPI.endTransaction () ; } return ok ; @@ -771,17 +783,9 @@ bool ACAN2517FD::sendViaTXQ (const CANFDMessage & inMessage) { bool ACAN2517FD::available (void) { mSPI.beginTransaction (mSPISettings) ; - #ifdef ARDUINO_ARCH_ESP32 - taskDISABLE_INTERRUPTS () ; - #else - noInterrupts () ; - #endif + turnOffInterrupts () ; const bool hasReceivedMessage = mDriverReceiveBuffer.count () > 0 ; - #ifdef ARDUINO_ARCH_ESP32 - taskENABLE_INTERRUPTS () ; - #else - interrupts () ; - #endif + turnOnInterrupts () ; mSPI.endTransaction () ; return hasReceivedMessage ; } @@ -808,11 +812,7 @@ bool ACAN2517FD::receive (CANFDMessage & outMessage) { data8 |= (1 << 1) ; // Receive FIFO Interrupt Enable writeRegister8Assume_SPI_transaction (INT_REGISTER + 2, data8) ; - #ifdef ARDUINO_ARCH_ESP32 - taskENABLE_INTERRUPTS () ; - #else - interrupts () ; - #endif + turnOnInterrupts () ; mSPI.endTransaction () ; } return hasReceivedMessage ; @@ -929,9 +929,7 @@ void ACAN2517FD::isr_poll_core (void) { writeRegister8Assume_SPI_transaction (FIFOSTA_REGISTER (RECEIVE_FIFO_INDEX), ~ (1 << 3)) ; } } - #ifdef ARDUINO_ARCH_ESP32 - taskENABLE_INTERRUPTS () ; - #endif + turnOnInterrupts () ; mSPI.endTransaction () ; } @@ -1102,17 +1100,9 @@ uint8_t ACAN2517FD::readRegister8Assume_SPI_transaction (const uint16_t inRegist void ACAN2517FD::writeRegister8 (const uint16_t inRegisterAddress, const uint8_t inValue) { mSPI.beginTransaction (mSPISettings) ; - #ifdef ARDUINO_ARCH_ESP32 - taskDISABLE_INTERRUPTS () ; - #else - noInterrupts () ; - #endif + turnOffInterrupts () ; writeRegister8Assume_SPI_transaction (inRegisterAddress, inValue) ; - #ifdef ARDUINO_ARCH_ESP32 - taskENABLE_INTERRUPTS () ; - #else - interrupts () ; - #endif + turnOnInterrupts () ; mSPI.endTransaction () ; } @@ -1120,17 +1110,9 @@ void ACAN2517FD::writeRegister8 (const uint16_t inRegisterAddress, const uint8_t uint8_t ACAN2517FD::readRegister8 (const uint16_t inRegisterAddress) { mSPI.beginTransaction (mSPISettings) ; - #ifdef ARDUINO_ARCH_ESP32 - taskDISABLE_INTERRUPTS () ; - #else - noInterrupts () ; - #endif + turnOffInterrupts () ; const uint8_t result = readRegister8Assume_SPI_transaction (inRegisterAddress) ; - #ifdef ARDUINO_ARCH_ESP32 - taskENABLE_INTERRUPTS () ; - #else - interrupts () ; - #endif +turnOnInterrupts () ; mSPI.endTransaction () ; return result ; } @@ -1139,17 +1121,9 @@ uint8_t ACAN2517FD::readRegister8 (const uint16_t inRegisterAddress) { uint16_t ACAN2517FD::readRegister16 (const uint16_t inRegisterAddress) { mSPI.beginTransaction (mSPISettings) ; - #ifdef ARDUINO_ARCH_ESP32 - taskDISABLE_INTERRUPTS () ; - #else - noInterrupts () ; - #endif + turnOffInterrupts () ; const uint16_t result = readRegister16Assume_SPI_transaction (inRegisterAddress) ; - #ifdef ARDUINO_ARCH_ESP32 - taskENABLE_INTERRUPTS () ; - #else - interrupts () ; - #endif +turnOnInterrupts () ; mSPI.endTransaction () ; return result ; } @@ -1158,17 +1132,9 @@ uint16_t ACAN2517FD::readRegister16 (const uint16_t inRegisterAddress) { void ACAN2517FD::writeRegister32 (const uint16_t inRegisterAddress, const uint32_t inValue) { mSPI.beginTransaction (mSPISettings) ; - #ifdef ARDUINO_ARCH_ESP32 - taskDISABLE_INTERRUPTS () ; - #else - noInterrupts () ; - #endif + turnOffInterrupts () ; writeRegister32Assume_SPI_transaction (inRegisterAddress, inValue) ; - #ifdef ARDUINO_ARCH_ESP32 - taskENABLE_INTERRUPTS () ; - #else - interrupts () ; - #endif +turnOnInterrupts () ; mSPI.endTransaction () ; } @@ -1176,17 +1142,9 @@ void ACAN2517FD::writeRegister32 (const uint16_t inRegisterAddress, const uint32 uint32_t ACAN2517FD::readRegister32 (const uint16_t inRegisterAddress) { mSPI.beginTransaction (mSPISettings) ; - #ifdef ARDUINO_ARCH_ESP32 - taskDISABLE_INTERRUPTS () ; - #else - noInterrupts () ; - #endif + turnOffInterrupts () ; const uint32_t result = readRegister32Assume_SPI_transaction (inRegisterAddress) ; - #ifdef ARDUINO_ARCH_ESP32 - taskENABLE_INTERRUPTS () ; - #else - interrupts () ; - #endif +turnOnInterrupts () ; mSPI.endTransaction () ; return result ; } @@ -1238,19 +1196,11 @@ void ACAN2517FD::setOperationMode (const ACAN2517FDSettings::OperationMode inOpe void ACAN2517FD::reset2517FD (void) { mSPI.beginTransaction (mSPISettings) ; // Check RESET is performed with 800 kHz clock - #ifdef ARDUINO_ARCH_ESP32 - taskDISABLE_INTERRUPTS () ; - #else - noInterrupts () ; - #endif + turnOffInterrupts () ; assertCS () ; mSPI.transfer16 (0x00) ; // Reset instruction: 0x0000 deassertCS () ; - #ifdef ARDUINO_ARCH_ESP32 - taskENABLE_INTERRUPTS () ; - #else - interrupts () ; - #endif +turnOnInterrupts () ; mSPI.endTransaction () ; } diff --git a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h index 779e7f7f..ec77b11c 100644 --- a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h +++ b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h @@ -8,12 +8,22 @@ //------------------------------------------------------------------------------ -#include -#include -#include -#include +#include "ACAN2517FDSettings.h" +#include "ACAN2517FD_ACANFDBuffer.h" +#include "ACAN2517FD_CANMessage.h" +#include "ACAN2517FDFilters.h" #include +//---------------------------------------------------------------------------------------------------------------------- +// Settings +//---------------------------------------------------------------------------------------------------------------------- +// +// Enable this if you want to disable the MCP2517FD compatability mode. This can slightly increase performance when +// running on the MCP2518FD but you risk hitting issues mentioned in the MCP2517FD errata-sheet when using this option +// on the MCP2517FD. +// +#define DISABLEMCP2517FDCOMPAT + //------------------------------------------------------------------------------ // ACAN2517FD class //------------------------------------------------------------------------------ diff --git a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDFilters.h b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDFilters.h index 632d2f76..68fb71fe 100644 --- a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDFilters.h +++ b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDFilters.h @@ -11,7 +11,7 @@ //------------------------------------------------------------------------------ -#include +#include "ACAN2517FD_CANFDMessage.h" //------------------------------------------------------------------------------ // ACAN2517FDFilters class diff --git a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDSettings.cpp b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDSettings.cpp index 4c5ccbd3..09184d30 100644 --- a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDSettings.cpp +++ b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDSettings.cpp @@ -5,7 +5,7 @@ // //------------------------------------------------------------------------------ -#include +#include "ACAN2517FDSettings.h" //------------------------------------------------------------------------------ diff --git a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDSettings.h b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDSettings.h index 0cc79802..38eb0381 100644 --- a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDSettings.h +++ b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDSettings.h @@ -9,7 +9,7 @@ //------------------------------------------------------------------------------ -#include +#include "ACAN2517FD_DataBitRateFactor.h" //------------------------------------------------------------------------------ // ACAN2517FDSettings class diff --git a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD_ACANFDBuffer.h b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD_ACANFDBuffer.h index cdbf59b4..e474f9f1 100644 --- a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD_ACANFDBuffer.h +++ b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD_ACANFDBuffer.h @@ -10,7 +10,7 @@ //------------------------------------------------------------------------------ -#include +#include "ACAN2517FD_CANFDMessage.h" //------------------------------------------------------------------------------ diff --git a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD_CANFDMessage.h b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD_CANFDMessage.h index f72b5bb4..64f3838b 100644 --- a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD_CANFDMessage.h +++ b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD_CANFDMessage.h @@ -11,7 +11,7 @@ //------------------------------------------------------------------------------ -#include +#include "ACAN2517FD_CANMessage.h" //------------------------------------------------------------------------------ // CANFDMessage class From de393d4ec08ca1f67ead9770a7955cd3a4a192be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 19 Aug 2025 09:56:01 +0300 Subject: [PATCH 034/245] Modify library with BE specific tweaks --- Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.cpp b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.cpp index ce91c391..2f190f98 100644 --- a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.cpp +++ b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.cpp @@ -929,7 +929,9 @@ void ACAN2517FD::isr_poll_core (void) { writeRegister8Assume_SPI_transaction (FIFOSTA_REGISTER (RECEIVE_FIFO_INDEX), ~ (1 << 3)) ; } } - turnOnInterrupts () ; + #ifdef ARDUINO_ARCH_ESP32 + taskENABLE_INTERRUPTS () ; + #endif mSPI.endTransaction () ; } From d715d169fe50dae9ca996d431168626d22cf9236 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 19 Aug 2025 21:27:29 +0300 Subject: [PATCH 035/245] Avoid oscillation in precharge incase it fails/timeoutus --- .../precharge_control/precharge_control.cpp | 14 ++++++++++++-- Software/src/devboard/utils/types.h | 3 ++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Software/src/communication/precharge_control/precharge_control.cpp b/Software/src/communication/precharge_control/precharge_control.cpp index efdf3ff9..3557c95e 100644 --- a/Software/src/communication/precharge_control/precharge_control.cpp +++ b/Software/src/communication/precharge_control/precharge_control.cpp @@ -64,6 +64,14 @@ void handle_precharge_control(unsigned long currentMillis) { auto hia4v1_pin = esp32hal->HIA4V1_PIN(); auto inverter_disconnect_contactor_pin = esp32hal->INVERTER_DISCONNECT_CONTACTOR_PIN(); + // If we're in FAILURE state, completely disable any further precharge attempts + if (datalayer.system.status.precharge_status == AUTO_PRECHARGE_FAILURE) { + pinMode(hia4v1_pin, OUTPUT); + digitalWrite(hia4v1_pin, LOW); + digitalWrite(inverter_disconnect_contactor_pin, ON); + return; // Exit immediately - no further processing allowed. Reboot required to recover + } + int32_t target_voltage = datalayer.battery.status.voltage_dV; int32_t external_voltage = datalayer_extended.meb.BMS_voltage_intermediate_dV; @@ -128,11 +136,13 @@ void handle_precharge_control(unsigned long currentMillis) { pinMode(hia4v1_pin, OUTPUT); digitalWrite(hia4v1_pin, LOW); digitalWrite(inverter_disconnect_contactor_pin, ON); - datalayer.system.status.precharge_status = AUTO_PRECHARGE_OFF; + datalayer.system.status.precharge_status = AUTO_PRECHARGE_FAILURE; #ifdef DEBUG_LOG - logging.printf("Precharge: Disabled (timeout reached / BMS fault) -> AUTO_PRECHARGE_OFF\n"); + logging.printf("Precharge: CRITICAL FAILURE (timeout/BMS fault) -> REQUIRES REBOOT\n"); #endif set_event(EVENT_AUTOMATIC_PRECHARGE_FAILURE, 0); + // Force stop any further precharge attempts + datalayer.system.settings.start_precharging = false; // Add event } else if (datalayer.system.status.battery_allows_contactor_closing) { diff --git a/Software/src/devboard/utils/types.h b/Software/src/devboard/utils/types.h index d1e15340..577241e8 100644 --- a/Software/src/devboard/utils/types.h +++ b/Software/src/devboard/utils/types.h @@ -28,7 +28,8 @@ enum PrechargeState { AUTO_PRECHARGE_START, AUTO_PRECHARGE_PRECHARGING, AUTO_PRECHARGE_OFF, - AUTO_PRECHARGE_COMPLETED + AUTO_PRECHARGE_COMPLETED, + AUTO_PRECHARGE_FAILURE }; #define DISCHARGING 1 From d20712f3014f4dc7b6e4f9b04bf22379d535c584 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 19 Aug 2025 21:30:18 +0300 Subject: [PATCH 036/245] Update event for precharge failure --- Software/src/devboard/utils/events.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/src/devboard/utils/events.cpp b/Software/src/devboard/utils/events.cpp index b5ba4a8f..32793827 100644 --- a/Software/src/devboard/utils/events.cpp +++ b/Software/src/devboard/utils/events.cpp @@ -262,7 +262,7 @@ String get_event_message_string(EVENTS_ENUM_TYPE event) { case EVENT_PRECHARGE_FAILURE: return "Battery failed to precharge. Check that capacitor is seated on high voltage output."; case EVENT_AUTOMATIC_PRECHARGE_FAILURE: - return "Automatic precharge failed to reach target voltae."; + return "Automatic precharge FAILURE. Failed to reach target voltage or BMS timeout. Reboot emulator to retry!"; case EVENT_INTERNAL_OPEN_FAULT: return "High voltage cable removed while battery running. Opening contactors!"; case EVENT_INVERTER_OPEN_CONTACTOR: From 0f647978fa54522fcb7047dca1f57addde6022ce Mon Sep 17 00:00:00 2001 From: Fredrik Date: Wed, 20 Aug 2025 11:23:22 +0200 Subject: [PATCH 037/245] Send correct data in 441 message --- Software/src/battery/BYD-ATTO-3-BATTERY.cpp | 25 +++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp index db969772..233b98d4 100644 --- a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp +++ b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp @@ -136,6 +136,16 @@ uint16_t estimateSOCstandard(uint16_t packVoltage) { // Linear interpolation fu return 0; // Default return for safety, should never reach here } +uint8_t compute441Checksum(const uint8_t* u8) // Computes the 441 checksum byte +{ + int sum = 0; + for (int i = 0; i < 7; ++i) { + sum += u8[i]; + } + uint8_t lsb = static_cast(sum & 0xFF); + return static_cast(~lsb & 0xFF); +} + void BydAttoBattery:: update_values() { //This function maps all the values fetched via CAN to the correct parameters used for modbus @@ -532,10 +542,17 @@ void BydAttoBattery::transmit_can(unsigned long currentMillis) { } if (counter_100ms > 3) { - ATTO_3_441.data.u8[4] = 0x9D; - ATTO_3_441.data.u8[5] = 0x01; - ATTO_3_441.data.u8[6] = 0xFF; - ATTO_3_441.data.u8[7] = 0xF5; + if (battery_voltage > 0){ + ATTO_3_441.data.u8[4] = (uint8_t)(battery_voltage - 1); + ATTO_3_441.data.u8[5] = ((battery_voltage - 1) >> 8); + ATTO_3_441.data.u8[6] = 0xFF; + ATTO_3_441.data.u8[7] = compute441Checksum(ATTO_3_441.data.u8); + } else { + ATTO_3_441.data.u8[4] = 0x0C; + ATTO_3_441.data.u8[5] = 0x00; + ATTO_3_441.data.u8[6] = 0xFF; + ATTO_3_441.data.u8[7] = 0x87; + } } transmit_can_frame(&ATTO_3_441); From 37f9d3e8f8c5b6bff6788c200e9a0ac123f06cf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 20 Aug 2025 12:58:45 +0300 Subject: [PATCH 038/245] Add Relion battery protocol --- Software/USER_SETTINGS.h | 1 + Software/src/battery/BATTERIES.cpp | 4 ++ Software/src/battery/BATTERIES.h | 1 + Software/src/battery/Battery.h | 1 + Software/src/battery/RELION-LV-BATTERY.cpp | 83 ++++++++++++++++++++++ Software/src/battery/RELION-LV-BATTERY.h | 43 +++++++++++ 6 files changed, 133 insertions(+) create mode 100644 Software/src/battery/RELION-LV-BATTERY.cpp create mode 100644 Software/src/battery/RELION-LV-BATTERY.h diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index a1d246fa..2dccf642 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -35,6 +35,7 @@ //#define DALY_BMS //#define RJXZS_BMS //#define RANGE_ROVER_PHEV_BATTERY +//#define RELION_BATTERY //#define RENAULT_KANGOO_BATTERY //#define RENAULT_TWIZY_BATTERY //#define RENAULT_ZOE_GEN1_BATTERY diff --git a/Software/src/battery/BATTERIES.cpp b/Software/src/battery/BATTERIES.cpp index 1aa07bc6..9b81a479 100644 --- a/Software/src/battery/BATTERIES.cpp +++ b/Software/src/battery/BATTERIES.cpp @@ -108,6 +108,8 @@ const char* name_for_battery_type(BatteryType type) { return RjxzsBms::Name; case BatteryType::RangeRoverPhev: return RangeRoverPhevBattery::Name; + case BatteryType::RelionBattery: + return RelionBattery::Name; case BatteryType::RenaultKangoo: return RenaultKangooBattery::Name; case BatteryType::RenaultTwizy: @@ -209,6 +211,8 @@ Battery* create_battery(BatteryType type) { return new RjxzsBms(); case BatteryType::RangeRoverPhev: return new RangeRoverPhevBattery(); + case BatteryType::RelionBattery: + return new RelionBattery(); case BatteryType::RenaultKangoo: return new RenaultKangooBattery(); case BatteryType::RenaultTwizy: diff --git a/Software/src/battery/BATTERIES.h b/Software/src/battery/BATTERIES.h index 3e89149c..2c649a47 100644 --- a/Software/src/battery/BATTERIES.h +++ b/Software/src/battery/BATTERIES.h @@ -39,6 +39,7 @@ void setup_can_shunt(); #include "ORION-BMS.h" #include "PYLON-BATTERY.h" #include "RANGE-ROVER-PHEV-BATTERY.h" +#include "RELION-LV-BATTERY.h" #include "RENAULT-KANGOO-BATTERY.h" #include "RENAULT-TWIZY.h" #include "RENAULT-ZOE-GEN1-BATTERY.h" diff --git a/Software/src/battery/Battery.h b/Software/src/battery/Battery.h index 2b163a95..da98a513 100644 --- a/Software/src/battery/Battery.h +++ b/Software/src/battery/Battery.h @@ -45,6 +45,7 @@ enum class BatteryType { MgHsPhev = 37, SamsungSdiLv = 38, HyundaiIoniq28 = 39, + RelionBattery = 40, Highest }; diff --git a/Software/src/battery/RELION-LV-BATTERY.cpp b/Software/src/battery/RELION-LV-BATTERY.cpp new file mode 100644 index 00000000..693a5f8f --- /dev/null +++ b/Software/src/battery/RELION-LV-BATTERY.cpp @@ -0,0 +1,83 @@ +#include "RELION-LV-BATTERY.h" +#include "../battery/BATTERIES.h" +#include "../communication/can/comm_can.h" +#include "../datalayer/datalayer.h" +#include "../devboard/utils/events.h" +/*CAN Type:CAN2.0(Extended) +BPS:250kbps +Data Length: 8 +Data Encoded Format:Motorola*/ + +void RelionBattery::update_values() { + + datalayer.battery.status.real_soc = battery_soc * 100; + + datalayer.battery.status.remaining_capacity_Wh = static_cast( + (static_cast(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh); + + datalayer.battery.status.soh_pptt = battery_soh * 100; + + datalayer.battery.status.voltage_dV = battery_total_voltage; + + datalayer.battery.status.current_dA = battery_total_current; //Charging negative, discharge positive + + datalayer.battery.status.max_charge_power_W = + ((battery_total_voltage / 10) * charge_current_A); //90A recommended charge current + + datalayer.battery.status.max_discharge_power_W = + ((battery_total_voltage / 10) * discharge_current_A); //150A max continous discharge current + + datalayer.battery.status.temperature_min_dC = max_cell_temperature * 10; + + datalayer.battery.status.temperature_max_dC = max_cell_temperature * 10; + + datalayer.battery.status.cell_max_voltage_mV = max_cell_voltage; + + datalayer.battery.status.cell_min_voltage_mV = min_cell_voltage; +} + +void RelionBattery::handle_incoming_can_frame(CAN_frame rx_frame) { + switch (rx_frame.ID) { + case 0x02018100: //ID1 + battery_total_voltage = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]); + break; + case 0x02028100: //ID2 + battery_total_current = ((rx_frame.data.u8[0] << 8) | rx_frame.data.u8[1]); + system_state = rx_frame.data.u8[2]; + battery_soc = rx_frame.data.u8[3]; + battery_soh = rx_frame.data.u8[4]; + most_serious_fault = rx_frame.data.u8[5]; + break; + case 0x02038100: //ID3 + max_cell_voltage = ((rx_frame.data.u8[0] << 8) | rx_frame.data.u8[1]); + min_cell_voltage = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); + break; + case 0x02628100: //Temperatures + max_cell_temperature = rx_frame.data.u8[0] - 50; + min_cell_temperature = rx_frame.data.u8[2] - 50; + break; + case 0x02648100: //Charging limitis + charge_current_A = ((rx_frame.data.u8[0] << 8) | rx_frame.data.u8[1]) - 800; + regen_charge_current_A = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]) - 800; + discharge_current_A = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]) - 800; + break; + default: + break; + } +} + +void RelionBattery::transmit_can(unsigned long currentMillis) { + // No periodic sending for this protocol +} + +void RelionBattery::setup(void) { // Performs one time setup at startup + strncpy(datalayer.system.info.battery_protocol, Name, 63); + datalayer.system.info.battery_protocol[63] = '\0'; + datalayer.battery.info.chemistry = LFP; + datalayer.battery.info.number_of_cells = 16; + datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; + datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; + datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; + datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; + datalayer.system.status.battery_allows_contactor_closing = true; +} diff --git a/Software/src/battery/RELION-LV-BATTERY.h b/Software/src/battery/RELION-LV-BATTERY.h new file mode 100644 index 00000000..53c59431 --- /dev/null +++ b/Software/src/battery/RELION-LV-BATTERY.h @@ -0,0 +1,43 @@ +#ifndef RELION_BATTERY_H +#define RELION_BATTERY_H + +#include "../system_settings.h" +#include "CanBattery.h" + +#ifdef RELION_BATTERY +#define SELECTED_BATTERY_CLASS RelionBattery +#endif + +class RelionBattery : public CanBattery { + public: + RelionBattery() : CanBattery(CAN_Speed::CAN_SPEED_250KBPS) {} + + virtual void setup(void); + virtual void handle_incoming_can_frame(CAN_frame rx_frame); + virtual void update_values(); + virtual void transmit_can(unsigned long currentMillis); + static constexpr const char* Name = "Relion LV protocol via 250kbps CAN"; + + private: + static const int MAX_PACK_VOLTAGE_DV = 584; //58.4V recommended charge voltage. BMS protection steps in at 60.8V + static const int MIN_PACK_VOLTAGE_DV = 440; //44.0V Recommended LV disconnect. BMS protection steps in at 40.0V + static const int MAX_CELL_DEVIATION_MV = 300; + static const int MAX_CELL_VOLTAGE_MV = 3800; //Battery is put into emergency stop if one cell goes over this value + static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value + + uint16_t battery_total_voltage = 500; + int16_t battery_total_current = 0; + uint8_t system_state = 0; + uint8_t battery_soc = 50; + uint8_t battery_soh = 99; + uint8_t most_serious_fault = 0; + uint16_t max_cell_voltage = 3300; + uint16_t min_cell_voltage = 3300; + int16_t max_cell_temperature = 0; + int16_t min_cell_temperature = 0; + int16_t charge_current_A = 0; + int16_t regen_charge_current_A = 0; + int16_t discharge_current_A = 0; +}; + +#endif From b349482f20486c74ca21ec15fbc8b40d18130e81 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 20 Aug 2025 10:27:01 +0000 Subject: [PATCH 039/245] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- Software/src/battery/BYD-ATTO-3-BATTERY.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp index 233b98d4..cf13aa26 100644 --- a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp +++ b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp @@ -136,14 +136,14 @@ uint16_t estimateSOCstandard(uint16_t packVoltage) { // Linear interpolation fu return 0; // Default return for safety, should never reach here } -uint8_t compute441Checksum(const uint8_t* u8) // Computes the 441 checksum byte +uint8_t compute441Checksum(const uint8_t* u8) // Computes the 441 checksum byte { - int sum = 0; - for (int i = 0; i < 7; ++i) { - sum += u8[i]; - } - uint8_t lsb = static_cast(sum & 0xFF); - return static_cast(~lsb & 0xFF); + int sum = 0; + for (int i = 0; i < 7; ++i) { + sum += u8[i]; + } + uint8_t lsb = static_cast(sum & 0xFF); + return static_cast(~lsb & 0xFF); } void BydAttoBattery:: @@ -542,7 +542,7 @@ void BydAttoBattery::transmit_can(unsigned long currentMillis) { } if (counter_100ms > 3) { - if (battery_voltage > 0){ + if (battery_voltage > 0) { ATTO_3_441.data.u8[4] = (uint8_t)(battery_voltage - 1); ATTO_3_441.data.u8[5] = ((battery_voltage - 1) >> 8); ATTO_3_441.data.u8[6] = 0xFF; From c988245d5f3c954a6cf482ddfab5da64ec62db50 Mon Sep 17 00:00:00 2001 From: mbuhansen <91631350+mbuhansen@users.noreply.github.com> Date: Wed, 20 Aug 2025 16:37:09 +0200 Subject: [PATCH 040/245] Update KOSTAL-RS485.cpp Add contactor test cycle. --- Software/src/inverter/KOSTAL-RS485.cpp | 56 ++++++++++++++++---------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/Software/src/inverter/KOSTAL-RS485.cpp b/Software/src/inverter/KOSTAL-RS485.cpp index ef5dfc6b..94bf8729 100644 --- a/Software/src/inverter/KOSTAL-RS485.cpp +++ b/Software/src/inverter/KOSTAL-RS485.cpp @@ -154,24 +154,18 @@ void KostalInverterProtocol::update_values() { float2frame(CYCLIC_DATA, (float)datalayer.battery.status.current_dA / 10, 18); // Last current float2frame(CYCLIC_DATA, (float)datalayer.battery.status.current_dA / 10, 22); // Should be Avg current(1s) - // Close contactors after 7 battery info frames requested - if (f2_startup_count > 7) { + // Close contactors after 20 battery info frames requested + if (f2_startup_count > 20) { datalayer.system.status.inverter_allows_contactor_closing = true; - dbg_message("inverter_allows_contactor_closing -> true"); + dbg_message("inverter_allows_contactor_closing -> true (info frame)"); } - // On startup, byte 56 seems to be always 0x00 couple of frames,. - if (f2_startup_count < 9) { - CYCLIC_DATA[56] = 0x00; - } else { + if (datalayer.system.status.inverter_allows_contactor_closing) { CYCLIC_DATA[56] = 0x01; - } - - // On startup, byte 59 seems to be always 0x02 couple of frames,. - if (f2_startup_count < 14) { - CYCLIC_DATA[59] = 0x02; - } else { CYCLIC_DATA[59] = 0x00; + } else { + CYCLIC_DATA[56] = 0x00; + CYCLIC_DATA[59] = 0x02; } #endif @@ -214,6 +208,12 @@ void KostalInverterProtocol::receive() // Runs as fast as possible to handle th { currentMillis = millis(); + // Auto-reset contactor_test_active after 5 seconds + if (contactortestTimerActive && (millis() - contactortestTimerStart >= 5000)) { + datalayer.system.status.inverter_allows_contactor_closing = true; + dbg_message("inverter_allows_contactor_closing -> true (Contactor test ended)"); + contactortestTimerActive = false; + } if (datalayer.system.status.battery_allows_contactor_closing & !contactorMillis) { contactorMillis = currentMillis; } @@ -232,7 +232,7 @@ void KostalInverterProtocol::receive() // Runs as fast as possible to handle th if (check_kostal_frame_crc(rx_index)) { incoming_message_counter = RS485_HEALTHY; - if (RS485_RXFRAME[1] == 'c' && info_sent) { + if (RS485_RXFRAME[1] == 0x63) { if (RS485_RXFRAME[6] == 0x47) { // Set time function - Do nothing. send_kostal(ACK_FRAME, 8); // ACK @@ -240,11 +240,20 @@ void KostalInverterProtocol::receive() // Runs as fast as possible to handle th if (RS485_RXFRAME[6] == 0x5E) { // Set State function if (RS485_RXFRAME[7] == 0x00) { + // Allow contactor closing + datalayer.system.status.inverter_allows_contactor_closing = true; + dbg_message("inverter_allows_contactor_closing -> true (5E 02)"); send_kostal(ACK_FRAME, 8); // ACK } else if (RS485_RXFRAME[7] == 0x04) { + // contactor test STATE, ACK sent + datalayer.system.status.inverter_allows_contactor_closing = false; + dbg_message("inverter_allows_contactor_closing -> false (Contactor test start)"); + send_kostal(ACK_FRAME, 8); // ACK + contactortestTimerStart = millis(); + contactortestTimerActive = true; + } else if (RS485_RXFRAME[7] == 0x00) { + // ERROR STATE, ACK sent send_kostal(ACK_FRAME, 8); // ACK - } else if (RS485_RXFRAME[7] == 0xFF) { - // no ACK sent } else { // Battery deep sleep? send_kostal(ACK_FRAME, 8); // ACK @@ -255,7 +264,7 @@ void KostalInverterProtocol::receive() // Runs as fast as possible to handle th //Reverse polarity, do nothing } else { int code = RS485_RXFRAME[6] + RS485_RXFRAME[7] * 0x100; - if (code == 0x44a && info_sent) { + if (code == 0x44a) { //Send cyclic data // TODO: Probably not a good idea to use the battery object here like this. if (battery) { @@ -265,7 +274,7 @@ void KostalInverterProtocol::receive() // Runs as fast as possible to handle th if (f2_startup_count < 15) { f2_startup_count++; } - uint8_t tmpframe[64]; //copy values to prevent data manipulation during rewrite/crc calculation + byte tmpframe[64]; //copy values to prevent data manipulation during rewrite/crc calculation memcpy(tmpframe, CYCLIC_DATA, 64); tmpframe[62] = calculate_kostal_crc(tmpframe, 62); null_stuffer(tmpframe, 64); @@ -274,19 +283,22 @@ void KostalInverterProtocol::receive() // Runs as fast as possible to handle th } if (code == 0x84a) { //Send battery info - uint8_t tmpframe[40]; //copy values to prevent data manipulation during rewrite/crc calculation + byte tmpframe[40]; //copy values to prevent data manipulation during rewrite/crc calculation memcpy(tmpframe, BATTERY_INFO, 40); tmpframe[38] = calculate_kostal_crc(tmpframe, 38); null_stuffer(tmpframe, 40); send_kostal(tmpframe, 40); - info_sent = true; + datalayer.system.status.inverter_allows_contactor_closing = false; + dbg_message("inverter_allows_contactor_closing -> false (battery info sent)"); + + // Kun første gang sættes startupMillis if (!startupMillis) { startupMillis = currentMillis; } } - if (code == 0x353 && info_sent) { + if (code == 0x353) { //Send battery error/status - uint8_t tmpframe[9]; //copy values to prevent data manipulation during rewrite/crc calculation + byte tmpframe[9]; //copy values to prevent data manipulation during rewrite/crc calculation memcpy(tmpframe, STATUS_FRAME, 9); tmpframe[7] = calculate_kostal_crc(tmpframe, 7); null_stuffer(tmpframe, 9); From 6645f3c1f3c572214c7056ce671a941569f98c2a Mon Sep 17 00:00:00 2001 From: mbuhansen <91631350+mbuhansen@users.noreply.github.com> Date: Wed, 20 Aug 2025 16:38:02 +0200 Subject: [PATCH 041/245] Update KOSTAL-RS485.h --- Software/src/inverter/KOSTAL-RS485.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Software/src/inverter/KOSTAL-RS485.h b/Software/src/inverter/KOSTAL-RS485.h index a14d5dca..19c4137d 100644 --- a/Software/src/inverter/KOSTAL-RS485.h +++ b/Software/src/inverter/KOSTAL-RS485.h @@ -43,6 +43,8 @@ class KostalInverterProtocol : public Rs485InverterProtocol { unsigned long currentMillis; unsigned long startupMillis = 0; unsigned long contactorMillis = 0; + unsigned long contactortestTimerStart = 0; + bool contactortestTimerActive = false; uint16_t rx_index = 0; bool RX_allow = false; From 93556780eb915578315ad9525ae7530214157337 Mon Sep 17 00:00:00 2001 From: mbuhansen <91631350+mbuhansen@users.noreply.github.com> Date: Wed, 20 Aug 2025 16:49:21 +0200 Subject: [PATCH 042/245] Update KOSTAL-RS485.cpp --- Software/src/inverter/KOSTAL-RS485.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Software/src/inverter/KOSTAL-RS485.cpp b/Software/src/inverter/KOSTAL-RS485.cpp index 94bf8729..4bc32e92 100644 --- a/Software/src/inverter/KOSTAL-RS485.cpp +++ b/Software/src/inverter/KOSTAL-RS485.cpp @@ -254,6 +254,8 @@ void KostalInverterProtocol::receive() // Runs as fast as possible to handle th } else if (RS485_RXFRAME[7] == 0x00) { // ERROR STATE, ACK sent send_kostal(ACK_FRAME, 8); // ACK + } else if (RS485_RXFRAME[7] == 0xFF) { + // no ACK sent } else { // Battery deep sleep? send_kostal(ACK_FRAME, 8); // ACK From d86a9bd3641cae36efee7692c5816fe9bb69ad8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 20 Aug 2025 21:13:45 +0300 Subject: [PATCH 043/245] Add HVIL status to webserver --- Software/src/battery/VOLVO-SPA-BATTERY.cpp | 26 +++++++-- Software/src/battery/VOLVO-SPA-BATTERY.h | 12 +++-- Software/src/battery/VOLVO-SPA-HTML.h | 59 ++++++++++++++++++--- Software/src/datalayer/datalayer_extended.h | 2 + 4 files changed, 83 insertions(+), 16 deletions(-) diff --git a/Software/src/battery/VOLVO-SPA-BATTERY.cpp b/Software/src/battery/VOLVO-SPA-BATTERY.cpp index dabd9f14..8b28d0df 100644 --- a/Software/src/battery/VOLVO-SPA-BATTERY.cpp +++ b/Software/src/battery/VOLVO-SPA-BATTERY.cpp @@ -7,7 +7,6 @@ void VolvoSpaBattery:: update_values() { //This function maps all the values fetched via CAN to the correct parameters used for the inverter - uint8_t cnt = 0; // Update webserver datalayer datalayer_extended.VolvoPolestar.soc_bms = SOC_BMS; @@ -88,6 +87,7 @@ void VolvoSpaBattery:: } #ifdef DEBUG_LOG + uint8_t cnt = 0; logging.print("BMS reported SOC%: "); logging.println(SOC_BMS); logging.print("Calculated SOC%: "); @@ -127,6 +127,7 @@ void VolvoSpaBattery:: logging.print(cell_voltages[cnt++]); logging.print(","); } + cnt = 0; logging.println(";"); #endif } @@ -272,18 +273,33 @@ void VolvoSpaBattery::handle_incoming_can_frame(CAN_frame rx_frame) { (rx_frame.data.u8[3] == 0x42)) // BECM module voltage supply { datalayer_extended.VolvoPolestar.BECMsupplyVoltage = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); + transmit_can_frame(&VOLVO_BECM_HVIL_Status_Req); //Send HVIL status readout command + } else if ((rx_frame.data.u8[0] == 0x04) && (rx_frame.data.u8[1] == 0x62) && (rx_frame.data.u8[2] == 0x49) && + (rx_frame.data.u8[3] == 0x1A)) // BECM HVIL status + { + datalayer_extended.VolvoPolestar.HVILstatusBits = (rx_frame.data.u8[4]); + transmit_can_frame(&VOLVO_DTCreadout); //Send DTC readout command } else if ((rx_frame.data.u8[0] == 0x10) && (rx_frame.data.u8[1] == 0x0B) && (rx_frame.data.u8[2] == 0x62) && (rx_frame.data.u8[3] == 0x4B)) // First response frame of cell voltages { cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6]); cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8); transmit_can_frame(&VOLVO_FlowControl); // Send flow control - rxConsecutiveFrames = 1; + rxConsecutiveFrames = true; } else if ((rx_frame.data.u8[0] == 0x10) && (rx_frame.data.u8[2] == 0x59) && (rx_frame.data.u8[3] == 0x03)) // First response frame for DTC with more than one code { + datalayer_extended.VolvoPolestar.DTCcount = ((rx_frame.data.u8[1] - 2) / 4); transmit_can_frame(&VOLVO_FlowControl); // Send flow control - } else if ((rx_frame.data.u8[0] == 0x21) && (rxConsecutiveFrames == 1)) { + } else if ((rx_frame.data.u8[1] == 0x59) && + (rx_frame.data.u8[2] == 0x03)) // Response frame for DTC with 0 or 1 code + { + if (rx_frame.data.u8[0] != 0x02) { + datalayer_extended.VolvoPolestar.DTCcount = 1; + } else { + datalayer_extended.VolvoPolestar.DTCcount = 0; + } + } else if ((rx_frame.data.u8[0] == 0x21) && (rxConsecutiveFrames)) { cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1]; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; @@ -303,7 +319,7 @@ void VolvoSpaBattery::handle_incoming_can_frame(CAN_frame rx_frame) { } transmit_can_frame(&VOLVO_SOH_Req); //Send SOH read request } - rxConsecutiveFrames = 0; + rxConsecutiveFrames = false; } break; default: @@ -314,7 +330,7 @@ void VolvoSpaBattery::handle_incoming_can_frame(CAN_frame rx_frame) { void VolvoSpaBattery::readCellVoltages() { battery_request_idx = 0; batteryModuleNumber = 0x10; - rxConsecutiveFrames = 0; + rxConsecutiveFrames = false; VOLVO_CELL_U_Req.data.u8[3] = batteryModuleNumber++; transmit_can_frame(&VOLVO_CELL_U_Req); //Send cell voltage read request for first module } diff --git a/Software/src/battery/VOLVO-SPA-BATTERY.h b/Software/src/battery/VOLVO-SPA-BATTERY.h index f9264f66..b5d4fcad 100644 --- a/Software/src/battery/VOLVO-SPA-BATTERY.h +++ b/Software/src/battery/VOLVO-SPA-BATTERY.h @@ -64,7 +64,7 @@ class VolvoSpaBattery : public CanBattery { uint16_t HvBattPwrLimChrgSlowAgi = 0; //0x177 uint8_t batteryModuleNumber = 0x10; // First battery module uint8_t battery_request_idx = 0; - uint8_t rxConsecutiveFrames = 0; + bool rxConsecutiveFrames = false; uint16_t min_max_voltage[2]; //contains cell min[0] and max[1] values in mV uint8_t cellcounter = 0; uint16_t cell_voltages[108]; //array with all the cellvoltages @@ -75,21 +75,17 @@ class VolvoSpaBattery : public CanBattery { .ext_ID = false, .DLC = 8, .ID = 0x536, - //.data = {0x00, 0x40, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00}}; //Network manage frame .data = {0x00, 0x40, 0x40, 0x01, 0x00, 0x00, 0x00, 0x00}}; //Network manage frame - CAN_frame VOLVO_140_CLOSE = {.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x140, .data = {0x00, 0x02, 0x00, 0xB7, 0xFF, 0x03, 0xFF, 0x82}}; //Close contactors message - CAN_frame VOLVO_140_OPEN = {.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x140, .data = {0x00, 0x02, 0x00, 0x9E, 0xFF, 0x03, 0xFF, 0x82}}; //Open contactor message - CAN_frame VOLVO_372 = { .FD = false, .ext_ID = false, @@ -117,6 +113,12 @@ class VolvoSpaBattery : public CanBattery { .DLC = 8, .ID = 0x735, .data = {0x03, 0x22, 0xF4, 0x42, 0x00, 0x00, 0x00, 0x00}}; //BECM supply voltage request frame + CAN_frame VOLVO_BECM_HVIL_Status_Req = { + .FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x735, + .data = {0x03, 0x22, 0x49, 0x1A, 0x00, 0x00, 0x00, 0x00}}; //BECM HVIL status request frame CAN_frame VOLVO_DTC_Erase = {.FD = false, .ext_ID = false, .DLC = 8, diff --git a/Software/src/battery/VOLVO-SPA-HTML.h b/Software/src/battery/VOLVO-SPA-HTML.h index dd65c9a2..9d832d51 100644 --- a/Software/src/battery/VOLVO-SPA-HTML.h +++ b/Software/src/battery/VOLVO-SPA-HTML.h @@ -9,11 +9,11 @@ class VolvoSpaHtmlRenderer : public BatteryHtmlRenderer { public: String get_status_html() { String content; - - content += "

BECM reported SOC: " + String(datalayer_extended.VolvoPolestar.soc_bms) + "

"; - content += "

Calculated SOC: " + String(datalayer_extended.VolvoPolestar.soc_calc) + "

"; - content += "

Rescaled SOC: " + String(datalayer_extended.VolvoPolestar.soc_rescaled / 10) + "

"; - content += "

BECM reported SOH: " + String(datalayer_extended.VolvoPolestar.soh_bms) + "

"; + content += "

BECM reported number of DTCs: " + String(datalayer_extended.VolvoPolestar.DTCcount) + "

"; + content += "

BECM reported SOC: " + String(datalayer_extended.VolvoPolestar.soc_bms / 10.0) + " %

"; + content += "

Calculated SOC: " + String(datalayer_extended.VolvoPolestar.soc_calc / 10.0) + " %

"; + content += "

Rescaled SOC: " + String(datalayer_extended.VolvoPolestar.soc_rescaled / 10.0) + " %

"; + content += "

BECM reported SOH: " + String(datalayer_extended.VolvoPolestar.soh_bms / 100.0) + " %

"; content += "

BECM supply voltage: " + String(datalayer_extended.VolvoPolestar.BECMsupplyVoltage) + " mV

"; content += "

HV voltage: " + String(datalayer_extended.VolvoPolestar.BECMBatteryVoltage) + " V

"; @@ -31,7 +31,54 @@ class VolvoSpaHtmlRenderer : public BatteryHtmlRenderer { content += "

Charge power limit slow aging: " + String(datalayer_extended.VolvoPolestar.HvBattPwrLimChrgSlowAgi) + " kW

"; - + content += "

HVIL Circuit A status: "; + switch (datalayer_extended.VolvoPolestar.HVILstatusBits & 0x01) { + case 0x01: + content += String("Open"); + break; + default: + content += String("Not valid"); + } + content += "

HVIL Circuit B status: "; + switch (datalayer_extended.VolvoPolestar.HVILstatusBits & 0x02) { + case 0x02: + content += String("Open"); + break; + default: + content += String("Closed"); + } + content += "

HVIL Circuit C status: "; + switch (datalayer_extended.VolvoPolestar.HVILstatusBits & 0x04) { + case 0x04: + content += String("Open"); + break; + default: + content += String("Closed"); + } + content += "

Positive contactor status: "; + switch (datalayer_extended.VolvoPolestar.HVILstatusBits & 0x08) { + case 0x08: + content += String("Open"); + break; + default: + content += String("Closed"); + } + content += "

Precharge Contactor status: "; + switch (datalayer_extended.VolvoPolestar.HVILstatusBits & 0x10) { + case 0x10: + content += String("Open"); + break; + default: + content += String("Closed"); + } + content += "

Negative Contactor status: "; + switch (datalayer_extended.VolvoPolestar.HVILstatusBits & 0x20) { + case 0x20: + content += String("Open"); + break; + default: + content += String("Closed"); + } content += "

HV system relay status: "; switch (datalayer_extended.VolvoPolestar.HVSysRlySts) { case 0: diff --git a/Software/src/datalayer/datalayer_extended.h b/Software/src/datalayer/datalayer_extended.h index 165588d4..091316da 100644 --- a/Software/src/datalayer/datalayer_extended.h +++ b/Software/src/datalayer/datalayer_extended.h @@ -783,6 +783,8 @@ struct DATALAYER_INFO_VOLVO_POLESTAR { uint8_t HVSysDCRlySts1 = 0; uint8_t HVSysDCRlySts2 = 0; uint8_t HVSysIsoRMonrSts = 0; + uint8_t DTCcount = 0; + uint8_t HVILstatusBits = 0; /** User requesting DTC reset via WebUI*/ bool UserRequestDTCreset = false; /** User requesting DTC readout via WebUI*/ From 2bad7682639dcdc60f6697122fe5ce213add0994 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 20 Aug 2025 21:22:58 +0300 Subject: [PATCH 044/245] Add Event for HVIL not seated --- Software/src/battery/VOLVO-SPA-BATTERY.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Software/src/battery/VOLVO-SPA-BATTERY.cpp b/Software/src/battery/VOLVO-SPA-BATTERY.cpp index 8b28d0df..ab6a9e3d 100644 --- a/Software/src/battery/VOLVO-SPA-BATTERY.cpp +++ b/Software/src/battery/VOLVO-SPA-BATTERY.cpp @@ -86,6 +86,13 @@ void VolvoSpaBattery:: } } + //Raise event if HVIL connector not seated + if ((datalayer_extended.VolvoPolestar.HVILstatusBits & 0x03) > 0) { + set_event(EVENT_HVIL_FAILURE, datalayer_extended.VolvoPolestar.HVILstatusBits); + } else { + clear_event(EVENT_HVIL_FAILURE); + } + #ifdef DEBUG_LOG uint8_t cnt = 0; logging.print("BMS reported SOC%: "); From 56bfcbe66410f8465b38efa8fb7d67d52fa2ffe5 Mon Sep 17 00:00:00 2001 From: mbuhansen <91631350+mbuhansen@users.noreply.github.com> Date: Wed, 20 Aug 2025 21:01:54 +0200 Subject: [PATCH 045/245] Update KOSTAL-RS485.cpp --- Software/src/inverter/KOSTAL-RS485.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/Software/src/inverter/KOSTAL-RS485.cpp b/Software/src/inverter/KOSTAL-RS485.cpp index 4bc32e92..eb982f20 100644 --- a/Software/src/inverter/KOSTAL-RS485.cpp +++ b/Software/src/inverter/KOSTAL-RS485.cpp @@ -232,7 +232,7 @@ void KostalInverterProtocol::receive() // Runs as fast as possible to handle th if (check_kostal_frame_crc(rx_index)) { incoming_message_counter = RS485_HEALTHY; - if (RS485_RXFRAME[1] == 0x63) { + if (RS485_RXFRAME[1] == 'c' && info_sent) { if (RS485_RXFRAME[6] == 0x47) { // Set time function - Do nothing. send_kostal(ACK_FRAME, 8); // ACK @@ -251,9 +251,6 @@ void KostalInverterProtocol::receive() // Runs as fast as possible to handle th send_kostal(ACK_FRAME, 8); // ACK contactortestTimerStart = millis(); contactortestTimerActive = true; - } else if (RS485_RXFRAME[7] == 0x00) { - // ERROR STATE, ACK sent - send_kostal(ACK_FRAME, 8); // ACK } else if (RS485_RXFRAME[7] == 0xFF) { // no ACK sent } else { @@ -266,7 +263,7 @@ void KostalInverterProtocol::receive() // Runs as fast as possible to handle th //Reverse polarity, do nothing } else { int code = RS485_RXFRAME[6] + RS485_RXFRAME[7] * 0x100; - if (code == 0x44a) { + if (code == 0x44a && info_sent) { //Send cyclic data // TODO: Probably not a good idea to use the battery object here like this. if (battery) { @@ -276,7 +273,7 @@ void KostalInverterProtocol::receive() // Runs as fast as possible to handle th if (f2_startup_count < 15) { f2_startup_count++; } - byte tmpframe[64]; //copy values to prevent data manipulation during rewrite/crc calculation + uint8_t tmpframe[64]; //copy values to prevent data manipulation during rewrite/crc calculation memcpy(tmpframe, CYCLIC_DATA, 64); tmpframe[62] = calculate_kostal_crc(tmpframe, 62); null_stuffer(tmpframe, 64); @@ -285,7 +282,7 @@ void KostalInverterProtocol::receive() // Runs as fast as possible to handle th } if (code == 0x84a) { //Send battery info - byte tmpframe[40]; //copy values to prevent data manipulation during rewrite/crc calculation + uint8_t tmpframe[40]; //copy values to prevent data manipulation during rewrite/crc calculation memcpy(tmpframe, BATTERY_INFO, 40); tmpframe[38] = calculate_kostal_crc(tmpframe, 38); null_stuffer(tmpframe, 40); @@ -300,7 +297,7 @@ void KostalInverterProtocol::receive() // Runs as fast as possible to handle th } if (code == 0x353) { //Send battery error/status - byte tmpframe[9]; //copy values to prevent data manipulation during rewrite/crc calculation + uint8_t tmpframe[9]; //copy values to prevent data manipulation during rewrite/crc calculation memcpy(tmpframe, STATUS_FRAME, 9); tmpframe[7] = calculate_kostal_crc(tmpframe, 7); null_stuffer(tmpframe, 9); From b8059de996d2bc36d568293eeba1ece34f31c780 Mon Sep 17 00:00:00 2001 From: mbuhansen <91631350+mbuhansen@users.noreply.github.com> Date: Wed, 20 Aug 2025 21:15:06 +0200 Subject: [PATCH 046/245] Update KOSTAL-RS485.cpp --- Software/src/inverter/KOSTAL-RS485.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Software/src/inverter/KOSTAL-RS485.cpp b/Software/src/inverter/KOSTAL-RS485.cpp index eb982f20..62ee8169 100644 --- a/Software/src/inverter/KOSTAL-RS485.cpp +++ b/Software/src/inverter/KOSTAL-RS485.cpp @@ -289,13 +289,12 @@ void KostalInverterProtocol::receive() // Runs as fast as possible to handle th send_kostal(tmpframe, 40); datalayer.system.status.inverter_allows_contactor_closing = false; dbg_message("inverter_allows_contactor_closing -> false (battery info sent)"); - - // Kun første gang sættes startupMillis + info_sent = true; if (!startupMillis) { startupMillis = currentMillis; } } - if (code == 0x353) { + if (code == 0x353 && info_sent) { //Send battery error/status uint8_t tmpframe[9]; //copy values to prevent data manipulation during rewrite/crc calculation memcpy(tmpframe, STATUS_FRAME, 9); From 980d914ffdc2d2be8f6418423af0c8a17326960d Mon Sep 17 00:00:00 2001 From: Matt Holmes Date: Thu, 21 Aug 2025 08:58:55 +0100 Subject: [PATCH 047/245] Adding event_level and event_level_color to mqtt information --- Software/src/devboard/mqtt/mqtt.cpp | 30 ++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/Software/src/devboard/mqtt/mqtt.cpp b/Software/src/devboard/mqtt/mqtt.cpp index 4a814230..5e94994d 100644 --- a/Software/src/devboard/mqtt/mqtt.cpp +++ b/Software/src/devboard/mqtt/mqtt.cpp @@ -141,7 +141,9 @@ SensorConfig batterySensorConfigTemplate[] = { {"balancing_active_cells", "Balancing Active Cells", "", "", "", always}}; SensorConfig globalSensorConfigTemplate[] = {{"bms_status", "BMS Status", "", "", "", always}, - {"pause_status", "Pause Status", "", "", "", always}}; + {"pause_status", "Pause Status", "", "", "", always}, + {"event_level", "Event Level", "", "", "", always}, + {"event_level_color", "Event Level Color", "", "", "", always}}; static std::list sensorConfigs; @@ -311,6 +313,32 @@ static bool publish_common_info(void) { set_battery_attributes(doc, datalayer.battery2, "_2", battery2->supports_charged_energy()); } } + + EVENTS_LEVEL_TYPE event_level = get_event_level(); + doc["event_level"] = String(event_level); + + // Use Home Assistant Colors https://github.com/home-assistant/core/blob/e2fdc6a98bdd22187688e70701fc3617423a714b/homeassistant/util/color.py#L19 + String event_level_color = ""; + switch (event_level) + { + case EVENT_LEVEL_INFO: + event_level_color = "green"; + break; + case EVENT_LEVEL_WARNING: + event_level_color = "yellow"; + break; + case EVENT_LEVEL_DEBUG: + case EVENT_LEVEL_UPDATE: + event_level_color = "blue"; + break; + case EVENT_LEVEL_ERROR: + event_level_color = "red"; + default: + event_level_color = "green"; + break; + } + doc["event_level_color"] = event_level_color; + serializeJson(doc, mqtt_msg); if (mqtt_publish(state_topic.c_str(), mqtt_msg, false) == false) { #ifdef DEBUG_LOG From d4f0e188fe7d09d4970fc8ff0ffac5b98af44845 Mon Sep 17 00:00:00 2001 From: Matt Holmes Date: Thu, 21 Aug 2025 17:33:15 +0100 Subject: [PATCH 048/245] Refactoring led handler, webserver and mqtt to all use a common BE status enum rather than relying on duplicatitng logic or using led color --- Software/src/devboard/mqtt/mqtt.cpp | 28 ++----------------- Software/src/devboard/utils/events.cpp | 27 ++++++++++++++++++ Software/src/devboard/utils/events.h | 11 ++++++++ Software/src/devboard/utils/led_handler.cpp | 18 ++++++------ Software/src/devboard/webserver/webserver.cpp | 15 ++++------ 5 files changed, 55 insertions(+), 44 deletions(-) diff --git a/Software/src/devboard/mqtt/mqtt.cpp b/Software/src/devboard/mqtt/mqtt.cpp index 5e94994d..0f7ca0b3 100644 --- a/Software/src/devboard/mqtt/mqtt.cpp +++ b/Software/src/devboard/mqtt/mqtt.cpp @@ -143,7 +143,7 @@ SensorConfig batterySensorConfigTemplate[] = { SensorConfig globalSensorConfigTemplate[] = {{"bms_status", "BMS Status", "", "", "", always}, {"pause_status", "Pause Status", "", "", "", always}, {"event_level", "Event Level", "", "", "", always}, - {"event_level_color", "Event Level Color", "", "", "", always}}; + {"emulator_status", "Emulator Status", "", "", "", always}}; static std::list sensorConfigs; @@ -314,30 +314,8 @@ static bool publish_common_info(void) { } } - EVENTS_LEVEL_TYPE event_level = get_event_level(); - doc["event_level"] = String(event_level); - - // Use Home Assistant Colors https://github.com/home-assistant/core/blob/e2fdc6a98bdd22187688e70701fc3617423a714b/homeassistant/util/color.py#L19 - String event_level_color = ""; - switch (event_level) - { - case EVENT_LEVEL_INFO: - event_level_color = "green"; - break; - case EVENT_LEVEL_WARNING: - event_level_color = "yellow"; - break; - case EVENT_LEVEL_DEBUG: - case EVENT_LEVEL_UPDATE: - event_level_color = "blue"; - break; - case EVENT_LEVEL_ERROR: - event_level_color = "red"; - default: - event_level_color = "green"; - break; - } - doc["event_level_color"] = event_level_color; + doc["event_level"] = get_event_level_string(get_event_level()); + doc["emulator_status"] = get_emulator_staus_string(get_emulator_status()); serializeJson(doc, mqtt_msg); if (mqtt_publish(state_topic.c_str(), mqtt_msg, false) == false) { diff --git a/Software/src/devboard/utils/events.cpp b/Software/src/devboard/utils/events.cpp index b5ba4a8f..2838cda0 100644 --- a/Software/src/devboard/utils/events.cpp +++ b/Software/src/devboard/utils/events.cpp @@ -14,6 +14,7 @@ typedef struct { static EVENT_TYPE events; static const char* EVENTS_ENUM_TYPE_STRING[] = {EVENTS_ENUM_TYPE(GENERATE_STRING)}; static const char* EVENTS_LEVEL_TYPE_STRING[] = {EVENTS_LEVEL_TYPE(GENERATE_STRING)}; +static const char* EMULATOR_STATUS_STRING[] = {EMULATOR_STATUS(GENERATE_STRING)}; /* Local function prototypes */ static void set_event(EVENTS_ENUM_TYPE event, uint8_t data, bool latched); @@ -393,6 +394,11 @@ const char* get_event_level_string(EVENTS_ENUM_TYPE event) { return EVENTS_LEVEL_TYPE_STRING[events.entries[event].level] + 12; } +const char* get_event_level_string(EVENTS_LEVEL_TYPE event_level) { + // Return the event level but skip "EVENT_LEVEL_TYPE_" that should always be first + return EVENTS_LEVEL_TYPE_STRING[event_level] + 17; +} + const EVENTS_STRUCT_TYPE* get_event_pointer(EVENTS_ENUM_TYPE event) { return &events.entries[event]; } @@ -401,6 +407,27 @@ EVENTS_LEVEL_TYPE get_event_level(void) { return events.level; } +EMULATOR_STATUS get_emulator_status() { + switch (events.level) { + case EVENT_LEVEL_DEBUG: + case EVENT_LEVEL_INFO: + return EMULATOR_STATUS::STATUS_OK; + case EVENT_LEVEL_WARNING: + return EMULATOR_STATUS::STATUS_WARNING; + case EVENT_LEVEL_UPDATE: + return EMULATOR_STATUS::STATUS_UPDATING; + case EVENT_LEVEL_ERROR: + return EMULATOR_STATUS::STATUS_ERROR; + default: + return EMULATOR_STATUS::STATUS_OK; + } +} + +const char* get_emulator_staus_string(EMULATOR_STATUS status) { + // Return the status string but skip "STATUS_" that should always be first + return EMULATOR_STATUS_STRING[status] + 7; +} + /* Local functions */ static void set_event(EVENTS_ENUM_TYPE event, uint8_t data, bool latched) { diff --git a/Software/src/devboard/utils/events.h b/Software/src/devboard/utils/events.h index 7048ee75..0a9d41d5 100644 --- a/Software/src/devboard/utils/events.h +++ b/Software/src/devboard/utils/events.h @@ -126,6 +126,14 @@ typedef enum { EVENTS_ENUM_TYPE(GENERATE_ENUM) } EVENTS_ENUM_TYPE; typedef enum { EVENTS_LEVEL_TYPE(GENERATE_ENUM) } EVENTS_LEVEL_TYPE; +#define EMULATOR_STATUS(XX) \ + XX(STATUS_OK) \ + XX(STATUS_WARNING) \ + XX(STATUS_ERROR) \ + XX(STATUS_UPDATING) \ + +typedef enum { EMULATOR_STATUS(GENERATE_ENUM) } EMULATOR_STATUS; + typedef enum { EVENT_STATE_PENDING = 0, EVENT_STATE_INACTIVE, @@ -151,8 +159,11 @@ struct EventData { const char* get_event_enum_string(EVENTS_ENUM_TYPE event); String get_event_message_string(EVENTS_ENUM_TYPE event); const char* get_event_level_string(EVENTS_ENUM_TYPE event); +const char* get_event_level_string(EVENTS_LEVEL_TYPE event_level); EVENTS_LEVEL_TYPE get_event_level(void); +EMULATOR_STATUS get_emulator_status(); +const char* get_emulator_staus_string(EMULATOR_STATUS status); void init_events(void); void set_event_latched(EVENTS_ENUM_TYPE event, uint8_t data); diff --git a/Software/src/devboard/utils/led_handler.cpp b/Software/src/devboard/utils/led_handler.cpp index 73fefb4d..0440723e 100644 --- a/Software/src/devboard/utils/led_handler.cpp +++ b/Software/src/devboard/utils/led_handler.cpp @@ -53,27 +53,25 @@ void LED::exe(void) { } // Set color - switch (get_event_level()) { - case EVENT_LEVEL_INFO: + switch (get_emulator_status()) { + case EMULATOR_STATUS::STATUS_OK: color = led_color::GREEN; pixels.setPixelColor(COLOR_GREEN(brightness)); // Green pulsing LED break; - case EVENT_LEVEL_WARNING: + case EMULATOR_STATUS::STATUS_WARNING: color = led_color::YELLOW; pixels.setPixelColor(COLOR_YELLOW(brightness)); // Yellow pulsing LED break; - case EVENT_LEVEL_DEBUG: - case EVENT_LEVEL_UPDATE: - color = led_color::BLUE; - pixels.setPixelColor(COLOR_BLUE(brightness)); // Blue pulsing LED - break; - case EVENT_LEVEL_ERROR: + case EMULATOR_STATUS::STATUS_ERROR: color = led_color::RED; pixels.setPixelColor(COLOR_RED(esp32hal->LED_MAX_BRIGHTNESS())); // Red LED full brightness break; - default: + case EMULATOR_STATUS::STATUS_UPDATING: + color = led_color::BLUE; + pixels.setPixelColor(COLOR_BLUE(brightness)); // Blue pulsing LED break; } + pixels.show(); // This sends the updated pixel color to the hardware. } diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 4949bd86..95181360 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -943,21 +943,18 @@ String processor(const String& var) { content += "
= messageDelays[messageIndex]) { + + // Transmit the current message + transmit_can_frame(messages[messageIndex]); + + // Move to the next message + messageIndex++; + } + } + + if (messageIndex >= 63) { + startMillis = currentMillis; // Start over! + messageIndex = 0; + } + + //Send 200ms CANFD message + if (currentMillis - previousMillis200ms >= INTERVAL_200_MS) { + previousMillis200ms = currentMillis; + + KIA64FD_7E4.data.u8[3] = KIA_7E4_COUNTER; + + if (ok_start_polling_battery) { + transmit_can_frame(&KIA64FD_7E4); + } + + KIA_7E4_COUNTER++; + if (KIA_7E4_COUNTER > 0x0D) { // gets up to 0x010C before repeating + KIA_7E4_COUNTER = 0x01; + } + } + //Send 10s CANFD message + if (currentMillis - previousMillis10s >= INTERVAL_10_S) { + previousMillis10s = currentMillis; + + ok_start_polling_battery = true; + } + } +} + +void Kia64FDBattery::setup(void) { // Performs one time setup at startup + strncpy(datalayer.system.info.battery_protocol, Name, 63); + datalayer.system.info.battery_protocol[63] = '\0'; + datalayer.system.status.battery_allows_contactor_closing = true; + datalayer.battery.info.number_of_cells = 96; + datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; + datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; + datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; + datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; + datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV; +} diff --git a/Software/src/battery/KIA-64FD-BATTERY.h b/Software/src/battery/KIA-64FD-BATTERY.h new file mode 100644 index 00000000..2b2806c9 --- /dev/null +++ b/Software/src/battery/KIA-64FD-BATTERY.h @@ -0,0 +1,635 @@ +#ifndef KIA_64_FD_BATTERY_H +#define KIA_64_FD_BATTERY_H +#include +#include "CanBattery.h" + +#define ESTIMATE_SOC_FROM_CELLVOLTAGE + +#ifdef KIA_HYUNDAI_64_FD_BATTERY +#define SELECTED_BATTERY_CLASS Kia64FDBattery +#endif + +class Kia64FDBattery : public CanBattery { + public: + virtual void setup(void); + virtual void handle_incoming_can_frame(CAN_frame rx_frame); + virtual void update_values(); + virtual void transmit_can(unsigned long currentMillis); + static constexpr const char* Name = "Kia 64kWh FD battery"; + + private: + uint16_t estimateSOC(uint16_t packVoltage, uint16_t cellCount, int16_t currentAmps); + uint16_t estimateSOCFromCell(uint16_t cellVoltage); + uint8_t calculateCRC(CAN_frame rx_frame, uint8_t length, uint8_t initial_value); + uint16_t selectSOC(uint16_t SOC_low, uint16_t SOC_high); + + static const int MAX_PACK_VOLTAGE_DV = 4032; //5000 = 500.0V + static const int MIN_PACK_VOLTAGE_DV = 2400; + static const int MAX_CELL_DEVIATION_MV = 150; + static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value + static const int MIN_CELL_VOLTAGE_MV = 2950; //Battery is put into emergency stop if one cell goes below this value + static const int MAXCHARGEPOWERALLOWED = 10000; + static const int MAXDISCHARGEPOWERALLOWED = 10000; + static const int RAMPDOWN_SOC = 9000; // 90.00 SOC% to start ramping down from max charge power towards 0 at 100.00% + static const int RAMPDOWNPOWERALLOWED = 10000; // What power we ramp down from towards top balancing + + // Used for SoC compensation - Define internal resistance value in milliohms for the entire pack + // How to calculate: voltage_drop_under_known_load [Volts] / load [Amps] = Resistance + static const int PACK_INTERNAL_RESISTANCE_MOHM = 200; // 200 milliohms for the whole pack + + unsigned long previousMillis200ms = 0; // will store last time a 200ms CAN Message was send + unsigned long previousMillis10s = 0; // will store last time a 10s CAN Message was send + + uint16_t inverterVoltageFrameHigh = 0; + uint16_t inverterVoltage = 0; + uint16_t soc_calculated = 0; + uint16_t SOC_BMS = 0; + uint16_t SOC_Display = 0; + uint16_t SOC_estimated_lowest = 0; + uint16_t SOC_estimated_highest = 0; + uint16_t batterySOH = 1000; + uint16_t CellVoltMax_mV = 3700; + uint16_t CellVoltMin_mV = 3700; + uint16_t batteryVoltage = 0; + int16_t leadAcidBatteryVoltage = 120; + int16_t batteryAmps = 0; + int16_t temperatureMax = 0; + int16_t temperatureMin = 0; + int16_t allowedDischargePower = 0; + int16_t allowedChargePower = 0; + int16_t poll_data_pid = 0; + uint8_t CellVmaxNo = 0; + uint8_t CellVminNo = 0; + uint8_t batteryManagementMode = 0; + uint8_t BMS_ign = 0; + bool startedUp = false; + bool ok_start_polling_battery = false; + uint8_t KIA_7E4_COUNTER = 0x01; + int8_t temperature_water_inlet = 0; + int8_t heatertemp = 0; + unsigned long startMillis = 0; + uint8_t messageIndex = 0; + + const unsigned char crc8_table[256] = { + // CRC8_SAE_J1850_ZER0 formula,0x1D Poly,initial value 0x3F,Final XOR value varies + 0x00, 0x1D, 0x3A, 0x27, 0x74, 0x69, 0x4E, 0x53, 0xE8, 0xF5, 0xD2, 0xCF, 0x9C, 0x81, 0xA6, 0xBB, 0xCD, 0xD0, 0xF7, + 0xEA, 0xB9, 0xA4, 0x83, 0x9E, 0x25, 0x38, 0x1F, 0x02, 0x51, 0x4C, 0x6B, 0x76, 0x87, 0x9A, 0xBD, 0xA0, 0xF3, 0xEE, + 0xC9, 0xD4, 0x6F, 0x72, 0x55, 0x48, 0x1B, 0x06, 0x21, 0x3C, 0x4A, 0x57, 0x70, 0x6D, 0x3E, 0x23, 0x04, 0x19, 0xA2, + 0xBF, 0x98, 0x85, 0xD6, 0xCB, 0xEC, 0xF1, 0x13, 0x0E, 0x29, 0x34, 0x67, 0x7A, 0x5D, 0x40, 0xFB, 0xE6, 0xC1, 0xDC, + 0x8F, 0x92, 0xB5, 0xA8, 0xDE, 0xC3, 0xE4, 0xF9, 0xAA, 0xB7, 0x90, 0x8D, 0x36, 0x2B, 0x0C, 0x11, 0x42, 0x5F, 0x78, + 0x65, 0x94, 0x89, 0xAE, 0xB3, 0xE0, 0xFD, 0xDA, 0xC7, 0x7C, 0x61, 0x46, 0x5B, 0x08, 0x15, 0x32, 0x2F, 0x59, 0x44, + 0x63, 0x7E, 0x2D, 0x30, 0x17, 0x0A, 0xB1, 0xAC, 0x8B, 0x96, 0xC5, 0xD8, 0xFF, 0xE2, 0x26, 0x3B, 0x1C, 0x01, 0x52, + 0x4F, 0x68, 0x75, 0xCE, 0xD3, 0xF4, 0xE9, 0xBA, 0xA7, 0x80, 0x9D, 0xEB, 0xF6, 0xD1, 0xCC, 0x9F, 0x82, 0xA5, 0xB8, + 0x03, 0x1E, 0x39, 0x24, 0x77, 0x6A, 0x4D, 0x50, 0xA1, 0xBC, 0x9B, 0x86, 0xD5, 0xC8, 0xEF, 0xF2, 0x49, 0x54, 0x73, + 0x6E, 0x3D, 0x20, 0x07, 0x1A, 0x6C, 0x71, 0x56, 0x4B, 0x18, 0x05, 0x22, 0x3F, 0x84, 0x99, 0xBE, 0xA3, 0xF0, 0xED, + 0xCA, 0xD7, 0x35, 0x28, 0x0F, 0x12, 0x41, 0x5C, 0x7B, 0x66, 0xDD, 0xC0, 0xE7, 0xFA, 0xA9, 0xB4, 0x93, 0x8E, 0xF8, + 0xE5, 0xC2, 0xDF, 0x8C, 0x91, 0xB6, 0xAB, 0x10, 0x0D, 0x2A, 0x37, 0x64, 0x79, 0x5E, 0x43, 0xB2, 0xAF, 0x88, 0x95, + 0xC6, 0xDB, 0xFC, 0xE1, 0x5A, 0x47, 0x60, 0x7D, 0x2E, 0x33, 0x14, 0x09, 0x7F, 0x62, 0x45, 0x58, 0x0B, 0x16, 0x31, + 0x2C, 0x97, 0x8A, 0xAD, 0xB0, 0xE3, 0xFE, 0xD9, 0xC4}; + + // Define the data points for %SOC depending on cell voltage + const uint8_t numPoints = 100; + + const uint16_t SOC[101] = {10000, 9900, 9800, 9700, 9600, 9500, 9400, 9300, 9200, 9100, 9000, 8900, 8800, 8700, 8600, + 8500, 8400, 8300, 8200, 8100, 8000, 7900, 7800, 7700, 7600, 7500, 7400, 7300, 7200, 7100, + 7000, 6900, 6800, 6700, 6600, 6500, 6400, 6300, 6200, 6100, 6000, 5900, 5800, 5700, 5600, + 5500, 5400, 5300, 5200, 5100, 5000, 4900, 4800, 4700, 4600, 4500, 4400, 4300, 4200, 4100, + 4000, 3900, 3800, 3700, 3600, 3500, 3400, 3300, 3200, 3100, 3000, 2900, 2800, 2700, 2600, + 2500, 2400, 2300, 2200, 2100, 2000, 1900, 1800, 1700, 1600, 1500, 1400, 1300, 1200, 1100, + 1000, 900, 800, 700, 600, 500, 400, 300, 200, 100, 0}; + + const uint16_t voltage[101] = { + 4200, 4173, 4148, 4124, 4102, 4080, 4060, 4041, 4023, 4007, 3993, 3980, 3969, 3959, 3953, 3950, 3941, + 3932, 3924, 3915, 3907, 3898, 3890, 3881, 3872, 3864, 3855, 3847, 3838, 3830, 3821, 3812, 3804, 3795, + 3787, 3778, 3770, 3761, 3752, 3744, 3735, 3727, 3718, 3710, 3701, 3692, 3684, 3675, 3667, 3658, 3650, + 3641, 3632, 3624, 3615, 3607, 3598, 3590, 3581, 3572, 3564, 3555, 3547, 3538, 3530, 3521, 3512, 3504, + 3495, 3487, 3478, 3470, 3461, 3452, 3444, 3435, 3427, 3418, 3410, 3401, 3392, 3384, 3375, 3367, 3358, + 3350, 3338, 3325, 3313, 3299, 3285, 3271, 3255, 3239, 3221, 3202, 3180, 3156, 3127, 3090, 3000}; + /* These messages are needed for contactor closing */ + uint8_t messageDelays[63] = {0, 0, 5, 10, 10, 15, 19, 19, 20, 20, 25, 30, 30, 35, 40, 40, + 45, 49, 49, 50, 50, 52, 53, 53, 54, 55, 60, 60, 65, 67, 67, 70, + 70, 75, 77, 77, 80, 80, 85, 90, 90, 95, 100, 100, 105, 110, 110, 115, + 119, 119, 120, 120, 125, 130, 130, 135, 140, 140, 145, 149, 149, 150, 150}; + static constexpr CAN_frame message_1 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x62, 0x36, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_2 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0xd4, 0x1b, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_3 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x24, 0x9b, 0x7b, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + + static constexpr CAN_frame message_4 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x24, 0x6f, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_5 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x92, 0x42, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_6 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0xd7, 0x05, 0x7c, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + + static constexpr CAN_frame message_7 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x30A, + .data = {0xb1, 0xe0, 0x26, 0x08, 0x54, 0x01, 0x04, 0x15, 0x00, 0x1a, 0x76, 0x00, 0x25, 0x01, 0x10, 0x27, + 0x4f, 0x06, 0x18, 0x04, 0x33, 0x15, 0x34, 0x28, 0x00, 0x00, 0x10, 0x06, 0x21, 0x00, 0x4b, 0x06}}; + + static constexpr CAN_frame message_8 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x320, + .data = {0xc6, 0xab, 0x26, 0x41, 0x00, 0x00, 0x01, 0x3c, 0xac, 0x0d, 0x40, 0x20, 0x05, 0xc8, 0xa0, 0x03, + 0x40, 0x20, 0x2b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_9 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0xee, 0x84, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_10 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x58, 0xa9, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_11 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x91, 0x5c, 0x7d, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + + static constexpr CAN_frame message_12 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0xa8, 0xdd, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_13 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x1e, 0xf0, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_14 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x5b, 0xb7, 0x7e, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + + static constexpr CAN_frame message_15 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0xec, 0x6d, 0x90, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_16 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x5a, 0x40, 0x90, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_17 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x1d, 0xee, 0x7f, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + + static constexpr CAN_frame message_18 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x2B5, + .data = {0xbd, 0xb2, 0x42, 0x00, 0x00, 0x00, 0x00, 0x80, 0x59, 0x00, 0x2b, 0x00, 0x00, 0x04, 0x00, 0x00, + 0xfa, 0xd0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_19 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x2E0, + .data = {0xc1, 0xf2, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x70, 0x01, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_20 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0xaa, 0x34, 0x91, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_21 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x1c, 0x19, 0x91, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_22 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x2D5, + .data = {0x79, 0xfb, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_23 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x2EA, + .data = {0x6e, 0xbb, 0xa0, 0x0d, 0x04, 0x01, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_24 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x306, + .data = {0x00, 0x00, 0x00, 0xd2, 0x06, 0x92, 0x05, 0x34, 0x07, 0x8e, 0x08, 0x73, 0x05, 0x80, 0x05, 0x83, + 0x05, 0x73, 0x05, 0x80, 0x05, 0xed, 0x01, 0xdd, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_25 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x308, + .data = {0xbe, 0x84, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x75, 0x6c, 0x86, 0x0d, 0xfb, 0xdf, 0x03, 0x36, 0xc3, 0x86, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_26 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x6b, 0xa2, 0x80, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + + static constexpr CAN_frame message_27 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x60, 0xdf, 0x92, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_28 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0xd6, 0xf2, 0x92, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_29 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x2d, 0xfb, 0x81, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + + static constexpr CAN_frame message_30 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x33A, + .data = {0x1a, 0x23, 0x26, 0x10, 0x27, 0x4f, 0x06, 0x00, 0xf8, 0x1b, 0x19, 0x04, 0x30, 0x01, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x2e, 0x2d, 0x81, 0x25, 0x20, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_31 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x350, + .data = {0x26, 0x82, 0x26, 0xf4, 0x01, 0x00, 0x00, 0x50, 0x90, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_32 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x26, 0x86, 0x93, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_33 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x90, 0xab, 0x93, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_34 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0xe7, 0x10, 0x82, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + + static constexpr CAN_frame message_35 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x2E5, + .data = {0x69, 0x8a, 0x3f, 0x01, 0x00, 0x00, 0x00, 0x15, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_36 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x3B5, + .data = {0xa3, 0xc8, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x36, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_37 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0xd5, 0x18, 0x94, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_38 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x63, 0x35, 0x94, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_39 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0xa1, 0x49, 0x83, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + + static constexpr CAN_frame message_40 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x93, 0x41, 0x95, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_41 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x25, 0x6c, 0x95, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_42 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x52, 0xd7, 0x84, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + + static constexpr CAN_frame message_43 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x59, 0xaa, 0x96, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_44 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0xef, 0x87, 0x96, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_45 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x14, 0x8e, 0x85, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + + static constexpr CAN_frame message_46 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x1f, 0xf3, 0x97, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_47 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0xa9, 0xde, 0x97, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_48 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0xde, 0x65, 0x86, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + + static constexpr CAN_frame message_49 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x30A, + .data = {0xd3, 0x11, 0x27, 0x08, 0x54, 0x01, 0x04, 0x15, 0x00, 0x1a, 0x76, 0x00, 0x25, 0x01, 0x10, 0x27, + 0x4f, 0x06, 0x19, 0x04, 0x33, 0x15, 0x34, 0x28, 0x00, 0x00, 0x10, 0x06, 0x21, 0x00, 0x4b, 0x06}}; + + static constexpr CAN_frame message_50 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x320, + .data = {0x80, 0xf2, 0x27, 0x41, 0x00, 0x00, 0x01, 0x3c, 0xac, 0x0d, 0x40, 0x20, 0x05, 0xc8, 0xa0, 0x03, + 0x40, 0x20, 0x2b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_51 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x9e, 0x87, 0x98, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_52 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x28, 0xaa, 0x98, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_53 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x98, 0x3c, 0x87, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + + static constexpr CAN_frame message_54 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0xd8, 0xde, 0x99, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_55 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x6e, 0xf3, 0x99, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_56 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x19, 0x48, 0x88, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + + static constexpr CAN_frame message_57 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x12, 0x35, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_58 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0xa4, 0x18, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_59 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x5f, 0x11, 0x89, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + + static constexpr CAN_frame message_60 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x2B5, + .data = {0xfb, 0xeb, 0x43, 0x00, 0x00, 0x00, 0x00, 0x80, 0x59, 0x00, 0x2b, 0x00, 0x00, 0x04, 0x00, 0x00, + 0xfa, 0xd0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_61 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x2C0, + .data = {0xcc, 0xcd, 0xa2, 0x21, 0x00, 0xa1, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_62 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x2E0, + .data = {0x87, 0xab, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x70, 0x01, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + static constexpr CAN_frame message_63 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x54, 0x6c, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + const CAN_frame* messages[64] = { + &message_1, &message_2, &message_3, &message_4, &message_5, &message_6, &message_7, &message_8, + &message_9, &message_10, &message_11, &message_12, &message_13, &message_14, &message_15, &message_16, + &message_17, &message_18, &message_19, &message_20, &message_21, &message_22, &message_23, &message_24, + &message_25, &message_26, &message_27, &message_28, &message_29, &message_30, &message_31, &message_32, + &message_33, &message_34, &message_35, &message_36, &message_37, &message_38, &message_39, &message_40, + &message_41, &message_42, &message_43, &message_44, &message_45, &message_46, &message_47, &message_48, + &message_49, &message_50, &message_51, &message_52, &message_53, &message_54, &message_55, &message_56, + &message_57, &message_58, &message_59, &message_60, &message_61, &message_62, &message_63}; + + /* PID polling messages */ + CAN_frame KIA64FD_7E4 = {.FD = true, + .ext_ID = false, + .DLC = 8, + .ID = 0x7E4, + .data = {0x03, 0x22, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00}}; //Poll PID 03 22 01 01 + CAN_frame KIA64FD_ack = { + .FD = true, + .ext_ID = false, + .DLC = 8, + .ID = 0x7E4, + .data = {0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; //Ack frame, correct PID is returned +}; + +#endif diff --git a/Software/src/battery/KIA-E-GMP-BATTERY.h b/Software/src/battery/KIA-E-GMP-BATTERY.h index bf1c769e..b807db4c 100644 --- a/Software/src/battery/KIA-E-GMP-BATTERY.h +++ b/Software/src/battery/KIA-E-GMP-BATTERY.h @@ -82,8 +82,6 @@ class KiaEGmpBattery : public CanBattery { int8_t heatertemp = 20; bool set_voltage_limits = false; uint8_t ticks_200ms_counter = 0; - uint8_t EGMP_1CF_counter = 0; - uint8_t EGMP_3XF_counter = 0; }; #endif From 2888402e5de4da553895b0279fc350831fd74c81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 21 Aug 2025 20:06:48 +0300 Subject: [PATCH 050/245] Move variables to private section --- Software/src/battery/KIA-E-GMP-BATTERY.cpp | 575 +-------------------- Software/src/battery/KIA-E-GMP-BATTERY.h | 509 +++++++++++++++++- 2 files changed, 513 insertions(+), 571 deletions(-) diff --git a/Software/src/battery/KIA-E-GMP-BATTERY.cpp b/Software/src/battery/KIA-E-GMP-BATTERY.cpp index 9a91a760..a4c8bd95 100644 --- a/Software/src/battery/KIA-E-GMP-BATTERY.cpp +++ b/Software/src/battery/KIA-E-GMP-BATTERY.cpp @@ -6,45 +6,8 @@ #include "../devboard/utils/logging.h" #include "../system_settings.h" -const unsigned char crc8_table[256] = - { // CRC8_SAE_J1850_ZER0 formula,0x1D Poly,initial value 0x3F,Final XOR value varies - 0x00, 0x1D, 0x3A, 0x27, 0x74, 0x69, 0x4E, 0x53, 0xE8, 0xF5, 0xD2, 0xCF, 0x9C, 0x81, 0xA6, 0xBB, 0xCD, 0xD0, - 0xF7, 0xEA, 0xB9, 0xA4, 0x83, 0x9E, 0x25, 0x38, 0x1F, 0x02, 0x51, 0x4C, 0x6B, 0x76, 0x87, 0x9A, 0xBD, 0xA0, - 0xF3, 0xEE, 0xC9, 0xD4, 0x6F, 0x72, 0x55, 0x48, 0x1B, 0x06, 0x21, 0x3C, 0x4A, 0x57, 0x70, 0x6D, 0x3E, 0x23, - 0x04, 0x19, 0xA2, 0xBF, 0x98, 0x85, 0xD6, 0xCB, 0xEC, 0xF1, 0x13, 0x0E, 0x29, 0x34, 0x67, 0x7A, 0x5D, 0x40, - 0xFB, 0xE6, 0xC1, 0xDC, 0x8F, 0x92, 0xB5, 0xA8, 0xDE, 0xC3, 0xE4, 0xF9, 0xAA, 0xB7, 0x90, 0x8D, 0x36, 0x2B, - 0x0C, 0x11, 0x42, 0x5F, 0x78, 0x65, 0x94, 0x89, 0xAE, 0xB3, 0xE0, 0xFD, 0xDA, 0xC7, 0x7C, 0x61, 0x46, 0x5B, - 0x08, 0x15, 0x32, 0x2F, 0x59, 0x44, 0x63, 0x7E, 0x2D, 0x30, 0x17, 0x0A, 0xB1, 0xAC, 0x8B, 0x96, 0xC5, 0xD8, - 0xFF, 0xE2, 0x26, 0x3B, 0x1C, 0x01, 0x52, 0x4F, 0x68, 0x75, 0xCE, 0xD3, 0xF4, 0xE9, 0xBA, 0xA7, 0x80, 0x9D, - 0xEB, 0xF6, 0xD1, 0xCC, 0x9F, 0x82, 0xA5, 0xB8, 0x03, 0x1E, 0x39, 0x24, 0x77, 0x6A, 0x4D, 0x50, 0xA1, 0xBC, - 0x9B, 0x86, 0xD5, 0xC8, 0xEF, 0xF2, 0x49, 0x54, 0x73, 0x6E, 0x3D, 0x20, 0x07, 0x1A, 0x6C, 0x71, 0x56, 0x4B, - 0x18, 0x05, 0x22, 0x3F, 0x84, 0x99, 0xBE, 0xA3, 0xF0, 0xED, 0xCA, 0xD7, 0x35, 0x28, 0x0F, 0x12, 0x41, 0x5C, - 0x7B, 0x66, 0xDD, 0xC0, 0xE7, 0xFA, 0xA9, 0xB4, 0x93, 0x8E, 0xF8, 0xE5, 0xC2, 0xDF, 0x8C, 0x91, 0xB6, 0xAB, - 0x10, 0x0D, 0x2A, 0x37, 0x64, 0x79, 0x5E, 0x43, 0xB2, 0xAF, 0x88, 0x95, 0xC6, 0xDB, 0xFC, 0xE1, 0x5A, 0x47, - 0x60, 0x7D, 0x2E, 0x33, 0x14, 0x09, 0x7F, 0x62, 0x45, 0x58, 0x0B, 0x16, 0x31, 0x2C, 0x97, 0x8A, 0xAD, 0xB0, - 0xE3, 0xFE, 0xD9, 0xC4}; - -// Define the data points for %SOC depending on cell voltage -const uint8_t numPoints = 100; - -const uint16_t SOC[] = {10000, 9900, 9800, 9700, 9600, 9500, 9400, 9300, 9200, 9100, 9000, 8900, 8800, 8700, 8600, - 8500, 8400, 8300, 8200, 8100, 8000, 7900, 7800, 7700, 7600, 7500, 7400, 7300, 7200, 7100, - 7000, 6900, 6800, 6700, 6600, 6500, 6400, 6300, 6200, 6100, 6000, 5900, 5800, 5700, 5600, - 5500, 5400, 5300, 5200, 5100, 5000, 4900, 4800, 4700, 4600, 4500, 4400, 4300, 4200, 4100, - 4000, 3900, 3800, 3700, 3600, 3500, 3400, 3300, 3200, 3100, 3000, 2900, 2800, 2700, 2600, - 2500, 2400, 2300, 2200, 2100, 2000, 1900, 1800, 1700, 1600, 1500, 1400, 1300, 1200, 1100, - 1000, 900, 800, 700, 600, 500, 400, 300, 200, 100, 0}; - -const uint16_t voltage[] = {4200, 4173, 4148, 4124, 4102, 4080, 4060, 4041, 4023, 4007, 3993, 3980, 3969, 3959, 3953, - 3950, 3941, 3932, 3924, 3915, 3907, 3898, 3890, 3881, 3872, 3864, 3855, 3847, 3838, 3830, - 3821, 3812, 3804, 3795, 3787, 3778, 3770, 3761, 3752, 3744, 3735, 3727, 3718, 3710, 3701, - 3692, 3684, 3675, 3667, 3658, 3650, 3641, 3632, 3624, 3615, 3607, 3598, 3590, 3581, 3572, - 3564, 3555, 3547, 3538, 3530, 3521, 3512, 3504, 3495, 3487, 3478, 3470, 3461, 3452, 3444, - 3435, 3427, 3418, 3410, 3401, 3392, 3384, 3375, 3367, 3358, 3350, 3338, 3325, 3313, 3299, - 3285, 3271, 3255, 3239, 3221, 3202, 3180, 3156, 3127, 3090, 3000}; - // Function to estimate SOC based on cell voltage -uint16_t estimateSOCFromCell(uint16_t cellVoltage) { +uint16_t KiaEGmpBattery::estimateSOCFromCell(uint16_t cellVoltage) { if (cellVoltage >= voltage[0]) { return SOC[0]; } @@ -111,7 +74,7 @@ uint16_t KiaEGmpBattery::estimateSOC(uint16_t packVoltage, uint16_t cellCount, i } // Fix: Change parameter types to uint16_t to match SOC values -uint16_t selectSOC(uint16_t SOC_low, uint16_t SOC_high) { +uint16_t KiaEGmpBattery::selectSOC(uint16_t SOC_low, uint16_t SOC_high) { if (SOC_low == 0 || SOC_high == 0) { return 0; // If either value is 0, return 0 } @@ -121,534 +84,7 @@ uint16_t selectSOC(uint16_t SOC_low, uint16_t SOC_high) { return (SOC_low < SOC_high) ? SOC_low : SOC_high; // Otherwise, return the lowest value } -/* These messages are needed for contactor closing */ -unsigned long startMillis = 0; -uint8_t messageIndex = 0; -uint8_t messageDelays[63] = {0, 0, 5, 10, 10, 15, 19, 19, 20, 20, 25, 30, 30, 35, 40, 40, - 45, 49, 49, 50, 50, 52, 53, 53, 54, 55, 60, 60, 65, 67, 67, 70, - 70, 75, 77, 77, 80, 80, 85, 90, 90, 95, 100, 100, 105, 110, 110, 115, - 119, 119, 120, 120, 125, 130, 130, 135, 140, 140, 145, 149, 149, 150, 150}; -static constexpr CAN_frame message_1 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x10A, - .data = {0x62, 0x36, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, - 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_2 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x120, - .data = {0xd4, 0x1b, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, - 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_3 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x19A, - .data = {0x24, 0x9b, 0x7b, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, - 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; - -static constexpr CAN_frame message_4 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x10A, - .data = {0x24, 0x6f, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, - 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_5 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x120, - .data = {0x92, 0x42, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, - 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_6 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x19A, - .data = {0xd7, 0x05, 0x7c, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, - 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; - -static constexpr CAN_frame message_7 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x30A, - .data = {0xb1, 0xe0, 0x26, 0x08, 0x54, 0x01, 0x04, 0x15, 0x00, 0x1a, 0x76, 0x00, 0x25, 0x01, 0x10, 0x27, - 0x4f, 0x06, 0x18, 0x04, 0x33, 0x15, 0x34, 0x28, 0x00, 0x00, 0x10, 0x06, 0x21, 0x00, 0x4b, 0x06}}; - -static constexpr CAN_frame message_8 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x320, - .data = {0xc6, 0xab, 0x26, 0x41, 0x00, 0x00, 0x01, 0x3c, 0xac, 0x0d, 0x40, 0x20, 0x05, 0xc8, 0xa0, 0x03, - 0x40, 0x20, 0x2b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_9 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x10A, - .data = {0xee, 0x84, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, - 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_10 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x120, - .data = {0x58, 0xa9, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, - 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; -static constexpr CAN_frame message_11 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x19A, - .data = {0x91, 0x5c, 0x7d, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, - 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; - -static constexpr CAN_frame message_12 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x10A, - .data = {0xa8, 0xdd, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, - 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_13 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x120, - .data = {0x1e, 0xf0, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, - 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_14 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x19A, - .data = {0x5b, 0xb7, 0x7e, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, - 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; - -static constexpr CAN_frame message_15 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x10A, - .data = {0xec, 0x6d, 0x90, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, - 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_16 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x120, - .data = {0x5a, 0x40, 0x90, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, - 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_17 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x19A, - .data = {0x1d, 0xee, 0x7f, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, - 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; - -static constexpr CAN_frame message_18 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x2B5, - .data = {0xbd, 0xb2, 0x42, 0x00, 0x00, 0x00, 0x00, 0x80, 0x59, 0x00, 0x2b, 0x00, 0x00, 0x04, 0x00, 0x00, - 0xfa, 0xd0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_19 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x2E0, - .data = {0xc1, 0xf2, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x70, 0x01, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_20 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x10A, - .data = {0xaa, 0x34, 0x91, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, - 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; -static constexpr CAN_frame message_21 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x120, - .data = {0x1c, 0x19, 0x91, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, - 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_22 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x2D5, - .data = {0x79, 0xfb, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_23 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x2EA, - .data = {0x6e, 0xbb, 0xa0, 0x0d, 0x04, 0x01, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xc7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_24 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x306, - .data = {0x00, 0x00, 0x00, 0xd2, 0x06, 0x92, 0x05, 0x34, 0x07, 0x8e, 0x08, 0x73, 0x05, 0x80, 0x05, 0x83, - 0x05, 0x73, 0x05, 0x80, 0x05, 0xed, 0x01, 0xdd, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_25 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x308, - .data = {0xbe, 0x84, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, - 0x75, 0x6c, 0x86, 0x0d, 0xfb, 0xdf, 0x03, 0x36, 0xc3, 0x86, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_26 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x19A, - .data = {0x6b, 0xa2, 0x80, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, - 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; - -static constexpr CAN_frame message_27 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x10A, - .data = {0x60, 0xdf, 0x92, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, - 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_28 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x120, - .data = {0xd6, 0xf2, 0x92, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, - 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_29 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x19A, - .data = {0x2d, 0xfb, 0x81, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, - 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; - -static constexpr CAN_frame message_30 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x33A, - .data = {0x1a, 0x23, 0x26, 0x10, 0x27, 0x4f, 0x06, 0x00, 0xf8, 0x1b, 0x19, 0x04, 0x30, 0x01, 0x00, 0x06, - 0x00, 0x00, 0x00, 0x2e, 0x2d, 0x81, 0x25, 0x20, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -static constexpr CAN_frame message_31 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x350, - .data = {0x26, 0x82, 0x26, 0xf4, 0x01, 0x00, 0x00, 0x50, 0x90, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_32 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x10A, - .data = {0x26, 0x86, 0x93, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, - 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_33 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x120, - .data = {0x90, 0xab, 0x93, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, - 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_34 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x19A, - .data = {0xe7, 0x10, 0x82, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, - 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; - -static constexpr CAN_frame message_35 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x2E5, - .data = {0x69, 0x8a, 0x3f, 0x01, 0x00, 0x00, 0x00, 0x15, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_36 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x3B5, - .data = {0xa3, 0xc8, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x36, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xc7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_37 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x10A, - .data = {0xd5, 0x18, 0x94, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, - 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_38 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x120, - .data = {0x63, 0x35, 0x94, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, - 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_39 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x19A, - .data = {0xa1, 0x49, 0x83, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, - 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; - -static constexpr CAN_frame message_40 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x10A, - .data = {0x93, 0x41, 0x95, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, - 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; -static constexpr CAN_frame message_41 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x120, - .data = {0x25, 0x6c, 0x95, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, - 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_42 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x19A, - .data = {0x52, 0xd7, 0x84, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, - 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; - -static constexpr CAN_frame message_43 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x10A, - .data = {0x59, 0xaa, 0x96, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, - 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_44 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x120, - .data = {0xef, 0x87, 0x96, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, - 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_45 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x19A, - .data = {0x14, 0x8e, 0x85, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, - 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; - -static constexpr CAN_frame message_46 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x10A, - .data = {0x1f, 0xf3, 0x97, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, - 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_47 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x120, - .data = {0xa9, 0xde, 0x97, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, - 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_48 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x19A, - .data = {0xde, 0x65, 0x86, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, - 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; - -static constexpr CAN_frame message_49 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x30A, - .data = {0xd3, 0x11, 0x27, 0x08, 0x54, 0x01, 0x04, 0x15, 0x00, 0x1a, 0x76, 0x00, 0x25, 0x01, 0x10, 0x27, - 0x4f, 0x06, 0x19, 0x04, 0x33, 0x15, 0x34, 0x28, 0x00, 0x00, 0x10, 0x06, 0x21, 0x00, 0x4b, 0x06}}; - -static constexpr CAN_frame message_50 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x320, - .data = {0x80, 0xf2, 0x27, 0x41, 0x00, 0x00, 0x01, 0x3c, 0xac, 0x0d, 0x40, 0x20, 0x05, 0xc8, 0xa0, 0x03, - 0x40, 0x20, 0x2b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -static constexpr CAN_frame message_51 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x10A, - .data = {0x9e, 0x87, 0x98, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, - 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_52 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x120, - .data = {0x28, 0xaa, 0x98, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, - 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_53 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x19A, - .data = {0x98, 0x3c, 0x87, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, - 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; - -static constexpr CAN_frame message_54 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x10A, - .data = {0xd8, 0xde, 0x99, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, - 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_55 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x120, - .data = {0x6e, 0xf3, 0x99, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, - 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_56 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x19A, - .data = {0x19, 0x48, 0x88, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, - 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; - -static constexpr CAN_frame message_57 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x10A, - .data = {0x12, 0x35, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, - 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_58 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x120, - .data = {0xa4, 0x18, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, - 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_59 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x19A, - .data = {0x5f, 0x11, 0x89, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, - 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; - -static constexpr CAN_frame message_60 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x2B5, - .data = {0xfb, 0xeb, 0x43, 0x00, 0x00, 0x00, 0x00, 0x80, 0x59, 0x00, 0x2b, 0x00, 0x00, 0x04, 0x00, 0x00, - 0xfa, 0xd0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_61 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x2C0, - .data = {0xcc, 0xcd, 0xa2, 0x21, 0x00, 0xa1, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_62 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x2E0, - .data = {0x87, 0xab, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x70, 0x01, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static constexpr CAN_frame message_63 = { - .FD = true, - .ext_ID = false, - .DLC = 32, - .ID = 0x10A, - .data = {0x54, 0x6c, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, - 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; -static const CAN_frame* messages[] = { - &message_1, &message_2, &message_3, &message_4, &message_5, &message_6, &message_7, &message_8, - &message_9, &message_10, &message_11, &message_12, &message_13, &message_14, &message_15, &message_16, - &message_17, &message_18, &message_19, &message_20, &message_21, &message_22, &message_23, &message_24, - &message_25, &message_26, &message_27, &message_28, &message_29, &message_30, &message_31, &message_32, - &message_33, &message_34, &message_35, &message_36, &message_37, &message_38, &message_39, &message_40, - &message_41, &message_42, &message_43, &message_44, &message_45, &message_46, &message_47, &message_48, - &message_49, &message_50, &message_51, &message_52, &message_53, &message_54, &message_55, &message_56, - &message_57, &message_58, &message_59, &message_60, &message_61, &message_62, &message_63}; -/* PID polling messages */ -CAN_frame EGMP_7E4 = {.FD = true, - .ext_ID = false, - .DLC = 8, - .ID = 0x7E4, - .data = {0x03, 0x22, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00}}; //Poll PID 03 22 01 01 -static constexpr CAN_frame EGMP_7E4_ack = { - .FD = true, - .ext_ID = false, - .DLC = 8, - .ID = 0x7E4, - .data = {0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; //Ack frame, correct PID is returned - -void set_cell_voltages(CAN_frame rx_frame, int start, int length, int startCell) { +void KiaEGmpBattery::set_cell_voltages(CAN_frame rx_frame, int start, int length, int startCell) { for (size_t i = 0; i < length; i++) { if ((rx_frame.data.u8[start + i] * 20) > 1000) { datalayer.battery.status.cell_voltages_mV[startCell + i] = (rx_frame.data.u8[start + i] * 20); @@ -682,7 +118,7 @@ void KiaEGmpBattery::set_voltage_minmax_limits() { } } -static uint8_t calculateCRC(CAN_frame rx_frame, uint8_t length, uint8_t initial_value) { +uint8_t KiaEGmpBattery::calculateCRC(CAN_frame rx_frame, uint8_t length, uint8_t initial_value) { uint8_t crc = initial_value; for (uint8_t j = 1; j < length; j++) { //start at 1, since 0 is the CRC crc = crc8_table[(crc ^ static_cast(rx_frame.data.u8[j])) % 256]; @@ -690,8 +126,7 @@ static uint8_t calculateCRC(CAN_frame rx_frame, uint8_t length, uint8_t initial_ return crc; } -void KiaEGmpBattery:: - update_values() { //This function maps all the values fetched via CAN to the correct parameters used for modbus +void KiaEGmpBattery::update_values() { #ifdef ESTIMATE_SOC_FROM_CELLVOLTAGE // Use the simplified pack-based SOC estimation with proper compensation diff --git a/Software/src/battery/KIA-E-GMP-BATTERY.h b/Software/src/battery/KIA-E-GMP-BATTERY.h index b807db4c..461f7d4a 100644 --- a/Software/src/battery/KIA-E-GMP-BATTERY.h +++ b/Software/src/battery/KIA-E-GMP-BATTERY.h @@ -30,6 +30,10 @@ class KiaEGmpBattery : public CanBattery { private: KiaEGMPHtmlRenderer renderer; uint16_t estimateSOC(uint16_t packVoltage, uint16_t cellCount, int16_t currentAmps); + uint16_t selectSOC(uint16_t SOC_low, uint16_t SOC_high); + uint16_t estimateSOCFromCell(uint16_t cellVoltage); + uint8_t calculateCRC(CAN_frame rx_frame, uint8_t length, uint8_t initial_value); + void set_cell_voltages(CAN_frame rx_frame, int start, int length, int startCell); void set_voltage_minmax_limits(); static const int MAX_PACK_VOLTAGE_DV = 8064; //5000 = 500.0V @@ -81,7 +85,510 @@ class KiaEGmpBattery : public CanBattery { int8_t powerRelayTemperature = 10; int8_t heatertemp = 20; bool set_voltage_limits = false; - uint8_t ticks_200ms_counter = 0; + + const unsigned char crc8_table[256] = { + // CRC8_SAE_J1850_ZER0 formula,0x1D Poly,initial value 0x3F,Final XOR value varies + 0x00, 0x1D, 0x3A, 0x27, 0x74, 0x69, 0x4E, 0x53, 0xE8, 0xF5, 0xD2, 0xCF, 0x9C, 0x81, 0xA6, 0xBB, 0xCD, 0xD0, 0xF7, + 0xEA, 0xB9, 0xA4, 0x83, 0x9E, 0x25, 0x38, 0x1F, 0x02, 0x51, 0x4C, 0x6B, 0x76, 0x87, 0x9A, 0xBD, 0xA0, 0xF3, 0xEE, + 0xC9, 0xD4, 0x6F, 0x72, 0x55, 0x48, 0x1B, 0x06, 0x21, 0x3C, 0x4A, 0x57, 0x70, 0x6D, 0x3E, 0x23, 0x04, 0x19, 0xA2, + 0xBF, 0x98, 0x85, 0xD6, 0xCB, 0xEC, 0xF1, 0x13, 0x0E, 0x29, 0x34, 0x67, 0x7A, 0x5D, 0x40, 0xFB, 0xE6, 0xC1, 0xDC, + 0x8F, 0x92, 0xB5, 0xA8, 0xDE, 0xC3, 0xE4, 0xF9, 0xAA, 0xB7, 0x90, 0x8D, 0x36, 0x2B, 0x0C, 0x11, 0x42, 0x5F, 0x78, + 0x65, 0x94, 0x89, 0xAE, 0xB3, 0xE0, 0xFD, 0xDA, 0xC7, 0x7C, 0x61, 0x46, 0x5B, 0x08, 0x15, 0x32, 0x2F, 0x59, 0x44, + 0x63, 0x7E, 0x2D, 0x30, 0x17, 0x0A, 0xB1, 0xAC, 0x8B, 0x96, 0xC5, 0xD8, 0xFF, 0xE2, 0x26, 0x3B, 0x1C, 0x01, 0x52, + 0x4F, 0x68, 0x75, 0xCE, 0xD3, 0xF4, 0xE9, 0xBA, 0xA7, 0x80, 0x9D, 0xEB, 0xF6, 0xD1, 0xCC, 0x9F, 0x82, 0xA5, 0xB8, + 0x03, 0x1E, 0x39, 0x24, 0x77, 0x6A, 0x4D, 0x50, 0xA1, 0xBC, 0x9B, 0x86, 0xD5, 0xC8, 0xEF, 0xF2, 0x49, 0x54, 0x73, + 0x6E, 0x3D, 0x20, 0x07, 0x1A, 0x6C, 0x71, 0x56, 0x4B, 0x18, 0x05, 0x22, 0x3F, 0x84, 0x99, 0xBE, 0xA3, 0xF0, 0xED, + 0xCA, 0xD7, 0x35, 0x28, 0x0F, 0x12, 0x41, 0x5C, 0x7B, 0x66, 0xDD, 0xC0, 0xE7, 0xFA, 0xA9, 0xB4, 0x93, 0x8E, 0xF8, + 0xE5, 0xC2, 0xDF, 0x8C, 0x91, 0xB6, 0xAB, 0x10, 0x0D, 0x2A, 0x37, 0x64, 0x79, 0x5E, 0x43, 0xB2, 0xAF, 0x88, 0x95, + 0xC6, 0xDB, 0xFC, 0xE1, 0x5A, 0x47, 0x60, 0x7D, 0x2E, 0x33, 0x14, 0x09, 0x7F, 0x62, 0x45, 0x58, 0x0B, 0x16, 0x31, + 0x2C, 0x97, 0x8A, 0xAD, 0xB0, 0xE3, 0xFE, 0xD9, 0xC4}; + // Define the data points for %SOC depending on cell voltage + const uint8_t numPoints = 100; + + const uint16_t SOC[101] = {10000, 9900, 9800, 9700, 9600, 9500, 9400, 9300, 9200, 9100, 9000, 8900, 8800, 8700, 8600, + 8500, 8400, 8300, 8200, 8100, 8000, 7900, 7800, 7700, 7600, 7500, 7400, 7300, 7200, 7100, + 7000, 6900, 6800, 6700, 6600, 6500, 6400, 6300, 6200, 6100, 6000, 5900, 5800, 5700, 5600, + 5500, 5400, 5300, 5200, 5100, 5000, 4900, 4800, 4700, 4600, 4500, 4400, 4300, 4200, 4100, + 4000, 3900, 3800, 3700, 3600, 3500, 3400, 3300, 3200, 3100, 3000, 2900, 2800, 2700, 2600, + 2500, 2400, 2300, 2200, 2100, 2000, 1900, 1800, 1700, 1600, 1500, 1400, 1300, 1200, 1100, + 1000, 900, 800, 700, 600, 500, 400, 300, 200, 100, 0}; + + const uint16_t voltage[101] = { + 4200, 4173, 4148, 4124, 4102, 4080, 4060, 4041, 4023, 4007, 3993, 3980, 3969, 3959, 3953, 3950, 3941, + 3932, 3924, 3915, 3907, 3898, 3890, 3881, 3872, 3864, 3855, 3847, 3838, 3830, 3821, 3812, 3804, 3795, + 3787, 3778, 3770, 3761, 3752, 3744, 3735, 3727, 3718, 3710, 3701, 3692, 3684, 3675, 3667, 3658, 3650, + 3641, 3632, 3624, 3615, 3607, 3598, 3590, 3581, 3572, 3564, 3555, 3547, 3538, 3530, 3521, 3512, 3504, + 3495, 3487, 3478, 3470, 3461, 3452, 3444, 3435, 3427, 3418, 3410, 3401, 3392, 3384, 3375, 3367, 3358, + 3350, 3338, 3325, 3313, 3299, 3285, 3271, 3255, 3239, 3221, 3202, 3180, 3156, 3127, 3090, 3000}; + /* These messages are needed for contactor closing */ + unsigned long startMillis = 0; + uint8_t messageIndex = 0; + uint8_t messageDelays[63] = {0, 0, 5, 10, 10, 15, 19, 19, 20, 20, 25, 30, 30, 35, 40, 40, + 45, 49, 49, 50, 50, 52, 53, 53, 54, 55, 60, 60, 65, 67, 67, 70, + 70, 75, 77, 77, 80, 80, 85, 90, 90, 95, 100, 100, 105, 110, 110, 115, + 119, 119, 120, 120, 125, 130, 130, 135, 140, 140, 145, 149, 149, 150, 150}; + static constexpr CAN_frame message_1 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x62, 0x36, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_2 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0xd4, 0x1b, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_3 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x24, 0x9b, 0x7b, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + static constexpr CAN_frame message_4 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x24, 0x6f, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_5 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x92, 0x42, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_6 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0xd7, 0x05, 0x7c, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + static constexpr CAN_frame message_7 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x30A, + .data = {0xb1, 0xe0, 0x26, 0x08, 0x54, 0x01, 0x04, 0x15, 0x00, 0x1a, 0x76, 0x00, 0x25, 0x01, 0x10, 0x27, + 0x4f, 0x06, 0x18, 0x04, 0x33, 0x15, 0x34, 0x28, 0x00, 0x00, 0x10, 0x06, 0x21, 0x00, 0x4b, 0x06}}; + static constexpr CAN_frame message_8 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x320, + .data = {0xc6, 0xab, 0x26, 0x41, 0x00, 0x00, 0x01, 0x3c, 0xac, 0x0d, 0x40, 0x20, 0x05, 0xc8, 0xa0, 0x03, + 0x40, 0x20, 0x2b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_9 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0xee, 0x84, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_10 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x58, 0xa9, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_11 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x91, 0x5c, 0x7d, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + static constexpr CAN_frame message_12 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0xa8, 0xdd, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_13 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x1e, 0xf0, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_14 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x5b, 0xb7, 0x7e, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + static constexpr CAN_frame message_15 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0xec, 0x6d, 0x90, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_16 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x5a, 0x40, 0x90, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_17 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x1d, 0xee, 0x7f, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + static constexpr CAN_frame message_18 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x2B5, + .data = {0xbd, 0xb2, 0x42, 0x00, 0x00, 0x00, 0x00, 0x80, 0x59, 0x00, 0x2b, 0x00, 0x00, 0x04, 0x00, 0x00, + 0xfa, 0xd0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_19 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x2E0, + .data = {0xc1, 0xf2, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x70, 0x01, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_20 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0xaa, 0x34, 0x91, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_21 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x1c, 0x19, 0x91, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_22 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x2D5, + .data = {0x79, 0xfb, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_23 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x2EA, + .data = {0x6e, 0xbb, 0xa0, 0x0d, 0x04, 0x01, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_24 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x306, + .data = {0x00, 0x00, 0x00, 0xd2, 0x06, 0x92, 0x05, 0x34, 0x07, 0x8e, 0x08, 0x73, 0x05, 0x80, 0x05, 0x83, + 0x05, 0x73, 0x05, 0x80, 0x05, 0xed, 0x01, 0xdd, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_25 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x308, + .data = {0xbe, 0x84, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x75, 0x6c, 0x86, 0x0d, 0xfb, 0xdf, 0x03, 0x36, 0xc3, 0x86, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_26 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x6b, 0xa2, 0x80, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + static constexpr CAN_frame message_27 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x60, 0xdf, 0x92, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_28 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0xd6, 0xf2, 0x92, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_29 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x2d, 0xfb, 0x81, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + static constexpr CAN_frame message_30 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x33A, + .data = {0x1a, 0x23, 0x26, 0x10, 0x27, 0x4f, 0x06, 0x00, 0xf8, 0x1b, 0x19, 0x04, 0x30, 0x01, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x2e, 0x2d, 0x81, 0x25, 0x20, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_31 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x350, + .data = {0x26, 0x82, 0x26, 0xf4, 0x01, 0x00, 0x00, 0x50, 0x90, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_32 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x26, 0x86, 0x93, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_33 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x90, 0xab, 0x93, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_34 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0xe7, 0x10, 0x82, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + static constexpr CAN_frame message_35 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x2E5, + .data = {0x69, 0x8a, 0x3f, 0x01, 0x00, 0x00, 0x00, 0x15, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_36 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x3B5, + .data = {0xa3, 0xc8, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x36, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_37 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0xd5, 0x18, 0x94, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_38 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x63, 0x35, 0x94, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_39 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0xa1, 0x49, 0x83, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + static constexpr CAN_frame message_40 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x93, 0x41, 0x95, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_41 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x25, 0x6c, 0x95, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_42 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x52, 0xd7, 0x84, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + static constexpr CAN_frame message_43 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x59, 0xaa, 0x96, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_44 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0xef, 0x87, 0x96, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_45 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x14, 0x8e, 0x85, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + static constexpr CAN_frame message_46 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x1f, 0xf3, 0x97, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_47 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0xa9, 0xde, 0x97, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_48 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0xde, 0x65, 0x86, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + static constexpr CAN_frame message_49 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x30A, + .data = {0xd3, 0x11, 0x27, 0x08, 0x54, 0x01, 0x04, 0x15, 0x00, 0x1a, 0x76, 0x00, 0x25, 0x01, 0x10, 0x27, + 0x4f, 0x06, 0x19, 0x04, 0x33, 0x15, 0x34, 0x28, 0x00, 0x00, 0x10, 0x06, 0x21, 0x00, 0x4b, 0x06}}; + static constexpr CAN_frame message_50 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x320, + .data = {0x80, 0xf2, 0x27, 0x41, 0x00, 0x00, 0x01, 0x3c, 0xac, 0x0d, 0x40, 0x20, 0x05, 0xc8, 0xa0, 0x03, + 0x40, 0x20, 0x2b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_51 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x9e, 0x87, 0x98, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_52 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x28, 0xaa, 0x98, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_53 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x98, 0x3c, 0x87, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + static constexpr CAN_frame message_54 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0xd8, 0xde, 0x99, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_55 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0x6e, 0xf3, 0x99, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_56 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x19, 0x48, 0x88, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + static constexpr CAN_frame message_57 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x12, 0x35, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_58 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x120, + .data = {0xa4, 0x18, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x37, 0x35, 0x37, 0x37, + 0xc9, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_59 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x19A, + .data = {0x5f, 0x11, 0x89, 0x55, 0x44, 0x64, 0xd8, 0x1b, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x11, 0x52, + 0x00, 0x12, 0x02, 0x64, 0x00, 0x00, 0x00, 0x08, 0x13, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00}}; + static constexpr CAN_frame message_60 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x2B5, + .data = {0xfb, 0xeb, 0x43, 0x00, 0x00, 0x00, 0x00, 0x80, 0x59, 0x00, 0x2b, 0x00, 0x00, 0x04, 0x00, 0x00, + 0xfa, 0xd0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_61 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x2C0, + .data = {0xcc, 0xcd, 0xa2, 0x21, 0x00, 0xa1, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_62 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x2E0, + .data = {0x87, 0xab, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x70, 0x01, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + static constexpr CAN_frame message_63 = { + .FD = true, + .ext_ID = false, + .DLC = 32, + .ID = 0x10A, + .data = {0x54, 0x6c, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x36, 0x39, 0x35, 0x35, + 0xc9, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x35, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}}; + const CAN_frame* messages[63] = { + &message_1, &message_2, &message_3, &message_4, &message_5, &message_6, &message_7, &message_8, + &message_9, &message_10, &message_11, &message_12, &message_13, &message_14, &message_15, &message_16, + &message_17, &message_18, &message_19, &message_20, &message_21, &message_22, &message_23, &message_24, + &message_25, &message_26, &message_27, &message_28, &message_29, &message_30, &message_31, &message_32, + &message_33, &message_34, &message_35, &message_36, &message_37, &message_38, &message_39, &message_40, + &message_41, &message_42, &message_43, &message_44, &message_45, &message_46, &message_47, &message_48, + &message_49, &message_50, &message_51, &message_52, &message_53, &message_54, &message_55, &message_56, + &message_57, &message_58, &message_59, &message_60, &message_61, &message_62, &message_63}; + /* PID polling messages */ + CAN_frame EGMP_7E4 = {.FD = true, + .ext_ID = false, + .DLC = 8, + .ID = 0x7E4, + .data = {0x03, 0x22, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00}}; //Poll PID 03 22 01 01 + static constexpr CAN_frame EGMP_7E4_ack = { + .FD = true, + .ext_ID = false, + .DLC = 8, + .ID = 0x7E4, + .data = {0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; //Ack frame, correct PID is returned }; #endif From cd7e0b51ea73c74dbda610a06f0502e8c6d9c290 Mon Sep 17 00:00:00 2001 From: Matt Holmes Date: Thu, 21 Aug 2025 18:13:17 +0100 Subject: [PATCH 051/245] Fixing typo and removing unused led_color enum --- Software/src/devboard/mqtt/mqtt.cpp | 2 +- Software/src/devboard/utils/events.cpp | 2 +- Software/src/devboard/utils/events.h | 2 +- Software/src/devboard/utils/led_handler.cpp | 8 -------- Software/src/devboard/utils/led_handler.h | 2 -- Software/src/devboard/utils/types.h | 1 - 6 files changed, 3 insertions(+), 14 deletions(-) diff --git a/Software/src/devboard/mqtt/mqtt.cpp b/Software/src/devboard/mqtt/mqtt.cpp index 0f7ca0b3..067c2b6c 100644 --- a/Software/src/devboard/mqtt/mqtt.cpp +++ b/Software/src/devboard/mqtt/mqtt.cpp @@ -315,7 +315,7 @@ static bool publish_common_info(void) { } doc["event_level"] = get_event_level_string(get_event_level()); - doc["emulator_status"] = get_emulator_staus_string(get_emulator_status()); + doc["emulator_status"] = get_emulator_status_string(get_emulator_status()); serializeJson(doc, mqtt_msg); if (mqtt_publish(state_topic.c_str(), mqtt_msg, false) == false) { diff --git a/Software/src/devboard/utils/events.cpp b/Software/src/devboard/utils/events.cpp index 2838cda0..92540492 100644 --- a/Software/src/devboard/utils/events.cpp +++ b/Software/src/devboard/utils/events.cpp @@ -423,7 +423,7 @@ EMULATOR_STATUS get_emulator_status() { } } -const char* get_emulator_staus_string(EMULATOR_STATUS status) { +const char* get_emulator_status_string(EMULATOR_STATUS status) { // Return the status string but skip "STATUS_" that should always be first return EMULATOR_STATUS_STRING[status] + 7; } diff --git a/Software/src/devboard/utils/events.h b/Software/src/devboard/utils/events.h index 0a9d41d5..6cfd3239 100644 --- a/Software/src/devboard/utils/events.h +++ b/Software/src/devboard/utils/events.h @@ -163,7 +163,7 @@ const char* get_event_level_string(EVENTS_LEVEL_TYPE event_level); EVENTS_LEVEL_TYPE get_event_level(void); EMULATOR_STATUS get_emulator_status(); -const char* get_emulator_staus_string(EMULATOR_STATUS status); +const char* get_emulator_status_string(EMULATOR_STATUS status); void init_events(void); void set_event_latched(EVENTS_ENUM_TYPE event, uint8_t data); diff --git a/Software/src/devboard/utils/led_handler.cpp b/Software/src/devboard/utils/led_handler.cpp index 0440723e..3bf4f765 100644 --- a/Software/src/devboard/utils/led_handler.cpp +++ b/Software/src/devboard/utils/led_handler.cpp @@ -32,10 +32,6 @@ void led_exe(void) { led->exe(); } -led_color led_get_color() { - return led->color; -} - void LED::exe(void) { // Update brightness @@ -55,19 +51,15 @@ void LED::exe(void) { // Set color switch (get_emulator_status()) { case EMULATOR_STATUS::STATUS_OK: - color = led_color::GREEN; pixels.setPixelColor(COLOR_GREEN(brightness)); // Green pulsing LED break; case EMULATOR_STATUS::STATUS_WARNING: - color = led_color::YELLOW; pixels.setPixelColor(COLOR_YELLOW(brightness)); // Yellow pulsing LED break; case EMULATOR_STATUS::STATUS_ERROR: - color = led_color::RED; pixels.setPixelColor(COLOR_RED(esp32hal->LED_MAX_BRIGHTNESS())); // Red LED full brightness break; case EMULATOR_STATUS::STATUS_UPDATING: - color = led_color::BLUE; pixels.setPixelColor(COLOR_BLUE(brightness)); // Blue pulsing LED break; } diff --git a/Software/src/devboard/utils/led_handler.h b/Software/src/devboard/utils/led_handler.h index 4d8e01ee..a28bb3a5 100644 --- a/Software/src/devboard/utils/led_handler.h +++ b/Software/src/devboard/utils/led_handler.h @@ -6,7 +6,6 @@ class LED { public: - led_color color = led_color::GREEN; LED(gpio_num_t pin, uint8_t maxBrightness) : pixels(pin), max_brightness(maxBrightness), brightness(maxBrightness), mode(led_mode_enum::CLASSIC) {} @@ -31,6 +30,5 @@ class LED { bool led_init(void); void led_exe(void); -led_color led_get_color(void); #endif // LED_H_ diff --git a/Software/src/devboard/utils/types.h b/Software/src/devboard/utils/types.h index d1e15340..45ec6034 100644 --- a/Software/src/devboard/utils/types.h +++ b/Software/src/devboard/utils/types.h @@ -21,7 +21,6 @@ enum class comm_interface { Highest }; -enum led_color { GREEN, YELLOW, RED, BLUE }; enum led_mode_enum { CLASSIC, FLOW, HEARTBEAT }; enum PrechargeState { AUTO_PRECHARGE_IDLE, From 2cf46fede4d62812bace9b4e8074ca707d852885 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 21 Aug 2025 17:17:21 +0000 Subject: [PATCH 052/245] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- Software/src/devboard/utils/events.h | 8 ++++---- Software/src/devboard/utils/led_handler.h | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Software/src/devboard/utils/events.h b/Software/src/devboard/utils/events.h index 6cfd3239..6b90cf44 100644 --- a/Software/src/devboard/utils/events.h +++ b/Software/src/devboard/utils/events.h @@ -127,10 +127,10 @@ typedef enum { EVENTS_ENUM_TYPE(GENERATE_ENUM) } EVENTS_ENUM_TYPE; typedef enum { EVENTS_LEVEL_TYPE(GENERATE_ENUM) } EVENTS_LEVEL_TYPE; #define EMULATOR_STATUS(XX) \ - XX(STATUS_OK) \ - XX(STATUS_WARNING) \ - XX(STATUS_ERROR) \ - XX(STATUS_UPDATING) \ + XX(STATUS_OK) \ + XX(STATUS_WARNING) \ + XX(STATUS_ERROR) \ + XX(STATUS_UPDATING) typedef enum { EMULATOR_STATUS(GENERATE_ENUM) } EMULATOR_STATUS; diff --git a/Software/src/devboard/utils/led_handler.h b/Software/src/devboard/utils/led_handler.h index a28bb3a5..903267af 100644 --- a/Software/src/devboard/utils/led_handler.h +++ b/Software/src/devboard/utils/led_handler.h @@ -6,7 +6,6 @@ class LED { public: - LED(gpio_num_t pin, uint8_t maxBrightness) : pixels(pin), max_brightness(maxBrightness), brightness(maxBrightness), mode(led_mode_enum::CLASSIC) {} From 39ef78fd4ad2f5c6c71ca263ed4aa0991282a567 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 21 Aug 2025 23:24:55 +0300 Subject: [PATCH 053/245] Add autodetection method for Stark V1 / V2 --- Software/src/devboard/hal/hw_stark.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Software/src/devboard/hal/hw_stark.h b/Software/src/devboard/hal/hw_stark.h index 6ed745d9..718eb04d 100644 --- a/Software/src/devboard/hal/hw_stark.h +++ b/Software/src/devboard/hal/hw_stark.h @@ -38,7 +38,16 @@ class StarkHal : public Esp32Hal { virtual gpio_num_t CAN_SE_PIN() { return GPIO_NUM_NC; } // CANFD_ADDON defines for MCP2517 - virtual gpio_num_t MCP2517_SCK() { return GPIO_NUM_17; } + // Stark CMR v1 has GPIO pin 16 for SCK, CMR v2 has GPIO pin 17. Only diff between the two boards + bool isStarkVersion1() { + size_t flashSize = ESP.getFlashChipSize(); + if (flashSize == 4 * 1024 * 1024) { + return true; + } else { //v2 + return false; + } + } + virtual gpio_num_t MCP2517_SCK() { return isStarkVersion1() ? GPIO_NUM_16 : GPIO_NUM_17; } virtual gpio_num_t MCP2517_SDI() { return GPIO_NUM_5; } virtual gpio_num_t MCP2517_SDO() { return GPIO_NUM_34; } virtual gpio_num_t MCP2517_CS() { return GPIO_NUM_18; } From 8494463926a7802b20bfbee2c62c19c7435d52b4 Mon Sep 17 00:00:00 2001 From: Jaakko Haakana Date: Fri, 22 Aug 2025 21:44:24 +0300 Subject: [PATCH 054/245] Add mock ESP class --- test/emul/Arduino.cpp | 2 ++ test/emul/Arduino.h | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/test/emul/Arduino.cpp b/test/emul/Arduino.cpp index 1f6940df..bdaa38d8 100644 --- a/test/emul/Arduino.cpp +++ b/test/emul/Arduino.cpp @@ -14,3 +14,5 @@ void pinMode(uint8_t pin, uint8_t mode) {} int max(int a, int b) { return (a > b) ? a : b; } + +ESPClass ESP; diff --git a/test/emul/Arduino.h b/test/emul/Arduino.h index 8024518d..5b8b13f1 100644 --- a/test/emul/Arduino.h +++ b/test/emul/Arduino.h @@ -24,4 +24,15 @@ void delay(unsigned long ms); void delayMicroseconds(unsigned long us); int max(int a, int b); +class ESPClass { +public: + size_t getFlashChipSize() { + // This is a placeholder for the actual implementation + // that retrieves the flash chip size. + return 4 * 1024 * 1024; // Example: returning 4MB + } +}; + +extern ESPClass ESP; + #endif From 36a6dd049bd24f5efc3bb162c62680f4a7d256a5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 22 Aug 2025 18:44:37 +0000 Subject: [PATCH 055/245] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- test/emul/Arduino.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/emul/Arduino.h b/test/emul/Arduino.h index 5b8b13f1..092d6424 100644 --- a/test/emul/Arduino.h +++ b/test/emul/Arduino.h @@ -25,12 +25,12 @@ void delayMicroseconds(unsigned long us); int max(int a, int b); class ESPClass { -public: - size_t getFlashChipSize() { - // This is a placeholder for the actual implementation - // that retrieves the flash chip size. - return 4 * 1024 * 1024; // Example: returning 4MB - } + public: + size_t getFlashChipSize() { + // This is a placeholder for the actual implementation + // that retrieves the flash chip size. + return 4 * 1024 * 1024; // Example: returning 4MB + } }; extern ESPClass ESP; From 100e7e6effc942cd22013e7ecd22f2b0779a78ae Mon Sep 17 00:00:00 2001 From: freddanastrom Date: Sat, 23 Aug 2025 14:33:51 +0200 Subject: [PATCH 056/245] Using a more robust way to check that voltage is available from BMS --- Software/src/battery/BYD-ATTO-3-BATTERY.cpp | 3 ++- Software/src/battery/BYD-ATTO-3-BATTERY.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp index cf13aa26..57acf348 100644 --- a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp +++ b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp @@ -393,6 +393,7 @@ void BydAttoBattery::handle_incoming_can_frame(CAN_frame rx_frame) { datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE; battery_voltage = ((rx_frame.data.u8[1] & 0x0F) << 8) | rx_frame.data.u8[0]; //battery_temperature_something = rx_frame.data.u8[7] - 40; resides in frame 7 + BMS_voltage_available = true; break; case 0x445: datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE; @@ -542,7 +543,7 @@ void BydAttoBattery::transmit_can(unsigned long currentMillis) { } if (counter_100ms > 3) { - if (battery_voltage > 0) { + if (BMS_voltage_available) { // Transmit battery voltage back to BMS when confirmed it's available, this closes the contactors ATTO_3_441.data.u8[4] = (uint8_t)(battery_voltage - 1); ATTO_3_441.data.u8[5] = ((battery_voltage - 1) >> 8); ATTO_3_441.data.u8[6] = 0xFF; diff --git a/Software/src/battery/BYD-ATTO-3-BATTERY.h b/Software/src/battery/BYD-ATTO-3-BATTERY.h index 3d409e84..9e8f7497 100644 --- a/Software/src/battery/BYD-ATTO-3-BATTERY.h +++ b/Software/src/battery/BYD-ATTO-3-BATTERY.h @@ -98,6 +98,7 @@ class BydAttoBattery : public CanBattery { unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send unsigned long previousMillis200 = 0; // will store last time a 200ms CAN Message was send bool SOC_method = false; + bool BMS_voltage_available = false; uint8_t counter_50ms = 0; uint8_t counter_100ms = 0; uint8_t frame6_counter = 0xB; From 68da99c4a0414784211de87052726b2999a7bdb0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 23 Aug 2025 12:36:01 +0000 Subject: [PATCH 057/245] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- Software/src/battery/BYD-ATTO-3-BATTERY.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp index 57acf348..14688bf9 100644 --- a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp +++ b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp @@ -543,7 +543,7 @@ void BydAttoBattery::transmit_can(unsigned long currentMillis) { } if (counter_100ms > 3) { - if (BMS_voltage_available) { // Transmit battery voltage back to BMS when confirmed it's available, this closes the contactors + if (BMS_voltage_available) { // Transmit battery voltage back to BMS when confirmed it's available, this closes the contactors ATTO_3_441.data.u8[4] = (uint8_t)(battery_voltage - 1); ATTO_3_441.data.u8[5] = ((battery_voltage - 1) >> 8); ATTO_3_441.data.u8[6] = 0xFF; From 38e900bed7322c98d7a92529ac81610722fb3085 Mon Sep 17 00:00:00 2001 From: James Brookes Date: Sun, 24 Aug 2025 15:05:44 +0100 Subject: [PATCH 058/245] Add pack SOC reset feature, fix BMS reset bug --- Software/src/battery/Battery.h | 2 + Software/src/battery/TESLA-BATTERY.cpp | 59 ++++++++++++++++++- Software/src/battery/TESLA-BATTERY.h | 4 ++ Software/src/datalayer/datalayer.h | 1 + .../webserver/advanced_battery_html.cpp | 4 ++ 5 files changed, 69 insertions(+), 1 deletion(-) diff --git a/Software/src/battery/Battery.h b/Software/src/battery/Battery.h index 2b163a95..45dd1919 100644 --- a/Software/src/battery/Battery.h +++ b/Software/src/battery/Battery.h @@ -73,6 +73,7 @@ class Battery { virtual bool supports_clear_isolation() { return false; } virtual bool supports_reset_BMS() { return false; } + virtual bool supports_reset_SOC() { return false; } virtual bool supports_reset_crash() { return false; } virtual bool supports_reset_NVROL() { return false; } virtual bool supports_reset_DTC() { return false; } @@ -91,6 +92,7 @@ class Battery { virtual void clear_isolation() {} virtual void reset_BMS() {} + virtual void reset_SOC() {} virtual void reset_crash() {} virtual void reset_contactor() {} virtual void reset_NVROL() {} diff --git a/Software/src/battery/TESLA-BATTERY.cpp b/Software/src/battery/TESLA-BATTERY.cpp index 32599f93..3a0f49e0 100644 --- a/Software/src/battery/TESLA-BATTERY.cpp +++ b/Software/src/battery/TESLA-BATTERY.cpp @@ -727,10 +727,26 @@ void TeslaBattery:: #ifdef DEBUG_LOG logging.println("ERROR: BMS reset failed due to contactors not being open, or BMS ECU not allowing it"); #endif //DEBUG_LOG - stateMachineBMSReset = 0; + stateMachineBMSReset = 0xFF; datalayer.battery.settings.user_requests_tesla_bms_reset = false; } } + if (datalayer.battery.settings.user_requests_tesla_soc_reset) { + if (datalayer.battery.status.real_soc < 1500 || datalayer.battery.status.real_soc > 9000) { + //Start the SOC reset statemachine, only if SOC < 15% or > 90% + stateMachineSOCReset = 0; + datalayer.battery.settings.user_requests_tesla_soc_reset = false; +#ifdef DEBUG_LOG + logging.println("SOC reset requested"); +#endif //DEBUG_LOG + } else { +#ifdef DEBUG_LOG + logging.println("ERROR: SOC reset failed due to SOC not being less than 15 or greater than 90"); +#endif //DEBUG_LOG + stateMachineSOCReset = 0xFF; + datalayer.battery.settings.user_requests_tesla_soc_reset = false; + } + } //Update 0x333 UI_chargeTerminationPct (bit 16, width 10) value to SOC max value - expose via UI? //One firmware version this was seen at bit 17 width 11 @@ -2312,6 +2328,47 @@ void TeslaBattery::transmit_can(unsigned long currentMillis) { break; } } + if (stateMachineSOCReset != 0xFF) { + //This implementation should be rewritten to actually reply to the UDS responses sent by the BMS + //While this may work, it is not the correct way to implement this + switch (stateMachineBMSReset) { + case 0: + TESLA_602.data = {0x02, 0x27, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00}; + transmit_can_frame(&TESLA_602); + stateMachineBMSReset = 1; + break; + case 1: + TESLA_602.data = {0x30, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00}; + transmit_can_frame(&TESLA_602); + stateMachineBMSReset = 2; + break; + case 2: + TESLA_602.data = {0x10, 0x12, 0x27, 0x06, 0x35, 0x34, 0x37, 0x36}; + transmit_can_frame(&TESLA_602); + stateMachineBMSReset = 3; + break; + case 3: + TESLA_602.data = {0x21, 0x31, 0x30, 0x33, 0x32, 0x3D, 0x3C, 0x3F}; + transmit_can_frame(&TESLA_602); + stateMachineBMSReset = 4; + break; + case 4: + TESLA_602.data = {0x22, 0x3E, 0x39, 0x38, 0x3B, 0x3A, 0x00, 0x00}; + transmit_can_frame(&TESLA_602); + //Should generate a CAN UDS log message indicating ECU unlocked + stateMachineBMSReset = 5; + break; + case 5: + TESLA_602.data = {0x04, 0x31, 0x01, 0x04, 0x07, 0x00, 0x00, 0x00}; + transmit_can_frame(&TESLA_602); + stateMachineBMSReset = 0xFF; + break; + default: + //Something went wrong. Reset all and cancel + stateMachineBMSReset = 0xFF; + break; + } + } if (stateMachineBMSQuery != 0xFF) { //This implementation should be rewritten to actually reply to the UDS responses sent by the BMS //While this may work, it is not the correct way to implement this query logic diff --git a/Software/src/battery/TESLA-BATTERY.h b/Software/src/battery/TESLA-BATTERY.h index bafdff3f..d2116bfb 100644 --- a/Software/src/battery/TESLA-BATTERY.h +++ b/Software/src/battery/TESLA-BATTERY.h @@ -33,6 +33,9 @@ class TeslaBattery : public CanBattery { bool supports_reset_BMS() { return true; } void reset_BMS() { datalayer.battery.settings.user_requests_tesla_bms_reset = true; } + bool supports_reset_SOC() { return true; } + void reset_SOC() { datalayer.battery.settings.user_requests_tesla_soc_reset = true; } + bool supports_charged_energy() { return true; } BatteryHtmlRenderer& get_status_renderer() { return renderer; } @@ -486,6 +489,7 @@ class TeslaBattery : public CanBattery { uint8_t stateMachineClearIsolationFault = 0xFF; uint8_t stateMachineBMSReset = 0xFF; + uint8_t stateMachineSOCReset = 0xFF; uint8_t stateMachineBMSQuery = 0xFF; uint16_t sendContactorClosingMessagesStill = 300; uint16_t battery_cell_max_v = 3300; diff --git a/Software/src/datalayer/datalayer.h b/Software/src/datalayer/datalayer.h index ad910865..6f23ca80 100644 --- a/Software/src/datalayer/datalayer.h +++ b/Software/src/datalayer/datalayer.h @@ -152,6 +152,7 @@ struct DATALAYER_BATTERY_SETTINGS_TYPE { bool user_requests_balancing = false; bool user_requests_tesla_isolation_clear = false; bool user_requests_tesla_bms_reset = false; + bool user_requests_tesla_soc_reset = false; /* Forced balancing max time & start timestamp */ uint32_t balancing_time_ms = 3600000; //1h default, (60min*60sec*1000ms) uint32_t balancing_start_time_ms = 0; //For keeping track when balancing started diff --git a/Software/src/devboard/webserver/advanced_battery_html.cpp b/Software/src/devboard/webserver/advanced_battery_html.cpp index f52ff189..c3ea86d1 100644 --- a/Software/src/devboard/webserver/advanced_battery_html.cpp +++ b/Software/src/devboard/webserver/advanced_battery_html.cpp @@ -25,6 +25,10 @@ std::vector battery_commands = { [](Battery* b) { b->reset_BMS(); }}, + {"resetSOC", "SOC reset", "reset SOC?", [](Battery* b) { return b && b->supports_reset_SOC(); }, + [](Battery* b) { + b->reset_SOC(); + }}, {"resetCrash", "Unlock crashed BMS", "reset crash data? Note this will unlock your BMS and enable contactor closing and SOC calculation.", [](Battery* b) { return b && b->supports_reset_crash(); }, From 0961aef9aa67628d78ddac4bf7eccddf1f2c670c Mon Sep 17 00:00:00 2001 From: James Brookes Date: Sun, 24 Aug 2025 15:09:40 +0100 Subject: [PATCH 059/245] Pre-commit changes --- Software/src/battery/Battery.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/src/battery/Battery.h b/Software/src/battery/Battery.h index 45dd1919..8c9afa50 100644 --- a/Software/src/battery/Battery.h +++ b/Software/src/battery/Battery.h @@ -92,7 +92,7 @@ class Battery { virtual void clear_isolation() {} virtual void reset_BMS() {} - virtual void reset_SOC() {} + virtual void reset_SOC() {} virtual void reset_crash() {} virtual void reset_contactor() {} virtual void reset_NVROL() {} From dfa289ce56c4d72ed3ff8861ff6e2255b09edef9 Mon Sep 17 00:00:00 2001 From: James Brookes Date: Sun, 24 Aug 2025 15:19:25 +0100 Subject: [PATCH 060/245] Fix copy paste omissions --- Software/src/battery/TESLA-BATTERY.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Software/src/battery/TESLA-BATTERY.cpp b/Software/src/battery/TESLA-BATTERY.cpp index 3a0f49e0..1211be41 100644 --- a/Software/src/battery/TESLA-BATTERY.cpp +++ b/Software/src/battery/TESLA-BATTERY.cpp @@ -2331,41 +2331,41 @@ void TeslaBattery::transmit_can(unsigned long currentMillis) { if (stateMachineSOCReset != 0xFF) { //This implementation should be rewritten to actually reply to the UDS responses sent by the BMS //While this may work, it is not the correct way to implement this - switch (stateMachineBMSReset) { + switch (stateMachineSOCReset) { case 0: TESLA_602.data = {0x02, 0x27, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00}; transmit_can_frame(&TESLA_602); - stateMachineBMSReset = 1; + stateMachineSOCReset = 1; break; case 1: TESLA_602.data = {0x30, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00}; transmit_can_frame(&TESLA_602); - stateMachineBMSReset = 2; + stateMachineSOCReset = 2; break; case 2: TESLA_602.data = {0x10, 0x12, 0x27, 0x06, 0x35, 0x34, 0x37, 0x36}; transmit_can_frame(&TESLA_602); - stateMachineBMSReset = 3; + stateMachineSOCReset = 3; break; case 3: TESLA_602.data = {0x21, 0x31, 0x30, 0x33, 0x32, 0x3D, 0x3C, 0x3F}; transmit_can_frame(&TESLA_602); - stateMachineBMSReset = 4; + stateMachineSOCReset = 4; break; case 4: TESLA_602.data = {0x22, 0x3E, 0x39, 0x38, 0x3B, 0x3A, 0x00, 0x00}; transmit_can_frame(&TESLA_602); //Should generate a CAN UDS log message indicating ECU unlocked - stateMachineBMSReset = 5; + stateMachineSOCReset = 5; break; case 5: TESLA_602.data = {0x04, 0x31, 0x01, 0x04, 0x07, 0x00, 0x00, 0x00}; transmit_can_frame(&TESLA_602); - stateMachineBMSReset = 0xFF; + stateMachineSOCReset = 0xFF; break; default: //Something went wrong. Reset all and cancel - stateMachineBMSReset = 0xFF; + stateMachineSOCReset = 0xFF; break; } } From e944951c55789b3961753d60d5c80d442244d91c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 24 Aug 2025 22:41:50 +0300 Subject: [PATCH 061/245] Remove HVIL Event since it was misleading on some packs --- Software/src/battery/VOLVO-SPA-BATTERY.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Software/src/battery/VOLVO-SPA-BATTERY.cpp b/Software/src/battery/VOLVO-SPA-BATTERY.cpp index ab6a9e3d..8b28d0df 100644 --- a/Software/src/battery/VOLVO-SPA-BATTERY.cpp +++ b/Software/src/battery/VOLVO-SPA-BATTERY.cpp @@ -86,13 +86,6 @@ void VolvoSpaBattery:: } } - //Raise event if HVIL connector not seated - if ((datalayer_extended.VolvoPolestar.HVILstatusBits & 0x03) > 0) { - set_event(EVENT_HVIL_FAILURE, datalayer_extended.VolvoPolestar.HVILstatusBits); - } else { - clear_event(EVENT_HVIL_FAILURE); - } - #ifdef DEBUG_LOG uint8_t cnt = 0; logging.print("BMS reported SOC%: "); From 18b24c9ee84f92fb48c8d2d53346612a8916fb25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Mon, 25 Aug 2025 15:56:55 +0300 Subject: [PATCH 062/245] Add cellvoltages to CAN reading --- Software/src/battery/RELION-LV-BATTERY.cpp | 86 ++++++++++++++++++++-- 1 file changed, 79 insertions(+), 7 deletions(-) diff --git a/Software/src/battery/RELION-LV-BATTERY.cpp b/Software/src/battery/RELION-LV-BATTERY.cpp index 693a5f8f..5a760f8a 100644 --- a/Software/src/battery/RELION-LV-BATTERY.cpp +++ b/Software/src/battery/RELION-LV-BATTERY.cpp @@ -38,29 +38,101 @@ void RelionBattery::update_values() { void RelionBattery::handle_incoming_can_frame(CAN_frame rx_frame) { switch (rx_frame.ID) { - case 0x02018100: //ID1 + case 0x02018100: //ID1 (Example frame 10 08 01 F0 00 00 00 00) + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; battery_total_voltage = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]); break; - case 0x02028100: //ID2 + case 0x02028100: //ID2 (Example frame 00 00 00 63 64 10 00 00) + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; battery_total_current = ((rx_frame.data.u8[0] << 8) | rx_frame.data.u8[1]); system_state = rx_frame.data.u8[2]; battery_soc = rx_frame.data.u8[3]; battery_soh = rx_frame.data.u8[4]; most_serious_fault = rx_frame.data.u8[5]; break; - case 0x02038100: //ID3 + case 0x02038100: //ID3 (Example frame 0C F9 01 04 0C A7 01 0F) + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; max_cell_voltage = ((rx_frame.data.u8[0] << 8) | rx_frame.data.u8[1]); min_cell_voltage = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); break; - case 0x02628100: //Temperatures - max_cell_temperature = rx_frame.data.u8[0] - 50; - min_cell_temperature = rx_frame.data.u8[2] - 50; - break; case 0x02648100: //Charging limitis + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; charge_current_A = ((rx_frame.data.u8[0] << 8) | rx_frame.data.u8[1]) - 800; regen_charge_current_A = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]) - 800; discharge_current_A = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]) - 800; break; + case 0x02048100: ///Temperatures min/max 2048100 [8] 47 01 01 47 01 01 00 00 + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + max_cell_temperature = rx_frame.data.u8[0] - 50; + min_cell_temperature = rx_frame.data.u8[2] - 50; + break; + case 0x02468100: ///Raw temperatures 2468100 [8] 47 47 47 47 47 47 47 47 + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x02478100: ///? 2478100 [8] 32 32 32 32 32 32 32 32 + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + //ID6 = 0x02108100 ~ 0x023F8100****** Cell Voltage 1~192****** + case 0x02108100: ///Cellvoltages 1 2108100 [8] 0C F9 0C F8 0C F8 0C F9 + datalayer.battery.status.cell_voltages_mV[0] = ((rx_frame.data.u8[0] << 8) | rx_frame.data.u8[1]); + datalayer.battery.status.cell_voltages_mV[1] = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]); + datalayer.battery.status.cell_voltages_mV[2] = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); + datalayer.battery.status.cell_voltages_mV[3] = ((rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]); + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x02118100: ///Cellvoltages 2 2118100 [8] 0C F8 0C F8 0C F9 0C F8 + datalayer.battery.status.cell_voltages_mV[4] = ((rx_frame.data.u8[0] << 8) | rx_frame.data.u8[1]); + datalayer.battery.status.cell_voltages_mV[5] = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]); + datalayer.battery.status.cell_voltages_mV[6] = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); + datalayer.battery.status.cell_voltages_mV[7] = ((rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]); + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x02128100: ///Cellvoltages 3 2128100 [8] 0C F8 0C F8 0C F9 0C F8 + datalayer.battery.status.cell_voltages_mV[8] = ((rx_frame.data.u8[0] << 8) | rx_frame.data.u8[1]); + datalayer.battery.status.cell_voltages_mV[9] = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]); + datalayer.battery.status.cell_voltages_mV[10] = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); + datalayer.battery.status.cell_voltages_mV[11] = ((rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]); + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x02138100: ///Cellvoltages 4 2138100 [8] 0C F9 0C CD 0C A7 00 00 + datalayer.battery.status.cell_voltages_mV[12] = ((rx_frame.data.u8[0] << 8) | rx_frame.data.u8[1]); + datalayer.battery.status.cell_voltages_mV[13] = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]); + datalayer.battery.status.cell_voltages_mV[14] = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x02058100: ///? 2058100 [8] 00 0C 00 00 00 00 00 00 + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x02068100: ///? 2068100 [8] 00 00 00 00 00 00 00 00 + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x02148100: ///? 2148100 [8] 00 00 00 00 00 00 00 00 + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x02508100: ///? 2508100 [8] 00 00 00 00 00 00 00 00 + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x02518100: ///? 2518100 [8] 00 00 00 00 00 00 00 00 + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x02528100: ///? 2528100 [8] 00 00 00 00 00 00 00 00 + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x02548100: ///? 2548100 [8] 00 00 00 00 00 00 00 00 + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x024A8100: ///? 24A8100 [8] 00 00 00 00 00 00 00 00 + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x02558100: ///? 2558100 [8] 00 00 00 00 00 00 00 00 + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x02538100: ///? 2538100 [8] 00 00 00 00 00 00 00 00 + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x02568100: ///? 2568100 [8] 00 00 00 00 00 00 00 00 + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; default: break; } From 7adee39d82d99ad5378b7d13c2b5d8f8170e7f22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Mon, 25 Aug 2025 19:28:33 +0300 Subject: [PATCH 063/245] Swap precharge/positive state --- Software/src/battery/VOLVO-SPA-HTML.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Software/src/battery/VOLVO-SPA-HTML.h b/Software/src/battery/VOLVO-SPA-HTML.h index 9d832d51..e1c503eb 100644 --- a/Software/src/battery/VOLVO-SPA-HTML.h +++ b/Software/src/battery/VOLVO-SPA-HTML.h @@ -55,7 +55,7 @@ class VolvoSpaHtmlRenderer : public BatteryHtmlRenderer { default: content += String("Closed"); } - content += "

Positive contactor status: "; + content += "

Precharge contactor status: "; switch (datalayer_extended.VolvoPolestar.HVILstatusBits & 0x08) { case 0x08: content += String("Open"); @@ -63,7 +63,7 @@ class VolvoSpaHtmlRenderer : public BatteryHtmlRenderer { default: content += String("Closed"); } - content += "

Precharge Contactor status: "; + content += "

Positive Contactor status: "; switch (datalayer_extended.VolvoPolestar.HVILstatusBits & 0x10) { case 0x10: content += String("Open"); From cd30370c85ad42eff4a80d501870e1700de23936 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 26 Aug 2025 21:45:20 +0300 Subject: [PATCH 064/245] Update Software.ino --- Software/Software.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/Software.ino b/Software/Software.ino index b3f8b66c..424f3979 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -41,7 +41,7 @@ volatile unsigned long long bmsResetTimeOffset = 0; // The current software version, shown on webserver -const char* version_number = "9.0.RC2experimental"; +const char* version_number = "9.0.RC3"; // Interval timers volatile unsigned long currentMillis = 0; From 62486d9bf1c70d959012a0056629240f7a320fab Mon Sep 17 00:00:00 2001 From: Matt Holmes Date: Fri, 1 Aug 2025 08:55:25 +0100 Subject: [PATCH 065/245] Initial version of Lilygo T_2CAN board --- Software/Software.ino | 2 +- Software/src/communication/can/comm_can.cpp | 124 +++++--- Software/src/communication/can/obd.h | 1 - Software/src/devboard/hal/hal.cpp | 11 +- Software/src/devboard/hal/hw_lilygo2can.h | 67 ++++ Software/src/devboard/webserver/webserver.cpp | 3 + .../src/lib/miwagner-ESP32-Arduino-CAN/CAN.c | 298 ------------------ .../src/lib/miwagner-ESP32-Arduino-CAN/CAN.h | 135 -------- .../miwagner-ESP32-Arduino-CAN/CAN_config.h | 71 ----- .../miwagner-ESP32-Arduino-CAN/ESP32CAN.cpp | 19 -- .../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h | 18 -- .../miwagner-ESP32-Arduino-CAN/can_regdef.h | 279 ---------------- boards/esp32s3_flash_16MB.json | 43 +++ platformio.ini | 19 ++ 14 files changed, 213 insertions(+), 877 deletions(-) create mode 100644 Software/src/devboard/hal/hw_lilygo2can.h delete mode 100644 Software/src/lib/miwagner-ESP32-Arduino-CAN/CAN.c delete mode 100644 Software/src/lib/miwagner-ESP32-Arduino-CAN/CAN.h delete mode 100644 Software/src/lib/miwagner-ESP32-Arduino-CAN/CAN_config.h delete mode 100644 Software/src/lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.cpp delete mode 100644 Software/src/lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h delete mode 100644 Software/src/lib/miwagner-ESP32-Arduino-CAN/can_regdef.h create mode 100644 boards/esp32s3_flash_16MB.json diff --git a/Software/Software.ino b/Software/Software.ino index 424f3979..bc395931 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -31,7 +31,7 @@ #include "src/devboard/wifi/wifi.h" #include "src/inverter/INVERTERS.h" -#if !defined(HW_LILYGO) && !defined(HW_STARK) && !defined(HW_3LB) && !defined(HW_DEVKIT) +#if !defined(HW_LILYGO) && !defined(HW_LILYGO2CAN) && !defined(HW_STARK) && !defined(HW_3LB) && !defined(HW_DEVKIT) #error You must select a target hardware in the USER_SETTINGS.h file! #endif diff --git a/Software/src/communication/can/comm_can.cpp b/Software/src/communication/can/comm_can.cpp index 4efec199..f0c14696 100644 --- a/Software/src/communication/can/comm_can.cpp +++ b/Software/src/communication/can/comm_can.cpp @@ -1,7 +1,8 @@ #include "comm_can.h" #include #include -#include "../../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h" +#include "esp_twai.h" +#include "esp_twai_onchip.h" #include "../../lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h" #include "../../lib/pierremolinaro-acan2515/ACAN2515.h" #include "CanReceiver.h" @@ -20,7 +21,11 @@ struct CanReceiverRegistration { static std::multimap can_receivers; // Parameters -CAN_device_t CAN_cfg; // CAN Config +twai_node_handle_t node_hdl = NULL; +twai_onchip_node_config_t node_config = { + .tx_queue_depth = 5 +}; + const uint8_t rx_queue_size = 10; // Receive Queue size volatile bool send_ok_native = 0; volatile bool send_ok_2515 = 0; @@ -57,6 +62,32 @@ ACAN2517FDSettings* settings2517; bool native_can_initialized = false; +static bool twai_rx_callback(twai_node_handle_t handle, const twai_rx_done_event_data_t *edata, void *user_ctx) +{ + uint8_t recv_buff[8]; + twai_frame_t rx_frame = { + .buffer = recv_buff, + .buffer_len = sizeof(recv_buff), + }; + if (ESP_OK == twai_node_receive_from_isr(handle, &rx_frame)) { + CAN_frame frame; + frame.ID = rx_frame.header.id; + frame.DLC = rx_frame.header.dlc; + frame.ext_ID = rx_frame.header.ide; + for (uint8_t i = 0; i < frame.DLC && i < 8; i++) { + frame.data.u8[i] = rx_frame.buffer[i]; + } + map_can_frame_to_variable(&frame, CAN_NATIVE); + + } + return false; +} + +twai_event_callbacks_t user_cbs = { + .on_rx_done = twai_rx_callback +}; + + bool init_CAN() { auto nativeIt = can_receivers.find(CAN_NATIVE); @@ -73,17 +104,17 @@ bool init_CAN() { digitalWrite(se_pin, LOW); } - CAN_cfg.speed = (CAN_speed_t)nativeIt->second.speed; - if (!esp32hal->alloc_pins("CAN", tx_pin, rx_pin)) { return false; } - CAN_cfg.tx_pin_id = tx_pin; - CAN_cfg.rx_pin_id = rx_pin; - CAN_cfg.rx_queue = xQueueCreate(rx_queue_size, sizeof(CAN_frame_t)); - // Init CAN Module - ESP32Can.CANInit(); + node_config.io_cfg.tx = tx_pin; + node_config.io_cfg.rx = rx_pin; + node_config.bit_timing.bitrate = (int)nativeIt->second.speed * 1000UL; + twai_new_node_onchip(&node_config, &node_hdl); + twai_node_register_event_callbacks(node_hdl, &user_cbs, NULL); + twai_node_enable(node_hdl); + native_can_initialized = true; } @@ -104,6 +135,15 @@ bool init_CAN() { #endif // DEBUG_LOG gBuffer.initWithSize(25); + //ToDo: Refactor this + pinMode(9, OUTPUT); + digitalWrite(9, HIGH); + delay(100); + digitalWrite(9, LOW); + delay(100); + digitalWrite(9, HIGH); + delay(100); + can2515 = new ACAN2515(cs_pin, SPI2515, int_pin); SPI2515.begin(sck_pin, miso_pin, mosi_pin); @@ -206,15 +246,15 @@ void transmit_can_frame_to_interface(const CAN_frame* tx_frame, int interface) { switch (interface) { case CAN_NATIVE: - CAN_frame_t frame; - frame.MsgID = tx_frame->ID; - frame.FIR.B.FF = tx_frame->ext_ID ? CAN_frame_ext : CAN_frame_std; - frame.FIR.B.DLC = tx_frame->DLC; - frame.FIR.B.RTR = CAN_no_RTR; - for (uint8_t i = 0; i < tx_frame->DLC; i++) { - frame.data.u8[i] = tx_frame->data.u8[i]; - } - send_ok_native = ESP32Can.CANWriteFrame(&frame); + twai_frame_t frame; + frame.header.id = tx_frame->ID; + frame.header.dlc = tx_frame->DLC; + frame.buffer = tx_frame->data.u8; + frame.buffer_len = tx_frame->DLC; + frame.header.ide = tx_frame->ext_ID ? 1 : 0; + + send_ok_native = twai_node_transmit(node_hdl, &frame, 0); + if (!send_ok_native) { datalayer.system.info.can_native_send_fail = true; } @@ -223,7 +263,7 @@ void transmit_can_frame_to_interface(const CAN_frame* tx_frame, int interface) { //Struct with ACAN2515 library format, needed to use the MCP2515 library for CAN2 CANMessage MCP2515Frame; MCP2515Frame.id = tx_frame->ID; - MCP2515Frame.ext = tx_frame->ext_ID ? CAN_frame_ext : CAN_frame_std; + MCP2515Frame.ext = tx_frame->ext_ID; MCP2515Frame.len = tx_frame->DLC; MCP2515Frame.rtr = false; for (uint8_t i = 0; i < MCP2515Frame.len; i++) { @@ -244,7 +284,7 @@ void transmit_can_frame_to_interface(const CAN_frame* tx_frame, int interface) { MCP2518Frame.type = CANFDMessage::CAN_DATA; } MCP2518Frame.id = tx_frame->ID; - MCP2518Frame.ext = tx_frame->ext_ID ? CAN_frame_ext : CAN_frame_std; + MCP2518Frame.ext = tx_frame->ext_ID; MCP2518Frame.len = tx_frame->DLC; for (uint8_t i = 0; i < MCP2518Frame.len; i++) { MCP2518Frame.data[i] = tx_frame->data.u8[i]; @@ -262,10 +302,6 @@ void transmit_can_frame_to_interface(const CAN_frame* tx_frame, int interface) { // Receive functions void receive_can() { - if (native_can_initialized) { - receive_frame_can_native(); // Receive CAN messages from native CAN port - } - if (can2515) { receive_frame_can_addon(); // Receive CAN messages on add-on MCP2515 chip } @@ -275,24 +311,7 @@ void receive_can() { } } -void receive_frame_can_native() { // This section checks if we have a complete CAN message incoming on native CAN port - CAN_frame_t rx_frame_native; - if (xQueueReceive(CAN_cfg.rx_queue, &rx_frame_native, 0) == pdTRUE) { - CAN_frame rx_frame; - rx_frame.ID = rx_frame_native.MsgID; - if (rx_frame_native.FIR.B.FF == CAN_frame_std) { - rx_frame.ext_ID = false; - } else { //CAN_frame_ext == 1 - rx_frame.ext_ID = true; - } - rx_frame.DLC = rx_frame_native.FIR.B.DLC; - for (uint8_t i = 0; i < rx_frame.DLC && i < 8; i++) { - rx_frame.data.u8[i] = rx_frame_native.data.u8[i]; - } - //message incoming, pass it on to the handler - map_can_frame_to_variable(&rx_frame, CAN_NATIVE); - } -} + void receive_frame_can_addon() { // This section checks if we have a complete CAN message incoming on add-on CAN port CAN_frame rx_frame; // Struct with our CAN format @@ -302,7 +321,7 @@ void receive_frame_can_addon() { // This section checks if we have a complete C can2515->receive(MCP2515frame); rx_frame.ID = MCP2515frame.id; - rx_frame.ext_ID = MCP2515frame.ext ? CAN_frame_ext : CAN_frame_std; + rx_frame.ext_ID = MCP2515frame.ext; rx_frame.DLC = MCP2515frame.len; for (uint8_t i = 0; i < MCP2515frame.len && i < 8; i++) { rx_frame.data.u8[i] = MCP2515frame.data[i]; @@ -415,7 +434,8 @@ void dump_can_frame(CAN_frame& frame, frameDirection msgDir) { void stop_can() { if (can_receivers.find(CAN_NATIVE) != can_receivers.end()) { - ESP32Can.CANStop(); + //ToDo: Implement + //ESP32Can.CANStop(); } if (can2515) { @@ -431,7 +451,8 @@ void stop_can() { void restart_can() { if (can_receivers.find(CAN_NATIVE) != can_receivers.end()) { - ESP32Can.CANInit(); + //ToDo: Implement + //ESP32Can.CANInit(); } if (can2515) { @@ -446,11 +467,12 @@ void restart_can() { } CAN_Speed change_can_speed(CAN_Interface interface, CAN_Speed speed) { - auto oldSpeed = (CAN_Speed)CAN_cfg.speed; - if (interface == CAN_Interface::CAN_NATIVE) { - CAN_cfg.speed = (CAN_speed_t)speed; + //ToDo: Implement + //auto oldSpeed = (CAN_Speed)CAN_cfg.speed; + //if (interface == CAN_Interface::CAN_NATIVE) { + // CAN_cfg.speed = (CAN_speed_t)speed; // ReInit native CAN module at new speed - ESP32Can.CANInit(); - } - return oldSpeed; + // ESP32Can.CANInit(); + //} + return speed; } diff --git a/Software/src/communication/can/obd.h b/Software/src/communication/can/obd.h index 96958d90..2a495820 100644 --- a/Software/src/communication/can/obd.h +++ b/Software/src/communication/can/obd.h @@ -1,7 +1,6 @@ #ifndef _OBD_H_ #define _OBD_H_ -#include "../../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h" #include "comm_can.h" void handle_obd_frame(CAN_frame& rx_frame); diff --git a/Software/src/devboard/hal/hal.cpp b/Software/src/devboard/hal/hal.cpp index 2d6c38ae..e58c45aa 100644 --- a/Software/src/devboard/hal/hal.cpp +++ b/Software/src/devboard/hal/hal.cpp @@ -3,21 +3,24 @@ #include "../../../USER_SETTINGS.h" #include -#include "hw_3LB.h" -#include "hw_devkit.h" -#include "hw_lilygo.h" -#include "hw_stark.h" Esp32Hal* esp32hal = nullptr; void init_hal() { #if defined(HW_LILYGO) + #include "hw_lilygo.h" esp32hal = new LilyGoHal(); +#elif defined(HW_LILYGO2CAN) + #include "hw_lilygo2can.h" + esp32hal = new LilyGo2CANHal(); #elif defined(HW_STARK) + #include "hw_stark.h" esp32hal = new StarkHal(); #elif defined(HW_3LB) + #include "hw_3LB.h" esp32hal = new ThreeLBHal(); #elif defined(HW_DEVKIT) + #include "hw_devkit.h" esp32hal = new DevKitHal(); #else #error "No HW defined." diff --git a/Software/src/devboard/hal/hw_lilygo2can.h b/Software/src/devboard/hal/hw_lilygo2can.h new file mode 100644 index 00000000..9699958a --- /dev/null +++ b/Software/src/devboard/hal/hw_lilygo2can.h @@ -0,0 +1,67 @@ +#ifndef __HW_LILYGO2CAN_H__ +#define __HW_LILYGO2CAN_H__ + +#include "hal.h" + +class LilyGo2CANHal : public Esp32Hal { + public: + const char* name() { return "LilyGo T_2CAN"; } + + virtual gpio_num_t CAN_TX_PIN() { return GPIO_NUM_7; } + virtual gpio_num_t CAN_RX_PIN() { return GPIO_NUM_6; } + + // Built In MCP2515 CAN_ADDON + virtual gpio_num_t MCP2515_SCK() { return GPIO_NUM_12; } + virtual gpio_num_t MCP2515_MOSI() { return GPIO_NUM_11; } + virtual gpio_num_t MCP2515_MISO() { return GPIO_NUM_13; } + virtual gpio_num_t MCP2515_CS() { return GPIO_NUM_10; } + virtual gpio_num_t MCP2515_INT() { return GPIO_NUM_8; } + + // CANFD_ADDON defines for MCP2517 + virtual gpio_num_t MCP2517_SCK() { return GPIO_NUM_17; } + virtual gpio_num_t MCP2517_SDI() { return GPIO_NUM_16; } + virtual gpio_num_t MCP2517_SDO() { return GPIO_NUM_18; } + virtual gpio_num_t MCP2517_CS() { return GPIO_NUM_15; } + virtual gpio_num_t MCP2517_INT() { return GPIO_NUM_14; } + + // Contactor handling + virtual gpio_num_t POSITIVE_CONTACTOR_PIN() { return GPIO_NUM_38; } + virtual gpio_num_t NEGATIVE_CONTACTOR_PIN() { return GPIO_NUM_39; } + virtual gpio_num_t PRECHARGE_PIN() { return GPIO_NUM_40; } + virtual gpio_num_t SECOND_BATTERY_CONTACTORS_PIN() { return GPIO_NUM_41; } + + // Automatic precharging + virtual gpio_num_t HIA4V1_PIN() { return GPIO_NUM_41; } + virtual gpio_num_t INVERTER_DISCONNECT_CONTACTOR_PIN() { return GPIO_NUM_40; } + + // SMA CAN contactor pins + virtual gpio_num_t INVERTER_CONTACTOR_ENABLE_PIN() { return GPIO_NUM_14; } + + virtual gpio_num_t INVERTER_CONTACTOR_ENABLE_LED_PIN() { return GPIO_NUM_5; } + + // LED + virtual gpio_num_t LED_PIN() { return GPIO_NUM_4; } + virtual uint8_t LED_MAX_BRIGHTNESS() { return 40; } + + // Equipment stop pin + virtual gpio_num_t EQUIPMENT_STOP_PIN() { return GPIO_NUM_5; } + + // Battery wake up pins + virtual gpio_num_t WUP_PIN1() { return GPIO_NUM_40; } + virtual gpio_num_t WUP_PIN2() { return GPIO_NUM_38; } + + std::vector available_interfaces() { + return {comm_interface::CanNative, comm_interface::CanAddonMcp2515}; + } +}; + +#define HalClass LilyGo2CANHal + +/* ----- Error checks below, don't change (can't be moved to separate file) ----- */ +#ifndef HW_CONFIGURED +#define HW_CONFIGURED +#else +#error Multiple HW defined! Please select a single HW +#endif + +#endif diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index c40373f3..71df6879 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -891,6 +891,9 @@ String processor(const String& var) { #ifdef HW_LILYGO content += " Hardware: LilyGo T-CAN485"; #endif // HW_LILYGO +#ifdef HW_LILYGO2CAN + content += " Hardware: LilyGo T_2CAN"; +#endif // HW_LILYGO2CAN #ifdef HW_STARK content += " Hardware: Stark CMR Module"; #endif // HW_STARK diff --git a/Software/src/lib/miwagner-ESP32-Arduino-CAN/CAN.c b/Software/src/lib/miwagner-ESP32-Arduino-CAN/CAN.c deleted file mode 100644 index 3a0bcc87..00000000 --- a/Software/src/lib/miwagner-ESP32-Arduino-CAN/CAN.c +++ /dev/null @@ -1,298 +0,0 @@ -/** - * @section License - * - * The MIT License (MIT) - * - * Copyright (c) 2017, Thomas Barth, barth-dev.de - * 2017, Jaime Breva, jbreva@nayarsystems.com - * 2018, Michael Wagner, mw@iot-make.de - * - * 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. - * - */ - -#include "CAN.h" - -#include "freertos/FreeRTOS.h" -#include "freertos/queue.h" - -#include "esp_intr_alloc.h" // Renamed when migrating ESP32 2.x -> 3.x -#include "soc/dport_reg.h" -#include "soc/gpio_sig_map.h" -#include - -#include "driver/gpio.h" - -#include "can_regdef.h" -#include "CAN_config.h" - -// CAN Filter - no acceptance filter -static CAN_filter_t __filter = { Dual_Mode, 0, 0, 0, 0, 0Xff, 0Xff, 0Xff, 0Xff }; - -static void CAN_read_frame_phy(); -static void CAN_isr(void *arg_p); -static int CAN_write_frame_phy(const CAN_frame_t *p_frame); -static SemaphoreHandle_t sem_tx_complete; - -static void CAN_isr(void *arg_p) { - - // Interrupt flag buffer - __CAN_IRQ_t interrupt; - BaseType_t higherPriorityTaskWoken = pdFALSE; - - // Read interrupt status and clear flags - interrupt = MODULE_CAN->IR.U; - - // Handle RX frame available interrupt - if ((interrupt & __CAN_IRQ_RX) != 0) - CAN_read_frame_phy(&higherPriorityTaskWoken); - - // Handle TX complete interrupt - // Handle error interrupts. - if ((interrupt & (__CAN_IRQ_TX | __CAN_IRQ_ERR //0x4 - | __CAN_IRQ_DATA_OVERRUN // 0x8 - | __CAN_IRQ_WAKEUP // 0x10 - | __CAN_IRQ_ERR_PASSIVE // 0x20 - | __CAN_IRQ_ARB_LOST // 0x40 - | __CAN_IRQ_BUS_ERR // 0x80 - )) != 0) { - xSemaphoreGiveFromISR(sem_tx_complete, &higherPriorityTaskWoken); - } - - // check if any higher priority task has been woken by any handler - if (higherPriorityTaskWoken) - portYIELD_FROM_ISR(); -} - -static void CAN_read_frame_phy(BaseType_t *higherPriorityTaskWoken) { - - // byte iterator - uint8_t __byte_i; - - // frame read buffer - CAN_frame_t __frame; - - // check if we have a queue. If not, operation is aborted. - if (CAN_cfg.rx_queue == NULL) { - // Let the hardware know the frame has been read. - MODULE_CAN->CMR.B.RRB = 1; - return; - } - - // get FIR - __frame.FIR.U = MODULE_CAN->MBX_CTRL.FCTRL.FIR.U; - - // check if this is a standard or extended CAN frame - // standard frame - if (__frame.FIR.B.FF == CAN_frame_std) { - - // Get Message ID - __frame.MsgID = _CAN_GET_STD_ID; - - // deep copy data bytes - for (__byte_i = 0; __byte_i < __frame.FIR.B.DLC; __byte_i++) - __frame.data.u8[__byte_i] = MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.STD.data[__byte_i]; - } - // extended frame - else { - - // Get Message ID - __frame.MsgID = _CAN_GET_EXT_ID; - - // deep copy data bytes - for (__byte_i = 0; __byte_i < __frame.FIR.B.DLC; __byte_i++) - __frame.data.u8[__byte_i] = MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.data[__byte_i]; - } - - // send frame to input queue - xQueueSendToBackFromISR(CAN_cfg.rx_queue, &__frame, higherPriorityTaskWoken); - - // Let the hardware know the frame has been read. - MODULE_CAN->CMR.B.RRB = 1; -} - -static int CAN_write_frame_phy(const CAN_frame_t *p_frame) { - - // byte iterator - uint8_t __byte_i; - - // copy frame information record - MODULE_CAN->MBX_CTRL.FCTRL.FIR.U = p_frame->FIR.U; - - // standard frame - if (p_frame->FIR.B.FF == CAN_frame_std) { - - // Write message ID - _CAN_SET_STD_ID(p_frame->MsgID); - - // Copy the frame data to the hardware - for (__byte_i = 0; __byte_i < p_frame->FIR.B.DLC; __byte_i++) - MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.STD.data[__byte_i] = p_frame->data.u8[__byte_i]; - - } - // extended frame - else { - - // Write message ID - _CAN_SET_EXT_ID(p_frame->MsgID); - - // Copy the frame data to the hardware - for (__byte_i = 0; __byte_i < p_frame->FIR.B.DLC; __byte_i++) - MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.data[__byte_i] = p_frame->data.u8[__byte_i]; - } - - // Transmit frame - MODULE_CAN->CMR.B.TR = 1; - - return 0; -} - -int CAN_init() { - - // Time quantum - double __tq; - - // enable module - DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_CAN_CLK_EN); - DPORT_SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_CAN_RST); //Added https://github.com/miwagner/ESP32-Arduino-CAN/pull/37/commits/feccb722866fbdcc7628b941efe9f79295b0cf81 - DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_CAN_RST); - - // configure TX pin - gpio_set_level(CAN_cfg.tx_pin_id, 1); - gpio_set_direction(CAN_cfg.tx_pin_id, GPIO_MODE_OUTPUT); - gpio_matrix_out(CAN_cfg.tx_pin_id, TWAI_TX_IDX, 0, 0); - gpio_pad_select_gpio(CAN_cfg.tx_pin_id); - - // configure RX pin - gpio_set_direction(CAN_cfg.rx_pin_id, GPIO_MODE_INPUT); - gpio_matrix_in(CAN_cfg.rx_pin_id, TWAI_RX_IDX, 0); - gpio_pad_select_gpio(CAN_cfg.rx_pin_id); - - // set to PELICAN mode - MODULE_CAN->CDR.B.CAN_M = 0x1; - - // synchronization jump width is the same for all baud rates - MODULE_CAN->BTR0.B.SJW = 0x1; - - // TSEG2 is the same for all baud rates - MODULE_CAN->BTR1.B.TSEG2 = 0x1; - - // select time quantum and set TSEG1 - switch (CAN_cfg.speed) { - case CAN_SPEED_1000KBPS: - MODULE_CAN->BTR1.B.TSEG1 = 0x4; - __tq = 0.125; - break; - - case CAN_SPEED_800KBPS: - MODULE_CAN->BTR1.B.TSEG1 = 0x6; - __tq = 0.125; - break; - - case CAN_SPEED_200KBPS: - MODULE_CAN->BTR1.B.TSEG1 = 0xc; - MODULE_CAN->BTR1.B.TSEG2 = 0x5; - __tq = 0.25; - break; - - default: - MODULE_CAN->BTR1.B.TSEG1 = 0xc; - __tq = ((float) 1000 / CAN_cfg.speed) / 16; - } - - // set baud rate prescaler - MODULE_CAN->BTR0.B.BRP = (uint8_t) round((((APB_CLK_FREQ * __tq) / 2) - 1) / 1000000) - 1; - - /* Set sampling - * 1 -> triple; the bus is sampled three times; recommended for low/medium speed buses (class A and B) where - * filtering spikes on the bus line is beneficial 0 -> single; the bus is sampled once; recommended for high speed - * buses (SAE class C)*/ - MODULE_CAN->BTR1.B.SAM = 0x0; - - // enable all interrupts - MODULE_CAN->IER.U = 0xef; //ESP32 V3 0XEF ESP32 NOT V3 0XFF - - // Set acceptance filter - MODULE_CAN->MOD.B.AFM = __filter.FM; - MODULE_CAN->MBX_CTRL.ACC.CODE[0] = __filter.ACR0; - MODULE_CAN->MBX_CTRL.ACC.CODE[1] = __filter.ACR1; - MODULE_CAN->MBX_CTRL.ACC.CODE[2] = __filter.ACR2; - MODULE_CAN->MBX_CTRL.ACC.CODE[3] = __filter.ACR3; - MODULE_CAN->MBX_CTRL.ACC.MASK[0] = __filter.AMR0; - MODULE_CAN->MBX_CTRL.ACC.MASK[1] = __filter.AMR1; - MODULE_CAN->MBX_CTRL.ACC.MASK[2] = __filter.AMR2; - MODULE_CAN->MBX_CTRL.ACC.MASK[3] = __filter.AMR3; - - // set to normal mode - MODULE_CAN->OCR.B.OCMODE = __CAN_OC_NOM; - - // clear error counters - MODULE_CAN->TXERR.U = 0; - MODULE_CAN->RXERR.U = 0; - (void) MODULE_CAN->ECC; - - // clear interrupt flags - (void) MODULE_CAN->IR.U; - - // install CAN ISR - esp_intr_alloc(ETS_CAN_INTR_SOURCE, 0, CAN_isr, NULL, NULL); - - // allocate the tx complete semaphore - sem_tx_complete = xSemaphoreCreateBinary(); - - // Showtime. Release Reset Mode. - MODULE_CAN->MOD.B.RM = 0; - - return 0; -} - -bool CAN_write_frame(const CAN_frame_t *p_frame) { - if (sem_tx_complete == NULL) { - return false; - } - - // Write the frame to the controller - CAN_write_frame_phy(p_frame); - - return xSemaphoreTake(sem_tx_complete, 20) == pdTRUE ? true : false; -} - -int CAN_stop() { - // enter reset mode - MODULE_CAN->MOD.B.RM = 1; - - return 0; -} - -int CAN_config_filter(const CAN_filter_t* p_filter) { - - __filter.FM = p_filter->FM; - __filter.ACR0 = p_filter->ACR0; - __filter.ACR1 = p_filter->ACR1; - __filter.ACR2 = p_filter->ACR2; - __filter.ACR3 = p_filter->ACR3; - __filter.AMR0 = p_filter->AMR0; - __filter.AMR1 = p_filter->AMR1; - __filter.AMR2 = p_filter->AMR2; - __filter.AMR3 = p_filter->AMR3; - - return 0; -} diff --git a/Software/src/lib/miwagner-ESP32-Arduino-CAN/CAN.h b/Software/src/lib/miwagner-ESP32-Arduino-CAN/CAN.h deleted file mode 100644 index d8e925b8..00000000 --- a/Software/src/lib/miwagner-ESP32-Arduino-CAN/CAN.h +++ /dev/null @@ -1,135 +0,0 @@ -/** - * @section License - * - * The MIT License (MIT) - * - * Copyright (c) 2017, Thomas Barth, barth-dev.de - * 2018, Michael Wagner, mw@iot-make.de - * 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. - */ - -#ifndef __DRIVERS_CAN_H__ -#define __DRIVERS_CAN_H__ - -#include -#include "CAN_config.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \brief CAN frame type (standard/extended) - */ -typedef enum { - CAN_frame_std = 0, /**< Standard frame, using 11 bit identifer. */ - CAN_frame_ext = 1 /**< Extended frame, using 29 bit identifer. */ -} CAN_frame_format_t; - -/** - * \brief CAN RTR - */ -typedef enum { - CAN_no_RTR = 0, /**< No RTR frame. */ - CAN_RTR = 1 /**< RTR frame. */ -} CAN_RTR_t; - -/** \brief Frame information record type */ -typedef union { - uint32_t U; /**< \brief Unsigned access */ - struct { - uint8_t DLC : 4; /**< \brief [3:0] DLC, Data length container */ - unsigned int unknown_2 : 2; /**< \brief \internal unknown */ - CAN_RTR_t RTR : 1; /**< \brief [6:6] RTR, Remote Transmission Request */ - CAN_frame_format_t FF : 1; /**< \brief [7:7] Frame Format, see# CAN_frame_format_t*/ - unsigned int reserved_24 : 24; /**< \brief \internal Reserved */ - } B; -} CAN_FIR_t; - -/** \brief CAN Frame structure */ -typedef struct { - CAN_FIR_t FIR; /**< \brief Frame information record*/ - uint32_t MsgID; /**< \brief Message ID */ - union { - uint8_t u8[8]; /**< \brief Payload byte access*/ - uint32_t u32[2]; /**< \brief Payload u32 access*/ - uint64_t u64; /**< \brief Payload u64 access*/ - } data; -} CAN_frame_t; - -typedef enum { - Dual_Mode=0, /**< \brief The dual acceptance filter option is enabled (two filters, each with the length of 16 bit are active) */ - Single_Mode=1 /**< \brief The single acceptance filter option is enabled (one filter with the length of 32 bit is active) */ -} CAN_filter_mode_t; - -/** \brief CAN Filter structure */ -typedef struct { - CAN_filter_mode_t FM:1; /**< \brief [0:0] Filter Mode */ - uint8_t ACR0; /**< \brief Acceptance Code Register ACR0 */ - uint8_t ACR1; /**< \brief Acceptance Code Register ACR1 */ - uint8_t ACR2; /**< \brief Acceptance Code Register ACR2 */ - uint8_t ACR3; /**< \brief Acceptance Code Register ACR3 */ - uint8_t AMR0; /**< \brief Acceptance Mask Register AMR0 */ - uint8_t AMR1; /**< \brief Acceptance Mask Register AMR1 */ - uint8_t AMR2; /**< \brief Acceptance Mask Register AMR2 */ - uint8_t AMR3; /**< \brief Acceptance Mask Register AMR3 */ -} CAN_filter_t; - -extern void gpio_matrix_in(uint32_t gpio, uint32_t signal_idx, bool inv); -extern void gpio_matrix_out(uint32_t gpio, uint32_t signal_idx, bool out_inv, bool oen_inv); -extern void gpio_pad_select_gpio(uint8_t gpio_num); - -/** - * \brief Initialize the CAN Module - * - * \return 0 CAN Module had been initialized - */ -int CAN_init(void); - -/** - * \brief Send a can frame - * - * \param p_frame Pointer to the frame to be send, see #CAN_frame_t - * \return 1 Frame has been written to the module - */ -bool CAN_write_frame(const CAN_frame_t *p_frame); - -/** - * \brief Stops the CAN Module - * - * \return 0 CAN Module was stopped - */ -int CAN_stop(void); - -/** - * \brief Config CAN Filter, must call before CANInit() - * - * \param p_filter Pointer to the filter, see #CAN_filter_t - * \return 0 CAN Filter had been initialized - */ -int CAN_config_filter(const CAN_filter_t* p_filter); - - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/Software/src/lib/miwagner-ESP32-Arduino-CAN/CAN_config.h b/Software/src/lib/miwagner-ESP32-Arduino-CAN/CAN_config.h deleted file mode 100644 index 2840a3c9..00000000 --- a/Software/src/lib/miwagner-ESP32-Arduino-CAN/CAN_config.h +++ /dev/null @@ -1,71 +0,0 @@ -/** - * @section License - * - * The MIT License (MIT) - * - * Copyright (c) 2017, Thomas Barth, barth-dev.de - * - * 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. - */ - -#ifndef __DRIVERS_CAN_CFG_H__ -#define __DRIVERS_CAN_CFG_H__ - -#include "freertos/FreeRTOS.h" -#include "freertos/queue.h" -#include "freertos/task.h" -#include "driver/gpio.h" -#include "freertos/semphr.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** \brief CAN Node Bus speed */ -typedef enum { - CAN_SPEED_100KBPS = 100, /**< \brief CAN Node runs at 100kBit/s. */ - CAN_SPEED_125KBPS = 125, /**< \brief CAN Node runs at 125kBit/s. */ - CAN_SPEED_200KBPS = 200, /**< \brief CAN Node runs at 250kBit/s. */ - CAN_SPEED_250KBPS = 250, /**< \brief CAN Node runs at 250kBit/s. */ - CAN_SPEED_500KBPS = 500, /**< \brief CAN Node runs at 500kBit/s. */ - CAN_SPEED_800KBPS = 800, /**< \brief CAN Node runs at 800kBit/s. */ - CAN_SPEED_1000KBPS = 1000 /**< \brief CAN Node runs at 1000kBit/s. */ -} CAN_speed_t; - -/** \brief CAN configuration structure */ -typedef struct { - CAN_speed_t speed; /**< \brief CAN speed. */ - gpio_num_t tx_pin_id; /**< \brief TX pin. */ - gpio_num_t rx_pin_id; /**< \brief RX pin. */ - QueueHandle_t rx_queue; /**< \brief Handler to FreeRTOS RX queue. */ - QueueHandle_t tx_queue; /**< \brief Handler to FreeRTOS TX queue. */ - TaskHandle_t tx_handle; /**< \brief Handler to FreeRTOS TX task. */ - TaskHandle_t rx_handle; /**< \brief Handler to FreeRTOS RX task. */ -} CAN_device_t; - -/** \brief CAN configuration reference */ -extern CAN_device_t CAN_cfg; - -#ifdef __cplusplus -} -#endif - -#endif /* __DRIVERS_CAN_CFG_H__ */ diff --git a/Software/src/lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.cpp b/Software/src/lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.cpp deleted file mode 100644 index cd025d08..00000000 --- a/Software/src/lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "ESP32CAN.h" -#include -#include "../../devboard/utils/events.h" - -int ESP32CAN::CANInit() { - return CAN_init(); -} -bool ESP32CAN::CANWriteFrame(const CAN_frame_t* p_frame) { - return CAN_write_frame(p_frame); -} - -int ESP32CAN::CANStop() { - return CAN_stop(); -} -int ESP32CAN::CANConfigFilter(const CAN_filter_t* p_filter) { - return CAN_config_filter(p_filter); -} - -ESP32CAN ESP32Can; diff --git a/Software/src/lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h b/Software/src/lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h deleted file mode 100644 index 00363825..00000000 --- a/Software/src/lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef ESP32CAN_H -#define ESP32CAN_H - -#include "../../lib/miwagner-ESP32-Arduino-CAN/CAN.h" -#include "../../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h" - -class ESP32CAN { - public: - bool tx_ok = true; - int CANInit(); - int CANConfigFilter(const CAN_filter_t* p_filter); - bool CANWriteFrame(const CAN_frame_t* p_frame); - int CANStop(); - void CANSetCfg(CAN_device_t* can_cfg); -}; - -extern ESP32CAN ESP32Can; -#endif diff --git a/Software/src/lib/miwagner-ESP32-Arduino-CAN/can_regdef.h b/Software/src/lib/miwagner-ESP32-Arduino-CAN/can_regdef.h deleted file mode 100644 index 967ac81a..00000000 --- a/Software/src/lib/miwagner-ESP32-Arduino-CAN/can_regdef.h +++ /dev/null @@ -1,279 +0,0 @@ -/** - * @section License - * - * The MIT License (MIT) - * - * Copyright (c) 2017, Thomas Barth, barth-dev.de - * - * 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. - */ - -#ifndef __DRIVERS_CAN_REGDEF_H_ -#define __DRIVERS_CAN_REGDEF_H_ - -#include "CAN.h" //CAN_FIR_t - -#ifdef __cplusplus -extern "C" { -#endif - -/** \brief Start address of CAN registers */ -#define MODULE_CAN ((volatile CAN_Module_t *) 0x3ff6b000) - -/** \brief Get standard message ID */ -#define _CAN_GET_STD_ID \ - (((uint32_t) MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.STD.ID[0] << 3) | (MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.STD.ID[1] >> 5)) - -/** \brief Get extended message ID */ -#define _CAN_GET_EXT_ID \ - (((uint32_t) MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[0] << 21) | \ - (MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[1] << 13) | (MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[2] << 5) | \ - (MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[3] >> 3)) - -/** \brief Set standard message ID */ -#define _CAN_SET_STD_ID(x) \ - MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.STD.ID[0] = ((x) >> 3); \ - MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.STD.ID[1] = ((x) << 5); - -/** \brief Set extended message ID */ -#define _CAN_SET_EXT_ID(x) \ - MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[0] = ((x) >> 21); \ - MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[1] = ((x) >> 13); \ - MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[2] = ((x) >> 5); \ - MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[3] = ((x) << 3); - -/** \brief Interrupt status register */ -typedef enum { - __CAN_IRQ_RX = BIT(0), /**< \brief RX Interrupt */ - __CAN_IRQ_TX = BIT(1), /**< \brief TX Interrupt */ - __CAN_IRQ_ERR = BIT(2), /**< \brief Error Interrupt */ - __CAN_IRQ_DATA_OVERRUN = BIT(3), /**< \brief Data Overrun Interrupt */ - __CAN_IRQ_WAKEUP = BIT(4), /**< \brief Wakeup Interrupt */ - __CAN_IRQ_ERR_PASSIVE = BIT(5), /**< \brief Passive Error Interrupt */ - __CAN_IRQ_ARB_LOST = BIT(6), /**< \brief Arbitration lost interrupt */ - __CAN_IRQ_BUS_ERR = BIT(7), /**< \brief Bus error Interrupt */ -} __CAN_IRQ_t; - -/** \brief OCMODE options. */ -typedef enum { - __CAN_OC_BOM = 0b00, /**< \brief bi-phase output mode */ - __CAN_OC_TOM = 0b01, /**< \brief test output mode */ - __CAN_OC_NOM = 0b10, /**< \brief normal output mode */ - __CAN_OC_COM = 0b11, /**< \brief clock output mode */ -} __CAN_OCMODE_t; - -/** - * CAN controller (SJA1000). - */ -typedef struct { - union { - uint32_t U; /**< \brief Unsigned access */ - struct { - unsigned int RM : 1; /**< \brief MOD.0 Reset Mode */ - unsigned int LOM : 1; /**< \brief MOD.1 Listen Only Mode */ - unsigned int STM : 1; /**< \brief MOD.2 Self Test Mode */ - unsigned int AFM : 1; /**< \brief MOD.3 Acceptance Filter Mode */ - unsigned int SM : 1; /**< \brief MOD.4 Sleep Mode */ - unsigned int reserved_27 : 27; /**< \brief \internal Reserved */ - } B; - } MOD; - union { - uint32_t U; /**< \brief Unsigned access */ - struct { - unsigned int TR : 1; /**< \brief CMR.0 Transmission Request */ - unsigned int AT : 1; /**< \brief CMR.1 Abort Transmission */ - unsigned int RRB : 1; /**< \brief CMR.2 Release Receive Buffer */ - unsigned int CDO : 1; /**< \brief CMR.3 Clear Data Overrun */ - unsigned int GTS : 1; /**< \brief CMR.4 Go To Sleep */ - unsigned int reserved_27 : 27; /**< \brief \internal Reserved */ - } B; - } CMR; - union { - uint32_t U; /**< \brief Unsigned access */ - struct { - unsigned int RBS : 1; /**< \brief SR.0 Receive Buffer Status */ - unsigned int DOS : 1; /**< \brief SR.1 Data Overrun Status */ - unsigned int TBS : 1; /**< \brief SR.2 Transmit Buffer Status */ - unsigned int TCS : 1; /**< \brief SR.3 Transmission Complete Status */ - unsigned int RS : 1; /**< \brief SR.4 Receive Status */ - unsigned int TS : 1; /**< \brief SR.5 Transmit Status */ - unsigned int ES : 1; /**< \brief SR.6 Error Status */ - unsigned int BS : 1; /**< \brief SR.7 Bus Status */ - unsigned int reserved_24 : 24; /**< \brief \internal Reserved */ - } B; - } SR; - union { - uint32_t U; /**< \brief Unsigned access */ - struct { - unsigned int RI : 1; /**< \brief IR.0 Receive Interrupt */ - unsigned int TI : 1; /**< \brief IR.1 Transmit Interrupt */ - unsigned int EI : 1; /**< \brief IR.2 Error Interrupt */ - unsigned int DOI : 1; /**< \brief IR.3 Data Overrun Interrupt */ - unsigned int WUI : 1; /**< \brief IR.4 Wake-Up Interrupt */ - unsigned int EPI : 1; /**< \brief IR.5 Error Passive Interrupt */ - unsigned int ALI : 1; /**< \brief IR.6 Arbitration Lost Interrupt */ - unsigned int BEI : 1; /**< \brief IR.7 Bus Error Interrupt */ - unsigned int reserved_24 : 24; /**< \brief \internal Reserved */ - } B; - } IR; - union { - uint32_t U; /**< \brief Unsigned access */ - struct { - unsigned int RIE : 1; /**< \brief IER.0 Receive Interrupt Enable */ - unsigned int TIE : 1; /**< \brief IER.1 Transmit Interrupt Enable */ - unsigned int EIE : 1; /**< \brief IER.2 Error Interrupt Enable */ - unsigned int DOIE : 1; /**< \brief IER.3 Data Overrun Interrupt Enable */ - unsigned int WUIE : 1; /**< \brief IER.4 Wake-Up Interrupt Enable */ - unsigned int EPIE : 1; /**< \brief IER.5 Error Passive Interrupt Enable */ - unsigned int ALIE : 1; /**< \brief IER.6 Arbitration Lost Interrupt Enable */ - unsigned int BEIE : 1; /**< \brief IER.7 Bus Error Interrupt Enable */ - unsigned int reserved_24 : 24; /**< \brief \internal Reserved */ - } B; - } IER; - uint32_t RESERVED0; - union { - uint32_t U; /**< \brief Unsigned access */ - struct { - unsigned int BRP : 6; /**< \brief BTR0[5:0] Baud Rate Prescaler */ - unsigned int SJW : 2; /**< \brief BTR0[7:6] Synchronization Jump Width*/ - unsigned int reserved_24 : 24; /**< \brief \internal Reserved */ - } B; - } BTR0; - union { - uint32_t U; /**< \brief Unsigned access */ - struct { - unsigned int TSEG1 : 4; /**< \brief BTR1[3:0] Timing Segment 1 */ - unsigned int TSEG2 : 3; /**< \brief BTR1[6:4] Timing Segment 2*/ - unsigned int SAM : 1; /**< \brief BTR1.7 Sampling*/ - unsigned int reserved_24 : 24; /**< \brief \internal Reserved */ - } B; - } BTR1; - union { - uint32_t U; /**< \brief Unsigned access */ - struct { - unsigned int OCMODE : 2; /**< \brief OCR[1:0] Output Control Mode, see # */ - unsigned int OCPOL0 : 1; /**< \brief OCR.2 Output Control Polarity 0 */ - unsigned int OCTN0 : 1; /**< \brief OCR.3 Output Control Transistor N0 */ - unsigned int OCTP0 : 1; /**< \brief OCR.4 Output Control Transistor P0 */ - unsigned int OCPOL1 : 1; /**< \brief OCR.5 Output Control Polarity 1 */ - unsigned int OCTN1 : 1; /**< \brief OCR.6 Output Control Transistor N1 */ - unsigned int OCTP1 : 1; /**< \brief OCR.7 Output Control Transistor P1 */ - unsigned int reserved_24 : 24; /**< \brief \internal Reserved */ - } B; - } OCR; - uint32_t RESERVED1[2]; - union { - uint32_t U; /**< \brief Unsigned access */ - struct { - unsigned int ALC : 8; /**< \brief ALC[7:0] Arbitration Lost Capture */ - unsigned int reserved_24 : 24; /**< \brief \internal Reserved */ - } B; - } ALC; - union { - uint32_t U; /**< \brief Unsigned access */ - struct { - unsigned int ECC : 8; /**< \brief ECC[7:0] Error Code Capture */ - unsigned int reserved_24 : 24; /**< \brief \internal Reserved */ - } B; - } ECC; - union { - uint32_t U; /**< \brief Unsigned access */ - struct { - unsigned int EWLR : 8; /**< \brief EWLR[7:0] Error Warning Limit */ - unsigned int reserved_24 : 24; /**< \brief \internal Reserved */ - } B; - } EWLR; - union { - uint32_t U; /**< \brief Unsigned access */ - struct { - unsigned int RXERR : 8; /**< \brief RXERR[7:0] Receive Error Counter */ - unsigned int reserved_24 : 24; /**< \brief \internal Reserved */ - } B; - } RXERR; - union { - uint32_t U; /**< \brief Unsigned access */ - struct { - unsigned int TXERR : 8; /**< \brief TXERR[7:0] Transmit Error Counter */ - unsigned int reserved_24 : 24; /**< \brief \internal Reserved */ - } B; - } TXERR; - - union { - struct { - uint32_t CODE[4]; /**< \brief Acceptance Message ID */ - uint32_t MASK[4]; /**< \brief Acceptance Mask */ - uint32_t RESERVED2[5]; - } ACC; /**< \brief Acceptance filtering */ - struct { - CAN_FIR_t FIR; /**< \brief Frame information record */ - union { - struct { - uint32_t ID[2]; /**< \brief Standard frame message-ID*/ - uint32_t data[8]; /**< \brief Standard frame payload */ - uint32_t reserved[2]; - } STD; /**< \brief Standard frame format */ - struct { - uint32_t ID[4]; /**< \brief Extended frame message-ID*/ - uint32_t data[8]; /**< \brief Extended frame payload */ - } EXT; /**< \brief Extended frame format */ - } TX_RX; /**< \brief RX/TX interface */ - } FCTRL; /**< \brief Function control regs */ - } MBX_CTRL; /**< \brief Mailbox control */ - union { - uint32_t U; /**< \brief Unsigned access */ - struct { - unsigned int RMC : 8; /**< \brief RMC[7:0] RX Message Counter */ - unsigned int reserved_24 : 24; /**< \brief \internal Reserved Enable */ - } B; - } RMC; - union { - uint32_t U; /**< \brief Unsigned access */ - struct { - unsigned int RBSA : 8; /**< \brief RBSA[7:0] RX Buffer Start Address */ - unsigned int reserved_24 : 24; /**< \brief \internal Reserved Enable */ - } B; - } RBSA; - union { - uint32_t U; /**< \brief Unsigned access */ - struct { - unsigned int COD : 3; /**< \brief CDR[2:0] CLKOUT frequency selector based of fOSC*/ - unsigned int COFF : 1; /**< \brief CDR.3 CLKOUT off*/ - unsigned int reserved_1 : 1; /**< \brief \internal Reserved */ - unsigned int - RXINTEN : 1; /**< \brief CDR.5 This bit allows the TX1 output to be used as a dedicated receive interrupt - output*/ - unsigned int - CBP : 1; /**< \brief CDR.6 allows to bypass the CAN input comparator and is only possible in reset mode.*/ - unsigned int - CAN_M : 1; /**< \brief CDR.7 If CDR.7 is at logic 0 the CAN controller operates in BasicCAN mode. If set to - logic 1 the CAN controller operates in PeliCAN mode. Write access is only possible in reset - mode*/ - unsigned int reserved_24 : 24; /**< \brief \internal Reserved */ - } B; - } CDR; - uint32_t IRAM[2]; -} CAN_Module_t; - -#ifdef __cplusplus -} -#endif - -#endif /* __DRIVERS_CAN_REGDEF_H_ */ diff --git a/boards/esp32s3_flash_16MB.json b/boards/esp32s3_flash_16MB.json new file mode 100644 index 00000000..2f4ff022 --- /dev/null +++ b/boards/esp32s3_flash_16MB.json @@ -0,0 +1,43 @@ +{ + "build": { + "arduino": { + "ldscript": "esp32s3_out.ld" + }, + "core": "esp32", + "extra_flags": [ + "-DARDUINO_ESP32S3_DEV", + "-DARDUINO_USB_MODE=1", + "-DARDUINO_RUNNING_CORE=1", + "-DARDUINO_EVENT_RUNNING_CORE=1" + ], + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "qio", + "mcu": "esp32s3", + "variant": "esp32s3" + }, + "connectivity": [ + "wifi" + ], + "debug": { + "default_tool": "esp-builtin", + "onboard_tools": [ + "esp-builtin" + ], + "openocd_target": "esp32s3.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "ESP32-S3-FLASH-16MB", + "upload": { + "flash_size": "16MB", + "maximum_ram_size": 327680, + "maximum_size": 16777216, + "require_upload_port": true, + "speed": 921600 + }, + "url": "null", + "vendor": "null" +} \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index f2f86972..16aaf9ab 100644 --- a/platformio.ini +++ b/platformio.ini @@ -50,3 +50,22 @@ board_build.partitions = min_spiffs.csv framework = arduino build_flags = -I include -DHW_STARK -DCOMMON_IMAGE -DDEBUG_VIA_USB lib_deps = + +[env:lilygo_2can_330] +platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.30/platform-espressif32.zip +board = esp32s3_flash_16MB +monitor_speed = 115200 +monitor_filters = default, time, log2file +board_build.arduino.partitions = default_16MB.csv +board_build.arduino.memory_type = qio_opi +framework = arduino +build_flags = + -I include + -D HW_LILYGO2CAN + -D COMMON_IMAGE + -D BOARD_HAS_PSRAM + -D ARDUINO_USB_MODE=1 + -D ARDUINO_USB_CDC_ON_BOOT=1 ;1 is to use the USB port as a serial port + -D ARDUINO_RUNNING_CORE=1 ; Arduino Runs On Core (setup, loop) + -D ARDUINO_EVENT_RUNNING_CORE=1 ; Events Run On Core +lib_deps = \ No newline at end of file From eb047badfd2c384290b77014b43ee68daf8fe01c Mon Sep 17 00:00:00 2001 From: Matt Holmes Date: Sun, 17 Aug 2025 19:23:07 +0100 Subject: [PATCH 066/245] Updating native can library to ACAN_ESP32 --- Software/src/communication/can/comm_can.cpp | 164 +++--- Software/src/devboard/hal/hal.h | 2 + Software/src/devboard/hal/hw_lilygo2can.h | 1 + .../pierremolinaro-acan-esp32/ACAN_ESP32.cpp | 526 ++++++++++++++++++ .../pierremolinaro-acan-esp32/ACAN_ESP32.h | 375 +++++++++++++ .../ACAN_ESP32_AcceptanceFilters.h | 214 +++++++ .../ACAN_ESP32_Buffer16.h | 135 +++++ .../ACAN_ESP32_CANMessage.h | 49 ++ .../ACAN_ESP32_Settings.cpp | 128 +++++ .../ACAN_ESP32_Settings.h | 155 ++++++ .../ACAN_ESP32_TWAI_base_address.h | 85 +++ 11 files changed, 1761 insertions(+), 73 deletions(-) create mode 100644 Software/src/lib/pierremolinaro-acan-esp32/ACAN_ESP32.cpp create mode 100644 Software/src/lib/pierremolinaro-acan-esp32/ACAN_ESP32.h create mode 100644 Software/src/lib/pierremolinaro-acan-esp32/ACAN_ESP32_AcceptanceFilters.h create mode 100644 Software/src/lib/pierremolinaro-acan-esp32/ACAN_ESP32_Buffer16.h create mode 100644 Software/src/lib/pierremolinaro-acan-esp32/ACAN_ESP32_CANMessage.h create mode 100644 Software/src/lib/pierremolinaro-acan-esp32/ACAN_ESP32_Settings.cpp create mode 100644 Software/src/lib/pierremolinaro-acan-esp32/ACAN_ESP32_Settings.h create mode 100644 Software/src/lib/pierremolinaro-acan-esp32/ACAN_ESP32_TWAI_base_address.h diff --git a/Software/src/communication/can/comm_can.cpp b/Software/src/communication/can/comm_can.cpp index f0c14696..5ed4be1b 100644 --- a/Software/src/communication/can/comm_can.cpp +++ b/Software/src/communication/can/comm_can.cpp @@ -1,8 +1,7 @@ #include "comm_can.h" #include #include -#include "esp_twai.h" -#include "esp_twai_onchip.h" +#include "../../lib/pierremolinaro-acan-esp32/ACAN_ESP32.h" #include "../../lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h" #include "../../lib/pierremolinaro-acan2515/ACAN2515.h" #include "CanReceiver.h" @@ -20,12 +19,6 @@ struct CanReceiverRegistration { static std::multimap can_receivers; -// Parameters -twai_node_handle_t node_hdl = NULL; -twai_onchip_node_config_t node_config = { - .tx_queue_depth = 5 -}; - const uint8_t rx_queue_size = 10; // Receive Queue size volatile bool send_ok_native = 0; volatile bool send_ok_2515 = 0; @@ -46,6 +39,8 @@ void register_can_receiver(CanReceiver* receiver, CAN_Interface interface, CAN_S DEBUG_PRINTF("CAN receiver registered, total: %d\n", can_receivers.size()); } +ACAN_ESP32_Settings* settingsespcan; + static const uint32_t QUARTZ_FREQUENCY = CRYSTAL_FREQUENCY_MHZ * 1000000UL; //MHZ configured in USER_SETTINGS.h SPIClass SPI2515; @@ -62,32 +57,6 @@ ACAN2517FDSettings* settings2517; bool native_can_initialized = false; -static bool twai_rx_callback(twai_node_handle_t handle, const twai_rx_done_event_data_t *edata, void *user_ctx) -{ - uint8_t recv_buff[8]; - twai_frame_t rx_frame = { - .buffer = recv_buff, - .buffer_len = sizeof(recv_buff), - }; - if (ESP_OK == twai_node_receive_from_isr(handle, &rx_frame)) { - CAN_frame frame; - frame.ID = rx_frame.header.id; - frame.DLC = rx_frame.header.dlc; - frame.ext_ID = rx_frame.header.ide; - for (uint8_t i = 0; i < frame.DLC && i < 8; i++) { - frame.data.u8[i] = rx_frame.buffer[i]; - } - map_can_frame_to_variable(&frame, CAN_NATIVE); - - } - return false; -} - -twai_event_callbacks_t user_cbs = { - .on_rx_done = twai_rx_callback -}; - - bool init_CAN() { auto nativeIt = can_receivers.find(CAN_NATIVE); @@ -108,14 +77,43 @@ bool init_CAN() { return false; } - node_config.io_cfg.tx = tx_pin; - node_config.io_cfg.rx = rx_pin; - node_config.bit_timing.bitrate = (int)nativeIt->second.speed * 1000UL; - twai_new_node_onchip(&node_config, &node_hdl); - twai_node_register_event_callbacks(node_hdl, &user_cbs, NULL); - twai_node_enable(node_hdl); + settingsespcan = new ACAN_ESP32_Settings((int)nativeIt->second.speed * 1000UL); + settingsespcan->mRequestedCANMode = ACAN_ESP32_Settings::NormalMode; + settingsespcan->mTxPin = tx_pin; + settingsespcan->mRxPin = rx_pin; - native_can_initialized = true; + const uint32_t errorCode = ACAN_ESP32::can.begin(*settingsespcan); + if (errorCode == 0) + { + native_can_initialized = true; +#ifdef DEBUG_LOG + logging.println("Native Can ok"); + logging.print("Bit Rate prescaler: "); + logging.println(settingsespcan->mBitRatePrescaler); + logging.print("Time Segment 1: "); + logging.println(settingsespcan->mTimeSegment1); + logging.print("Time Segment 2: "); + logging.println(settingsespcan->mTimeSegment2); + logging.print ("RJW: ") ; + logging.println (settingsespcan->mRJW) ; + logging.print("Triple Sampling: "); + logging.println(settingsespcan->mTripleSampling ? "yes" : "no"); + logging.print("Actual bit rate: "); + logging.print(settingsespcan->actualBitRate()); + logging.println(" bit/s"); + logging.print("Exact bit rate ? "); + logging.println(settingsespcan->exactBitRate() ? "yes" : "no"); + logging.print("Sample point: "); + logging.print(settingsespcan->samplePointFromBitStart()); + logging.println("%"); +#endif // DEBUG_LOG + } else { +#ifdef DEBUG_LOG + logging.print("Error Native Can: 0x"); + logging.println(errorCode, HEX); +#endif // DEBUG_LOG + return false; + } } auto addonIt = can_receivers.find(CAN_ADDON_MCP2515); @@ -125,6 +123,7 @@ bool init_CAN() { auto sck_pin = esp32hal->MCP2515_SCK(); auto miso_pin = esp32hal->MCP2515_MISO(); auto mosi_pin = esp32hal->MCP2515_MOSI(); + auto rst_pin = esp32hal->MCP2515_RST(); if (!esp32hal->alloc_pins("CAN", cs_pin, int_pin, sck_pin, miso_pin, mosi_pin)) { return false; @@ -135,14 +134,15 @@ bool init_CAN() { #endif // DEBUG_LOG gBuffer.initWithSize(25); - //ToDo: Refactor this - pinMode(9, OUTPUT); - digitalWrite(9, HIGH); - delay(100); - digitalWrite(9, LOW); - delay(100); - digitalWrite(9, HIGH); - delay(100); + if(rst_pin != GPIO_NUM_NC) { + pinMode(rst_pin, OUTPUT); + digitalWrite(rst_pin, HIGH); + delay(100); + digitalWrite(rst_pin, LOW); + delay(100); + digitalWrite(rst_pin, HIGH); + delay(100); + } can2515 = new ACAN2515(cs_pin, SPI2515, int_pin); @@ -156,11 +156,11 @@ bool init_CAN() { const uint16_t errorCode2515 = can2515->begin(*settings2515, [] { can2515->isr(); }); if (errorCode2515 == 0) { #ifdef DEBUG_LOG - logging.println("Can ok"); + logging.println("MCP2515 Can ok"); #endif // DEBUG_LOG } else { #ifdef DEBUG_LOG - logging.print("Error Can: 0x"); + logging.print("Error MCP2515 Can: 0x"); logging.println(errorCode2515, HEX); #endif // DEBUG_LOG set_event(EVENT_CANMCP2515_INIT_FAILURE, (uint8_t)errorCode2515); @@ -244,21 +244,21 @@ void transmit_can_frame_to_interface(const CAN_frame* tx_frame, int interface) { #endif switch (interface) { - case CAN_NATIVE: + case CAN_NATIVE: { - twai_frame_t frame; - frame.header.id = tx_frame->ID; - frame.header.dlc = tx_frame->DLC; - frame.buffer = tx_frame->data.u8; - frame.buffer_len = tx_frame->DLC; - frame.header.ide = tx_frame->ext_ID ? 1 : 0; - - send_ok_native = twai_node_transmit(node_hdl, &frame, 0); + CANMessage frame; + frame.id = tx_frame->ID; + frame.ext = tx_frame->ext_ID; + frame.len = tx_frame->DLC; + for (uint8_t i = 0; i < frame.len; i++) { + frame.data[i] = tx_frame->data.u8[i]; + } + send_ok_native = ACAN_ESP32::can.tryToSend(frame); if (!send_ok_native) { datalayer.system.info.can_native_send_fail = true; } - break; + } break; case CAN_ADDON_MCP2515: { //Struct with ACAN2515 library format, needed to use the MCP2515 library for CAN2 CANMessage MCP2515Frame; @@ -302,6 +302,10 @@ void transmit_can_frame_to_interface(const CAN_frame* tx_frame, int interface) { // Receive functions void receive_can() { + if (native_can_initialized) { + receive_frame_can_native(); // Receive CAN messages from native CAN port + } + if (can2515) { receive_frame_can_addon(); // Receive CAN messages on add-on MCP2515 chip } @@ -311,7 +315,25 @@ void receive_can() { } } +void receive_frame_can_native() { // This section checks if we have a complete CAN message incoming on native CAN port + CANMessage frame; + if (ACAN_ESP32::can.available()) { + if(ACAN_ESP32::can.receive(frame)){ + + CAN_frame rx_frame; + rx_frame.ID = frame.id; + rx_frame.ext_ID = frame.ext; + rx_frame.DLC = frame.len; + for (uint8_t i = 0; i < frame.len && i < 8; i++) { + rx_frame.data.u8[i] = frame.data[i]; + } + + //message incoming, pass it on to the handler + map_can_frame_to_variable(&rx_frame, CAN_NATIVE); + } + } +} void receive_frame_can_addon() { // This section checks if we have a complete CAN message incoming on add-on CAN port CAN_frame rx_frame; // Struct with our CAN format @@ -434,8 +456,7 @@ void dump_can_frame(CAN_frame& frame, frameDirection msgDir) { void stop_can() { if (can_receivers.find(CAN_NATIVE) != can_receivers.end()) { - //ToDo: Implement - //ESP32Can.CANStop(); + ACAN_ESP32::can.end(); } if (can2515) { @@ -451,8 +472,7 @@ void stop_can() { void restart_can() { if (can_receivers.find(CAN_NATIVE) != can_receivers.end()) { - //ToDo: Implement - //ESP32Can.CANInit(); + ACAN_ESP32::can.begin(*settingsespcan); } if (can2515) { @@ -467,12 +487,10 @@ void restart_can() { } CAN_Speed change_can_speed(CAN_Interface interface, CAN_Speed speed) { - //ToDo: Implement - //auto oldSpeed = (CAN_Speed)CAN_cfg.speed; - //if (interface == CAN_Interface::CAN_NATIVE) { - // CAN_cfg.speed = (CAN_speed_t)speed; - // ReInit native CAN module at new speed - // ESP32Can.CANInit(); - //} + auto oldSpeed = (CAN_Speed)settingsespcan->mDesiredBitRate; + if (interface == CAN_Interface::CAN_NATIVE) { + settingsespcan->mDesiredBitRate = (int)speed; + ACAN_ESP32::can.begin(*settingsespcan); + } return speed; -} +} \ No newline at end of file diff --git a/Software/src/devboard/hal/hal.h b/Software/src/devboard/hal/hal.h index b96fc574..e70770d6 100644 --- a/Software/src/devboard/hal/hal.h +++ b/Software/src/devboard/hal/hal.h @@ -109,6 +109,8 @@ class Esp32Hal { virtual gpio_num_t MCP2515_CS() { return GPIO_NUM_NC; } // INT output of MCP2515 virtual gpio_num_t MCP2515_INT() { return GPIO_NUM_NC; } + // Reset pin for MCP2515 + virtual gpio_num_t MCP2515_RST() { return GPIO_NUM_NC; } // CANFD_ADDON defines for MCP2517 virtual gpio_num_t MCP2517_SCK() { return GPIO_NUM_NC; } diff --git a/Software/src/devboard/hal/hw_lilygo2can.h b/Software/src/devboard/hal/hw_lilygo2can.h index 9699958a..907c0413 100644 --- a/Software/src/devboard/hal/hw_lilygo2can.h +++ b/Software/src/devboard/hal/hw_lilygo2can.h @@ -16,6 +16,7 @@ class LilyGo2CANHal : public Esp32Hal { virtual gpio_num_t MCP2515_MISO() { return GPIO_NUM_13; } virtual gpio_num_t MCP2515_CS() { return GPIO_NUM_10; } virtual gpio_num_t MCP2515_INT() { return GPIO_NUM_8; } + virtual gpio_num_t MCP2515_RST() { return GPIO_NUM_9; } // CANFD_ADDON defines for MCP2517 virtual gpio_num_t MCP2517_SCK() { return GPIO_NUM_17; } diff --git a/Software/src/lib/pierremolinaro-acan-esp32/ACAN_ESP32.cpp b/Software/src/lib/pierremolinaro-acan-esp32/ACAN_ESP32.cpp new file mode 100644 index 00000000..b7159266 --- /dev/null +++ b/Software/src/lib/pierremolinaro-acan-esp32/ACAN_ESP32.cpp @@ -0,0 +1,526 @@ +//------------------------------------------------------------------------------ +// Include files +//------------------------------------------------------------------------------ + +#include "ACAN_ESP32.h" + +//------------------------------------------------------------------------------ + +//--- Header for periph_module_enable +#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0) + #include +#else + #include +#endif + +#include // For ESP32 board manager + +//------------------------------------------------------------------------------ +// ESP32 Critical Section +//------------------------------------------------------------------------------ + +// taskENTER_CRITICAL() of FREE-RTOS is deprecated as portENTER_CRITICAL() in ESP32 +//--- https://esp32.com/viewtopic.php?t=1703 +static portMUX_TYPE portMux = portMUX_INITIALIZER_UNLOCKED ; + +//------------------------------------------------------------------------------ +// CONSTRUCTOR for ESP32C6 (2 TWAI controllers) +//------------------------------------------------------------------------------ + +#ifdef CONFIG_IDF_TARGET_ESP32C6 + ACAN_ESP32::ACAN_ESP32 (const uint32_t inBaseAddress, + const uint32_t inTxPinIndexSelector, + const uint32_t inRxPinIndexSelector, + const periph_module_t inPeriphModule, + const periph_interrupt_t inInterruptSource, + const uint32_t inClockEnableAddress) : + twaiBaseAddress (inBaseAddress), + twaiTxPinSelector (inTxPinIndexSelector), + twaiRxPinSelector (inRxPinIndexSelector), + twaiPeriphModule (inPeriphModule), + twaiInterruptSource (inInterruptSource), + twaiClockEnableAddress (inClockEnableAddress), + mAcceptedFrameFormat (ACAN_ESP32_Filter::standardAndExtended), + mDriverReceiveBuffer (), + mDriverTransmitBuffer (), + mDriverIsSending (false) { +} +#endif + +//------------------------------------------------------------------------------ +// CONSTRUCTOR for others (1 TWAI controller) +//------------------------------------------------------------------------------ + +#ifndef CONFIG_IDF_TARGET_ESP32C6 + ACAN_ESP32::ACAN_ESP32 (void) : + mAcceptedFrameFormat (ACAN_ESP32_Filter::standardAndExtended), + mDriverReceiveBuffer (), + mDriverTransmitBuffer (), + mDriverIsSending (false) { +} +#endif + +//------------------------------------------------------------------------------ +// Set the GPIO pins +//------------------------------------------------------------------------------ + +void ACAN_ESP32::setGPIOPins (const gpio_num_t inTXPin, + const gpio_num_t inRXPin) { +//--- Set TX pin + pinMode (inTXPin, OUTPUT) ; + pinMatrixOutAttach (inTXPin, twaiTxPinSelector, false, false) ; +//--- Set RX pin + pinMode (inRXPin, INPUT) ; + pinMatrixInAttach (inRXPin, twaiRxPinSelector, false) ; +} + +//------------------------------------------------------------------------------ +// Set the Requested Mode +//------------------------------------------------------------------------------ + +void ACAN_ESP32::setRequestedCANMode (const ACAN_ESP32_Settings & inSettings, + const ACAN_ESP32_Filter & inFilter) { +// ESP32 CAN Operation Mode Configuration +// +// Supported Mode MODE Registers +// - Normal Mode - Reset -> bit(0) +// - No ACK - ListenOnly -> bit(1) +// - Acceptance Filter - SelfTest -> bit(2) +// - Acceptance Filter -> bit(3) + + uint8_t requestedMode = 0 ; + switch (inSettings.mRequestedCANMode) { + case ACAN_ESP32_Settings::NormalMode : + break ; + case ACAN_ESP32_Settings::ListenOnlyMode : + requestedMode = TWAI_LISTEN_ONLY_MODE ; + break ; + case ACAN_ESP32_Settings::LoopBackMode : + requestedMode = TWAI_SELF_TEST_MODE ; + break ; + } + + if (inFilter.mAMFSingle) { + requestedMode |= TWAI_RX_FILTER_MODE ; + } + + TWAI_MODE_REG () = requestedMode | TWAI_RESET_MODE ; + uint32_t unusedResult __attribute__((unused)) = TWAI_MODE_REG () ; + + do{ + TWAI_MODE_REG () = requestedMode ; + }while ((TWAI_MODE_REG () & TWAI_RESET_MODE) != 0) ; +} + +//------------------------------------------------------------------------------ +// Set the Bus timing Registers +//------------------------------------------------------------------------------ + +inline void ACAN_ESP32::setBitTimingSettings (const ACAN_ESP32_Settings & inSettings) { +// BUS TIMING Configuration of ESP32 CAN +// ACAN_ESP32_Settings calculates the best values for the desired bit Rate. + +//--- Caution! TWAI_BUS_TIMING_0_REG is specific +#ifdef CONFIG_IDF_TARGET_ESP32 + // BTR0 : bit (0 - 5) -> Baud Rate Prescaller (BRP) + // bit (6 - 7) -> Resynchronization Jump Width (RJW) + TWAI_BUS_TIMING_0_REG () = + ((inSettings.mRJW - 1) << 6) | // SJW + ((inSettings.mBitRatePrescaler - 1) << 0) // BRP + ; +#elif defined (CONFIG_IDF_TARGET_ESP32S3) + // BTR0 : bit (00 - 13) -> Baud Rate Prescaller (BRP) + // bit (14 - 15) -> Resynchronization Jump Width (RJW) + TWAI_BUS_TIMING_0_REG () = + ((inSettings.mRJW - 1) << 14) | // SJW + ((inSettings.mBitRatePrescaler - 1) << 0) // BRP + ; +#elif defined (CONFIG_IDF_TARGET_ESP32S2) + // BTR0 : bit (00 - 13) -> Baud Rate Prescaller (BRP) + // bit (14 - 15) -> Resynchronization Jump Width (RJW) + TWAI_BUS_TIMING_0_REG () = + ((inSettings.mRJW - 1) << 14) | // SJW + ((inSettings.mBitRatePrescaler - 1) << 0) // BRP + ; +#elif defined (CONFIG_IDF_TARGET_ESP32C3) + // BTR0 : bit (00 - 13) -> Baud Rate Prescaller (BRP) + // bit (14 - 15) -> Resynchronization Jump Width (RJW) + TWAI_BUS_TIMING_0_REG () = + ((inSettings.mRJW - 1) << 14) | // SJW + ((inSettings.mBitRatePrescaler - 1) << 0) // BRP + ; +#elif defined (CONFIG_IDF_TARGET_ESP32C6) + // BTR0 : bit (00 - 13) -> Baud Rate Prescaller (BRP) + // bit (14 - 15) -> Resynchronization Jump Width (RJW) + TWAI_BUS_TIMING_0_REG () = + ((inSettings.mRJW - 1) << 14) | // SJW + ((inSettings.mBitRatePrescaler - 1) << 0) // BRP + ; +#else + #error "Unknown board" +#endif + +//--- BTR1: bit (0 - 3) -> TimeSegment 1 (Tseg1) +// bit (4 - 6) -> TimeSegment 2 (Tseg2) +// bit (7) -> TripleSampling? (SAM) + TWAI_BUS_TIMING_1_REG () = + ((inSettings.mTripleSampling) << 7) | // Sampling + ((inSettings.mTimeSegment2 - 1) << 4) | // Tseg2 + ((inSettings.mTimeSegment1 - 1) << 0) // Tseg1 + ; +} + +//------------------------------------------------------------------------------ + +void ACAN_ESP32::setAcceptanceFilter (const ACAN_ESP32_Filter & inFilter) { +//--- Write the Code and Mask Registers with Acceptance Filter Settings + if (inFilter.mAMFSingle) { + TWAI_MODE_REG () |= TWAI_RX_FILTER_MODE ; + } + mAcceptedFrameFormat = inFilter.mFormat ; + + TWAI_ACC_CODE_FILTER (0) = inFilter.mACR0 ; + TWAI_ACC_CODE_FILTER (1) = inFilter.mACR1 ; + TWAI_ACC_CODE_FILTER (2) = inFilter.mACR2 ; + TWAI_ACC_CODE_FILTER (3) = inFilter.mACR3 ; + + TWAI_ACC_MASK_FILTER (0) = inFilter.mAMR0 ; + TWAI_ACC_MASK_FILTER (1) = inFilter.mAMR1 ; + TWAI_ACC_MASK_FILTER (2) = inFilter.mAMR2 ; + TWAI_ACC_MASK_FILTER (3) = inFilter.mAMR3 ; +} + +//------------------------------------------------------------------------------ +// BEGIN +//------------------------------------------------------------------------------ + +uint32_t ACAN_ESP32::begin (const ACAN_ESP32_Settings & inSettings, + const ACAN_ESP32_Filter & inFilterSettings) { +// Serial.println (twaiBaseAddress, HEX) ; + uint32_t errorCode = 0 ; // Ok by default +//--------------------------------- Enable CAN module clock (only for ESP32C6) + #ifdef CONFIG_IDF_TARGET_ESP32C6 + #define ACAN_CLOCK_ENABLE_REG (*((volatile uint32_t *) twaiClockEnableAddress)) + ACAN_CLOCK_ENABLE_REG = 1 << 22 ; + //--- Display enable settings (for twai0) +// #define ACAN_PCR_TWAI0_CONF_REG (*((volatile uint32_t *) (0x60096000 + 0x05C))) +// #define ACAN_PCR_TWAI0_FUNC_CLK_CONF_REG (*((volatile uint32_t *) (0x60096000 + 0x060))) +// Serial.print ("Reset reg (should be 1): 0x") ; +// Serial.println (ACAN_PCR_TWAI0_CONF_REG, HEX) ; +// Serial.print ("Clock select reg (should be 0x400000): 0x") ; +// Serial.println (ACAN_PCR_TWAI0_FUNC_CLK_CONF_REG, HEX) ; + #endif +//--------------------------------- Enable CAN module + periph_module_enable (twaiPeriphModule) ; +//--------------------------------- Set GPIO pins + setGPIOPins (inSettings.mTxPin, inSettings.mRxPin); +//--------------------------------- Required: It is must to enter RESET Mode to write the Configuration Registers + TWAI_CMD_REG () = TWAI_ABORT_TX ; + TWAI_MODE_REG () = TWAI_RESET_MODE ; + while ((TWAI_MODE_REG () & TWAI_RESET_MODE) == 0) { + TWAI_MODE_REG () = TWAI_RESET_MODE ; + } + if ((TWAI_MODE_REG () & TWAI_RESET_MODE) == 0) { + errorCode = kNotInResetModeInConfiguration ; + } +//--------------------------------- Disable Interupts + TWAI_INT_ENA_REG () = 0 ; + if (mInterruptHandler != nullptr) { + esp_intr_free (mInterruptHandler) ; + mInterruptHandler = nullptr ; + } +//--------------------------------- Use Pelican Mode + TWAI_CLOCK_DIVIDER_REG () = TWAI_EXT_MODE ; +//---- Check the Register access and bit timing settings before writing to the Bit Timing Registers + TWAI_BUS_TIMING_0_REG () = 0x55 ; + bool ok = TWAI_BUS_TIMING_0_REG () == 0x55 ; + if (ok) { + TWAI_BUS_TIMING_0_REG () = 0xAA ; + ok = TWAI_BUS_TIMING_0_REG () == 0xAA ; + } + if (!ok) { + errorCode |= kCANRegistersError ; + } +//----------------------------------- If ok, check the bit timing settings are correct + if (!inSettings.mBitRateClosedToDesiredRate) { + errorCode |= kTooFarFromDesiredBitRate; + } + errorCode |= inSettings.CANBitSettingConsistency (); +//----------------------------------- Allocate buffer + if (!mDriverReceiveBuffer.initWithSize (inSettings.mDriverReceiveBufferSize)) { + errorCode |= kCannotAllocateDriverReceiveBuffer ; + } + if (!mDriverTransmitBuffer.initWithSize (inSettings.mDriverTransmitBufferSize)) { + errorCode |= kCannotAllocateDriverTransmitBuffer ; + } +//--------------------------------- Set Bus timing Registers + if (errorCode == 0) { + setBitTimingSettings (inSettings) ; + } +//--------------------------------- Set the Acceptance Filter + setAcceptanceFilter (inFilterSettings) ; +//--------------------------------- Set and clear the error counters to default value + TWAI_ERR_WARNING_LIMIT_REG () = 96 ; + TWAI_RX_ERR_CNT_REG () = 0 ; +//--------------------------------- Clear the Interrupt Registers + const uint32_t unusedVariable __attribute__((unused)) = TWAI_INT_RAW_REG () ; +//--------------------------------- Set Interrupt Service Routine + esp_intr_alloc (twaiInterruptSource, 0, isr, this, & mInterruptHandler) ; +//--------------------------------- Enable Interupts + TWAI_INT_ENA_REG () = TWAI_TX_INT_ENA | TWAI_RX_INT_ENA ; +//--------------------------------- Set to Requested Mode + setRequestedCANMode (inSettings, inFilterSettings) ; +//--- + return errorCode ; +} + +//---------------------------------------------------------------------------------------- +// Stop CAN controller and uninstall ISR +//---------------------------------------------------------------------------------------- + +void ACAN_ESP32::end (void) { +//--------------------------------- Abort any pending transfer, don't care about resetting + TWAI_CMD_REG () = TWAI_ABORT_TX ; + +//--------------------------------- Disable Interupts + TWAI_INT_ENA_REG () = 0 ; + if (mInterruptHandler != nullptr) { + esp_intr_free (mInterruptHandler) ; + mInterruptHandler = nullptr ; + } + +//--------------------------------- Disable CAN module + periph_module_disable (PERIPH_TWAI_MODULE) ; +} + +//------------------------------------------------------------------------------ +//--- Status Flags (returns 0 if no error) +// Bit 0 : hardware receive FIFO overflow +// Bit 1 : driver receive FIFO overflow +// Bit 2 : bus off +// Bit 3 : reset mode + +uint32_t ACAN_ESP32::statusFlags (void) const { + uint32_t result = 0 ; // Ok + const uint32_t status = TWAI_STATUS_REG () ; +//--- Hardware receive FIFO overflow ? + if ((status & TWAI_OVERRUN_ST) != 0) { + result |= 1U << 0 ; + } +//--- Driver receive FIFO overflow ? + if (mDriverReceiveBuffer.didOverflow ()) { + result |= 1U << 1 ; + } +//--- Bus off ? + if ((status & TWAI_BUS_OFF_ST) != 0) { + result |= 1U << 2 ; + } +//--- Reset mode ? + if ((TWAI_MODE_REG () & TWAI_RESET_MODE) != 0) { + result |= 1U << 3 ; + } +//--- + return result ; +} + +//------------------------------------------------------------------------------ + +bool ACAN_ESP32::recoverFromBusOff (void) const { + const bool isBusOff = (TWAI_STATUS_REG () & TWAI_BUS_OFF_ST) != 0 ; + const bool inResetMode = (TWAI_MODE_REG () & TWAI_RESET_MODE) != 0 ; + const bool recover = isBusOff && inResetMode ; + if (recover) { + TWAI_MODE_REG () &= ~ TWAI_RESET_MODE ; + } + return recover ; +} + +//------------------------------------------------------------------------------ +// Interrupt Handler +//------------------------------------------------------------------------------ + +void IRAM_ATTR ACAN_ESP32::isr (void * inUserArgument) { + ACAN_ESP32 * myDriver = (ACAN_ESP32 *) inUserArgument ; + + portENTER_CRITICAL (&portMux) ; + const uint32_t interrupt = myDriver->TWAI_INT_RAW_REG () ; + if ((interrupt & TWAI_RX_INT_ST) != 0) { + myDriver->handleRXInterrupt () ; + } + if ((interrupt & TWAI_TX_INT_ST) != 0) { + myDriver->handleTXInterrupt () ; + } + portEXIT_CRITICAL (&portMux) ; + + portYIELD_FROM_ISR () ; +} + +//------------------------------------------------------------------------------ + +void ACAN_ESP32::handleTXInterrupt (void) { + CANMessage message ; + const bool sendmsg = mDriverTransmitBuffer.remove (message) ; + if (sendmsg) { + internalSendMessage (message) ; + }else { + mDriverIsSending = false ; + } +} + +//------------------------------------------------------------------------------ + +void ACAN_ESP32::handleRXInterrupt (void) { + CANMessage frame; + getReceivedMessage (frame) ; + switch (mAcceptedFrameFormat) { + case ACAN_ESP32_Filter::standard : + if (!frame.ext) { + mDriverReceiveBuffer.append (frame) ; + } + break ; + case ACAN_ESP32_Filter::extended : + if (frame.ext) { + mDriverReceiveBuffer.append (frame) ; + } + break ; + case ACAN_ESP32_Filter::standardAndExtended : + mDriverReceiveBuffer.append (frame) ; + break ; + } +} + +//------------------------------------------------------------------------------ +// RECEPTION +//------------------------------------------------------------------------------ + +bool ACAN_ESP32::available (void) const { + const bool hasReceivedMessage = mDriverReceiveBuffer.count () > 0 ; + return hasReceivedMessage ; +} + +//------------------------------------------------------------------------------ + +bool ACAN_ESP32::receive (CANMessage & outMessage) { + portENTER_CRITICAL (&portMux) ; + const bool hasReceivedMessage = mDriverReceiveBuffer.remove (outMessage) ; + portEXIT_CRITICAL (&portMux) ; + return hasReceivedMessage ; +} + +//------------------------------------------------------------------------------ + +void ACAN_ESP32::getReceivedMessage (CANMessage & outFrame) { + const uint32_t frameInfo = TWAI_FRAME_INFO () ; + + outFrame.len = frameInfo & 0xF; + if (outFrame.len > 8) { + outFrame.len = 8 ; + } + outFrame.rtr = (frameInfo & TWAI_RTR) != 0 ; + outFrame.ext = (frameInfo & TWAI_FRAME_FORMAT_EFF) != 0 ; + + if (!outFrame.ext) { //--- Standard Frame + outFrame.id = uint32_t (TWAI_ID_SFF(0)) << 3 ; + outFrame.id |= uint32_t (TWAI_ID_SFF(1)) >> 5 ; + + for (uint8_t i=0 ; i> 3 ; + for (uint8_t i=0 ; i> 3) & 255 ; + TWAI_ID_SFF(1) = (inFrame.id << 5) & 255 ; + //--- Set data + for (uint8_t i=0 ; i> 21) & 255 ; + TWAI_ID_EFF(1) = (inFrame.id >> 13) & 255 ; + TWAI_ID_EFF(2) = (inFrame.id >> 5) & 255 ; + TWAI_ID_EFF(3) = (inFrame.id << 3) & 255 ; + //--- Set data + for (uint8_t i=0 ; i + +//---------------------------------------------------------------------------------------- + +class ACAN_ESP32_Filter { + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + public: typedef enum : uint8_t { standard, extended, standardAndExtended } Format ; + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + public: typedef enum : uint8_t { data, remote, dataAndRemote } Type ; + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // public properties + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + public: uint8_t mACR0 ; + public: uint8_t mACR1 ; + public: uint8_t mACR2 ; + public: uint8_t mACR3 ; + public: uint8_t mAMR0 ; + public: uint8_t mAMR1 ; + public: uint8_t mAMR2 ; + public: uint8_t mAMR3 ; + public: bool mAMFSingle ; + public: Format mFormat ; + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Default private constructor + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + private: ACAN_ESP32_Filter (void) : + mACR0 (0), + mACR1 (0), + mACR2 (0), + mACR3 (0), + mAMR0 (0xFF), + mAMR1 (0xFF), + mAMR2 (0xFF), + mAMR3 (0xFF), + mAMFSingle (false), + mFormat (standardAndExtended) { + } + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Accept all filter + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + public: static inline ACAN_ESP32_Filter acceptAll (void) { + return ACAN_ESP32_Filter () ; + } + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Accept only standard frames + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + public: static inline ACAN_ESP32_Filter acceptStandardFrames (void) { + ACAN_ESP32_Filter result ; + result.mFormat = standard ; + return result ; + } + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Accept only extended frames + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + public: static inline ACAN_ESP32_Filter acceptExtendedFrames (void) { + ACAN_ESP32_Filter result ; + result.mFormat = extended ; + return result ; + } + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // singleStandardFilter: see SJA100 datasheet, figure 9 page 45 (and figure 10 page 46) + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + public: static inline ACAN_ESP32_Filter singleStandardFilter (const ACAN_ESP32_Filter::Type inType, + const uint16_t inIdentifier, + const uint16_t inDontCareMask) { + ACAN_ESP32_Filter result ; + result.mAMFSingle = true ; // Single Filter + result.mFormat = standard ; + result.mACR0 = uint8_t (inIdentifier >> 3) ; + result.mACR1 = uint8_t (inIdentifier << 5) ; + result.mAMR0 = uint8_t (inDontCareMask >> 3) ; + result.mAMR1 = uint8_t (inDontCareMask << 5) | 0x0F ; + switch (inType) { + case data : + break ; + case remote : + result.mACR1 |= 0x10 ; + break ; + case dataAndRemote : + result.mAMR1 |= 0x10 ; + break ; + } + return result ; + } + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // singleExtendedFilter: see SJA100 datasheet, figure 10 page 46 (and figure 9 page 45) + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + public: static inline ACAN_ESP32_Filter singleExtendedFilter (const ACAN_ESP32_Filter::Type inType, + const uint32_t inIdentifier, + const uint32_t inDontCareMask) { + ACAN_ESP32_Filter result ; + result.mAMFSingle = true ; // Single Filter + result.mFormat = extended ; + result.mACR0 = uint8_t (inIdentifier >> 21) ; + result.mACR1 = uint8_t (inIdentifier >> 13) ; + result.mACR2 = uint8_t (inIdentifier >> 5) ; + result.mACR3 = uint8_t (inIdentifier << 3) ; + + result.mAMR0 = uint8_t (inDontCareMask >> 21) ; + result.mAMR1 = uint8_t (inDontCareMask >> 13) ; + result.mAMR2 = uint8_t (inDontCareMask >> 5) ; + result.mAMR3 = uint8_t (inDontCareMask << 3) ; + + switch (inType) { + case data : + break ; + case remote : + result.mACR3 |= 0x04 ; + break ; + case dataAndRemote : + result.mAMR3 |= 0x04 ; + break ; + } + return result ; + } + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // dualStandardFilter: see SJA100 datasheet, figure 11 page 47 (and figure 12 page 48) + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + public: static inline ACAN_ESP32_Filter dualStandardFilter (const ACAN_ESP32_Filter::Type inType0, + const uint16_t inIdentifier0, + const uint16_t inDontCareMask0, + const ACAN_ESP32_Filter::Type inType1, + const uint16_t inIdentifier1, + const uint16_t inDontCareMask1) { + ACAN_ESP32_Filter result ; + result.mAMFSingle = false ; // Dual Filter + result.mFormat = standard ; + + result.mACR0 = uint8_t (inIdentifier0 >> 3) ; + result.mACR1 = uint8_t (inIdentifier0 << 5) ; + result.mAMR0 = uint8_t (inDontCareMask0 >> 3) ; + result.mAMR1 = uint8_t (inDontCareMask0 << 5) | 0x0F ; + switch (inType0) { + case data : + break ; + case remote : + result.mACR1 |= 0x10 ; + break ; + case dataAndRemote : + result.mAMR1 |= 0x10 ; + break ; + } + + result.mACR2 = uint8_t (inIdentifier1 >> 3) ; + result.mACR3 = uint8_t (inIdentifier1 << 5) ; + result.mAMR2 = uint8_t (inDontCareMask1 >> 3) ; + result.mAMR3 = uint8_t (inDontCareMask1 << 5) | 0x0F ; + switch (inType1) { + case data : + break ; + case remote : + result.mACR3 |= 0x10 ; + break ; + case dataAndRemote : + result.mAMR3 |= 0x10 ; + break ; + } + return result ; + } + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // dualExtendedFilter: see SJA100 datasheet, figure 12 page 48 (and figure 11 page 47) + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + public: static inline ACAN_ESP32_Filter dualExtendedFilter (const uint32_t inIdentifier0, + const uint32_t inDontCareMask0, + const uint32_t inIdentifier1, + const uint32_t inDontCareMask1) { + ACAN_ESP32_Filter result ; + result.mAMFSingle = false ; // Dual Filter + result.mFormat = extended ; + + result.mACR0 = uint8_t (inIdentifier0 >> 21) ; + result.mACR1 = uint8_t (inIdentifier0 >> 13) ; + result.mAMR0 = uint8_t (inDontCareMask0 >> 21) ; + result.mAMR1 = uint8_t (inDontCareMask0 >> 13) ; + + result.mACR2 = uint8_t (inIdentifier1 >> 21) ; + result.mACR3 = uint8_t (inIdentifier1 << 13) ; + result.mAMR2 = uint8_t (inDontCareMask1 >> 21) ; + result.mAMR3 = uint8_t (inDontCareMask1 << 13) ; + + return result ; + } + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +} ; + +//---------------------------------------------------------------------------------------- diff --git a/Software/src/lib/pierremolinaro-acan-esp32/ACAN_ESP32_Buffer16.h b/Software/src/lib/pierremolinaro-acan-esp32/ACAN_ESP32_Buffer16.h new file mode 100644 index 00000000..f3bd874e --- /dev/null +++ b/Software/src/lib/pierremolinaro-acan-esp32/ACAN_ESP32_Buffer16.h @@ -0,0 +1,135 @@ +//---------------------------------------------------------------------------------------- + +#pragma once + +//---------------------------------------------------------------------------------------- + +#include "ACAN_ESP32_CANMessage.h" + +//---------------------------------------------------------------------------------------- + +class ACAN_ESP32_Buffer16 { + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Default constructor + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + public: ACAN_ESP32_Buffer16 (void) : + mBuffer (NULL), + mSize (0), + mReadIndex (0), + mCount (0), + mPeakCount (0) { + } + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Destructor + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + public: ~ ACAN_ESP32_Buffer16 (void) { + delete [] mBuffer ; + } + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Private properties + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + private: CANMessage * mBuffer ; + private: uint16_t mSize ; + private: uint16_t mReadIndex ; + private: uint16_t mCount ; + private: uint16_t mPeakCount ; // > mSize if overflow did occur + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Accessors + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + public: inline uint16_t size (void) const { return mSize ; } + public: inline uint16_t count (void) const { return mCount ; } + public: inline uint16_t peakCount (void) const { return mPeakCount ; } + public: inline uint16_t didOverflow (void) const { return mPeakCount > mSize ; } + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // initWithSize + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + public: bool initWithSize (const uint16_t inSize) { + delete [] mBuffer ; + mBuffer = new CANMessage [inSize] ; + const bool ok = mBuffer != NULL ; + mSize = ok ? inSize : 0 ; + mReadIndex = 0 ; + mCount = 0 ; + mPeakCount = 0 ; + return ok ; + } + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // append + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + public: bool append (const CANMessage & inMessage) { + const bool ok = mCount < mSize ; + if (ok) { + uint16_t writeIndex = mReadIndex + mCount ; + if (writeIndex >= mSize) { + writeIndex -= mSize ; + } + mBuffer [writeIndex] = inMessage ; + mCount += 1 ; + if (mPeakCount < mCount) { + mPeakCount = mCount ; + } + }else{ + mPeakCount = mSize + 1 ; // Overflow + } + return ok ; + } + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Remove + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + public: bool remove (CANMessage & outMessage) { + const bool ok = mCount > 0 ; + if (ok) { + outMessage = mBuffer [mReadIndex] ; + mCount -= 1 ; + mReadIndex += 1 ; + if (mReadIndex == mSize) { + mReadIndex = 0 ; + } + } + return ok ; + } + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Free + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + public: void free (void) { + delete [] mBuffer ; mBuffer = nullptr ; + mSize = 0 ; + mReadIndex = 0 ; + mCount = 0 ; + mPeakCount = 0 ; + } + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Reset Peak Count + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + public: inline void resetPeakCount (void) { mPeakCount = mCount ; } + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // No copy + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + private: ACAN_ESP32_Buffer16 (const ACAN_ESP32_Buffer16 &) = delete ; + private: ACAN_ESP32_Buffer16 & operator = (const ACAN_ESP32_Buffer16 &) = delete ; + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +} ; + +//---------------------------------------------------------------------------------------- diff --git a/Software/src/lib/pierremolinaro-acan-esp32/ACAN_ESP32_CANMessage.h b/Software/src/lib/pierremolinaro-acan-esp32/ACAN_ESP32_CANMessage.h new file mode 100644 index 00000000..f5132c4c --- /dev/null +++ b/Software/src/lib/pierremolinaro-acan-esp32/ACAN_ESP32_CANMessage.h @@ -0,0 +1,49 @@ +//---------------------------------------------------------------------------------------- +// Generic CAN Message +// by Pierre Molinaro +// +// This file is common to the following libraries +// https://github.com/pierremolinaro/acan +// https://github.com/pierremolinaro/acan2515 +// https://github.com/pierremolinaro/acan2517 +// https://github.com/pierremolinaro/acan2517FD +// +//---------------------------------------------------------------------------------------- + +#ifndef GENERIC_CAN_MESSAGE_DEFINED +#define GENERIC_CAN_MESSAGE_DEFINED + +//---------------------------------------------------------------------------------------- + +#include + +//---------------------------------------------------------------------------------------- + +class CANMessage { + public : uint32_t id = 0 ; // Frame identifier + public : bool ext = false ; // false -> standard frame, true -> extended frame + public : bool rtr = false ; // false -> data frame, true -> remote frame + public : uint8_t idx = 0 ; // This field is used by the driver + public : uint8_t len = 0 ; // Length of data (0 ... 8) + public : union { + uint64_t data64 ; // Caution: subject to endianness + int64_t data_s64 ; // Caution: subject to endianness + uint32_t data32 [2] ; // Caution: subject to endianness + int32_t data_s32 [2] ; // Caution: subject to endianness + float dataFloat [2] ; // Caution: subject to endianness + uint16_t data16 [4] ; // Caution: subject to endianness + int16_t data_s16 [4] ; // Caution: subject to endianness + int8_t data_s8 [8] ; + uint8_t data [8] = {0, 0, 0, 0, 0, 0, 0, 0} ; + } ; +} ; + +//---------------------------------------------------------------------------------------- + +typedef enum {kStandard, kExtended} tFrameFormat ; +typedef enum {kData, kRemote} tFrameKind ; +typedef void (*ACANCallBackRoutine) (const CANMessage & inMessage) ; + +//---------------------------------------------------------------------------------------- + +#endif diff --git a/Software/src/lib/pierremolinaro-acan-esp32/ACAN_ESP32_Settings.cpp b/Software/src/lib/pierremolinaro-acan-esp32/ACAN_ESP32_Settings.cpp new file mode 100644 index 00000000..ef31db89 --- /dev/null +++ b/Software/src/lib/pierremolinaro-acan-esp32/ACAN_ESP32_Settings.cpp @@ -0,0 +1,128 @@ +//---------------------------------------------------------------------------------------- +// Include files +//---------------------------------------------------------------------------------------- + +#include "ACAN_ESP32_Settings.h" + +//---------------------------------------------------------------------------------------- +// CAN Settings +//---------------------------------------------------------------------------------------- + +ACAN_ESP32_Settings::ACAN_ESP32_Settings (const uint32_t inDesiredBitRate, + const uint32_t inTolerancePPM) : +mDesiredBitRate (inDesiredBitRate) { + uint32_t TQCount = MAX_TQ ; // TQ: min(3) max(25) + uint32_t bestBRP = MAX_BRP ; // Setting for slowest bit rate + uint32_t bestTQCount = MAX_TQ ; // Setting for slowest bit rate + uint32_t smallestError = UINT32_MAX ; + const uint32_t CANClock = CAN_CLOCK () ; + uint32_t BRP = CANClock / (inDesiredBitRate * TQCount) ; // BRP: min(2) max(128) +//--- Loop for finding best BRP and best TQCount + while ((TQCount >= MIN_TQ) && (BRP <= MAX_BRP)) { + //--- Compute error using BRP (caution: BRP should be > 0) + if (BRP >= MIN_BRP) { + const uint32_t error = CANClock - (inDesiredBitRate * TQCount * BRP) ; // error is always >= 0 + if (error < smallestError) { + smallestError = error ; + bestBRP = BRP ; + bestTQCount = TQCount ; + } + } + //--- Compute error using BRP+1 (caution: BRP+1 should be <= 128) + if (BRP < MAX_BRP) { + const uint32_t error = (inDesiredBitRate * TQCount * (BRP + 1)) - CANClock ; // error is always >= 0 + if (error < smallestError) { + smallestError = error ; + bestBRP = BRP + 1 ; + bestTQCount = TQCount ; + } + } + //--- Continue with next value of TQCount + TQCount -= 1 ; + BRP = CANClock / (inDesiredBitRate * TQCount) ; + } +//--- Set the BRP + mBitRatePrescaler = uint8_t (bestBRP) ; +//--- Compute PS2 (2 <= TSeg2 <= 8) + const uint32_t PS2 = 2 + 3 * (bestTQCount - 5) / 10 ; + mTimeSegment2 = uint8_t (PS2) ; +//--- Compute PS1 (1 <= PS1 <= 16) + const uint32_t PS1 = bestTQCount - PS2 - SYNC_SEGMENT ; + mTimeSegment1 = uint8_t (PS1) ; +//--- SJW (1...4) min of Tseg2 + mRJW = (mTimeSegment2 > 4) ? 4 : mTimeSegment2 ; +//--- Triple sampling ? + mTripleSampling = (inDesiredBitRate <= 125000) && (mTimeSegment2 > 2) ; +//--- Final check of the configuration + const uint32_t W = bestTQCount * mDesiredBitRate * mBitRatePrescaler ; + const uint64_t diff = (CANClock > W) ? (CANClock - W) : (W - CANClock) ; + const uint64_t ppm = uint64_t (1000UL * 1000UL) ; + mBitRateClosedToDesiredRate = (diff * ppm) <= (uint64_t (W) * inTolerancePPM) ; +} + +//---------------------------------------------------------------------------------------- + +uint32_t ACAN_ESP32_Settings::actualBitRate (void) const { + const uint32_t TQCount = SYNC_SEGMENT + mTimeSegment1 + mTimeSegment2 ; + return CAN_CLOCK () / mBitRatePrescaler / TQCount ; +} + +//---------------------------------------------------------------------------------------- + +bool ACAN_ESP32_Settings::exactBitRate (void) const { + const uint32_t TQCount = SYNC_SEGMENT + mTimeSegment1 + mTimeSegment2 ; + return CAN_CLOCK () == (mDesiredBitRate * mBitRatePrescaler * TQCount) ; +} + +//---------------------------------------------------------------------------------------- + +uint32_t ACAN_ESP32_Settings::ppmFromDesiredBitRate(void) const { + const uint32_t TQCount = SYNC_SEGMENT + mTimeSegment1 + mTimeSegment2 ; + const uint32_t W = TQCount * mDesiredBitRate * mBitRatePrescaler ; + const uint32_t CANClock = CAN_CLOCK () ; + const uint64_t diff = (CANClock > W) ? (CANClock - W) : (W - CANClock) ; + const uint64_t ppm = (uint64_t)(1000UL * 1000UL) ; // UL suffix is required for Arduino Uno + return uint32_t ((diff * ppm) / W) ; +} + +//---------------------------------------------------------------------------------------- + +uint32_t ACAN_ESP32_Settings::samplePointFromBitStart (void) const { + const uint32_t TQCount = SYNC_SEGMENT + mTimeSegment1 + mTimeSegment2 ; + const uint32_t samplePoint = SYNC_SEGMENT + mTimeSegment1 - mTripleSampling ; + const uint32_t partPerCent = 100 ; + return (samplePoint * partPerCent) / TQCount ; +} + +//---------------------------------------------------------------------------------------- + +uint16_t ACAN_ESP32_Settings::CANBitSettingConsistency (void) const { + uint16_t errorCode = 0 ; // No error + if (mBitRatePrescaler < MIN_BRP) { + errorCode |= kBitRatePrescalerIsZero ; + }else if (mBitRatePrescaler > MAX_BRP) { + errorCode |= kBitRatePrescalerIsGreaterThan64 ; + } + if (mTimeSegment1 == 0) { + errorCode |= kTimeSegment1IsZero ; + }else if ((mTimeSegment2 == 2) && mTripleSampling) { + errorCode |= kTimeSegment2Is2AndTripleSampling ; + }else if (mTimeSegment1 > MAX_TIME_SEGMENT_1) { + errorCode |= kTimeSegment1IsGreaterThan16 ; + } + if (mTimeSegment2 < 2) { + errorCode |= kTimeSegment2IsLowerThan2 ; + }else if (mTimeSegment2 > MAX_TIME_SEGMENT_2) { + errorCode |= kTimeSegment2IsGreaterThan8 ; + } + if (mRJW == 0) { + errorCode |= kRJWIsZero ; + }else if (mRJW > mTimeSegment2) { + errorCode |= kRJWIsGreaterThanTimeSegment2 ; + }else if (mRJW > MAX_SJW) { + errorCode |= kRJWIsGreaterThan4 ; + } + return errorCode ; +} + +//---------------------------------------------------------------------------------------- diff --git a/Software/src/lib/pierremolinaro-acan-esp32/ACAN_ESP32_Settings.h b/Software/src/lib/pierremolinaro-acan-esp32/ACAN_ESP32_Settings.h new file mode 100644 index 00000000..9f1d937e --- /dev/null +++ b/Software/src/lib/pierremolinaro-acan-esp32/ACAN_ESP32_Settings.h @@ -0,0 +1,155 @@ +//---------------------------------------------------------------------------------------- + +#pragma once + +//---------------------------------------------------------------------------------------- +// Include files +//---------------------------------------------------------------------------------------- + +#include + +//--- For getting getApbFrequency function declaration +#ifdef ARDUINO + #include +#endif + +//---------------------------------------------------------------------------------------- +// CAN CLOCK +//---------------------------------------------------------------------------------------- + +#ifdef ARDUINO + inline uint32_t CAN_CLOCK (void) { return getApbFrequency () / 2 ; }// APB_CLK_FREQ: 80 MHz APB CLOCK +#else + inline uint32_t CAN_CLOCK (void) { return 40 * 1000 * 1000 ; } // 40 MHz +#endif + +//---------------------------------------------------------------------------------------- +// ESP32 ACANSettings class +//---------------------------------------------------------------------------------------- + +class ACAN_ESP32_Settings { + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // ENUMERATED TYPE + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + //--- CAN driver operating modes + public: typedef enum + #ifdef ARDUINO + : uint8_t + #endif + { + NormalMode, + ListenOnlyMode, + LoopBackMode + } CANMode ; + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // CONSTRUCTOR + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + public: ACAN_ESP32_Settings (const uint32_t inDesiredBitRate, + const uint32_t inTolerancePPM = 1000) ; + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // CAN PINS + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + #ifdef ARDUINO + public: gpio_num_t mTxPin = GPIO_NUM_5 ; + public: gpio_num_t mRxPin = GPIO_NUM_4 ; + #endif + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // CAN BIT TIMING + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + public: uint32_t mDesiredBitRate ; // In kb/s + public: uint8_t mBitRatePrescaler = 0 ; // 1...64 + public: uint8_t mTimeSegment1 = 0 ; // 1...16 + public: uint8_t mTimeSegment2 = 0 ; // 2...8 + public: uint8_t mRJW = 0 ; // 1...4 + public: bool mTripleSampling = false ; // true --> triple sampling, false --> single sampling + public: bool mBitRateClosedToDesiredRate = false ; // The above configuration is correct + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Max values + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + public: static const uint8_t SYNC_SEGMENT = 1 ; // Fixed Sync Segment + public: static const uint32_t MAX_BRP = 64 ; + public: static const uint32_t MIN_BRP = 1 ; + public: static const uint32_t MAX_TQ = 25 ; + public: static const uint32_t MIN_TQ = 5 ; + public: static const uint8_t MAX_TIME_SEGMENT_1 = 16 ; + public: static const uint8_t MAX_TIME_SEGMENT_2 = 8 ; + public: static const uint8_t MAX_SJW = 4 ; + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Requested mode + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + public: CANMode mRequestedCANMode = NormalMode ; + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Receive buffer size + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + public: uint16_t mDriverReceiveBufferSize = 32 ; + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Transmit buffer sizes + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + public: uint16_t mDriverTransmitBufferSize = 16 ; + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Compute actual bit rate + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + public: uint32_t actualBitRate (void) const ; + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Exact bit rate ? + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + public: bool exactBitRate (void) const ; + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Distance between actual bit rate and requested bit rate (in ppm, part-per-million) + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + public: uint32_t ppmFromDesiredBitRate (void) const; + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Distance of sample point from bit start (in ppc, part-per-cent, denoted by %) + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + public: uint32_t samplePointFromBitStart (void) const; + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Bit settings are consistent ? (returns 0 if ok) + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + public: uint16_t CANBitSettingConsistency (void) const ; + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Constants returned by CANBitSettingConsistency + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + public: static const uint16_t kBitRatePrescalerIsZero = 1 << 0 ; + public: static const uint16_t kBitRatePrescalerIsGreaterThan64 = 1 << 1 ; + public: static const uint16_t kTimeSegment1IsZero = 1 << 2 ; + public: static const uint16_t kTimeSegment1IsGreaterThan16 = 1 << 3 ; + public: static const uint16_t kTimeSegment2IsLowerThan2 = 1 << 4 ; + public: static const uint16_t kTimeSegment2IsGreaterThan8 = 1 << 5 ; + public: static const uint16_t kTimeSegment2Is2AndTripleSampling = 1 << 6 ; + public: static const uint16_t kRJWIsZero = 1 << 7 ; + public: static const uint16_t kRJWIsGreaterThan4 = 1 << 8 ; + public: static const uint16_t kRJWIsGreaterThanTimeSegment2 = 1 << 9 ; + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +} ; + +//---------------------------------------------------------------------------------------- diff --git a/Software/src/lib/pierremolinaro-acan-esp32/ACAN_ESP32_TWAI_base_address.h b/Software/src/lib/pierremolinaro-acan-esp32/ACAN_ESP32_TWAI_base_address.h new file mode 100644 index 00000000..ce9d0d7c --- /dev/null +++ b/Software/src/lib/pierremolinaro-acan-esp32/ACAN_ESP32_TWAI_base_address.h @@ -0,0 +1,85 @@ +//------------------------------------------------------------------------------ +// ESP32 TWAI REGISTER BASE +// See sdkconfig.h files for defining CONFIG_IDF_TARGET_xxxx +// DR_REG_TWAI_BASE is defined in: +// - ~/Library/Arduino15/packages/esp32/hardware/esp32/2.0.11/tools/sdk/esp32c3/include/soc/esp32c3/include/soc/soc.h +// - ~//Library/Arduino15/packages/esp32/hardware/esp32/2.0.11/tools/sdk/esp32s3/include/soc/esp32s3/include/soc/soc.h +// DR_REG_CAN_BASE is defined in: +// - ~//Library/Arduino15/packages/esp32/hardware/esp32/2.0.11/tools/sdk/esp32/include/soc/esp32/include/soc/soc.h +// No definition for ESP32-S2 in ~/Library/Arduino15/packages/esp32/hardware/esp32/2.0.11/tools/sdk/esp32s2/include/soc/esp32s2/include/soc/soc.h +// However the ~/Library/Arduino15/packages/esp32/hardware/esp32/2.0.11/tools/ide-debug/svd/esp32s2.svd file defines +// the TWAI base address: 0x3F42B000 +// But ESP32S2 reference manual (§3.3.5) gives two addresses: +// - 0x3F40_0000 + 0x0002_B000 = 0x3F42_B000 from PeriBus1 (faster) +// - 0x6000_0000 + 0x0002_B000 = 0x6002_B000 from PeriBus2 +// +//------------------------------------------------------------------------------ + +#pragma once + +//------------------------------------------------------------------------------ +// Include files +//------------------------------------------------------------------------------ + +#include + +#include +#include +#include +#include +#include +#include + +//------------------------------------------------------------------------------ +// In ESP32 2.x board managers, ETS_TWAI_INTR_SOURCE is an int constant. +// +// In ESP32 3.0.0 board manager, ETS_TWAI_INTR_SOURCE is an periph_interrput_t constant. +// +// In ESP32 3.3.0-alpha1 board manager, periph_interrput_t has been deprecated +// in favor of periph_interrupt_t. +//------------------------------------------------------------------------------ + +// esp32/hardware/esp32/3.3.0-alpha1/cores/esp32/esp_arduino_version.h +#include + +#if ESP_ARDUINO_VERSION < ESP_ARDUINO_VERSION_VAL(3, 0, 0) + typedef int periph_interrupt_t ; +#elif ESP_ARDUINO_VERSION < ESP_ARDUINO_VERSION_VAL(3, 3, 0) + typedef periph_interrput_t periph_interrupt_t ; +#endif + +//------------------------------------------------------------------------------ + +#if defined (CONFIG_IDF_TARGET_ESP32S3) + static const uint32_t twaiBaseAddress = DR_REG_TWAI_BASE ; // 0x6002B000 + static const uint32_t twaiTxPinSelector = TWAI_TX_IDX ; + static const uint32_t twaiRxPinSelector = TWAI_RX_IDX ; + static const periph_module_t twaiPeriphModule = PERIPH_TWAI_MODULE ; + static const periph_interrupt_t twaiInterruptSource = ETS_TWAI_INTR_SOURCE ; +#elif defined (CONFIG_IDF_TARGET_ESP32S2) + static const uint32_t twaiBaseAddress = 0x3F42B000 ; + static const uint32_t twaiTxPinSelector = TWAI_TX_IDX ; + static const uint32_t twaiRxPinSelector = TWAI_RX_IDX ; + static const periph_module_t twaiPeriphModule = PERIPH_TWAI_MODULE ; + static const periph_interrupt_t twaiInterruptSource = ETS_TWAI_INTR_SOURCE ; +#elif defined (CONFIG_IDF_TARGET_ESP32C3) + static const uint32_t twaiBaseAddress = DR_REG_TWAI_BASE ; // 0x6002B000 + static const uint32_t twaiTxPinSelector = TWAI_TX_IDX ; + static const uint32_t twaiRxPinSelector = TWAI_RX_IDX ; + static const periph_module_t twaiPeriphModule = PERIPH_TWAI_MODULE ; + static const periph_interrupt_t twaiInterruptSource = ETS_TWAI_INTR_SOURCE ; +#elif defined (CONFIG_IDF_TARGET_ESP32C6) + // twaiBaseAddress, twaiTxPinSelector, twaiRxPinSelector, twaiPeriphModule + // and twaiInterruptSource are defined as instance properties + // of ACAN_ESP32 class +#elif defined (CONFIG_IDF_TARGET_ESP32) + static const uint32_t twaiBaseAddress = DR_REG_CAN_BASE ; // 0x3ff6B000 + static const uint32_t twaiTxPinSelector = TWAI_TX_IDX ; + static const uint32_t twaiRxPinSelector = TWAI_RX_IDX ; + static const periph_module_t twaiPeriphModule = PERIPH_TWAI_MODULE ; + static const periph_interrupt_t twaiInterruptSource = ETS_TWAI_INTR_SOURCE ; +#else + #error "ESP32 TWAI (CAN) module not handled for this platform" +#endif + +//------------------------------------------------------------------------------ From 4e0ea84fad64d4da89bc6e6aa3e7a31a7df5e77f Mon Sep 17 00:00:00 2001 From: Matt Holmes Date: Fri, 22 Aug 2025 11:23:32 +0100 Subject: [PATCH 067/245] Adding ability to configure Can Addon frequency in common image --- Software/src/communication/can/comm_can.cpp | 9 ++++++++- Software/src/communication/can/comm_can.h | 1 + Software/src/communication/nvm/comm_nvm.cpp | 1 + Software/src/devboard/webserver/settings_html.cpp | 7 +++++++ Software/src/devboard/webserver/webserver.cpp | 3 +++ 5 files changed, 20 insertions(+), 1 deletion(-) diff --git a/Software/src/communication/can/comm_can.cpp b/Software/src/communication/can/comm_can.cpp index 5ed4be1b..1f82a655 100644 --- a/Software/src/communication/can/comm_can.cpp +++ b/Software/src/communication/can/comm_can.cpp @@ -41,7 +41,8 @@ void register_can_receiver(CanReceiver* receiver, CAN_Interface interface, CAN_S ACAN_ESP32_Settings* settingsespcan; -static const uint32_t QUARTZ_FREQUENCY = CRYSTAL_FREQUENCY_MHZ * 1000000UL; //MHZ configured in USER_SETTINGS.h +uint8_t user_selected_can_addon_crystal_frequency_mhz = 0; +static uint32_t QUARTZ_FREQUENCY; SPIClass SPI2515; ACAN2515* can2515; @@ -59,6 +60,12 @@ bool native_can_initialized = false; bool init_CAN() { + if(user_selected_can_addon_crystal_frequency_mhz > 0){ + QUARTZ_FREQUENCY = user_selected_can_addon_crystal_frequency_mhz * 1000000UL; + } else { + QUARTZ_FREQUENCY = CRYSTAL_FREQUENCY_MHZ * 1000000UL; + } + auto nativeIt = can_receivers.find(CAN_NATIVE); if (nativeIt != can_receivers.end()) { auto se_pin = esp32hal->CAN_SE_PIN(); diff --git a/Software/src/communication/can/comm_can.h b/Software/src/communication/can/comm_can.h index 6e8e6a4b..2b7ece97 100644 --- a/Software/src/communication/can/comm_can.h +++ b/Software/src/communication/can/comm_can.h @@ -4,6 +4,7 @@ #include "../../devboard/utils/types.h" extern bool use_canfd_as_can; +extern uint8_t user_selected_can_addon_crystal_frequency_mhz; void dump_can_frame(CAN_frame& frame, frameDirection msgDir); void transmit_can_frame_to_interface(const CAN_frame* tx_frame, int interface); diff --git a/Software/src/communication/nvm/comm_nvm.cpp b/Software/src/communication/nvm/comm_nvm.cpp index 91adc099..36904535 100644 --- a/Software/src/communication/nvm/comm_nvm.cpp +++ b/Software/src/communication/nvm/comm_nvm.cpp @@ -103,6 +103,7 @@ void init_stored_settings() { user_selected_inverter_ah_capacity = settings.getUInt("INVAHCAPACITY", 0); user_selected_inverter_battery_type = settings.getUInt("INVBTYPE", 0); user_selected_inverter_ignore_contactors = settings.getBool("INVICNT", false); + user_selected_can_addon_crystal_frequency_mhz = settings.getUInt("CANFREQ", 8); auto readIf = [](const char* settingName) { auto batt1If = (comm_interface)settings.getUInt(settingName, (int)comm_interface::CanNative); diff --git a/Software/src/devboard/webserver/settings_html.cpp b/Software/src/devboard/webserver/settings_html.cpp index 7ca338ce..8b1bf9ee 100644 --- a/Software/src/devboard/webserver/settings_html.cpp +++ b/Software/src/devboard/webserver/settings_html.cpp @@ -500,6 +500,10 @@ String settings_processor(const String& var, BatteryEmulatorSettingsStore& setti return settings.getBool("INVICNT") ? "checked" : ""; } + if (var == "CANFREQ") { + return String(settings.getUInt("CANFREQ", 8)); + } + return String(); } @@ -823,6 +827,9 @@ const char* getCANInterfaceName(CAN_Interface interface) {

+ + + diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 71df6879..eb3870cb 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -515,6 +515,9 @@ void init_webserver() { } else if (p->name() == "INVBTYPE") { auto type = atoi(p->value().c_str()); settings.saveUInt("INVBTYPE", (int)type); + } else if (p->name() == "CANFREQ"){ + auto type = atoi(p->value().c_str()); + settings.saveUInt("CANFREQ", type); } for (auto& boolSetting : boolSettings) { From 588c78f1d67c7cb296a9e0b6c204da75e8c5fe0f Mon Sep 17 00:00:00 2001 From: Matt Holmes Date: Fri, 22 Aug 2025 20:25:12 +0100 Subject: [PATCH 068/245] Adding the ability to set default configuration for specific hardware such as a 16mhz crystal for the Lilygo T-2CAN --- Software/src/communication/nvm/comm_nvm.cpp | 2 ++ Software/src/devboard/hal/hal.h | 3 +++ Software/src/devboard/hal/hw_lilygo2can.h | 7 +++++++ 3 files changed, 12 insertions(+) diff --git a/Software/src/communication/nvm/comm_nvm.cpp b/Software/src/communication/nvm/comm_nvm.cpp index 36904535..9a2233f3 100644 --- a/Software/src/communication/nvm/comm_nvm.cpp +++ b/Software/src/communication/nvm/comm_nvm.cpp @@ -33,6 +33,8 @@ void init_stored_settings() { settings.putBool("EQUIPMENT_STOP", datalayer.system.settings.equipment_stop_active); #endif // LOAD_SAVED_SETTINGS_ON_BOOT + esp32hal->set_default_configuration_values(); + char tempSSIDstring[63]; // Allocate buffer with sufficient size size_t lengthSSID = settings.getString("SSID", tempSSIDstring, sizeof(tempSSIDstring)); if (lengthSSID > 0) { // Successfully read the string from memory. Set it to SSID! diff --git a/Software/src/devboard/hal/hal.h b/Software/src/devboard/hal/hal.h index e70770d6..fc8564d3 100644 --- a/Software/src/devboard/hal/hal.h +++ b/Software/src/devboard/hal/hal.h @@ -7,6 +7,7 @@ #include "../../../src/devboard/utils/events.h" #include "../../../src/devboard/utils/logging.h" #include "../../../src/devboard/utils/types.h" +#include // Hardware Abstraction Layer base class. // Derive a class to define board-specific parameters such as GPIO pin numbers @@ -24,6 +25,8 @@ class Esp32Hal { virtual int MODBUS_CORE() { return 0; } virtual int WIFICORE() { return 0; } + virtual void set_default_configuration_values(){} + template bool alloc_pins(const char* name, Pins... pins) { std::vector requested_pins = {static_cast(pins)...}; diff --git a/Software/src/devboard/hal/hw_lilygo2can.h b/Software/src/devboard/hal/hw_lilygo2can.h index 907c0413..45eb76a8 100644 --- a/Software/src/devboard/hal/hw_lilygo2can.h +++ b/Software/src/devboard/hal/hw_lilygo2can.h @@ -7,6 +7,13 @@ class LilyGo2CANHal : public Esp32Hal { public: const char* name() { return "LilyGo T_2CAN"; } + virtual void set_default_configuration_values() { + BatteryEmulatorSettingsStore settings; + if(!settings.settingExists("CANFREQ")){ + settings.saveUInt("CANFREQ", 16); + } + } + virtual gpio_num_t CAN_TX_PIN() { return GPIO_NUM_7; } virtual gpio_num_t CAN_RX_PIN() { return GPIO_NUM_6; } From 5fae1c645d9fc56e3ce7a0e3d5e467a8b7b15019 Mon Sep 17 00:00:00 2001 From: Matt Holmes Date: Wed, 27 Aug 2025 19:14:54 +0100 Subject: [PATCH 069/245] Fixing unit tests and formatting --- Software/src/communication/can/comm_can.cpp | 17 ++++++------ Software/src/communication/nvm/comm_nvm.h | 1 + Software/src/devboard/hal/hal.cpp | 10 +++---- Software/src/devboard/hal/hal.h | 4 +-- Software/src/devboard/hal/hw_lilygo2can.h | 2 +- Software/src/devboard/webserver/webserver.cpp | 2 +- test/emul/Preferences.h | 27 +++++++++++++++++++ test/emul/WString.h | 3 +++ 8 files changed, 48 insertions(+), 18 deletions(-) create mode 100644 test/emul/Preferences.h diff --git a/Software/src/communication/can/comm_can.cpp b/Software/src/communication/can/comm_can.cpp index 1f82a655..59cf8053 100644 --- a/Software/src/communication/can/comm_can.cpp +++ b/Software/src/communication/can/comm_can.cpp @@ -1,8 +1,8 @@ #include "comm_can.h" #include #include -#include "../../lib/pierremolinaro-acan-esp32/ACAN_ESP32.h" #include "../../lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h" +#include "../../lib/pierremolinaro-acan-esp32/ACAN_ESP32.h" #include "../../lib/pierremolinaro-acan2515/ACAN2515.h" #include "CanReceiver.h" #include "USER_SETTINGS.h" @@ -60,7 +60,7 @@ bool native_can_initialized = false; bool init_CAN() { - if(user_selected_can_addon_crystal_frequency_mhz > 0){ + if (user_selected_can_addon_crystal_frequency_mhz > 0) { QUARTZ_FREQUENCY = user_selected_can_addon_crystal_frequency_mhz * 1000000UL; } else { QUARTZ_FREQUENCY = CRYSTAL_FREQUENCY_MHZ * 1000000UL; @@ -90,8 +90,7 @@ bool init_CAN() { settingsespcan->mRxPin = rx_pin; const uint32_t errorCode = ACAN_ESP32::can.begin(*settingsespcan); - if (errorCode == 0) - { + if (errorCode == 0) { native_can_initialized = true; #ifdef DEBUG_LOG logging.println("Native Can ok"); @@ -101,8 +100,8 @@ bool init_CAN() { logging.println(settingsespcan->mTimeSegment1); logging.print("Time Segment 2: "); logging.println(settingsespcan->mTimeSegment2); - logging.print ("RJW: ") ; - logging.println (settingsespcan->mRJW) ; + logging.print("RJW: "); + logging.println(settingsespcan->mRJW); logging.print("Triple Sampling: "); logging.println(settingsespcan->mTripleSampling ? "yes" : "no"); logging.print("Actual bit rate: "); @@ -141,7 +140,7 @@ bool init_CAN() { #endif // DEBUG_LOG gBuffer.initWithSize(25); - if(rst_pin != GPIO_NUM_NC) { + if (rst_pin != GPIO_NUM_NC) { pinMode(rst_pin, OUTPUT); digitalWrite(rst_pin, HIGH); delay(100); @@ -326,7 +325,7 @@ void receive_frame_can_native() { // This section checks if we have a complete CANMessage frame; if (ACAN_ESP32::can.available()) { - if(ACAN_ESP32::can.receive(frame)){ + if (ACAN_ESP32::can.receive(frame)) { CAN_frame rx_frame; rx_frame.ID = frame.id; @@ -500,4 +499,4 @@ CAN_Speed change_can_speed(CAN_Interface interface, CAN_Speed speed) { ACAN_ESP32::can.begin(*settingsespcan); } return speed; -} \ No newline at end of file +} diff --git a/Software/src/communication/nvm/comm_nvm.h b/Software/src/communication/nvm/comm_nvm.h index 846c1014..2f554a93 100644 --- a/Software/src/communication/nvm/comm_nvm.h +++ b/Software/src/communication/nvm/comm_nvm.h @@ -2,6 +2,7 @@ #define _COMM_NVM_H_ #include +#include #include #include "../../datalayer/datalayer.h" #include "../../devboard/utils/events.h" diff --git a/Software/src/devboard/hal/hal.cpp b/Software/src/devboard/hal/hal.cpp index e58c45aa..ab10943f 100644 --- a/Software/src/devboard/hal/hal.cpp +++ b/Software/src/devboard/hal/hal.cpp @@ -8,19 +8,19 @@ Esp32Hal* esp32hal = nullptr; void init_hal() { #if defined(HW_LILYGO) - #include "hw_lilygo.h" +#include "hw_lilygo.h" esp32hal = new LilyGoHal(); #elif defined(HW_LILYGO2CAN) - #include "hw_lilygo2can.h" +#include "hw_lilygo2can.h" esp32hal = new LilyGo2CANHal(); #elif defined(HW_STARK) - #include "hw_stark.h" +#include "hw_stark.h" esp32hal = new StarkHal(); #elif defined(HW_3LB) - #include "hw_3LB.h" +#include "hw_3LB.h" esp32hal = new ThreeLBHal(); #elif defined(HW_DEVKIT) - #include "hw_devkit.h" +#include "hw_devkit.h" esp32hal = new DevKitHal(); #else #error "No HW defined." diff --git a/Software/src/devboard/hal/hal.h b/Software/src/devboard/hal/hal.h index fc8564d3..178c39c1 100644 --- a/Software/src/devboard/hal/hal.h +++ b/Software/src/devboard/hal/hal.h @@ -4,10 +4,10 @@ #include #include #include +#include "../../../src/communication/nvm/comm_nvm.h" #include "../../../src/devboard/utils/events.h" #include "../../../src/devboard/utils/logging.h" #include "../../../src/devboard/utils/types.h" -#include // Hardware Abstraction Layer base class. // Derive a class to define board-specific parameters such as GPIO pin numbers @@ -25,7 +25,7 @@ class Esp32Hal { virtual int MODBUS_CORE() { return 0; } virtual int WIFICORE() { return 0; } - virtual void set_default_configuration_values(){} + virtual void set_default_configuration_values() {} template bool alloc_pins(const char* name, Pins... pins) { diff --git a/Software/src/devboard/hal/hw_lilygo2can.h b/Software/src/devboard/hal/hw_lilygo2can.h index 45eb76a8..91743121 100644 --- a/Software/src/devboard/hal/hw_lilygo2can.h +++ b/Software/src/devboard/hal/hw_lilygo2can.h @@ -9,7 +9,7 @@ class LilyGo2CANHal : public Esp32Hal { virtual void set_default_configuration_values() { BatteryEmulatorSettingsStore settings; - if(!settings.settingExists("CANFREQ")){ + if (!settings.settingExists("CANFREQ")) { settings.saveUInt("CANFREQ", 16); } } diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index eb3870cb..d969c40e 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -515,7 +515,7 @@ void init_webserver() { } else if (p->name() == "INVBTYPE") { auto type = atoi(p->value().c_str()); settings.saveUInt("INVBTYPE", (int)type); - } else if (p->name() == "CANFREQ"){ + } else if (p->name() == "CANFREQ") { auto type = atoi(p->value().c_str()); settings.saveUInt("CANFREQ", type); } diff --git a/test/emul/Preferences.h b/test/emul/Preferences.h new file mode 100644 index 00000000..71bef291 --- /dev/null +++ b/test/emul/Preferences.h @@ -0,0 +1,27 @@ +#ifndef PREFERENCES +#define PREFERENCES + +#include + +class Preferences { + +public: + Preferences() {} + + bool begin(const char *name, bool readOnly = false, const char *partition_label = NULL); + void end(); + bool clear(); + + size_t putUInt(const char *key, uint32_t value) { return 0; } + size_t putBool(const char *key, bool value) { return 0; } + size_t putString(const char *key, const char *value) { return 0; } + size_t putString(const char *key, String value) { return 0; } + + bool isKey(const char *key) { return false; } + + uint32_t getUInt(const char *key, uint32_t defaultValue = 0) { return 0; } + bool getBool(const char *key, bool defaultValue = false) { return false; } + size_t getString(const char *key, char *value, size_t maxLen) { return 0; } + String getString(const char *key, String defaultValue = String()) { return String(); } +}; +#endif \ No newline at end of file diff --git a/test/emul/WString.h b/test/emul/WString.h index 3d276163..2d75df9f 100644 --- a/test/emul/WString.h +++ b/test/emul/WString.h @@ -43,6 +43,9 @@ class String { // Accessor const std::string& str() const { return data; } + // Comparison operators + bool operator==(const String& rhs) const { return data == rhs.data; } + // Concatenation String operator+(const String& rhs) const { return String(data + rhs.data); } From 9774e591da6dee3b018bb97b8ca89c4b4a7bff66 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 27 Aug 2025 18:16:39 +0000 Subject: [PATCH 070/245] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- test/emul/Preferences.h | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/test/emul/Preferences.h b/test/emul/Preferences.h index 71bef291..b1091def 100644 --- a/test/emul/Preferences.h +++ b/test/emul/Preferences.h @@ -5,23 +5,23 @@ class Preferences { -public: + public: Preferences() {} - bool begin(const char *name, bool readOnly = false, const char *partition_label = NULL); + bool begin(const char* name, bool readOnly = false, const char* partition_label = NULL); void end(); bool clear(); - size_t putUInt(const char *key, uint32_t value) { return 0; } - size_t putBool(const char *key, bool value) { return 0; } - size_t putString(const char *key, const char *value) { return 0; } - size_t putString(const char *key, String value) { return 0; } + size_t putUInt(const char* key, uint32_t value) { return 0; } + size_t putBool(const char* key, bool value) { return 0; } + size_t putString(const char* key, const char* value) { return 0; } + size_t putString(const char* key, String value) { return 0; } - bool isKey(const char *key) { return false; } - - uint32_t getUInt(const char *key, uint32_t defaultValue = 0) { return 0; } - bool getBool(const char *key, bool defaultValue = false) { return false; } - size_t getString(const char *key, char *value, size_t maxLen) { return 0; } - String getString(const char *key, String defaultValue = String()) { return String(); } + bool isKey(const char* key) { return false; } + + uint32_t getUInt(const char* key, uint32_t defaultValue = 0) { return 0; } + bool getBool(const char* key, bool defaultValue = false) { return false; } + size_t getString(const char* key, char* value, size_t maxLen) { return 0; } + String getString(const char* key, String defaultValue = String()) { return String(); } }; -#endif \ No newline at end of file +#endif From daabd2ba809bef94440e78f944c46e7105078e18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 27 Aug 2025 21:28:02 +0300 Subject: [PATCH 071/245] Update Software.ino --- Software/Software.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/Software.ino b/Software/Software.ino index 424f3979..508a0549 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -41,7 +41,7 @@ volatile unsigned long long bmsResetTimeOffset = 0; // The current software version, shown on webserver -const char* version_number = "9.0.RC3"; +const char* version_number = "9.0.RC3experimental"; // Interval timers volatile unsigned long currentMillis = 0; From 2b42641c6b859124e6acfb9e35cc96a769c8ef23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 28 Aug 2025 23:10:12 +0300 Subject: [PATCH 072/245] Correct SOH reading. Add all PIDs as comments. Add isolation resistance --- .../battery/HYUNDAI-IONIQ-28-BATTERY-HTML.cpp | 4 +- .../src/battery/HYUNDAI-IONIQ-28-BATTERY.cpp | 76 ++++++++++++++----- .../src/battery/HYUNDAI-IONIQ-28-BATTERY.h | 10 +-- 3 files changed, 62 insertions(+), 28 deletions(-) diff --git a/Software/src/battery/HYUNDAI-IONIQ-28-BATTERY-HTML.cpp b/Software/src/battery/HYUNDAI-IONIQ-28-BATTERY-HTML.cpp index 819deeb4..3b013dc4 100644 --- a/Software/src/battery/HYUNDAI-IONIQ-28-BATTERY-HTML.cpp +++ b/Software/src/battery/HYUNDAI-IONIQ-28-BATTERY-HTML.cpp @@ -5,9 +5,7 @@ String HyundaiIoniq28BatteryHtmlRenderer::get_status_html() { String content; content += "

12V voltage: " + String(batt.get_lead_acid_voltage() / 10.0, 1) + "

"; content += "

Temperature, power relay: " + String(batt.get_power_relay_temperature()) + "

"; - content += "

Battery relay: " + String(batt.get_battery_relay_mode()) + "

"; content += "

Batterymanagement mode: " + String(batt.get_battery_management_mode()) + "

"; - content += "

BMS ignition: " + String(batt.get_battery_ignition_mode()) + "

"; - + content += "

Isolation resistance: " + String(batt.get_isolation_resistance()) + " kOhm

"; return content; } diff --git a/Software/src/battery/HYUNDAI-IONIQ-28-BATTERY.cpp b/Software/src/battery/HYUNDAI-IONIQ-28-BATTERY.cpp index c9b8cd4a..f89b7420 100644 --- a/Software/src/battery/HYUNDAI-IONIQ-28-BATTERY.cpp +++ b/Software/src/battery/HYUNDAI-IONIQ-28-BATTERY.cpp @@ -88,7 +88,11 @@ void HyundaiIoniq28Battery::handle_incoming_can_frame(CAN_frame rx_frame) { break; case 0x21: //First frame in PID group if (incoming_poll_group == 1) { //21 14 13 24 13 24 04 00 - batteryRelay = rx_frame.data.u8[7]; + //SOC = rx_frame.data.u8[1]; + //available_charge_power = 2 & 3 + //available_discharge_power = 4 & 5 + //status_bits? = 6 + //battery_current_highbyte = rx_frame.data.u8[7]; } else if (incoming_poll_group == 2) { //21 AD AD AD AD AD AD AC cellvoltages_mv[0] = (rx_frame.data.u8[1] * 20); cellvoltages_mv[1] = (rx_frame.data.u8[2] * 20); @@ -113,11 +117,19 @@ void HyundaiIoniq28Battery::handle_incoming_can_frame(CAN_frame rx_frame) { cellvoltages_mv[68] = (rx_frame.data.u8[5] * 20); cellvoltages_mv[69] = (rx_frame.data.u8[6] * 20); cellvoltages_mv[70] = (rx_frame.data.u8[7] * 20); + } else if (incoming_poll_group == 5) { //21 0 0 0 0 0 0f0f + //battery_module_6_temperature = rx_frame.data.u8[6]; + //battery_module_7_temperature = rx_frame.data.u8[7]; } break; case 0x22: //Second datarow in PID group if (incoming_poll_group == 1) { //22 00 0C FF 17 16 17 17 - + //battery_current_lowbyte = rx_frame.data.u8[1]; + //battery_DC_voltage = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]); + //battery_max_temperature = rx_frame.data.u8[4]; + //battery_min_temperature = rx_frame.data.u8[5]; + //battery_module_1_temperature = rx_frame.data.u8[6]; + //battery_module_2_temperature = rx_frame.data.u8[7]; } else if (incoming_poll_group == 2) { //22 AD AC AC AD AD AD AD cellvoltages_mv[7] = (rx_frame.data.u8[1] * 20); cellvoltages_mv[8] = (rx_frame.data.u8[2] * 20); @@ -142,14 +154,26 @@ void HyundaiIoniq28Battery::handle_incoming_can_frame(CAN_frame rx_frame) { cellvoltages_mv[75] = (rx_frame.data.u8[5] * 20); cellvoltages_mv[76] = (rx_frame.data.u8[6] * 20); cellvoltages_mv[77] = (rx_frame.data.u8[7] * 20); + } else if (incoming_poll_group == 5) { //22 10 0d 0c 0e 0d 26 48 + //battery_module_8_temperature = rx_frame.data.u8[1]; + //battery_module_9_temperature = rx_frame.data.u8[2]; + //battery_module_10_temperature = rx_frame.data.u8[3]; + //battery_module_11_temperature = rx_frame.data.u8[4]; + //battery_module_12_temperature = rx_frame.data.u8[5]; + //available_charge_power = ((rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]); } else if (incoming_poll_group == 6) { batteryManagementMode = rx_frame.data.u8[5]; } break; - case 0x23: //Third datarow in PID group - if (incoming_poll_group == 1) { //23 17 17 17 00 17 AD 25 + case 0x23: //Third datarow in PID group + if (incoming_poll_group == 1) { //23 17 17 17 00 17 AD 25 + //battery_module_3_temperature = rx_frame.data.u8[1]; + //battery_module_4_temperature = rx_frame.data.u8[2]; + //battery_module_5_temperature = rx_frame.data.u8[3]; + //battery_inlet_temperature = rx_frame.data.u8[5]; CellVoltMax_mV = (rx_frame.data.u8[6] * 20); //(volts *50) *20 =mV - } else if (incoming_poll_group == 2) { //23 AD AD AD AD AB AD AD + //cellmaxvoltage_number = rx_frame.data.u8[7]; + } else if (incoming_poll_group == 2) { //23 AD AD AD AD AB AD AD cellvoltages_mv[14] = (rx_frame.data.u8[1] * 20); cellvoltages_mv[15] = (rx_frame.data.u8[2] * 20); cellvoltages_mv[16] = (rx_frame.data.u8[3] * 20); @@ -174,13 +198,22 @@ void HyundaiIoniq28Battery::handle_incoming_can_frame(CAN_frame rx_frame) { cellvoltages_mv[83] = (rx_frame.data.u8[6] * 20); cellvoltages_mv[84] = (rx_frame.data.u8[7] * 20); } else if (incoming_poll_group == 5) { - heatertemp = rx_frame.data.u8[7]; + //available_discharge_power = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2]); + //battery_cell_mV_deviation = rx_frame.data.u8[3]; + //airbag_h/wire_duty = rx_frame.data.u8[5]; + heatertemperature_1 = rx_frame.data.u8[6]; + heatertemperature_2 = rx_frame.data.u8[7]; } break; case 0x24: //Fourth datarow in PID group if (incoming_poll_group == 1) { //24 AA 4F 00 00 77 00 14 CellVoltMin_mV = (rx_frame.data.u8[1] * 20); //(volts *50) *20 =mV - } else if (incoming_poll_group == 2) { //24 AD AD AD AD AD AD AB + //mincellvoltage_number = rx_frame.data.u8[2]; + //fan_status = rx_frame.data.u8[3]; + //fan_feedback_signal = rx_frame.data.u8[4]; + //aux_battery_voltage = rx_frame.data.u8[5]; + //cumulative_charge_current_highbyte = ((rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]); + } else if (incoming_poll_group == 2) { //24 AD AD AD AD AD AD AB cellvoltages_mv[21] = (rx_frame.data.u8[1] * 20); cellvoltages_mv[22] = (rx_frame.data.u8[2] * 20); cellvoltages_mv[23] = (rx_frame.data.u8[3] * 20); @@ -204,13 +237,19 @@ void HyundaiIoniq28Battery::handle_incoming_can_frame(CAN_frame rx_frame) { cellvoltages_mv[89] = (rx_frame.data.u8[5] * 20); cellvoltages_mv[90] = (rx_frame.data.u8[6] * 20); cellvoltages_mv[91] = (rx_frame.data.u8[7] * 20); - } else if (incoming_poll_group == 5) { - batterySOH = ((rx_frame.data.u8[2] << 8) + rx_frame.data.u8[3]); + } else if (incoming_poll_group == 5) { //24 3 e8 5 3 e8 m34 6e + batterySOH = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2]); + //max_deterioration_cell_number = rx_frame.data.u8[3] + //min_deterioration = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); + //min_deterioration_cell_number = rx_frame.data.u8[6] + //SOC_display = rx_frame.data.u8[7] } break; case 0x25: //Fifth datarow in PID group if (incoming_poll_group == 1) { //25 5C A9 00 14 5F D3 00 - + //cumulative_charge_current_lowbyte = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2]); + //cumulative_discharge_current = ((rx_frame.data.u8[3] << 24) | (rx_frame.data.u8[4] << 16) | (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6]); + //cumulative_charge_energy_highbyte = rx_frame.data.u8[7]; } else if (incoming_poll_group == 2) { //25 AD AD AD AD 00 00 00 cellvoltages_mv[28] = (rx_frame.data.u8[1] * 20); cellvoltages_mv[29] = (rx_frame.data.u8[2] * 20); @@ -231,19 +270,22 @@ void HyundaiIoniq28Battery::handle_incoming_can_frame(CAN_frame rx_frame) { break; case 0x26: //Sixth datarow in PID group if (incoming_poll_group == 1) { //26 07 84 F9 00 07 42 8F + //cumulative_charge_energy_lowbyte = (rx_frame.data.u8[1] << 16) | (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]); + //cumulative_discharge_energy = ((rx_frame.data.u8[4] << 24) | (rx_frame.data.u8[5] << 16) | (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]); } else if (incoming_poll_group == 5) { } break; case 0x27: //Seventh datarow in PID group if (incoming_poll_group == 1) { //27 03 3F A1 EB 00 19 99 - BMS_ign = rx_frame.data.u8[6]; - inverterVoltageFrameHigh = rx_frame.data.u8[7]; + //cumulative_operating_time = ((rx_frame.data.u8[1] << 24) | (rx_frame.data.u8[2] << 16) | (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4]); + //bitfield (41 off, 45 car on) = rx_frame.data.u8[5]; + inverterVoltage = ((rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]); } break; case 0x28: //Eighth datarow in PID group if (incoming_poll_group == 1) { //28 7F FF 7F FF 03 E8 00 - inverterVoltage = (inverterVoltageFrameHigh << 8) + rx_frame.data.u8[1]; + isolation_resistance = ((rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6]); } break; } @@ -378,10 +420,6 @@ uint8_t HyundaiIoniq28Battery::get_battery_management_mode() const { return batteryManagementMode; } -uint8_t HyundaiIoniq28Battery::get_battery_ignition_mode() const { - return BMS_ign; -} - -uint8_t HyundaiIoniq28Battery::get_battery_relay_mode() const { - return batteryRelay; +uint16_t HyundaiIoniq28Battery::get_isolation_resistance() const { + return isolation_resistance; } diff --git a/Software/src/battery/HYUNDAI-IONIQ-28-BATTERY.h b/Software/src/battery/HYUNDAI-IONIQ-28-BATTERY.h index 313915d5..4409e52c 100644 --- a/Software/src/battery/HYUNDAI-IONIQ-28-BATTERY.h +++ b/Software/src/battery/HYUNDAI-IONIQ-28-BATTERY.h @@ -25,10 +25,9 @@ class HyundaiIoniq28Battery : public CanBattery { // Getter methods for HTML renderer uint16_t get_lead_acid_voltage() const; + uint16_t get_isolation_resistance() const; int16_t get_power_relay_temperature() const; uint8_t get_battery_management_mode() const; - uint8_t get_battery_ignition_mode() const; - uint8_t get_battery_relay_mode() const; private: HyundaiIoniq28BatteryHtmlRenderer renderer; @@ -53,18 +52,17 @@ class HyundaiIoniq28Battery : public CanBattery { uint16_t allowedDischargePower = 0; uint16_t allowedChargePower = 0; uint16_t batteryVoltage = 3700; - uint16_t inverterVoltageFrameHigh = 0; uint16_t inverterVoltage = 0; + uint16_t isolation_resistance = 1000; uint16_t cellvoltages_mv[96]; uint16_t leadAcidBatteryVoltage = 120; int16_t batteryAmps = 0; int16_t temperatureMax = 0; int16_t temperatureMin = 0; uint8_t batteryManagementMode = 0; - uint8_t BMS_ign = 0; - uint8_t batteryRelay = 0; uint8_t counter_200 = 0; - int8_t heatertemp = 0; + int8_t heatertemperature_1 = 0; + int8_t heatertemperature_2 = 0; int8_t powerRelayTemperature = 0; bool startedUp = false; uint8_t incoming_poll_group = 0xFF; From 91273c7763f9e8b8761202bd8832a9abe081774c Mon Sep 17 00:00:00 2001 From: mbuhansen <91631350+mbuhansen@users.noreply.github.com> Date: Fri, 29 Aug 2025 19:15:17 +0200 Subject: [PATCH 073/245] Fix contactor test timer start variable assignment --- Software/src/inverter/KOSTAL-RS485.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/src/inverter/KOSTAL-RS485.cpp b/Software/src/inverter/KOSTAL-RS485.cpp index 62ee8169..66547eeb 100644 --- a/Software/src/inverter/KOSTAL-RS485.cpp +++ b/Software/src/inverter/KOSTAL-RS485.cpp @@ -249,7 +249,7 @@ void KostalInverterProtocol::receive() // Runs as fast as possible to handle th datalayer.system.status.inverter_allows_contactor_closing = false; dbg_message("inverter_allows_contactor_closing -> false (Contactor test start)"); send_kostal(ACK_FRAME, 8); // ACK - contactortestTimerStart = millis(); + contactortestTimerStart = currentMillis; contactortestTimerActive = true; } else if (RS485_RXFRAME[7] == 0xFF) { // no ACK sent From 1ec6af8ea0aa3845f3a6af18fdee32c74c3adf9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Fri, 29 Aug 2025 20:56:29 +0300 Subject: [PATCH 074/245] Add configurable Tesla options --- Software/src/battery/BATTERIES.cpp | 8 ++++ Software/src/battery/BATTERIES.h | 7 +++ Software/src/communication/nvm/comm_nvm.cpp | 6 +++ .../src/devboard/webserver/settings_html.cpp | 44 +++++++++++++++++++ Software/src/devboard/webserver/webserver.cpp | 16 ++++++- 5 files changed, 79 insertions(+), 2 deletions(-) diff --git a/Software/src/battery/BATTERIES.cpp b/Software/src/battery/BATTERIES.cpp index 247e61e5..4ea2c24f 100644 --- a/Software/src/battery/BATTERIES.cpp +++ b/Software/src/battery/BATTERIES.cpp @@ -317,6 +317,14 @@ void setup_battery() { } #endif +/* User-selected Tesla settings */ +bool user_selected_tesla_digital_HVIL = false; +uint16_t user_selected_tesla_GTW_country = 0; +bool user_selected_tesla_GTW_rightHandDrive = false; +uint16_t user_selected_tesla_GTW_mapRegion = 0; +uint16_t user_selected_tesla_GTW_chassisType = 0; +uint16_t user_selected_tesla_GTW_packEnergy = 0; + /* User-selected voltages used for custom-BMS batteries (RJXZS etc.) */ #if defined(MAX_CUSTOM_PACK_VOLTAGE_DV) && defined(MIN_CUSTOM_PACK_VOLTAGE_DV) && \ defined(MAX_CUSTOM_CELL_VOLTAGE_MV) && defined(MIN_CUSTOM_CELL_VOLTAGE_MV) diff --git a/Software/src/battery/BATTERIES.h b/Software/src/battery/BATTERIES.h index 5c5539a6..f26dcf6e 100644 --- a/Software/src/battery/BATTERIES.h +++ b/Software/src/battery/BATTERIES.h @@ -61,4 +61,11 @@ extern uint16_t user_selected_min_pack_voltage_dV; extern uint16_t user_selected_max_cell_voltage_mV; extern uint16_t user_selected_min_cell_voltage_mV; +extern bool user_selected_tesla_digital_HVIL; +extern uint16_t user_selected_tesla_GTW_country; +extern bool user_selected_tesla_GTW_rightHandDrive; +extern uint16_t user_selected_tesla_GTW_mapRegion; +extern uint16_t user_selected_tesla_GTW_chassisType; +extern uint16_t user_selected_tesla_GTW_packEnergy; + #endif diff --git a/Software/src/communication/nvm/comm_nvm.cpp b/Software/src/communication/nvm/comm_nvm.cpp index 91adc099..7d513c81 100644 --- a/Software/src/communication/nvm/comm_nvm.cpp +++ b/Software/src/communication/nvm/comm_nvm.cpp @@ -103,6 +103,12 @@ void init_stored_settings() { user_selected_inverter_ah_capacity = settings.getUInt("INVAHCAPACITY", 0); user_selected_inverter_battery_type = settings.getUInt("INVBTYPE", 0); user_selected_inverter_ignore_contactors = settings.getBool("INVICNT", false); + user_selected_tesla_digital_HVIL = settings.getBool("DIGITALHVIL", false); + user_selected_tesla_GTW_country = settings.getUInt("GTWCOUNTRY", 0); + user_selected_tesla_GTW_rightHandDrive = settings.getBool("GTWRHD", false); + user_selected_tesla_GTW_mapRegion = settings.getUInt("GTWMAPREG", 0); + user_selected_tesla_GTW_chassisType = settings.getUInt("GTWCHASSIS", 0); + user_selected_tesla_GTW_packEnergy = settings.getUInt("GTWPACK", 0); auto readIf = [](const char* settingName) { auto batt1If = (comm_interface)settings.getUInt(settingName, (int)comm_interface::CanNative); diff --git a/Software/src/devboard/webserver/settings_html.cpp b/Software/src/devboard/webserver/settings_html.cpp index 7ca338ce..176a0776 100644 --- a/Software/src/devboard/webserver/settings_html.cpp +++ b/Software/src/devboard/webserver/settings_html.cpp @@ -500,6 +500,30 @@ String settings_processor(const String& var, BatteryEmulatorSettingsStore& setti return settings.getBool("INVICNT") ? "checked" : ""; } + if (var == "DIGITALHVIL") { + return settings.getBool("DIGITALHVIL") ? "checked" : ""; + } + + if (var == "GTWCOUNTRY") { + return String(settings.getUInt("GTWCOUNTRY", 0)); + } + + if (var == "GTWRHD") { + return settings.getBool("GTWRHD") ? "checked" : ""; + } + + if (var == "GTWMAPREG") { + return String(settings.getUInt("GTWMAPREG", 0)); + } + + if (var == "GTWCHASSIS") { + return String(settings.getUInt("GTWCHASSIS", 0)); + } + + if (var == "GTWPACK") { + return String(settings.getUInt("GTWPACK", 0)); + } + return String(); } @@ -686,6 +710,11 @@ const char* getCANInterfaceName(CAN_Interface interface) { display: contents; } + form .if-tesla { display: none; } + form[data-battery="32"] .if-tesla, form[data-battery="33"] .if-tesla { + display: contents; + } + form .if-dblbtr { display: none; } form[data-dblbtr="true"] .if-dblbtr { display: contents; @@ -735,6 +764,21 @@ const char* getCANInterfaceName(CAN_Interface interface) { %BATTTYPE% +
+ + + + + + + + + + + + +
+
- - - - - - - - + + + +
From a1e5bc57d04d6dd553aa4c4fb23137b6d6d80360 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Fri, 29 Aug 2025 23:16:36 +0300 Subject: [PATCH 076/245] Remove ifdef from logging --- Software/src/battery/BMW-IX-BATTERY.cpp | 40 +--------- Software/src/battery/BMW-PHEV-BATTERY.cpp | 22 +----- Software/src/battery/CHADEMO-BATTERY.cpp | 76 ++----------------- .../src/battery/IMIEV-CZERO-ION-BATTERY.cpp | 12 --- Software/src/battery/JAGUAR-IPACE-BATTERY.cpp | 15 ---- Software/src/battery/KIA-E-GMP-BATTERY.cpp | 4 - Software/src/battery/MEB-BATTERY.cpp | 22 ------ Software/src/battery/MG-HS-PHEV-BATTERY.cpp | 6 -- .../src/battery/RENAULT-KANGOO-BATTERY.cpp | 33 -------- Software/src/battery/TESLA-BATTERY.cpp | 29 ------- Software/src/battery/TEST-FAKE-BATTERY.cpp | 15 ---- Software/src/battery/VOLVO-SPA-BATTERY.cpp | 55 -------------- .../src/battery/VOLVO-SPA-HYBRID-BATTERY.cpp | 65 ---------------- Software/src/charger/CHEVY-VOLT-CHARGER.cpp | 5 -- Software/src/communication/can/comm_can.cpp | 12 --- Software/src/communication/can/obd.cpp | 2 - .../comm_contactorcontrol.cpp | 2 - .../precharge_control/precharge_control.cpp | 16 ---- Software/src/devboard/mqtt/mqtt.cpp | 26 ------- Software/src/devboard/sdcard/sdcard.cpp | 10 --- Software/src/devboard/utils/events.cpp | 2 - .../src/devboard/webserver/events_html.cpp | 6 -- Software/src/devboard/webserver/webserver.cpp | 6 -- Software/src/devboard/wifi/wifi.cpp | 21 ----- Software/src/inverter/FOXESS-CAN.cpp | 27 ++----- .../src/inverter/ModbusInverterProtocol.cpp | 16 ---- Software/src/inverter/SMA-BYD-H-CAN.cpp | 2 - Software/src/inverter/SMA-BYD-HVS-CAN.cpp | 2 - Software/src/inverter/SMA-TRIPOWER-CAN.cpp | 2 - Software/src/inverter/SOLAX-CAN.cpp | 8 -- 30 files changed, 14 insertions(+), 545 deletions(-) diff --git a/Software/src/battery/BMW-IX-BATTERY.cpp b/Software/src/battery/BMW-IX-BATTERY.cpp index d7117ed0..be16b640 100644 --- a/Software/src/battery/BMW-IX-BATTERY.cpp +++ b/Software/src/battery/BMW-IX-BATTERY.cpp @@ -307,9 +307,7 @@ void BmwIXBattery::handle_incoming_can_frame(CAN_frame rx_frame) { if ((rx_frame.data.u8[6] << 8 | rx_frame.data.u8[7]) == 10000 || (rx_frame.data.u8[8] << 8 | rx_frame.data.u8[9]) == 10000) { //Qualifier Invalid Mode - Request Reboot -#ifdef DEBUG_LOG logging.println("Cell MinMax Qualifier Invalid - Requesting BMS Reset"); -#endif // DEBUG_LOG //set_event(EVENT_BATTERY_VALUE_UNAVAILABLE, (millis())); //Eventually need new Info level event type transmit_can_frame(&BMWiX_6F4_REQUEST_HARD_RESET); } else { //Only ingest values if they are not the 10V Error state @@ -378,15 +376,11 @@ void BmwIXBattery::transmit_can(unsigned long currentMillis) { // Detect edge if (ContactorCloseRequest.previous == false && ContactorCloseRequest.present == true) { // Rising edge detected -#ifdef DEBUG_LOG logging.println("Rising edge detected. Resetting 10ms counter."); -#endif // DEBUG_LOG counter_10ms = 0; // reset counter } else if (ContactorCloseRequest.previous == true && ContactorCloseRequest.present == false) { // Dropping edge detected -#ifdef DEBUG_LOG logging.println("Dropping edge detected. Resetting 10ms counter."); -#endif // DEBUG_LOG counter_10ms = 0; // reset counter } ContactorCloseRequest.previous = ContactorCloseRequest.present; @@ -465,12 +459,10 @@ void BmwIXBattery::setup(void) { // Performs one time setup at startup void BmwIXBattery::HandleIncomingUserRequest(void) { // Debug user request to open or close the contactors -#ifdef DEBUG_LOG logging.print("User request: contactor close: "); logging.print(userRequestContactorClose); logging.print(" User request: contactor open: "); logging.println(userRequestContactorOpen); -#endif // DEBUG_LOG if ((userRequestContactorClose == false) && (userRequestContactorOpen == false)) { // do nothing } else if ((userRequestContactorClose == true) && (userRequestContactorOpen == false)) { @@ -487,11 +479,9 @@ void BmwIXBattery::HandleIncomingUserRequest(void) { // set user request to false userRequestContactorClose = false; userRequestContactorOpen = false; -// print error, as both these flags shall not be true at the same time -#ifdef DEBUG_LOG + // print error, as both these flags shall not be true at the same time logging.println( "Error: user requested contactors to close and open at the same time. Contactors have been opened."); -#endif // DEBUG_LOG } } @@ -499,16 +489,12 @@ void BmwIXBattery::HandleIncomingInverterRequest(void) { InverterContactorCloseRequest.present = datalayer.system.status.inverter_allows_contactor_closing; // Detect edge if (InverterContactorCloseRequest.previous == false && InverterContactorCloseRequest.present == true) { -// Rising edge detected -#ifdef DEBUG_LOG + // Rising edge detected logging.println("Inverter requests to close contactors"); -#endif // DEBUG_LOG BmwIxCloseContactors(); } else if (InverterContactorCloseRequest.previous == true && InverterContactorCloseRequest.present == false) { -// Falling edge detected -#ifdef DEBUG_LOG + // Falling edge detected logging.println("Inverter requests to open contactors"); -#endif // DEBUG_LOG BmwIxOpenContactors(); } // else: do nothing @@ -517,16 +503,12 @@ void BmwIXBattery::HandleIncomingInverterRequest(void) { } void BmwIXBattery::BmwIxCloseContactors(void) { -#ifdef DEBUG_LOG logging.println("Closing contactors"); -#endif // DEBUG_LOG contactorCloseReq = true; } void BmwIXBattery::BmwIxOpenContactors(void) { -#ifdef DEBUG_LOG logging.println("Opening contactors"); -#endif // DEBUG_LOG contactorCloseReq = false; counter_100ms = 0; // reset counter, such that keep contactors closed message sequence starts from the beginning } @@ -552,46 +534,34 @@ void BmwIXBattery::HandleBmwIxCloseContactorsRequest(uint16_t counter_10ms) { if (counter_10ms == 0) { // @0 ms transmit_can_frame(&BMWiX_510); -#ifdef DEBUG_LOG logging.println("Transmitted 0x510 - 1/6"); -#endif // DEBUG_LOG } else if (counter_10ms == 5) { // @50 ms transmit_can_frame(&BMWiX_276); -#ifdef DEBUG_LOG logging.println("Transmitted 0x276 - 2/6"); -#endif // DEBUG_LOG } else if (counter_10ms == 10) { // @100 ms BMWiX_510.data.u8[2] = 0x04; // TODO: check if needed transmit_can_frame(&BMWiX_510); -#ifdef DEBUG_LOG logging.println("Transmitted 0x510 - 3/6"); -#endif // DEBUG_LOG } else if (counter_10ms == 20) { // @200 ms BMWiX_510.data.u8[2] = 0x10; // TODO: check if needed BMWiX_510.data.u8[5] = 0x80; // needed to close contactors transmit_can_frame(&BMWiX_510); -#ifdef DEBUG_LOG logging.println("Transmitted 0x510 - 4/6"); -#endif // DEBUG_LOG } else if (counter_10ms == 30) { // @300 ms BMWiX_16E.data.u8[0] = 0x6A; BMWiX_16E.data.u8[1] = 0xAD; transmit_can_frame(&BMWiX_16E); -#ifdef DEBUG_LOG logging.println("Transmitted 0x16E - 5/6"); -#endif // DEBUG_LOG } else if (counter_10ms == 50) { // @500 ms BMWiX_16E.data.u8[0] = 0x03; BMWiX_16E.data.u8[1] = 0xA9; transmit_can_frame(&BMWiX_16E); -#ifdef DEBUG_LOG logging.println("Transmitted 0x16E - 6/6"); -#endif // DEBUG_LOG ContactorState.closed = true; ContactorState.open = false; } @@ -613,9 +583,7 @@ void BmwIXBattery::BmwIxKeepContactorsClosed(uint8_t counter_100ms) { 0xC9, 0x3A, 0xF7}; // Explicit declaration, to prevent modification by other functions if (counter_100ms == 0) { -#ifdef DEBUG_LOG logging.println("Sending keep contactors closed messages started"); -#endif // DEBUG_LOG // @0 ms transmit_can_frame(&BMWiX_510); } else if (counter_100ms == 7) { @@ -631,9 +599,7 @@ void BmwIXBattery::BmwIxKeepContactorsClosed(uint8_t counter_100ms) { BMWiX_16E.data.u8[0] = 0x02; BMWiX_16E.data.u8[1] = 0xA7; transmit_can_frame(&BMWiX_16E); -#ifdef DEBUG_LOG logging.println("Sending keep contactors closed messages finished"); -#endif // DEBUG_LOG } else if (counter_100ms == 140) { // @14000 ms // reset counter (outside of this function) diff --git a/Software/src/battery/BMW-PHEV-BATTERY.cpp b/Software/src/battery/BMW-PHEV-BATTERY.cpp index 8241c74a..8d76e34b 100644 --- a/Software/src/battery/BMW-PHEV-BATTERY.cpp +++ b/Software/src/battery/BMW-PHEV-BATTERY.cpp @@ -122,9 +122,7 @@ bool BmwPhevBattery::storeUDSPayload(const uint8_t* payload, uint8_t length) { if (gUDSContext.UDS_bytesReceived + length > sizeof(gUDSContext.UDS_buffer)) { // Overflow => abort gUDSContext.UDS_inProgress = false; -#ifdef DEBUG_LOG logging.println("UDS Payload Overflow"); -#endif // DEBUG_LOG return false; } memcpy(&gUDSContext.UDS_buffer[gUDSContext.UDS_bytesReceived], payload, length); @@ -134,9 +132,7 @@ bool BmwPhevBattery::storeUDSPayload(const uint8_t* payload, uint8_t length) { // If we’ve reached or exceeded the expected length, mark complete if (gUDSContext.UDS_bytesReceived >= gUDSContext.UDS_expectedLength) { gUDSContext.UDS_inProgress = false; - // #ifdef DEBUG_LOG // logging.println("Recived all expected UDS bytes"); - // #endif // DEBUG_LOG } return true; } @@ -196,9 +192,7 @@ void BmwPhevBattery::wake_battery_via_canbus() { change_can_speed(original_speed); -#ifdef DEBUG_LOG logging.println("Sent magic wakeup packet to SME at 100kbps..."); -#endif } void BmwPhevBattery::update_values() { //This function maps all the values fetched via CAN to the battery datalayer @@ -246,9 +240,7 @@ void BmwPhevBattery::update_values() { //This function maps all the values fetc datalayer.battery.status.cell_min_voltage_mV = 9999; //Stale values force stop datalayer.battery.status.cell_max_voltage_mV = 9999; //Stale values force stop set_event(EVENT_STALE_VALUE, 0); -#ifdef DEBUG_LOG logging.println("Stale Min/Max voltage values detected during charge/discharge sending - 9999mV..."); -#endif // DEBUG_LOG } else { datalayer.battery.status.cell_min_voltage_mV = min_cell_voltage; //Value is alive @@ -398,11 +390,7 @@ void BmwPhevBattery::handle_incoming_can_frame(CAN_frame rx_frame) { rx_frame.data.u8[4] == 0xAD) { //Balancing Status 01 Active 03 Not Active 7DLC F1 05 71 03 AD 6B 01 balancing_status = (rx_frame.data.u8[6]); - // #ifdef DEBUG_LOG - - // logging.println("Balancing Status received"); - - // #endif // DEBUG_LOG + //logging.println("Balancing Status received"); } break; @@ -525,7 +513,6 @@ void BmwPhevBattery::handle_incoming_can_frame(CAN_frame rx_frame) { battery_current = ((int32_t)((gUDSContext.UDS_buffer[3] << 24) | (gUDSContext.UDS_buffer[4] << 16) | (gUDSContext.UDS_buffer[5] << 8) | gUDSContext.UDS_buffer[6])) * 0.1; -#ifdef DEBUG_LOG logging.print("Received current/amps measurement data: "); logging.print(battery_current); logging.print(" - "); @@ -538,7 +525,6 @@ void BmwPhevBattery::handle_incoming_can_frame(CAN_frame rx_frame) { logging.print(" "); } logging.println(); // new line at the end -#endif // DEBUG_LOG } //Cell Min/Max @@ -553,7 +539,6 @@ void BmwPhevBattery::handle_incoming_can_frame(CAN_frame rx_frame) { min_cell_voltage = (gUDSContext.UDS_buffer[9] << 8 | gUDSContext.UDS_buffer[10]) / 10; max_cell_voltage = (gUDSContext.UDS_buffer[11] << 8 | gUDSContext.UDS_buffer[12]) / 10; } else { -#ifdef DEBUG_LOG logging.println("Cell Min Max Invalid 65535 or 0..."); logging.print("Received data: "); for (uint16_t i = 0; i < gUDSContext.UDS_bytesReceived; i++) { @@ -565,7 +550,6 @@ void BmwPhevBattery::handle_incoming_can_frame(CAN_frame rx_frame) { logging.print(" "); } logging.println(); // new line at the end -#endif // DEBUG_LOG } } @@ -618,16 +602,12 @@ void BmwPhevBattery::handle_incoming_can_frame(CAN_frame rx_frame) { if (rx_frame.data.u8[6] > 0 && rx_frame.data.u8[6] < 255) { battery_temperature_min = (rx_frame.data.u8[6] - 50); } else { -#ifdef DEBUG_LOG logging.println("Pre parsed Cell Temp Min is Invalid "); -#endif } if (rx_frame.data.u8[7] > 0 && rx_frame.data.u8[7] < 255) { battery_temperature_max = (rx_frame.data.u8[7] - 50); } else { -#ifdef DEBUG_LOG logging.println("Pre parsed Cell Temp Max is Invalid "); -#endif } break; diff --git a/Software/src/battery/CHADEMO-BATTERY.cpp b/Software/src/battery/CHADEMO-BATTERY.cpp index 5ec6217f..07257708 100644 --- a/Software/src/battery/CHADEMO-BATTERY.cpp +++ b/Software/src/battery/CHADEMO-BATTERY.cpp @@ -116,7 +116,6 @@ void ChademoBattery::process_vehicle_charging_session(CAN_frame rx_frame) { x102_chg_session.ChargingCurrentRequest = newChargingCurrentRequest; x102_chg_session.TargetBatteryVoltage = newTargetBatteryVoltage; -#ifdef DEBUG_LOG //Note on p131 uint8_t chargingrate = 0; if (x100_chg_lim.ConstantOfChargingRateIndication > 0) { @@ -124,7 +123,6 @@ void ChademoBattery::process_vehicle_charging_session(CAN_frame rx_frame) { logging.print("Charge Rate (kW): "); logging.println(chargingrate); } -#endif //Table A.26—Charge control termination command patterns -- should echo x108 handling @@ -136,41 +134,31 @@ void ChademoBattery::process_vehicle_charging_session(CAN_frame rx_frame) { */ if ((CHADEMO_Status == CHADEMO_INIT && vehicle_permission) || (x102_chg_session.s.status.StatusVehicleChargingEnabled && !vehicle_permission)) { -#ifdef DEBUG_LOG logging.println("Inconsistent charge/discharge state."); -#endif CHADEMO_Status = CHADEMO_FAULT; return; } if (x102_chg_session.f.fault.FaultBatteryOverVoltage) { -#ifdef DEBUG_LOG logging.println("Vehicle indicates fault, battery over voltage."); -#endif CHADEMO_Status = CHADEMO_STOP; return; } if (x102_chg_session.f.fault.FaultBatteryUnderVoltage) { -#ifdef DEBUG_LOG logging.println("Vehicle indicates fault, battery under voltage."); -#endif CHADEMO_Status = CHADEMO_STOP; return; } if (x102_chg_session.f.fault.FaultBatteryCurrentDeviation) { -#ifdef DEBUG_LOG logging.println("Vehicle indicates fault, battery current deviation. Possible EVSE issue?"); -#endif CHADEMO_Status = CHADEMO_STOP; return; } if (x102_chg_session.f.fault.FaultBatteryVoltageDeviation) { -#ifdef DEBUG_LOG logging.println("Vehicle indicates fault, battery voltage deviation. Possible EVSE issue?"); -#endif CHADEMO_Status = CHADEMO_STOP; return; } @@ -183,18 +171,14 @@ void ChademoBattery::process_vehicle_charging_session(CAN_frame rx_frame) { //FIXME condition nesting or more stanzas needed here for clear determination of cessation reason if (CHADEMO_Status == CHADEMO_POWERFLOW && EVSE_mode == CHADEMO_CHARGE && !vehicle_permission) { -#ifdef DEBUG_LOG logging.println("State of charge ceiling reached or charging interrupted, stop charging"); -#endif CHADEMO_Status = CHADEMO_STOP; return; } if (vehicle_permission && CHADEMO_Status == CHADEMO_NEGOTIATE) { CHADEMO_Status = CHADEMO_EV_ALLOWED; -#ifdef DEBUG_LOG logging.println("STATE shift to CHADEMO_EV_ALLOWED in process_vehicle_charging_session()"); -#endif return; } @@ -203,23 +187,17 @@ void ChademoBattery::process_vehicle_charging_session(CAN_frame rx_frame) { // consider relocating if (vehicle_permission && CHADEMO_Status == CHADEMO_EVSE_PREPARE && priorTargetBatteryVoltage == 0 && newTargetBatteryVoltage > 0 && x102_chg_session.s.status.StatusVehicleChargingEnabled) { -#ifdef DEBUG_LOG logging.println("STATE SHIFT to EVSE_START reached in process_vehicle_charging_session()"); -#endif CHADEMO_Status = CHADEMO_EVSE_START; return; } if (vehicle_permission && evse_permission && CHADEMO_Status == CHADEMO_POWERFLOW) { -#ifdef DEBUG_LOG logging.println("updating vehicle request in process_vehicle_charging_session()"); -#endif return; } -#ifdef DEBUG_LOG logging.println("UNHANDLED CHADEMO STATE, try unplugging chademo cable, reboot emulator, and retry!"); -#endif return; } @@ -231,8 +209,7 @@ void ChademoBattery::process_vehicle_charging_limits(CAN_frame rx_frame) { x200_discharge_limits.MinimumBatteryDischargeLevel = rx_frame.data.u8[6]; x200_discharge_limits.MaxRemainingCapacityForCharging = rx_frame.data.u8[7]; -#ifdef DEBUG_LOG -/* unsigned long currentMillis = millis(); + /* unsigned long currentMillis = millis(); if (currentMillis - previousMillis5000 >= INTERVAL_5_S) { previousMillis5000 = currentMillis; logging.println("x200 Max remaining capacity for charging/discharging:"); @@ -240,16 +217,13 @@ void ChademoBattery::process_vehicle_charging_limits(CAN_frame rx_frame) { logging.println(0xFF - x200_discharge_limits.MaxRemainingCapacityForCharging); } */ -#endif if (get_measured_voltage() <= x200_discharge_limits.MinimumDischargeVoltage && CHADEMO_Status > CHADEMO_NEGOTIATE) { -#ifdef DEBUG_LOG logging.println("x200 minimum discharge voltage met or exceeded, stopping."); logging.print("Measured: "); logging.print(get_measured_voltage()); logging.print("Minimum voltage: "); logging.print(x200_discharge_limits.MinimumDischargeVoltage); -#endif CHADEMO_Status = CHADEMO_STOP; } } @@ -264,7 +238,6 @@ void ChademoBattery::process_vehicle_discharge_estimate(CAN_frame rx_frame) { x201_discharge_estimate.ApproxDischargeCompletionTime = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[1]); x201_discharge_estimate.AvailableVehicleEnergy = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[3]); -#ifdef DEBUG_LOG if (currentMillis - previousMillis5000 >= INTERVAL_5_S) { previousMillis5000 = currentMillis; logging.print("x201 availabile vehicle energy, completion time: "); @@ -272,7 +245,6 @@ void ChademoBattery::process_vehicle_discharge_estimate(CAN_frame rx_frame) { logging.print("x201 approx vehicle completion time: "); logging.println(x201_discharge_estimate.ApproxDischargeCompletionTime); } -#endif } void ChademoBattery::process_vehicle_dynamic_control(CAN_frame rx_frame) { @@ -615,10 +587,8 @@ void ChademoBattery::transmit_can(unsigned long currentMillis) { // TODO need an update_evse_dynamic_control(..) function above before we send 118 // 110.0.0 if (x102_chg_session.ControlProtocolNumberEV >= 0x03) { //Only send the following on Chademo 2.0 vehicles? -#ifdef DEBUG_LOG //FIXME REMOVE logging.println("REMOVE: proto 2.0"); -#endif transmit_can_frame(&CHADEMO_118); } } @@ -656,16 +626,12 @@ void ChademoBattery::handle_chademo_sequence() { /* ------------------- State override conditions checks ------------------- */ /* ------------------------------------------------------------------------------ */ if (CHADEMO_Status >= CHADEMO_EV_ALLOWED && x102_chg_session.s.status.StatusVehicleShifterPosition) { -#ifdef DEBUG_LOG logging.println("Vehicle is not parked, abort."); -#endif CHADEMO_Status = CHADEMO_STOP; } if (CHADEMO_Status >= CHADEMO_EV_ALLOWED && !vehicle_permission) { -#ifdef DEBUG_LOG logging.println("Vehicle charge/discharge permission ended, stop."); -#endif CHADEMO_Status = CHADEMO_STOP; } @@ -678,25 +644,19 @@ void ChademoBattery::handle_chademo_sequence() { plug_inserted = digitalRead(pin7); if (!plug_inserted) { -#ifdef DEBUG_LOG -// Commented unless needed for debug -// logging.println("CHADEMO plug is not inserted."); -#endif + // Commented unless needed for debug + // logging.println("CHADEMO plug is not inserted."); return; } CHADEMO_Status = CHADEMO_CONNECTED; -#ifdef DEBUG_LOG logging.println("CHADEMO plug is inserted. Provide EVSE power to vehicle to trigger initialization."); -#endif break; case CHADEMO_CONNECTED: -#ifdef DEBUG_LOG // Commented unless needed for debug //logging.println("CHADEMO_CONNECTED State"); -#endif /* plug_inserted is .. essentially a volatile of sorts, so verify */ if (plug_inserted) { /* If connection is detectable, jumpstart handshake by @@ -729,17 +689,13 @@ void ChademoBattery::handle_chademo_sequence() { /* Vehicle and EVSE dance */ //TODO if pin 4 / j goes high, -#ifdef DEBUG_LOG -// Commented unless needed for debug -// logging.println("CHADEMO_NEGOTIATE State"); -#endif + // Commented unless needed for debug + // logging.println("CHADEMO_NEGOTIATE State"); x109_evse_state.s.status.ChgDischStopControl = 1; break; case CHADEMO_EV_ALLOWED: -#ifdef DEBUG_LOG // Commented unless needed for debug logging.println("CHADEMO_EV_ALLOWED State"); -#endif // If we are in this state, vehicle_permission was already set to true...but re-verify // that pin 4 (j) reads high if (vehicle_permission) { @@ -754,10 +710,8 @@ void ChademoBattery::handle_chademo_sequence() { } break; case CHADEMO_EVSE_PREPARE: -#ifdef DEBUG_LOG // Commented unless needed for debug logging.println("CHADEMO_EVSE_PREPARE State"); -#endif /* TODO voltage check of output < 20v * insulation test hypothetically happens here before triggering PIN 10 high * see Table A.28—Requirements for the insulation test for output DC circuit @@ -790,19 +744,15 @@ void ChademoBattery::handle_chademo_sequence() { //state changes to CHADEMO_EVSE_START only upon receipt of charging session request break; case CHADEMO_EVSE_START: -#ifdef DEBUG_LOG // Commented unless needed for debug logging.println("CHADEMO_EVSE_START State"); -#endif datalayer.system.status.battery_allows_contactor_closing = true; x109_evse_state.s.status.ChgDischStopControl = 1; x109_evse_state.s.status.EVSE_status = 0; CHADEMO_Status = CHADEMO_EVSE_CONTACTORS_ENABLED; -#ifdef DEBUG_LOG logging.println("Initiating contactors"); -#endif /* break rather than fall through because contactors are not instantaneous; * worth giving it a cycle to finish @@ -810,18 +760,14 @@ void ChademoBattery::handle_chademo_sequence() { break; case CHADEMO_EVSE_CONTACTORS_ENABLED: -#ifdef DEBUG_LOG // Commented unless needed for debug logging.println("CHADEMO_EVSE_CONTACTORS State"); -#endif /* check whether contactors ready, because externally dependent upon inverter allow during discharge */ if (contactors_ready) { -#ifdef DEBUG_LOG logging.println("Contactors ready"); logging.print("Voltage: "); logging.println(get_measured_voltage()); -#endif /* transition to POWERFLOW state if discharge compatible on both sides */ if (x109_evse_state.discharge_compatible && x102_chg_session.s.status.StatusVehicleDischargeCompatible && (EVSE_mode == CHADEMO_DISCHARGE || EVSE_mode == CHADEMO_BIDIRECTIONAL)) { @@ -840,10 +786,8 @@ void ChademoBattery::handle_chademo_sequence() { /* break or fall through ? TODO */ break; case CHADEMO_POWERFLOW: -#ifdef DEBUG_LOG // Commented unless needed for debug logging.println("CHADEMO_POWERFLOW State"); -#endif /* POWERFLOW for charging, discharging, and bidirectional */ /* Interpretation */ if (x102_chg_session.s.status.StatusVehicleShifterPosition) { @@ -860,9 +804,7 @@ void ChademoBattery::handle_chademo_sequence() { } if (get_measured_voltage() <= x200_discharge_limits.MinimumDischargeVoltage) { -#ifdef DEBUG_LOG logging.println("x200 minimum discharge voltage met or exceeded, stopping."); -#endif CHADEMO_Status = CHADEMO_STOP; } @@ -871,10 +813,8 @@ void ChademoBattery::handle_chademo_sequence() { x109_evse_state.s.status.EVSE_status = 1; break; case CHADEMO_STOP: -#ifdef DEBUG_LOG // Commented unless needed for debug logging.println("CHADEMO_STOP State"); -#endif /* back to CHADEMO_IDLE after teardown */ x109_evse_state.s.status.ChgDischStopControl = 1; x109_evse_state.s.status.EVSE_status = 0; @@ -899,17 +839,13 @@ void ChademoBattery::handle_chademo_sequence() { break; case CHADEMO_FAULT: -#ifdef DEBUG_LOG // Commented unless needed for debug logging.println("CHADEMO_FAULT State"); -#endif /* Once faulted, never departs CHADEMO_FAULT state unless device is power cycled as a safety measure */ x109_evse_state.s.status.EVSE_error = 1; x109_evse_state.s.status.ChgDischError = 1; x109_evse_state.s.status.ChgDischStopControl = 1; -#ifdef DEBUG_LOG logging.println("CHADEMO fault encountered, tearing down to make safe"); -#endif digitalWrite(pin10, LOW); digitalWrite(pin2, LOW); evse_permission = false; @@ -919,9 +855,7 @@ void ChademoBattery::handle_chademo_sequence() { break; default: -#ifdef DEBUG_LOG logging.println("UNHANDLED CHADEMO_STATE, setting FAULT"); -#endif CHADEMO_Status = CHADEMO_FAULT; break; } diff --git a/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp b/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp index 85f3519a..694a6d09 100644 --- a/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp +++ b/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp @@ -75,12 +75,9 @@ void ImievCZeroIonBattery:: } if (!BMU_Detected) { -#ifdef DEBUG_LOG logging.println("BMU not detected, check wiring!"); -#endif } -#ifdef DEBUG_LOG logging.println("Battery Values"); logging.print("BMU SOC: "); logging.print(BMU_SOC); @@ -90,15 +87,6 @@ void ImievCZeroIonBattery:: logging.print(BMU_PackVoltage); logging.print(" BMU_Power: "); logging.print(BMU_Power); - logging.print(" Cell max voltage: "); - logging.print(max_volt_cel); - logging.print(" Cell min voltage: "); - logging.print(min_volt_cel); - logging.print(" Cell max temp: "); - logging.print(max_temp_cel); - logging.print(" Cell min temp: "); - logging.println(min_temp_cel); -#endif } void ImievCZeroIonBattery::handle_incoming_can_frame(CAN_frame rx_frame) { diff --git a/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp b/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp index 443e6d98..2f43438f 100644 --- a/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp +++ b/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp @@ -102,21 +102,6 @@ void JaguarIpaceBattery::update_values() { } else { clear_event(EVENT_BATTERY_ISOLATION); } - -/*Finally print out values to serial if configured to do so*/ -#ifdef DEBUG_LOG - logging.println("Values going to inverter"); - print_units("SOH%: ", (datalayer.battery.status.soh_pptt * 0.01), "% "); - print_units(", SOC%: ", (datalayer.battery.status.reported_soc * 0.01), "% "); - print_units(", Voltage: ", (datalayer.battery.status.voltage_dV * 0.1), "V "); - print_units(", Max discharge power: ", datalayer.battery.status.max_discharge_power_W, "W "); - print_units(", Max charge power: ", datalayer.battery.status.max_charge_power_W, "W "); - print_units(", Max temp: ", (datalayer.battery.status.temperature_max_dC * 0.1), "°C "); - print_units(", Min temp: ", (datalayer.battery.status.temperature_min_dC * 0.1), "°C "); - print_units(", Max cell voltage: ", datalayer.battery.status.cell_max_voltage_mV, "mV "); - print_units(", Min cell voltage: ", datalayer.battery.status.cell_min_voltage_mV, "mV "); - logging.println(""); -#endif } void JaguarIpaceBattery::handle_incoming_can_frame(CAN_frame rx_frame) { diff --git a/Software/src/battery/KIA-E-GMP-BATTERY.cpp b/Software/src/battery/KIA-E-GMP-BATTERY.cpp index a4c8bd95..ba6b1c06 100644 --- a/Software/src/battery/KIA-E-GMP-BATTERY.cpp +++ b/Software/src/battery/KIA-E-GMP-BATTERY.cpp @@ -55,7 +55,6 @@ uint16_t KiaEGmpBattery::estimateSOC(uint16_t packVoltage, uint16_t cellCount, i // Calculate average cell voltage in millivolts uint16_t avgCellVoltage = compensatedPackVoltageMv / cellCount; -#ifdef DEBUG_LOG logging.print("Pack: "); logging.print(packVoltage / 10.0); logging.print("V, Current: "); @@ -67,7 +66,6 @@ uint16_t KiaEGmpBattery::estimateSOC(uint16_t packVoltage, uint16_t cellCount, i logging.print("V, Avg Cell: "); logging.print(avgCellVoltage); logging.println("mV"); -#endif // Use the cell voltage lookup table to estimate SOC return estimateSOCFromCell(avgCellVoltage); @@ -187,7 +185,6 @@ void KiaEGmpBattery::update_values() { /* Safeties verified. Perform USB serial printout if configured to do so */ -#ifdef DEBUG_LOG logging.println(); //sepatator logging.println("Values from battery: "); logging.print("SOC BMS: "); @@ -244,7 +241,6 @@ void KiaEGmpBattery::update_values() { logging.print(" | Inverter "); logging.print(inverterVoltage); logging.println(" Volts"); -#endif } // Getter implementations for HTML renderer diff --git a/Software/src/battery/MEB-BATTERY.cpp b/Software/src/battery/MEB-BATTERY.cpp index ebd02f7a..0fe468f7 100644 --- a/Software/src/battery/MEB-BATTERY.cpp +++ b/Software/src/battery/MEB-BATTERY.cpp @@ -171,9 +171,7 @@ uint8_t vw_crc_calc(uint8_t* inputBytes, uint8_t length, uint32_t address) { magicByte = MB16A954A6[counter]; break; default: // this won't lead to correct CRC checksums -#ifdef DEBUG_LOG logging.println("Checksum request unknown"); -#endif magicByte = 0x00; break; } @@ -310,9 +308,7 @@ void MebBattery:: void MebBattery::handle_incoming_can_frame(CAN_frame rx_frame) { last_can_msg_timestamp = millis(); if (first_can_msg == 0) { -#ifdef DEBUG_LOG logging.printf("MEB: First CAN msg received\n"); -#endif first_can_msg = last_can_msg_timestamp; } @@ -326,9 +322,7 @@ void MebBattery::handle_incoming_can_frame(CAN_frame rx_frame) { if (rx_frame.data.u8[0] != vw_crc_calc(rx_frame.data.u8, rx_frame.DLC, rx_frame.ID)) { //If CRC does not match calc datalayer.battery.status.CAN_error_counter++; -#ifdef DEBUG_LOG logging.printf("MEB: Msg 0x%04X CRC error\n", rx_frame.ID); -#endif return; } default: @@ -700,29 +694,23 @@ void MebBattery::handle_incoming_can_frame(CAN_frame rx_frame) { case 3: // EXTERN CHARGING case 4: // AC_CHARGING case 6: // DC_CHARGING -#ifdef DEBUG_LOG if (!datalayer.system.status.battery_allows_contactor_closing) logging.printf("MEB: Contactors closed\n"); -#endif if (datalayer.battery.status.real_bms_status != BMS_FAULT) datalayer.battery.status.real_bms_status = BMS_ACTIVE; datalayer.system.status.battery_allows_contactor_closing = true; hv_requested = false; break; case 5: // Error -#ifdef DEBUG_LOG if (datalayer.system.status.battery_allows_contactor_closing) logging.printf("MEB: Contactors opened\n"); -#endif datalayer.battery.status.real_bms_status = BMS_FAULT; datalayer.system.status.battery_allows_contactor_closing = false; hv_requested = false; break; case 7: // Init -#ifdef DEBUG_LOG if (datalayer.system.status.battery_allows_contactor_closing) logging.printf("MEB: Contactors opened\n"); -#endif if (datalayer.battery.status.real_bms_status != BMS_FAULT) datalayer.battery.status.real_bms_status = BMS_STANDBY; datalayer.system.status.battery_allows_contactor_closing = false; @@ -730,10 +718,8 @@ void MebBattery::handle_incoming_can_frame(CAN_frame rx_frame) { break; case 2: // BALANCING default: -#ifdef DEBUG_LOG if (datalayer.system.status.battery_allows_contactor_closing) logging.printf("MEB: Contactors opened\n"); -#endif if (datalayer.battery.status.real_bms_status != BMS_FAULT) datalayer.battery.status.real_bms_status = BMS_STANDBY; datalayer.system.status.battery_allows_contactor_closing = false; @@ -1276,10 +1262,8 @@ void MebBattery::handle_incoming_can_frame(CAN_frame rx_frame) { handle_obd_frame(rx_frame); break; default: -#ifdef DEBUG_LOG logging.printf("Unknown CAN frame received:\n"); dump_can_frame(rx_frame, MSG_RX); -#endif break; } datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; @@ -1292,10 +1276,8 @@ void MebBattery::handle_incoming_can_frame(CAN_frame rx_frame) { void MebBattery::transmit_can(unsigned long currentMillis) { if (currentMillis - last_can_msg_timestamp > 500) { -#ifdef DEBUG_LOG if (first_can_msg) logging.printf("MEB: No CAN msg received for 500ms\n"); -#endif can_msg_received = RX_DEFAULT; first_can_msg = 0; if (datalayer.battery.status.real_bms_status != BMS_FAULT) { @@ -1373,7 +1355,6 @@ void MebBattery::transmit_can(unsigned long currentMillis) { ((int32_t)datalayer_extended.meb.BMS_voltage_intermediate_dV)) < 200))))) { hv_requested = true; datalayer.system.settings.start_precharging = false; -#ifdef DEBUG_LOG if (MEB_503.data.u8[3] == BMS_TARGET_HV_OFF) { logging.printf("MEB: Requesting HV\n"); } @@ -1385,7 +1366,6 @@ void MebBattery::transmit_can(unsigned long currentMillis) { logging.printf("MEB: Precharge bit set to inactive\n"); } } -#endif MEB_503.data.u8[1] = 0x30 | (datalayer.system.status.precharge_status == AUTO_PRECHARGE_PRECHARGING ? 0x80 : 0x00); MEB_503.data.u8[3] = BMS_TARGET_AC_CHARGING; @@ -1399,7 +1379,6 @@ void MebBattery::transmit_can(unsigned long currentMillis) { datalayer.system.settings.start_precharging = true; } -#ifdef DEBUG_LOG if (MEB_503.data.u8[3] != BMS_TARGET_HV_OFF) { logging.printf("MEB: Requesting HV_OFF\n"); } @@ -1411,7 +1390,6 @@ void MebBattery::transmit_can(unsigned long currentMillis) { logging.printf("MEB: Precharge bit set to inactive\n"); } } -#endif MEB_503.data.u8[1] = 0x10 | (datalayer.system.status.precharge_status == AUTO_PRECHARGE_PRECHARGING ? 0x80 : 0x00); MEB_503.data.u8[3] = BMS_TARGET_HV_OFF; diff --git a/Software/src/battery/MG-HS-PHEV-BATTERY.cpp b/Software/src/battery/MG-HS-PHEV-BATTERY.cpp index 5250e346..a8395067 100644 --- a/Software/src/battery/MG-HS-PHEV-BATTERY.cpp +++ b/Software/src/battery/MG-HS-PHEV-BATTERY.cpp @@ -147,11 +147,9 @@ void MgHsPHEVBattery::handle_incoming_can_frame(CAN_frame rx_frame) { // 15 = isolation fault // 0/8 = checking -#ifdef DEBUG_LOG if (rx_frame.data.u8[1] != previousState) { logging.printf("MG_HS_PHEV: Battery status changed to %d (%d)\n", rx_frame.data.u8[1], rx_frame.data.u8[0]); } -#endif if (rx_frame.data.u8[1] == 0xf && previousState != 0xf) { // Isolation fault, set event @@ -168,18 +166,14 @@ void MgHsPHEVBattery::handle_incoming_can_frame(CAN_frame rx_frame) { // A weird 'stuck' state where the battery won't reconnect datalayer.system.status.battery_allows_contactor_closing = false; if (!datalayer.system.status.BMS_startup_in_progress) { -#ifdef DEBUG_LOG logging.printf("MG_HS_PHEV: Stuck, resetting.\n"); -#endif start_bms_reset(); } } else if (rx_frame.data.u8[1] == 0xf) { // A fault state (likely isolation failure) datalayer.system.status.battery_allows_contactor_closing = false; if (!datalayer.system.status.BMS_startup_in_progress) { -#ifdef DEBUG_LOG logging.printf("MG_HS_PHEV: Fault, resetting.\n"); -#endif start_bms_reset(); } } else { diff --git a/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp b/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp index 3de1cbf0..3de224e9 100644 --- a/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp +++ b/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp @@ -51,39 +51,6 @@ void RenaultKangooBattery:: datalayer.battery.status.cell_min_voltage_mV = LB_Cell_Min_Voltage; datalayer.battery.status.cell_max_voltage_mV = LB_Cell_Max_Voltage; - -#ifdef DEBUG_LOG - logging.println("Values going to inverter:"); - logging.print("SOH%: "); - logging.print(datalayer.battery.status.soh_pptt); - logging.print(", SOC% scaled: "); - logging.print(datalayer.battery.status.reported_soc); - logging.print(", Voltage: "); - logging.print(datalayer.battery.status.voltage_dV); - logging.print(", Max discharge power: "); - logging.print(datalayer.battery.status.max_discharge_power_W); - logging.print(", Max charge power: "); - logging.print(datalayer.battery.status.max_charge_power_W); - logging.print(", Max temp: "); - logging.print(datalayer.battery.status.temperature_max_dC); - logging.print(", Min temp: "); - logging.print(datalayer.battery.status.temperature_min_dC); - logging.print(", BMS Status (3=OK): "); - logging.print(datalayer.battery.status.bms_status); - - logging.println("Battery values: "); - logging.print("Real SOC: "); - logging.print(LB_SOC); - logging.print(", Current: "); - logging.print(LB_Current); - logging.print(", kWh remain: "); - logging.print(LB_kWh_Remaining); - logging.print(", max mV: "); - logging.print(LB_Cell_Max_Voltage); - logging.print(", min mV: "); - logging.print(LB_Cell_Min_Voltage); - -#endif } void RenaultKangooBattery::handle_incoming_can_frame(CAN_frame rx_frame) { diff --git a/Software/src/battery/TESLA-BATTERY.cpp b/Software/src/battery/TESLA-BATTERY.cpp index 1211be41..f5a3db24 100644 --- a/Software/src/battery/TESLA-BATTERY.cpp +++ b/Software/src/battery/TESLA-BATTERY.cpp @@ -720,13 +720,9 @@ void TeslaBattery:: //Start the BMS ECU reset statemachine, only if contactors are OPEN and BMS ECU allows it stateMachineBMSReset = 0; datalayer.battery.settings.user_requests_tesla_bms_reset = false; -#ifdef DEBUG_LOG logging.println("BMS reset requested"); -#endif //DEBUG_LOG } else { -#ifdef DEBUG_LOG logging.println("ERROR: BMS reset failed due to contactors not being open, or BMS ECU not allowing it"); -#endif //DEBUG_LOG stateMachineBMSReset = 0xFF; datalayer.battery.settings.user_requests_tesla_bms_reset = false; } @@ -736,13 +732,9 @@ void TeslaBattery:: //Start the SOC reset statemachine, only if SOC < 15% or > 90% stateMachineSOCReset = 0; datalayer.battery.settings.user_requests_tesla_soc_reset = false; -#ifdef DEBUG_LOG logging.println("SOC reset requested"); -#endif //DEBUG_LOG } else { -#ifdef DEBUG_LOG logging.println("ERROR: SOC reset failed due to SOC not being less than 15 or greater than 90"); -#endif //DEBUG_LOG stateMachineSOCReset = 0xFF; datalayer.battery.settings.user_requests_tesla_soc_reset = false; } @@ -1002,8 +994,6 @@ void TeslaBattery:: } } -#ifdef DEBUG_LOG - printFaultCodesIfActive(); logging.print("BMS Contactors State: "); logging.print(getBMSContactorState(battery_contactor)); // Display what state the BMS thinks the contactors are in @@ -1061,7 +1051,6 @@ void TeslaBattery:: logging.printf("PCS_ambientTemp: %.2f°C, DCDC_Temp: %.2f°C, ChgPhA: %.2f°C, ChgPhB: %.2f°C, ChgPhC: %.2f°C.\n", PCS_ambientTemp * 0.1 + 40, PCS_dcdcTemp * 0.1 + 40, PCS_chgPhATemp * 0.1 + 40, PCS_chgPhBTemp * 0.1 + 40, PCS_chgPhCTemp * 0.1 + 40); -#endif //DEBUG_LOG } void TeslaBattery::handle_incoming_can_frame(CAN_frame rx_frame) { @@ -1911,9 +1900,7 @@ void TeslaBattery::handle_incoming_can_frame(CAN_frame rx_frame) { if (stateMachineBMSQuery != 0xFF && stateMachineBMSReset == 0xFF) { if (memcmp(rx_frame.data.u8, "\x02\x50\x03\xAA\xAA\xAA\xAA\xAA", 8) == 0) { //Received initial response, proceed to actual query -#ifdef DEBUG_LOG logging.println("CAN UDS: Received BMS query initial handshake reply"); -#endif //DEBUG_LOG stateMachineBMSQuery = 1; break; } @@ -1922,9 +1909,7 @@ void TeslaBattery::handle_incoming_can_frame(CAN_frame rx_frame) { battery_partNumber[0] = rx_frame.data.u8[5]; battery_partNumber[1] = rx_frame.data.u8[6]; battery_partNumber[2] = rx_frame.data.u8[7]; -#ifdef DEBUG_LOG logging.println("CAN UDS: Received BMS query data frame"); -#endif //DEBUG_LOG stateMachineBMSQuery = 2; break; } @@ -1937,32 +1922,25 @@ void TeslaBattery::handle_incoming_can_frame(CAN_frame rx_frame) { battery_partNumber[7] = rx_frame.data.u8[5]; battery_partNumber[8] = rx_frame.data.u8[6]; battery_partNumber[9] = rx_frame.data.u8[7]; -#ifdef DEBUG_LOG logging.println("CAN UDS: Received BMS query second data frame"); -#endif //DEBUG_LOG break; } if (memcmp(&rx_frame.data.u8[0], "\x22", 1) == 0) { //Final part of part number battery_partNumber[10] = rx_frame.data.u8[1]; battery_partNumber[11] = rx_frame.data.u8[2]; -#ifdef DEBUG_LOG logging.println("CAN UDS: Received BMS query final data frame"); -#endif //DEBUG_LOG break; } if (memcmp(rx_frame.data.u8, "\x23\x00\x00\x00\xAA\xAA\xAA\xAA", 8) == 0) { //Received final frame -#ifdef DEBUG_LOG logging.println("CAN UDS: Received BMS query termination frame"); -#endif //DEBUG_LOG parsed_battery_partNumber = true; stateMachineBMSQuery = 0xFF; break; } } //BMS Reset -#ifdef DEBUG_LOG if (stateMachineBMSQuery == 0xFF) { // Make sure this is reset request not query if (memcmp(rx_frame.data.u8, "\x02\x67\x06\xAA\xAA\xAA\xAA\xAA", 8) == 0) { logging.println("CAN UDS: ECU unlocked"); @@ -1972,7 +1950,6 @@ void TeslaBattery::handle_incoming_can_frame(CAN_frame rx_frame) { logging.println("CAN UDS: ECU reset positive response, 1 second downtime"); } } -#endif //DEBUG_LOG break; default: break; @@ -2375,25 +2352,19 @@ void TeslaBattery::transmit_can(unsigned long currentMillis) { switch (stateMachineBMSQuery) { case 0: //Initial request -#ifdef DEBUG_LOG logging.println("CAN UDS: Sending BMS query initial handshake"); -#endif //DEBUG_LOG TESLA_602.data = {0x02, 0x10, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}; transmit_can_frame(&TESLA_602); break; case 1: //Send query -#ifdef DEBUG_LOG logging.println("CAN UDS: Sending BMS query for pack part number"); -#endif //DEBUG_LOG TESLA_602.data = {0x03, 0x22, 0xF0, 0x14, 0x00, 0x00, 0x00, 0x00}; transmit_can_frame(&TESLA_602); break; case 2: //Flow control -#ifdef DEBUG_LOG logging.println("CAN UDS: Sending BMS query flow control"); -#endif //DEBUG_LOG TESLA_602.data = {0x30, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00}; transmit_can_frame(&TESLA_602); break; diff --git a/Software/src/battery/TEST-FAKE-BATTERY.cpp b/Software/src/battery/TEST-FAKE-BATTERY.cpp index eb3acd54..b7b18bc8 100644 --- a/Software/src/battery/TEST-FAKE-BATTERY.cpp +++ b/Software/src/battery/TEST-FAKE-BATTERY.cpp @@ -42,21 +42,6 @@ void TestFakeBattery:: //Fake that we get CAN messages datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE; - -/*Finally print out values to serial if configured to do so*/ -#ifdef DEBUG_LOG - logging.println("FAKE Values going to inverter"); - print_units("SOH%: ", (datalayer_battery->status.soh_pptt * 0.01), "% "); - print_units(", SOC%: ", (datalayer_battery->status.reported_soc * 0.01), "% "); - print_units(", Voltage: ", (datalayer_battery->status.voltage_dV * 0.1), "V "); - print_units(", Max discharge power: ", datalayer_battery->status.max_discharge_power_W, "W "); - print_units(", Max charge power: ", datalayer_battery->status.max_charge_power_W, "W "); - print_units(", Max temp: ", (datalayer_battery->status.temperature_max_dC * 0.1), "°C "); - print_units(", Min temp: ", (datalayer_battery->status.temperature_min_dC * 0.1), "°C "); - print_units(", Max cell voltage: ", datalayer_battery->status.cell_max_voltage_mV, "mV "); - print_units(", Min cell voltage: ", datalayer_battery->status.cell_min_voltage_mV, "mV "); - logging.println(""); -#endif } void TestFakeBattery::handle_incoming_can_frame(CAN_frame rx_frame) { diff --git a/Software/src/battery/VOLVO-SPA-BATTERY.cpp b/Software/src/battery/VOLVO-SPA-BATTERY.cpp index 8b28d0df..e251f9c7 100644 --- a/Software/src/battery/VOLVO-SPA-BATTERY.cpp +++ b/Software/src/battery/VOLVO-SPA-BATTERY.cpp @@ -85,51 +85,6 @@ void VolvoSpaBattery:: datalayer.battery.info.total_capacity_Wh = 69511; } } - -#ifdef DEBUG_LOG - uint8_t cnt = 0; - logging.print("BMS reported SOC%: "); - logging.println(SOC_BMS); - logging.print("Calculated SOC%: "); - logging.println(SOC_CALC); - logging.print("Rescaled SOC%: "); - logging.println(datalayer.battery.status.reported_soc / 100); - logging.print("Battery current: "); - logging.println(BATT_I); - logging.print("Battery voltage: "); - logging.println(BATT_U); - logging.print("Battery maximum voltage limit: "); - logging.println(MAX_U); - logging.print("Battery minimum voltage limit: "); - logging.println(MIN_U); - logging.print("Discharge limit: "); - logging.println(HvBattPwrLimDchaSoft); - logging.print("Battery Error Indication: "); - logging.println(BATT_ERR_INDICATION); - logging.print("Maximum battery temperature: "); - logging.println(BATT_T_MAX / 10); - logging.print("Minimum battery temperature: "); - logging.println(BATT_T_MIN / 10); - logging.print("Average battery temperature: "); - logging.println(BATT_T_AVG / 10); - logging.print("BMS Highest cell voltage: "); - logging.println(CELL_U_MAX * 10); - logging.print("BMS Lowest cell voltage: "); - logging.println(CELL_U_MIN * 10); - logging.print("BMS Highest cell nr: "); - logging.println(CELL_ID_U_MAX); - logging.print("Highest cell voltage: "); - logging.println(min_max_voltage[1]); - logging.print("Lowest cell voltage: "); - logging.println(min_max_voltage[0]); - logging.print("Cell voltage,"); - while (cnt < 108) { - logging.print(cell_voltages[cnt++]); - logging.print(","); - } - cnt = 0; - logging.println(";"); -#endif } void VolvoSpaBattery::handle_incoming_can_frame(CAN_frame rx_frame) { @@ -140,9 +95,7 @@ void VolvoSpaBattery::handle_incoming_can_frame(CAN_frame rx_frame) { BATT_I = (0 - ((((rx_frame.data.u8[6] & 0x7F) * 256.0 + rx_frame.data.u8[7]) * 0.1) - 1638)); else { BATT_I = 0; -#ifdef DEBUG_LOG logging.println("BATT_I not valid"); -#endif } if ((rx_frame.data.u8[2] & 0x08) == 0x08) @@ -195,9 +148,7 @@ void VolvoSpaBattery::handle_incoming_can_frame(CAN_frame rx_frame) { BATT_ERR_INDICATION = ((rx_frame.data.u8[0] & 0x40) >> 6); else { BATT_ERR_INDICATION = 0; -#ifdef DEBUG_LOG logging.println("BATT_ERR_INDICATION not valid"); -#endif } if ((rx_frame.data.u8[0] & 0x20) == 0x20) { BATT_T_MAX = ((rx_frame.data.u8[2] & 0x1F) * 256.0 + rx_frame.data.u8[3]); @@ -207,9 +158,7 @@ void VolvoSpaBattery::handle_incoming_can_frame(CAN_frame rx_frame) { BATT_T_MAX = 0; BATT_T_MIN = 0; BATT_T_AVG = 0; -#ifdef DEBUG_LOG logging.println("BATT_T not valid"); -#endif } break; case 0x369: @@ -217,9 +166,7 @@ void VolvoSpaBattery::handle_incoming_can_frame(CAN_frame rx_frame) { HvBattPwrLimDchaSoft = (((rx_frame.data.u8[6] & 0x03) * 256 + rx_frame.data.u8[6]) >> 2); } else { HvBattPwrLimDchaSoft = 0; -#ifdef DEBUG_LOG logging.println("HvBattPwrLimDchaSoft not valid"); -#endif } break; case 0x175: @@ -246,9 +193,7 @@ void VolvoSpaBattery::handle_incoming_can_frame(CAN_frame rx_frame) { SOC_BMS = ((rx_frame.data.u8[6] & 0x03) * 256 + rx_frame.data.u8[7]); } else { SOC_BMS = 0; -#ifdef DEBUG_LOG logging.println("SOC_BMS not valid"); -#endif } if ((rx_frame.data.u8[0] & 0x04) == 0x04) { diff --git a/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.cpp b/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.cpp index 9d54b8fd..7afc2ada 100644 --- a/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.cpp +++ b/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.cpp @@ -70,51 +70,6 @@ void VolvoSpaHybridBattery:: for (int i = 0; i < 102; ++i) { datalayer.battery.status.cell_voltages_mV[i] = cell_voltages[i]; } - -#ifdef DEBUG_LOG - logging.print("BMS reported SOC%: "); - logging.println(SOC_BMS); - logging.print("Calculated SOC%: "); - logging.println(SOC_CALC); - logging.print("Rescaled SOC%: "); - logging.println(datalayer.battery.status.reported_soc / 100); - logging.print("Battery current: "); - logging.println(BATT_I); - logging.print("Battery voltage: "); - logging.println(BATT_U); - logging.print("Battery maximum voltage limit: "); - logging.println(MAX_U); - logging.print("Battery minimum voltage limit: "); - logging.println(MIN_U); - logging.print("Remaining Energy: "); - logging.println(remaining_capacity); - logging.print("Discharge limit: "); - logging.println(HvBattPwrLimDchaSoft); - logging.print("Battery Error Indication: "); - logging.println(BATT_ERR_INDICATION); - logging.print("Maximum battery temperature: "); - logging.println(BATT_T_MAX / 10); - logging.print("Minimum battery temperature: "); - logging.println(BATT_T_MIN / 10); - logging.print("Average battery temperature: "); - logging.println(BATT_T_AVG / 10); - logging.print("BMS Highest cell voltage: "); - logging.println(CELL_U_MAX); - logging.print("BMS Lowest cell voltage: "); - logging.println(CELL_U_MIN); - logging.print("BMS Highest cell nr: "); - logging.println(CELL_ID_U_MAX); - logging.print("Highest cell voltage: "); - logging.println(min_max_voltage[1]); - logging.print("Lowest cell voltage: "); - logging.println(min_max_voltage[0]); - logging.print("Cell voltage,"); - while (cnt < 102) { - logging.print(cell_voltages[cnt++]); - logging.print(","); - } - logging.println(";"); -#endif } void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) { @@ -125,9 +80,7 @@ void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) { BATT_I = (0 - ((((rx_frame.data.u8[6] & 0x7F) * 256.0 + rx_frame.data.u8[7]) * 0.1) - 1638)); else { BATT_I = 0; -#ifdef DEBUG_LOG logging.println("BATT_I not valid"); -#endif } if ((rx_frame.data.u8[2] & 0x08) == 0x08) @@ -148,9 +101,7 @@ void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) { BATT_U = (((rx_frame.data.u8[0] & 0x07) * 256.0 + rx_frame.data.u8[1]) * 0.25); else { BATT_U = 0; -#ifdef DEBUG_LOG logging.println("BATT_U not valid"); -#endif } if ((rx_frame.data.u8[0] & 0x40) == 0x40) @@ -185,9 +136,7 @@ void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) { BATT_ERR_INDICATION = ((rx_frame.data.u8[0] & 0x40) >> 6); else { BATT_ERR_INDICATION = 0; -#ifdef DEBUG_LOG logging.println("BATT_ERR_INDICATION not valid"); -#endif } if ((rx_frame.data.u8[0] & 0x20) == 0x20) { BATT_T_MAX = ((rx_frame.data.u8[2] & 0x1F) * 256.0 + rx_frame.data.u8[3]); @@ -197,9 +146,7 @@ void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) { BATT_T_MAX = 0; BATT_T_MIN = 0; BATT_T_AVG = 0; -#ifdef DEBUG_LOG logging.println("BATT_T not valid"); -#endif } break; case 0x369: @@ -207,9 +154,7 @@ void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) { HvBattPwrLimDchaSoft = (((rx_frame.data.u8[6] & 0x03) * 256 + rx_frame.data.u8[6]) >> 2); } else { HvBattPwrLimDchaSoft = 0; -#ifdef DEBUG_LOG logging.println("HvBattPwrLimDchaSoft not valid"); -#endif } break; case 0x175: @@ -240,9 +185,7 @@ void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) { SOC_BMS = ((rx_frame.data.u8[6] & 0x03) * 256 + rx_frame.data.u8[7]); } else { SOC_BMS = 0; -#ifdef DEBUG_LOG logging.println("SOC_BMS not valid"); -#endif } if ((rx_frame.data.u8[0] & 0x04) == 0x04) @@ -251,9 +194,7 @@ void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) { else { //CELL_U_MAX = 0; ; -#ifdef DEBUG_LOG logging.println("CELL_U_MAX not valid"); -#endif } if ((rx_frame.data.u8[0] & 0x02) == 0x02) @@ -262,9 +203,7 @@ void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) { else { //CELL_U_MIN = 0; ; -#ifdef DEBUG_LOG logging.println("CELL_U_MIN not valid"); -#endif } if ((rx_frame.data.u8[0] & 0x08) == 0x08) @@ -273,9 +212,7 @@ void VolvoSpaHybridBattery::handle_incoming_can_frame(CAN_frame rx_frame) { else { //CELL_ID_U_MAX = 0; ; -#ifdef DEBUG_LOG logging.println("CELL_ID_U_MAX not valid"); -#endif } break; case 0x635: // Diag request response @@ -545,9 +482,7 @@ void VolvoSpaHybridBattery::transmit_can(unsigned long currentMillis) { previousMillis60s = currentMillis; if (true) { readCellVoltages(); -#ifdef DEBUG_LOG logging.println("Requesting cell voltages"); -#endif } } } diff --git a/Software/src/charger/CHEVY-VOLT-CHARGER.cpp b/Software/src/charger/CHEVY-VOLT-CHARGER.cpp index 62dad719..f981e869 100644 --- a/Software/src/charger/CHEVY-VOLT-CHARGER.cpp +++ b/Software/src/charger/CHEVY-VOLT-CHARGER.cpp @@ -71,9 +71,6 @@ void ChevyVoltCharger::map_can_frame_to_variable(CAN_frame rx_frame) { datalayer.charger.CAN_charger_still_alive = CAN_STILL_ALIVE; // Let system know charger is sending CAN break; default: -#ifdef DEBUG_LOG - logging.printf("CAN Rcv unknown frame MsgID=%x\n", rx_frame.ID); -#endif break; } } @@ -146,7 +143,6 @@ void ChevyVoltCharger::transmit_can(unsigned long currentMillis) { transmit_can_frame(&charger_set_targets); } -#ifdef DEBUG_LOG /* Serial echo every 5s of charger stats */ if (currentMillis - previousMillis5000ms >= INTERVAL_5_S) { previousMillis5000ms = currentMillis; @@ -156,5 +152,4 @@ void ChevyVoltCharger::transmit_can(unsigned long currentMillis) { logging.printf("Charger mode=%s\n", (charger_mode > MODE_DISABLED) ? "Enabled" : "Disabled"); logging.printf("Charger HVset=%uV,%uA finishCurrent=%uA\n", setpoint_HV_VDC, setpoint_HV_IDC, setpoint_HV_IDC_END); } -#endif } diff --git a/Software/src/communication/can/comm_can.cpp b/Software/src/communication/can/comm_can.cpp index 4efec199..5ba5b868 100644 --- a/Software/src/communication/can/comm_can.cpp +++ b/Software/src/communication/can/comm_can.cpp @@ -99,9 +99,7 @@ bool init_CAN() { return false; } -#ifdef DEBUG_LOG logging.println("Dual CAN Bus (ESP32+MCP2515) selected"); -#endif // DEBUG_LOG gBuffer.initWithSize(25); can2515 = new ACAN2515(cs_pin, SPI2515, int_pin); @@ -115,14 +113,10 @@ bool init_CAN() { settings2515->mRequestedMode = ACAN2515Settings::NormalMode; const uint16_t errorCode2515 = can2515->begin(*settings2515, [] { can2515->isr(); }); if (errorCode2515 == 0) { -#ifdef DEBUG_LOG logging.println("Can ok"); -#endif // DEBUG_LOG } else { -#ifdef DEBUG_LOG logging.print("Error Can: 0x"); logging.println(errorCode2515, HEX); -#endif // DEBUG_LOG set_event(EVENT_CANMCP2515_INIT_FAILURE, (uint8_t)errorCode2515); return false; } @@ -147,9 +141,7 @@ bool init_CAN() { canfd = new ACAN2517FD(cs_pin, SPI2517, int_pin); -#ifdef DEBUG_LOG logging.println("CAN FD add-on (ESP32+MCP2517) selected"); -#endif // DEBUG_LOG SPI2517.begin(sck_pin, sdo_pin, sdi_pin); auto bitRate = (int)speed * 1000UL; settings2517 = new ACAN2517FDSettings( @@ -162,7 +154,6 @@ bool init_CAN() { const uint32_t errorCode2517 = canfd->begin(*settings2517, [] { canfd->isr(); }); canfd->poll(); if (errorCode2517 == 0) { -#ifdef DEBUG_LOG logging.print("Bit Rate prescaler: "); logging.println(settings2517->mBitRatePrescaler); logging.print("Arbitration Phase segment 1: "); @@ -179,12 +170,9 @@ bool init_CAN() { logging.print("Arbitration Sample point: "); logging.print(settings2517->arbitrationSamplePointFromBitStart()); logging.println("%"); -#endif // DEBUG_LOG } else { -#ifdef DEBUG_LOG logging.print("CAN-FD Configuration error 0x"); logging.println(errorCode2517, HEX); -#endif // DEBUG_LOG set_event(EVENT_CANMCP2517FD_INIT_FAILURE, (uint8_t)errorCode2517); return false; } diff --git a/Software/src/communication/can/obd.cpp b/Software/src/communication/can/obd.cpp index c01d9b24..ec2bc49b 100644 --- a/Software/src/communication/can/obd.cpp +++ b/Software/src/communication/can/obd.cpp @@ -24,7 +24,6 @@ void show_dtc(uint8_t byte0, uint8_t byte1) { } void handle_obd_frame(CAN_frame& rx_frame) { -#ifdef DEBUG_LOG if (rx_frame.data.u8[1] == 0x7F) { const char* error_str = "?"; switch (rx_frame.data.u8[3]) { // See https://automotive.wiki/index.php/ISO_14229 @@ -107,7 +106,6 @@ void handle_obd_frame(CAN_frame& rx_frame) { } } dump_can_frame(rx_frame, MSG_RX); -#endif } void transmit_obd_can_frame(unsigned int address, int interface, bool canFD) { diff --git a/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp b/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp index 6ea7798a..8983807e 100644 --- a/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp +++ b/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp @@ -147,12 +147,10 @@ bool init_contactors() { } static void dbg_contactors(const char* state) { -#ifdef DEBUG_LOG logging.print("["); logging.print(millis()); logging.print(" ms] contactors control: "); logging.println(state); -#endif } // Main functions of the handle_contactors include checking if inverter allows for closing, checking battery 2, checking BMS power output, and actual contactor closing/precharge via GPIO diff --git a/Software/src/communication/precharge_control/precharge_control.cpp b/Software/src/communication/precharge_control/precharge_control.cpp index efdf3ff9..743bf0c5 100644 --- a/Software/src/communication/precharge_control/precharge_control.cpp +++ b/Software/src/communication/precharge_control/precharge_control.cpp @@ -40,9 +40,7 @@ bool init_precharge_control() { } // Setup PWM Channel Frequency and Resolution -#ifdef DEBUG_LOG logging.printf("Precharge control initialised\n"); -#endif auto hia4v1_pin = esp32hal->HIA4V1_PIN(); auto inverter_disconnect_contactor_pin = esp32hal->INVERTER_DISCONNECT_CONTACTOR_PIN(); @@ -79,9 +77,7 @@ void handle_precharge_control(unsigned long currentMillis) { ledcWriteTone(hia4v1_pin, freq); // Set frequency and set dutycycle to 50% prechargeStartTime = currentMillis; datalayer.system.status.precharge_status = AUTO_PRECHARGE_PRECHARGING; -#ifdef DEBUG_LOG logging.printf("Precharge: Starting sequence\n"); -#endif digitalWrite(inverter_disconnect_contactor_pin, OFF); break; @@ -106,10 +102,8 @@ void handle_precharge_control(unsigned long currentMillis) { freq = Precharge_max_PWM_Freq; if (freq < Precharge_min_PWM_Freq) freq = Precharge_min_PWM_Freq; -#ifdef DEBUG_LOG logging.printf("Precharge: Target: %d V Extern: %d V Frequency: %u\n", target_voltage / 10, external_voltage / 10, freq); -#endif ledcWriteTone(hia4v1_pin, freq); } @@ -120,18 +114,14 @@ void handle_precharge_control(unsigned long currentMillis) { digitalWrite(hia4v1_pin, LOW); digitalWrite(inverter_disconnect_contactor_pin, ON); datalayer.system.status.precharge_status = AUTO_PRECHARGE_IDLE; -#ifdef DEBUG_LOG logging.printf("Precharge: Disabling Precharge bms not standby/active or equipment stop\n"); -#endif } else if (currentMillis - prechargeStartTime >= MAX_PRECHARGE_TIME_MS || datalayer.battery.status.real_bms_status == BMS_FAULT) { pinMode(hia4v1_pin, OUTPUT); digitalWrite(hia4v1_pin, LOW); digitalWrite(inverter_disconnect_contactor_pin, ON); datalayer.system.status.precharge_status = AUTO_PRECHARGE_OFF; -#ifdef DEBUG_LOG logging.printf("Precharge: Disabled (timeout reached / BMS fault) -> AUTO_PRECHARGE_OFF\n"); -#endif set_event(EVENT_AUTOMATIC_PRECHARGE_FAILURE, 0); // Add event @@ -140,18 +130,14 @@ void handle_precharge_control(unsigned long currentMillis) { digitalWrite(hia4v1_pin, LOW); digitalWrite(inverter_disconnect_contactor_pin, ON); datalayer.system.status.precharge_status = AUTO_PRECHARGE_COMPLETED; -#ifdef DEBUG_LOG logging.printf("Precharge: Disabled (contacts closed) -> COMPLETED\n"); -#endif } break; case AUTO_PRECHARGE_COMPLETED: if (datalayer.system.settings.equipment_stop_active || datalayer.battery.status.bms_status != ACTIVE) { datalayer.system.status.precharge_status = AUTO_PRECHARGE_IDLE; -#ifdef DEBUG_LOG logging.printf("Precharge: equipment stop activated -> IDLE\n"); -#endif } break; @@ -162,9 +148,7 @@ void handle_precharge_control(unsigned long currentMillis) { datalayer.system.status.precharge_status = AUTO_PRECHARGE_IDLE; pinMode(hia4v1_pin, OUTPUT); digitalWrite(hia4v1_pin, LOW); -#ifdef DEBUG_LOG logging.printf("Precharge: equipment stop activated -> IDLE\n"); -#endif } break; diff --git a/Software/src/devboard/mqtt/mqtt.cpp b/Software/src/devboard/mqtt/mqtt.cpp index 067c2b6c..f23d3be4 100644 --- a/Software/src/devboard/mqtt/mqtt.cpp +++ b/Software/src/devboard/mqtt/mqtt.cpp @@ -319,9 +319,7 @@ static bool publish_common_info(void) { serializeJson(doc, mqtt_msg); if (mqtt_publish(state_topic.c_str(), mqtt_msg, false) == false) { -#ifdef DEBUG_LOG logging.println("Common info MQTT msg could not be sent"); -#endif // DEBUG_LOG return false; } doc.clear(); @@ -392,9 +390,7 @@ static bool publish_cell_voltages(void) { serializeJson(doc, mqtt_msg, sizeof(mqtt_msg)); if (!mqtt_publish(state_topic.c_str(), mqtt_msg, false)) { -#ifdef DEBUG_LOG logging.println("Cell voltage MQTT msg could not be sent"); -#endif // DEBUG_LOG return false; } doc.clear(); @@ -413,9 +409,7 @@ static bool publish_cell_voltages(void) { serializeJson(doc, mqtt_msg, sizeof(mqtt_msg)); if (!mqtt_publish(state_topic_2.c_str(), mqtt_msg, false)) { -#ifdef DEBUG_LOG logging.println("Cell voltage MQTT msg could not be sent"); -#endif // DEBUG_LOG return false; } doc.clear(); @@ -440,9 +434,7 @@ static bool publish_cell_balancing(void) { serializeJson(doc, mqtt_msg, sizeof(mqtt_msg)); if (!mqtt_publish(state_topic.c_str(), mqtt_msg, false)) { -#ifdef DEBUG_LOG logging.println("Cell balancing MQTT msg could not be sent"); -#endif // DEBUG_LOG return false; } doc.clear(); @@ -460,9 +452,7 @@ static bool publish_cell_balancing(void) { serializeJson(doc, mqtt_msg, sizeof(mqtt_msg)); if (!mqtt_publish(state_topic_2.c_str(), mqtt_msg, false)) { -#ifdef DEBUG_LOG logging.println("Cell balancing MQTT msg could not be sent"); -#endif // DEBUG_LOG return false; } doc.clear(); @@ -523,9 +513,7 @@ bool publish_events() { serializeJson(doc, mqtt_msg); if (!mqtt_publish(state_topic.c_str(), mqtt_msg, false)) { -#ifdef DEBUG_LOG logging.println("Common info MQTT msg could not be sent"); -#endif // DEBUG_LOG return false; } else { set_event_MQTTpublished(event_handle); @@ -541,9 +529,7 @@ bool publish_events() { static bool publish_buttons_discovery(void) { if (ha_autodiscovery_enabled) { if (ha_buttons_published == false) { -#ifdef DEBUG_LOG logging.println("Publishing buttons discovery"); -#endif // DEBUG_LOG static JsonDocument doc; for (int i = 0; i < sizeof(buttonConfigs) / sizeof(buttonConfigs[0]); i++) { @@ -573,16 +559,12 @@ void mqtt_message_received(char* topic_raw, int topic_len, char* data, int data_ char* topic = strndup(topic_raw, topic_len); -#ifdef DEBUG_LOG logging.printf("MQTT message arrived: [%.*s]\n", topic_len, topic); -#endif // DEBUG_LOG #ifdef REMOTE_BMS_RESET const char* bmsreset_topic = generateButtonTopic("BMSRESET").c_str(); if (strcmp(topic, bmsreset_topic) == 0) { -#ifdef DEBUG_LOG logging.println("Triggering BMS reset"); -#endif // DEBUG_LOG start_bms_reset(); } #endif // REMOTE_BMS_RESET @@ -616,21 +598,16 @@ static void mqtt_event_handler(void* handler_args, esp_event_base_t base, int32_ publish_buttons_discovery(); subscribe(); -#ifdef DEBUG_LOG logging.println("MQTT connected"); -#endif // DEBUG_LOG break; case MQTT_EVENT_DISCONNECTED: set_event(EVENT_MQTT_DISCONNECT, 0); -#ifdef DEBUG_LOG logging.println("MQTT disconnected!"); -#endif // DEBUG_LOG break; case MQTT_EVENT_DATA: mqtt_message_received(event->topic, event->topic_len, event->data, event->data_len); break; case MQTT_EVENT_ERROR: -#ifdef DEBUG_LOG logging.println("MQTT_ERROR"); logging.print("reported from esp-tls"); logging.println(event->error_handle->esp_tls_last_esp_err); @@ -638,7 +615,6 @@ static void mqtt_event_handler(void* handler_args, esp_event_base_t base, int32_ logging.println(event->error_handle->esp_tls_stack_err); logging.print("captured as transport's socket errno"); logging.println(strerror(event->error_handle->esp_transport_sock_errno)); -#endif // DEBUG_LOG break; } } @@ -723,9 +699,7 @@ void mqtt_loop(void) { if (client_started == false) { esp_mqtt_client_start(client); client_started = true; -#ifdef DEBUG_LOG logging.println("MQTT initialized"); -#endif // DEBUG_LOG return; } diff --git a/Software/src/devboard/sdcard/sdcard.cpp b/Software/src/devboard/sdcard/sdcard.cpp index 078b6301..0948c1b4 100644 --- a/Software/src/devboard/sdcard/sdcard.cpp +++ b/Software/src/devboard/sdcard/sdcard.cpp @@ -164,9 +164,7 @@ void init_logging_buffers() { #if defined(LOG_CAN_TO_SD) can_bufferHandle = xRingbufferCreate(32 * 1024, RINGBUF_TYPE_BYTEBUF); if (can_bufferHandle == NULL) { -#ifdef DEBUG_LOG logging.println("Failed to create CAN ring buffer!"); -#endif // DEBUG_LOG return; } #endif // defined(LOG_CAN_TO_SD) @@ -174,9 +172,7 @@ void init_logging_buffers() { #if defined(LOG_TO_SD) log_bufferHandle = xRingbufferCreate(1024, RINGBUF_TYPE_BYTEBUF); if (log_bufferHandle == NULL) { -#ifdef DEBUG_LOG logging.println("Failed to create log ring buffer!"); -#endif // DEBUG_LOG return; } #endif // defined(LOG_TO_SD) @@ -196,22 +192,16 @@ bool init_sdcard() { SD_MMC.setPins(sclk_pin, mosi_pin, miso_pin); if (!SD_MMC.begin("/root", true, true, SDMMC_FREQ_HIGHSPEED)) { set_event_latched(EVENT_SD_INIT_FAILED, 0); -#ifdef DEBUG_LOG logging.println("SD Card initialization failed!"); -#endif // DEBUG_LOG return false; } clear_event(EVENT_SD_INIT_FAILED); -#ifdef DEBUG_LOG logging.println("SD Card initialization successful."); -#endif // DEBUG_LOG sd_card_active = true; -#ifdef DEBUG_LOG log_sdcard_details(); -#endif // DEBUG_LOG return true; } diff --git a/Software/src/devboard/utils/events.cpp b/Software/src/devboard/utils/events.cpp index 2ce9c1bf..33dfbc87 100644 --- a/Software/src/devboard/utils/events.cpp +++ b/Software/src/devboard/utils/events.cpp @@ -159,9 +159,7 @@ void reset_all_events() { } events.level = EVENT_LEVEL_INFO; update_bms_status(); -#ifdef DEBUG_LOG logging.println("All events have been cleared."); -#endif } void set_event_MQTTpublished(EVENTS_ENUM_TYPE event) { diff --git a/Software/src/devboard/webserver/events_html.cpp b/Software/src/devboard/webserver/events_html.cpp index 737e8735..53fd4d14 100644 --- a/Software/src/devboard/webserver/events_html.cpp +++ b/Software/src/devboard/webserver/events_html.cpp @@ -46,12 +46,6 @@ String events_processor(const String& var) { EVENTS_ENUM_TYPE event_handle = event.event_handle; event_pointer = event.event_pointer; -#ifdef DEBUG_LOG - logging.println("Showing Event: " + String(get_event_enum_string(event_handle)) + - " count: " + String(event_pointer->occurences) + " seconds: " + String(event_pointer->timestamp) + - " data: " + String(event_pointer->data) + - " level: " + String(get_event_level_string(event_handle))); -#endif content.concat("
"); content.concat("
" + String(get_event_enum_string(event_handle)) + "
"); content.concat("
" + String(get_event_level_string(event_handle)) + "
"); diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index c40373f3..90dd64a0 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -1470,9 +1470,7 @@ void onOTAProgress(size_t current, size_t final) { // Log every 1 second if (millis() - ota_progress_millis > 1000) { ota_progress_millis = millis(); -#ifdef DEBUG_LOG logging.printf("OTA Progress Current: %u bytes, Final: %u bytes\n", current, final); -#endif // DEBUG_LOG // Reset the "watchdog" ota_timeout_timer.reset(); } @@ -1489,13 +1487,9 @@ void onOTAEnd(bool success) { // Max Charge/Discharge = 0; CAN = stop; contactors = open setBatteryPause(true, true, true, false); // a reboot will be done by the OTA library. no need to do anything here -#ifdef DEBUG_LOG logging.println("OTA update finished successfully!"); -#endif // DEBUG_LOG } else { -#ifdef DEBUG_LOG logging.println("There was an error during OTA update!"); -#endif // DEBUG_LOG //try to Resume the battery pause and CAN communication setBatteryPause(false, false); } diff --git a/Software/src/devboard/wifi/wifi.cpp b/Software/src/devboard/wifi/wifi.cpp index 58a98814..53425b0d 100644 --- a/Software/src/devboard/wifi/wifi.cpp +++ b/Software/src/devboard/wifi/wifi.cpp @@ -130,33 +130,22 @@ void wifi_monitor() { // Try WiFi.reconnect() if it was successfully connected at least once if (hasConnectedBefore) { lastReconnectAttempt = currentMillis; // Reset reconnection attempt timer -#ifdef DEBUG_LOG logging.println("Wi-Fi reconnect attempt..."); -#endif if (WiFi.reconnect()) { -#ifdef DEBUG_LOG logging.println("Wi-Fi reconnect attempt sucess..."); -#endif reconnectAttempts = 0; // Reset the attempt counter on successful reconnect } else { -#ifdef DEBUG_LOG logging.println("Wi-Fi reconnect attempt error..."); -#endif reconnectAttempts++; if (reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) { -#ifdef DEBUG_LOG logging.println("Failed to reconnect multiple times, forcing a full connection attempt..."); -#endif FullReconnectToWiFi(); } } } else { // If no previous connection, force a full connection attempt if (currentMillis - lastReconnectAttempt > current_full_reconnect_interval) { -#ifdef DEBUG_LOG logging.println("No previous OK connection, force a full connection attempt..."); -#endif - wifiap_enabled = true; WiFi.mode(WIFI_AP_STA); init_WiFi_AP(); @@ -188,17 +177,13 @@ void connectToWiFi() { if (WiFi.status() != WL_CONNECTED) { lastReconnectAttempt = millis(); // Reset the reconnect attempt timer -#ifdef DEBUG_LOG logging.println("Connecting to Wi-Fi..."); -#endif DEBUG_PRINTF("Connecting to Wi-Fi SSID: %s, password: %s, Channel: %d\n", ssid.c_str(), password.c_str(), wifi_channel); WiFi.begin(ssid.c_str(), password.c_str(), wifi_channel); } else { -#ifdef DEBUG_LOG logging.println("Wi-Fi already connected."); -#endif } } @@ -220,11 +205,9 @@ void onWifiConnect(WiFiEvent_t event, WiFiEventInfo_t info) { void onWifiGotIP(WiFiEvent_t event, WiFiEventInfo_t info) { //clear disconnects events if we got a IP clear_event(EVENT_WIFI_DISCONNECT); -#ifdef DEBUG_LOG logging.print("Wi-Fi Got IP. "); logging.print("IP address: "); logging.println(WiFi.localIP().toString()); -#endif } // Event handler for Wi-Fi disconnection @@ -233,9 +216,7 @@ void onWifiDisconnect(WiFiEvent_t event, WiFiEventInfo_t info) { if (connected_once) { set_event(EVENT_WIFI_DISCONNECT, 0); } -#ifdef DEBUG_LOG logging.println("Wi-Fi disconnected."); -#endif //we dont do anything here, the reconnect will be handled by the monitor //too many events received when the connection is lost //normal reconnect retry start at first 2 seconds @@ -254,9 +235,7 @@ void init_mDNS() { // Initialize mDNS .local resolution if (!MDNS.begin(mdnsHost)) { -#ifdef DEBUG_LOG logging.println("Error setting up MDNS responder!"); -#endif } else { // Advertise via bonjour the service so we can auto discover these battery emulators on the local network. MDNS.addService(mdnsHost, "tcp", 80); diff --git a/Software/src/inverter/FOXESS-CAN.cpp b/Software/src/inverter/FOXESS-CAN.cpp index d9148d83..772120a6 100644 --- a/Software/src/inverter/FOXESS-CAN.cpp +++ b/Software/src/inverter/FOXESS-CAN.cpp @@ -523,40 +523,23 @@ void FoxessCanInverter::map_can_frame_to_variable(CAN_frame rx_frame) { if (rx_frame.ID == 0x1871) { datalayer.system.status.CAN_inverter_still_alive = CAN_STILL_ALIVE; if (rx_frame.data.u8[0] == 0x03) { //0x1871 [0x03, 0x06, 0x17, 0x05, 0x09, 0x09, 0x28, 0x22] -//This message is sent by the inverter every '6' seconds (0.5s after the pack serial numbers) -//and contains a timestamp in bytes 2-7 i.e. ,,
,,, -#ifdef DEBUG_LOG - logging.println("Inverter sends current time and date"); -#endif + //This message is sent by the inverter every '6' seconds (0.5s after the pack serial numbers) + //and contains a timestamp in bytes 2-7 i.e. ,,
,,, } else if (rx_frame.data.u8[0] == 0x01) { if (rx_frame.data.u8[4] == 0x00) { -// Inverter wants to know bms info (every 1s) -#ifdef DEBUG_LOG - logging.println("Inverter requests 1s BMS info, we reply"); -#endif + // Inverter wants to know bms info (every 1s) send_bms_info = true; } else if (rx_frame.data.u8[4] == 0x01) { // b4 0x01 , 0x1871 [0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00] //Inverter wants to know all individual cellvoltages (occurs 6 seconds after valid BMS reply) -#ifdef DEBUG_LOG - logging.println("Inverter requests individual battery pack status, we reply"); -#endif send_individual_pack_status = true; } else if (rx_frame.data.u8[4] == 0x04) { // b4 0x01 , 0x1871 [0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00] //Inverter wants to know all individual cellvoltages (occurs 6 seconds after valid BMS reply) -#ifdef DEBUG_LOG - logging.println("Inverter requests cellvoltages and temps, we reply"); -#endif send_cellvoltages = true; } } else if (rx_frame.data.u8[0] == 0x02) { //0x1871 [0x02, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00] -// Ack message -#ifdef DEBUG_LOG - logging.println("Inverter acks, no reply needed"); -#endif + // Ack message } else if (rx_frame.data.u8[0] == 0x05) { //0x1871 [0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00] -#ifdef DEBUG_LOG - logging.println("Inverter wants to know serial numbers, we reply"); -#endif + // Inverter wants to know serial numbers, we reply send_serial_numbers = true; } } diff --git a/Software/src/inverter/ModbusInverterProtocol.cpp b/Software/src/inverter/ModbusInverterProtocol.cpp index b669f955..244ebab3 100644 --- a/Software/src/inverter/ModbusInverterProtocol.cpp +++ b/Software/src/inverter/ModbusInverterProtocol.cpp @@ -35,9 +35,7 @@ ModbusMessage ModbusInverterProtocol::FC03(ModbusMessage request) { if ((addr + words) > MBPV_MAX) { // Yes - send respective error response response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_ADDRESS); -#ifdef DEBUG_LOG logging.printf("Modbus FC03 error: illegal request addr=%d words=%d\n", addr, words); -#endif return response; } @@ -48,10 +46,6 @@ ModbusMessage ModbusInverterProtocol::FC03(ModbusMessage request) { response.add((uint16_t)(mbPV[addr + i])); } - // #ifdef DEBUG_LOG - // logging.printf("Modbus FC03 response: %d %d\n", addr, mbPV[addr]); - // #endif - return response; } @@ -67,9 +61,7 @@ ModbusMessage ModbusInverterProtocol::FC06(ModbusMessage request) { if ((addr) > MBPV_MAX) { // Yes - send respective error response response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_ADDRESS); -#ifdef DEBUG_LOG logging.printf("Modbus FC06 error: illegal request addr=%d val=%d\n", addr, val); -#endif return response; } @@ -97,18 +89,14 @@ ModbusMessage ModbusInverterProtocol::FC16(ModbusMessage request) { || (words > 123)) // can't support more than this in request packet { // Yes - send respective error response response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_VALUE); -#ifdef DEBUG_LOG logging.printf("Modbus FC16 error: bad registers addr=%d words=%d bytes=%d\n", addr, words, bytes); -#endif return response; } // Address overflow? if ((addr + words) > MBPV_MAX) { // Yes - send respective error response response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_ADDRESS); -#ifdef DEBUG_LOG logging.printf("Modbus FC16 error: overflow addr=%d words=%d\n", addr, words); -#endif return response; } @@ -145,20 +133,16 @@ ModbusMessage ModbusInverterProtocol::FC23(ModbusMessage request) { || (read_words > 125)) // can't fit more than this in the response packet { // Yes - send respective error response response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_VALUE); -#ifdef DEBUG_LOG logging.printf("Modbus FC23 error: bad registers write_addr=%d write_words=%d write_bytes=%d read_words=%d\n", write_addr, write_words, write_bytes, read_words); -#endif return response; } // Address overflow? if (((write_addr + write_words) > MBPV_MAX) || ((read_addr + read_words) > MBPV_MAX)) { // Yes - send respective error response response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_ADDRESS); -#ifdef DEBUG_LOG logging.printf("Modbus FC23 error: overflow write_addr=%d write_words=%d read_addr=%d read_words=%d\n", write_addr, write_words, read_addr, read_words); -#endif return response; } diff --git a/Software/src/inverter/SMA-BYD-H-CAN.cpp b/Software/src/inverter/SMA-BYD-H-CAN.cpp index 8a878484..0b0184e5 100644 --- a/Software/src/inverter/SMA-BYD-H-CAN.cpp +++ b/Software/src/inverter/SMA-BYD-H-CAN.cpp @@ -227,9 +227,7 @@ void SmaBydHInverter::map_can_frame_to_variable(CAN_frame rx_frame) { break; case 0x5E7: //Message originating from SMA inverter - Pairing request case 0x660: //Message originating from SMA inverter - Pairing request -#ifdef DEBUG_LOG logging.println("Received SMA pairing request"); -#endif // DEBUG_LOG pairing_events++; set_event(EVENT_SMA_PAIRING, pairing_events); datalayer.system.status.CAN_inverter_still_alive = CAN_STILL_ALIVE; diff --git a/Software/src/inverter/SMA-BYD-HVS-CAN.cpp b/Software/src/inverter/SMA-BYD-HVS-CAN.cpp index 4fa07ae7..1c4727aa 100644 --- a/Software/src/inverter/SMA-BYD-HVS-CAN.cpp +++ b/Software/src/inverter/SMA-BYD-HVS-CAN.cpp @@ -218,9 +218,7 @@ void SmaBydHvsInverter::map_can_frame_to_variable(CAN_frame rx_frame) { break; case 0x5E7: //Message originating from SMA inverter - Pairing request case 0x660: //Message originating from SMA inverter - Pairing request -#ifdef DEBUG_LOG logging.println("Received SMA pairing request"); -#endif // DEBUG_LOG pairing_events++; set_event(EVENT_SMA_PAIRING, pairing_events); datalayer.system.status.CAN_inverter_still_alive = CAN_STILL_ALIVE; diff --git a/Software/src/inverter/SMA-TRIPOWER-CAN.cpp b/Software/src/inverter/SMA-TRIPOWER-CAN.cpp index 341ea255..172cd4c0 100644 --- a/Software/src/inverter/SMA-TRIPOWER-CAN.cpp +++ b/Software/src/inverter/SMA-TRIPOWER-CAN.cpp @@ -126,9 +126,7 @@ void SmaTripowerInverter::map_can_frame_to_variable(CAN_frame rx_frame) { break; case 0x5E7: //Message originating from SMA inverter - Pairing request case 0x660: //Message originating from SMA inverter - Pairing request -#ifdef DEBUG_LOG logging.println("Received SMA pairing request"); -#endif // DEBUG_LOG pairing_events++; set_event(EVENT_SMA_PAIRING, pairing_events); datalayer.system.status.CAN_inverter_still_alive = CAN_STILL_ALIVE; diff --git a/Software/src/inverter/SOLAX-CAN.cpp b/Software/src/inverter/SOLAX-CAN.cpp index 16df4f2a..4117a2a9 100644 --- a/Software/src/inverter/SOLAX-CAN.cpp +++ b/Software/src/inverter/SOLAX-CAN.cpp @@ -147,9 +147,7 @@ void SolaxInverter::map_can_frame_to_variable(CAN_frame rx_frame) { switch (STATE) { case (BATTERY_ANNOUNCE): -#ifdef DEBUG_LOG logging.println("Solax Battery State: Announce"); -#endif datalayer.system.status.inverter_allows_contactor_closing = false; SOLAX_1875.data.u8[4] = (0x00); // Inform Inverter: Contactor 0=off, 1=on. for (uint8_t i = 0; i < number_of_batteries; i++) { @@ -183,9 +181,7 @@ void SolaxInverter::map_can_frame_to_variable(CAN_frame rx_frame) { transmit_can_frame(&SOLAX_1878); transmit_can_frame(&SOLAX_1801); // Announce that the battery will be connected STATE = CONTACTOR_CLOSED; // Jump to Contactor Closed State -#ifdef DEBUG_LOG logging.println("Solax Battery State: Contactor Closed"); -#endif break; case (CONTACTOR_CLOSED): @@ -213,14 +209,10 @@ void SolaxInverter::map_can_frame_to_variable(CAN_frame rx_frame) { if (rx_frame.ID == 0x1871 && rx_frame.data.u64 == __builtin_bswap64(0x0500010000000000)) { transmit_can_frame(&SOLAX_1881); transmit_can_frame(&SOLAX_1882); -#ifdef DEBUG_LOG logging.println("1871 05-frame received from inverter"); -#endif } if (rx_frame.ID == 0x1871 && rx_frame.data.u8[0] == (0x03)) { -#ifdef DEBUG_LOG logging.println("1871 03-frame received from inverter"); -#endif } } From aa9a4d429eb857d061e4d8dfbcb886b42c4c0033 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sat, 30 Aug 2025 00:22:43 +0300 Subject: [PATCH 077/245] Make USB/WEB debug configurable --- Software/Software.ino | 5 +- Software/USER_SETTINGS.h | 6 +- Software/src/battery/BMW-SBOX.cpp | 17 +-- Software/src/battery/DALY-BMS.cpp | 21 +--- Software/src/communication/nvm/comm_nvm.cpp | 3 + Software/src/datalayer/datalayer.h | 4 + Software/src/devboard/utils/logging.cpp | 105 +++++++++--------- Software/src/devboard/utils/logging.h | 22 ++-- .../devboard/webserver/debug_logging_html.cpp | 8 +- .../src/devboard/webserver/settings_html.cpp | 14 +++ Software/src/devboard/webserver/webserver.cpp | 24 ++-- Software/src/inverter/KOSTAL-RS485.cpp | 18 --- Software/src/inverter/KOSTAL-RS485.h | 7 -- Software/src/inverter/PYLON-LV-CAN.cpp | 23 ---- 14 files changed, 115 insertions(+), 162 deletions(-) diff --git a/Software/Software.ino b/Software/Software.ino index 508a0549..e139e0d1 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -64,7 +64,7 @@ void setup() { init_serial(); - // We print this after setting up serial, such that is also printed to serial with DEBUG_VIA_USB set. + // We print this after setting up serial, so that is also printed if configured to do so logging.printf("Battery emulator %s build " __DATE__ " " __TIME__ "\n", version_number); init_events(); @@ -352,9 +352,6 @@ void init_serial() { // Init Serial monitor Serial.begin(115200); while (!Serial) {} -#ifdef DEBUG_VIA_USB - Serial.println("__ OK __"); -#endif // DEBUG_VIA_USB } void check_interconnect_available() { diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index ff6ccfe0..2d07ca35 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -109,10 +109,6 @@ //#define INTERLOCK_REQUIRED //Nissan LEAF specific setting, if enabled requires both high voltage conenctors to be seated before starting //#define LOG_TO_SD //Enable this line to log diagnostic data to SD card (WARNING, raises CPU load, do not use for production) //#define LOG_CAN_TO_SD //Enable this line to log incoming/outgoing CAN & CAN-FD messages to SD card (WARNING, raises CPU load, do not use for production) -//#define DEBUG_VIA_USB //Enable this line to have the USB port output serial diagnostic data while program runs (WARNING, raises CPU load, do not use for production) -//#define DEBUG_VIA_WEB //Enable this line to log diagnostic data while program runs, which can be viewed via webpage (WARNING, slightly raises CPU load, do not use for production) -//#define DEBUG_CAN_DATA //Enable this line to print incoming/outgoing CAN & CAN-FD messages to USB serial (WARNING, raises CPU load, do not use for production) - /* CAN options */ //#define CAN_ADDON //Enable this line to activate an isolated secondary CAN Bus using add-on MCP2515 chip (Needed for some inverters / double battery) #define CRYSTAL_FREQUENCY_MHZ 8 //CAN_ADDON option, what is your MCP2515 add-on boards crystal frequency? @@ -225,7 +221,7 @@ extern IPAddress gateway; extern IPAddress subnet; #endif -#if defined(DEBUG_VIA_USB) || defined(DEBUG_VIA_WEB) || defined(LOG_TO_SD) +#if defined(DEBUG_VIA_USB) || defined(LOG_TO_SD) #define DEBUG_LOG #endif diff --git a/Software/src/battery/BMW-SBOX.cpp b/Software/src/battery/BMW-SBOX.cpp index c4e00add..ff90ce44 100644 --- a/Software/src/battery/BMW-SBOX.cpp +++ b/Software/src/battery/BMW-SBOX.cpp @@ -2,6 +2,7 @@ #include #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" +#include "../devboard/utils/logging.h" uint8_t reverse_bits(uint8_t byte) { uint8_t reversed = 0; @@ -122,9 +123,7 @@ void BmwSbox::transmit_can(unsigned long currentMillis) { SBOX_100.data.u8[0] = 0x86; // Precharge relay only prechargeStartTime = currentMillis; contactorStatus = NEGATIVE; -#ifdef DEBUG_VIA_USB - Serial.println("S-BOX Precharge relay engaged"); -#endif + logging.println("S-BOX Precharge relay engaged"); break; case NEGATIVE: if (currentMillis - prechargeStartTime >= CONTACTOR_CONTROL_T1) { @@ -132,9 +131,7 @@ void BmwSbox::transmit_can(unsigned long currentMillis) { negativeStartTime = currentMillis; contactorStatus = POSITIVE; datalayer.shunt.precharging = true; -#ifdef DEBUG_VIA_USB - Serial.println("S-BOX Negative relay engaged"); -#endif + logging.println("S-BOX Negative relay engaged"); } break; case POSITIVE: @@ -145,18 +142,14 @@ void BmwSbox::transmit_can(unsigned long currentMillis) { positiveStartTime = currentMillis; contactorStatus = PRECHARGE_OFF; datalayer.shunt.precharging = false; -#ifdef DEBUG_VIA_USB - Serial.println("S-BOX Positive relay engaged"); -#endif + logging.println("S-BOX Positive relay engaged"); } break; case PRECHARGE_OFF: if (currentMillis - positiveStartTime >= CONTACTOR_CONTROL_T3) { SBOX_100.data.u8[0] = 0x6A; // Negative + Positive contactorStatus = COMPLETED; -#ifdef DEBUG_VIA_USB - Serial.println("S-BOX Precharge relay released"); -#endif + logging.println("S-BOX Precharge relay released"); datalayer.shunt.contactors_engaged = true; } break; diff --git a/Software/src/battery/DALY-BMS.cpp b/Software/src/battery/DALY-BMS.cpp index 49d0314a..38c8bd1e 100644 --- a/Software/src/battery/DALY-BMS.cpp +++ b/Software/src/battery/DALY-BMS.cpp @@ -108,18 +108,16 @@ uint32_t decode_uint32be(uint8_t data[8], uint8_t offset) { ((uint32_t)data[offset + 3]); } -#ifdef DEBUG_VIA_USB void dump_buff(const char* msg, uint8_t* buff, uint8_t len) { - Serial.print("[DALY-BMS] "); - Serial.print(msg); + logging.print("[DALY-BMS] "); + logging.print(msg); for (int i = 0; i < len; i++) { - Serial.print(buff[i] >> 4, HEX); - Serial.print(buff[i] & 0xf, HEX); - Serial.print(" "); + logging.print(buff[i] >> 4, HEX); + logging.print(buff[i] & 0xf, HEX); + logging.print(" "); } - Serial.println(); + logging.println(); } -#endif void decode_packet(uint8_t command, uint8_t data[8]) { datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; @@ -180,9 +178,7 @@ void DalyBms::transmit_rs485(unsigned long currentMillis) { tx_buff[2] = nextCommand; tx_buff[3] = 8; tx_buff[12] = calculate_checksum(tx_buff); -#ifdef DEBUG_VIA_USB dump_buff("transmitting: ", tx_buff, 13); -#endif Serial2.write(tx_buff, 13); nextCommand++; if (nextCommand > 0x98) @@ -202,17 +198,12 @@ void DalyBms::receive() { if (recv_len > 0 && recv_buff[0] != 0xA5 || recv_len > 1 && recv_buff[1] != 0x01 || recv_len > 2 && (recv_buff[2] < 0x90 || recv_buff[2] > 0x98) || recv_len > 3 && recv_buff[3] != 8 || recv_len > 12 && recv_buff[12] != calculate_checksum(recv_buff)) { - -#ifdef DEBUG_VIA_USB dump_buff("dropping partial rx: ", recv_buff, recv_len); -#endif recv_len = 0; } if (recv_len > 12) { -#ifdef DEBUG_VIA_USB dump_buff("decoding successfull rx: ", recv_buff, recv_len); -#endif decode_packet(recv_buff[2], &recv_buff[4]); recv_len = 0; lastPacket = millis(); diff --git a/Software/src/communication/nvm/comm_nvm.cpp b/Software/src/communication/nvm/comm_nvm.cpp index 91adc099..70856a57 100644 --- a/Software/src/communication/nvm/comm_nvm.cpp +++ b/Software/src/communication/nvm/comm_nvm.cpp @@ -135,6 +135,9 @@ void init_stored_settings() { remote_bms_reset = settings.getBool("REMBMSRESET", false); use_canfd_as_can = settings.getBool("CANFDASCAN", false); + datalayer.system.info.usb_logging_active = settings.getBool("USBENABLED", false); + datalayer.system.info.web_logging_active = settings.getBool("WEBENABLED", false); + // WIFI AP is enabled by default unless disabled in the settings wifiap_enabled = settings.getBool("WIFIAPENABLED", true); passwordAP = settings.getString("APPASSWORD", "123456789").c_str(); diff --git a/Software/src/datalayer/datalayer.h b/Software/src/datalayer/datalayer.h index 6f23ca80..19154a99 100644 --- a/Software/src/datalayer/datalayer.h +++ b/Software/src/datalayer/datalayer.h @@ -241,6 +241,10 @@ struct DATALAYER_SYSTEM_INFO_TYPE { size_t logged_can_messages_offset = 0; /** bool, determines if CAN messages should be logged for webserver */ bool can_logging_active = false; + /** bool, determines if USB serial logging should occur */ + bool usb_logging_active = false; + /** bool, determines if general logging should be active for webserver */ + bool web_logging_active = false; /** uint8_t, enumeration which CAN interface should be used for log playback */ uint8_t can_replay_interface = CAN_NATIVE; /** bool, determines if CAN replay should loop or not */ diff --git a/Software/src/devboard/utils/logging.cpp b/Software/src/devboard/utils/logging.cpp index 93a2586a..e20607fe 100644 --- a/Software/src/devboard/utils/logging.cpp +++ b/Software/src/devboard/utils/logging.cpp @@ -3,19 +3,17 @@ #include "../../datalayer/datalayer.h" #include "../sdcard/sdcard.h" -#if defined(LOG_CAN_TO_SD) || defined(LOG_TO_SD) -#if !defined(HW_LILYGO) -#error The SD card logging feature is only available on LilyGo hardware -#endif -#endif - #define MAX_LINE_LENGTH_PRINTF 128 #define MAX_LENGTH_TIME_STR 14 bool previous_message_was_newline = true; void Logging::add_timestamp(size_t size) { -#ifdef DEBUG_LOG + // Check if any logging is enabled at runtime + if (!datalayer.system.info.web_logging_active && !datalayer.system.info.usb_logging_active) { + return; + } + char* message_string = datalayer.system.info.logged_can_messages; int offset = datalayer.system.info.logged_can_messages_offset; // Keeps track of the current position in the buffer size_t message_string_size = sizeof(datalayer.system.info.logged_can_messages); @@ -23,43 +21,44 @@ void Logging::add_timestamp(size_t size) { char* timestr; static char timestr_buffer[MAX_LENGTH_TIME_STR]; -#ifdef DEBUG_VIA_WEB - if (!datalayer.system.info.can_logging_active) { - /* If web debug is active and can logging is inactive, - * we use the debug logging memory directly for writing the timestring */ - if (offset + size + MAX_LENGTH_TIME_STR > message_string_size) { - offset = 0; + if (datalayer.system.info.web_logging_active) { + if (!datalayer.system.info.can_logging_active) { + /* If web debug is active and can logging is inactive, + * we use the debug logging memory directly for writing the timestring */ + if (offset + size + MAX_LENGTH_TIME_STR > message_string_size) { + offset = 0; + } + timestr = datalayer.system.info.logged_can_messages + offset; + } else { + timestr = timestr_buffer; } - timestr = datalayer.system.info.logged_can_messages + offset; } else { timestr = timestr_buffer; } -#else - timestr = timestr_buffer; -#endif // DEBUG_VIA_WEB offset += min(MAX_LENGTH_TIME_STR - 1, snprintf(timestr, MAX_LENGTH_TIME_STR, "%8lu.%03lu ", currentTime / 1000, currentTime % 1000)); -#ifdef DEBUG_VIA_WEB - if (!datalayer.system.info.can_logging_active) { + if (datalayer.system.info.web_logging_active && !datalayer.system.info.can_logging_active) { datalayer.system.info.logged_can_messages_offset = offset; // Update offset in buffer } -#endif // DEBUG_VIA_WEB + // LOG_TO_SD remains as compile-time option for now #ifdef LOG_TO_SD add_log_to_buffer((uint8_t*)timestr, MAX_LENGTH_TIME_STR); #endif // LOG_TO_SD -#ifdef DEBUG_VIA_USB - Serial.write(timestr); -#endif // DEBUG_VIA_USB - -#endif // DEBUG_LOG + if (datalayer.system.info.usb_logging_active) { + Serial.write(timestr); + } } size_t Logging::write(const uint8_t* buffer, size_t size) { -#ifdef DEBUG_LOG + // Check if any logging is enabled at runtime + if (!datalayer.system.info.web_logging_active && !datalayer.system.info.usb_logging_active) { + return 0; + } + if (previous_message_was_newline) { add_timestamp(size); } @@ -67,11 +66,12 @@ size_t Logging::write(const uint8_t* buffer, size_t size) { #ifdef LOG_TO_SD add_log_to_buffer(buffer, size); #endif -#ifdef DEBUG_VIA_USB - Serial.write(buffer, size); -#endif -#ifdef DEBUG_VIA_WEB - if (!datalayer.system.info.can_logging_active) { + + if (datalayer.system.info.usb_logging_active) { + Serial.write(buffer, size); + } + + if (datalayer.system.info.web_logging_active && !datalayer.system.info.can_logging_active) { char* message_string = datalayer.system.info.logged_can_messages; int offset = datalayer.system.info.logged_can_messages_offset; // Keeps track of the current position in the buffer size_t message_string_size = sizeof(datalayer.system.info.logged_can_messages); @@ -82,16 +82,17 @@ size_t Logging::write(const uint8_t* buffer, size_t size) { memcpy(message_string + offset, buffer, size); datalayer.system.info.logged_can_messages_offset = offset + size; // Update offset in buffer } -#endif // DEBUG_VIA_WEB previous_message_was_newline = buffer[size - 1] == '\n'; return size; -#endif // DEBUG_LOG - return 0; } void Logging::printf(const char* fmt, ...) { -#ifdef DEBUG_LOG + // Check if any logging is enabled at runtime + if (!datalayer.system.info.web_logging_active && !datalayer.system.info.usb_logging_active) { + return; + } + if (previous_message_was_newline) { add_timestamp(MAX_LINE_LENGTH_PRINTF); } @@ -101,21 +102,22 @@ void Logging::printf(const char* fmt, ...) { int offset = datalayer.system.info.logged_can_messages_offset; // Keeps track of the current position in the buffer static char buffer[MAX_LINE_LENGTH_PRINTF]; char* message_buffer; -#ifdef DEBUG_VIA_WEB - if (!datalayer.system.info.can_logging_active) { - /* If web debug is active and can logging is inactive, - * we use the debug logging memory directly for writing the output */ - if (offset + MAX_LINE_LENGTH_PRINTF > message_string_size) { - // Not enough space, reset and start from the beginning - offset = 0; + + if (datalayer.system.info.web_logging_active) { + if (!datalayer.system.info.can_logging_active) { + /* If web debug is active and can logging is inactive, + * we use the debug logging memory directly for writing the output */ + if (offset + MAX_LINE_LENGTH_PRINTF > message_string_size) { + // Not enough space, reset and start from the beginning + offset = 0; + } + message_buffer = message_string + offset; + } else { + message_buffer = buffer; } - message_buffer = message_string + offset; } else { message_buffer = buffer; } -#else - message_buffer = buffer; -#endif // DEBUG_VIA_WEB va_list(args); va_start(args, fmt); @@ -126,18 +128,15 @@ void Logging::printf(const char* fmt, ...) { add_log_to_buffer((uint8_t*)message_buffer, size); #endif // LOG_TO_SD -#ifdef DEBUG_VIA_USB - Serial.write(message_buffer, size); -#endif // DEBUG_VIA_USB + if (datalayer.system.info.usb_logging_active) { + Serial.write(message_buffer, size); + } -#ifdef DEBUG_VIA_WEB - if (!datalayer.system.info.can_logging_active) { + if (datalayer.system.info.web_logging_active && !datalayer.system.info.can_logging_active) { // Data was already added to buffer, just move offset datalayer.system.info.logged_can_messages_offset = offset + size; // Keeps track of the current position in the buffer } -#endif // DEBUG_VIA_WEB previous_message_was_newline = message_buffer[size - 1] == '\n'; -#endif // DEBUG_LOG } diff --git a/Software/src/devboard/utils/logging.h b/Software/src/devboard/utils/logging.h index 49a844a9..61325e99 100644 --- a/Software/src/devboard/utils/logging.h +++ b/Software/src/devboard/utils/logging.h @@ -4,6 +4,7 @@ #include #include #include "../../../USER_SETTINGS.h" +#include "../../datalayer/datalayer.h" #include "types.h" class Logging : public Print { @@ -13,18 +14,23 @@ class Logging : public Print { virtual size_t write(const uint8_t* buffer, size_t size); virtual size_t write(uint8_t) { return 0; } void printf(const char* fmt, ...); - void log_bms_status(real_bms_status_enum bms_status); Logging() {} }; extern Logging logging; -#ifdef DEBUG_LOG -#define DEBUG_PRINTF(fmt, ...) logging.printf(fmt, ##__VA_ARGS__) -#define DEBUG_PRINTLN(str) logging.println(str) -#else -#define DEBUG_PRINTF(fmt, ...) ((void)0) -#define DEBUG_PRINTLN(str) ((void)0) -#endif +// Replace compile-time macros with runtime checks +#define DEBUG_PRINTF(fmt, ...) \ + do { \ + if (datalayer.system.info.web_logging_active || datalayer.system.info.usb_logging_active) { \ + logging.printf(fmt, ##__VA_ARGS__); \ + } \ + } while (0) +#define DEBUG_PRINTLN(str) \ + do { \ + if (datalayer.system.info.web_logging_active || datalayer.system.info.usb_logging_active) { \ + logging.println(str); \ + } \ + } while (0) #endif // __LOGGING_H__ diff --git a/Software/src/devboard/webserver/debug_logging_html.cpp b/Software/src/devboard/webserver/debug_logging_html.cpp index f9657f9a..ae8a7fbd 100644 --- a/Software/src/devboard/webserver/debug_logging_html.cpp +++ b/Software/src/devboard/webserver/debug_logging_html.cpp @@ -3,7 +3,6 @@ #include "../../datalayer/datalayer.h" #include "index_html.h" -#if defined(DEBUG_VIA_WEB) || defined(LOG_TO_SD) char* strnchr(const char* s, int c, size_t n) { // Like strchr, but only searches the first 'n' bytes of the string. @@ -54,9 +53,9 @@ String debug_logger_processor(void) { ".can-message { background-color: #404E57; margin-bottom: 5px; padding: 10px; border-radius: 5px; font-family: " "monospace; }"; content += ""; -#ifdef DEBUG_VIA_WEB - content += " "; -#endif + if (datalayer.system.info.web_logging_active) { + content += " "; + } content += " "; #ifdef LOG_TO_SD content += " "; @@ -107,4 +106,3 @@ String debug_logger_processor(void) { content += index_html_footer; return content; } -#endif diff --git a/Software/src/devboard/webserver/settings_html.cpp b/Software/src/devboard/webserver/settings_html.cpp index 7ca338ce..753a490a 100644 --- a/Software/src/devboard/webserver/settings_html.cpp +++ b/Software/src/devboard/webserver/settings_html.cpp @@ -254,6 +254,14 @@ String settings_processor(const String& var, BatteryEmulatorSettingsStore& setti return settings.getBool("WIFIAPENABLED", wifiap_enabled) ? "checked" : ""; } + if (var == "USBENABLED") { + return settings.getBool("USBENABLED") ? "checked" : ""; + } + + if (var == "WEBENABLED") { + return settings.getBool("WEBENABLED") ? "checked" : ""; + } + if (var == "MQTTENABLED") { return settings.getBool("MQTTENABLED") ? "checked" : ""; } @@ -862,6 +870,12 @@ const char* getCANInterfaceName(CAN_Interface interface) { + + + + + + diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 90dd64a0..a227225f 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -267,13 +267,13 @@ void init_webserver() { } }); -#if defined(DEBUG_VIA_WEB) || defined(LOG_TO_SD) - // Route for going to debug logging web page - server.on("/log", HTTP_GET, [](AsyncWebServerRequest* request) { - AsyncWebServerResponse* response = request->beginResponse(200, "text/html", debug_logger_processor()); - request->send(response); - }); -#endif // DEBUG_VIA_WEB + if (datalayer.system.info.web_logging_active) { //|| defined(LOG_TO_SD) + // Route for going to debug logging web page + server.on("/log", HTTP_GET, [](AsyncWebServerRequest* request) { + AsyncWebServerResponse* response = request->beginResponse(200, "text/html", debug_logger_processor()); + request->send(response); + }); + } // Define the handler to stop can logging server.on("/stop_can_logging", HTTP_GET, [](AsyncWebServerRequest* request) { @@ -413,8 +413,8 @@ void init_webserver() { }; const char* boolSettingNames[] = { - "DBLBTR", "CNTCTRL", "CNTCTRLDBL", "PWMCNTCTRL", "PERBMSRESET", "REMBMSRESET", - "CANFDASCAN", "WIFIAPENABLED", "MQTTENABLED", "HADISC", "MQTTTOPICS", "INVICNT", + "DBLBTR", "CNTCTRL", "CNTCTRLDBL", "PWMCNTCTRL", "PERBMSRESET", "REMBMSRESET", "USBENABLED", + "WEBENABLED", "CANFDASCAN", "WIFIAPENABLED", "MQTTENABLED", "HADISC", "MQTTTOPICS", "INVICNT", }; // Handles the form POST from UI to save settings of the common image @@ -1388,9 +1388,9 @@ String processor(const String& var) { content += " "; content += " "; content += " "; -#if defined(DEBUG_VIA_WEB) || defined(LOG_TO_SD) - content += " "; -#endif // DEBUG_VIA_WEB + if (datalayer.system.info.web_logging_active) { //|| defined(LOG_TO_SD) + content += " "; + } content += " "; content += " "; content += ""; diff --git a/Software/src/inverter/KOSTAL-RS485.cpp b/Software/src/inverter/KOSTAL-RS485.cpp index ef5dfc6b..bd556138 100644 --- a/Software/src/inverter/KOSTAL-RS485.cpp +++ b/Software/src/inverter/KOSTAL-RS485.cpp @@ -14,16 +14,13 @@ void KostalInverterProtocol::float2frame(uint8_t* arr, float value, uint8_t fram } static void dbg_timestamp(void) { -#ifdef DEBUG_KOSTAL_RS485_DATA logging.print("["); logging.print(millis()); logging.print(" ms] "); -#endif } static void dbg_frame(uint8_t* frame, int len, const char* prefix) { dbg_timestamp(); -#ifdef DEBUG_KOSTAL_RS485_DATA logging.print(prefix); logging.print(": "); for (uint8_t i = 0; i < len; i++) { @@ -34,26 +31,11 @@ static void dbg_frame(uint8_t* frame, int len, const char* prefix) { logging.print(" "); } logging.println(""); -#endif -#ifdef DEBUG_KOSTAL_RS485_DATA_USB - Serial.print(prefix); - Serial.print(": "); - for (uint8_t i = 0; i < len; i++) { - if (frame[i] < 0x10) { - Serial.print("0"); - } - Serial.print(frame[i], HEX); - Serial.print(" "); - } - Serial.println(""); -#endif } static void dbg_message(const char* msg) { dbg_timestamp(); -#ifdef DEBUG_KOSTAL_RS485_DATA logging.println(msg); -#endif } /* https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing#Encoding_examples */ diff --git a/Software/src/inverter/KOSTAL-RS485.h b/Software/src/inverter/KOSTAL-RS485.h index a14d5dca..86e2257d 100644 --- a/Software/src/inverter/KOSTAL-RS485.h +++ b/Software/src/inverter/KOSTAL-RS485.h @@ -8,13 +8,6 @@ #define SELECTED_INVERTER_CLASS KostalInverterProtocol #endif -//#define DEBUG_KOSTAL_RS485_DATA // Enable this line to get TX / RX printed out via logging -//#define DEBUG_KOSTAL_RS485_DATA_USB // Enable this line to get TX / RX printed out via USB - -#if defined(DEBUG_KOSTAL_RS485_DATA) && !defined(DEBUG_LOG) -#error "enable LOG_TO_SD, DEBUG_VIA_USB or DEBUG_VIA_WEB in order to use DEBUG_KOSTAL_RS485_DATA" -#endif - class KostalInverterProtocol : public Rs485InverterProtocol { public: const char* name() override { return Name; } diff --git a/Software/src/inverter/PYLON-LV-CAN.cpp b/Software/src/inverter/PYLON-LV-CAN.cpp index 7028f558..324196c4 100644 --- a/Software/src/inverter/PYLON-LV-CAN.cpp +++ b/Software/src/inverter/PYLON-LV-CAN.cpp @@ -107,34 +107,11 @@ void PylonLvInverter::map_can_frame_to_variable(CAN_frame rx_frame) { } } -#ifdef DEBUG_VIA_USB -void dump_frame(CAN_frame* frame) { - Serial.print("[PYLON-LV] sending CAN frame "); - Serial.print(frame->ID, HEX); - Serial.print(": "); - for (int i = 0; i < 8; i++) { - Serial.print(frame->data.u8[i] >> 4, HEX); - Serial.print(frame->data.u8[i] & 0xf, HEX); - Serial.print(" "); - } - Serial.println(); -} -#endif - void PylonLvInverter::transmit_can(unsigned long currentMillis) { if (currentMillis - previousMillis1000ms >= 1000) { previousMillis1000ms = currentMillis; -#ifdef DEBUG_VIA_USB - dump_frame(&PYLON_351); - dump_frame(&PYLON_355); - dump_frame(&PYLON_356); - dump_frame(&PYLON_359); - dump_frame(&PYLON_35C); - dump_frame(&PYLON_35E); -#endif - transmit_can_frame(&PYLON_351); transmit_can_frame(&PYLON_355); transmit_can_frame(&PYLON_356); From 5df70719995e1ee63af4c49203b5addb6f0ba369 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sat, 30 Aug 2025 00:29:22 +0300 Subject: [PATCH 078/245] Fix unit test failing --- Software/src/devboard/utils/events.cpp | 654 ++++++++++++------------- 1 file changed, 326 insertions(+), 328 deletions(-) diff --git a/Software/src/devboard/utils/events.cpp b/Software/src/devboard/utils/events.cpp index 33dfbc87..3d912124 100644 --- a/Software/src/devboard/utils/events.cpp +++ b/Software/src/devboard/utils/events.cpp @@ -159,336 +159,334 @@ void reset_all_events() { } events.level = EVENT_LEVEL_INFO; update_bms_status(); - logging.println("All events have been cleared."); -} -void set_event_MQTTpublished(EVENTS_ENUM_TYPE event) { - events.entries[event].MQTTpublished = true; -} - -String get_event_message_string(EVENTS_ENUM_TYPE event) { - switch (event) { - case EVENT_CANMCP2517FD_INIT_FAILURE: - return "CAN-FD initialization failed. Check hardware or bitrate settings"; - case EVENT_CANMCP2515_INIT_FAILURE: - return "CAN-MCP addon initialization failed. Check hardware"; - case EVENT_CANFD_BUFFER_FULL: - return "MCP2518FD message failed to send. Buffer full or no one on the bus to ACK the message!"; - case EVENT_CAN_BUFFER_FULL: - return "MCP2515 message failed to send. Buffer full or no one on the bus to ACK the message!"; - case EVENT_TASK_OVERRUN: - return "Task took too long to complete. CPU load might be too high. Info message, no action required."; - case EVENT_CAN_CORRUPTED_WARNING: - return "High amount of corrupted CAN messages detected. Check CAN wire shielding!"; - case EVENT_CAN_NATIVE_TX_FAILURE: - return "CAN_NATIVE failed to transmit, or no one on the bus to ACK the message!"; - case EVENT_CAN_BATTERY_MISSING: - return "Battery not sending messages via CAN for the last 60 seconds. Check wiring!"; - case EVENT_CAN_BATTERY2_MISSING: - return "Secondary battery not sending messages via CAN for the last 60 seconds. Check wiring!"; - case EVENT_CAN_CHARGER_MISSING: - return "Charger not sending messages via CAN for the last 60 seconds. Check wiring!"; - case EVENT_CAN_INVERTER_MISSING: - return "Inverter not sending messages via CAN for the last 60 seconds. Check wiring!"; - case EVENT_CONTACTOR_WELDED: - return "Contactors sticking/welded. Inspect battery with caution!"; - case EVENT_CONTACTOR_OPEN: - return "Battery decided to open contactors. Inspect battery!"; - case EVENT_CPU_OVERHEATING: - return "Battery-Emulator CPU overheating! Increase airflow/cooling to increase hardware lifespan!"; - case EVENT_CPU_OVERHEATED: - return "Battery-Emulator CPU melting! Performing controlled shutdown until temperature drops!"; - case EVENT_CHARGE_LIMIT_EXCEEDED: - return "Inverter is charging faster than battery is allowing."; - case EVENT_DISCHARGE_LIMIT_EXCEEDED: - return "Inverter is discharging faster than battery is allowing."; - case EVENT_WATER_INGRESS: - return "Water leakage inside battery detected. Operation halted. Inspect battery!"; - case EVENT_12V_LOW: - return "12V battery source below required voltage to safely close contactors. Inspect the supply/battery!"; - case EVENT_SOC_PLAUSIBILITY_ERROR: - return "SOC reported by battery not plausible. Restart battery!"; - case EVENT_SOC_UNAVAILABLE: - return "SOC not sent by BMS. Calibrate BMS via app."; - case EVENT_STALE_VALUE: - return "Important values detected as stale. System might have locked up!"; - case EVENT_KWH_PLAUSIBILITY_ERROR: - return "kWh remaining reported by battery not plausible. Battery needs cycling."; - case EVENT_BALANCING_START: - return "Balancing has started"; - case EVENT_BALANCING_END: - return "Balancing has ended"; - case EVENT_BATTERY_EMPTY: - return "Battery is completely discharged"; - case EVENT_BATTERY_FULL: - return "Battery is fully charged"; - case EVENT_BATTERY_FUSE: - return "Battery internal fuse blown. Inspect battery"; - case EVENT_BATTERY_FROZEN: - return "Battery is too cold to operate optimally. Consider warming it up!"; - case EVENT_BATTERY_CAUTION: - return "Battery has raised a general caution flag. Might want to inspect it closely."; - case EVENT_BATTERY_CHG_STOP_REQ: - return "Battery raised caution indicator AND requested charge stop. Inspect battery status!"; - case EVENT_BATTERY_DISCHG_STOP_REQ: - return "Battery raised caution indicator AND requested discharge stop. Inspect battery status!"; - case EVENT_BATTERY_CHG_DISCHG_STOP_REQ: - return "Battery raised caution indicator AND requested charge/discharge stop. Inspect battery status!"; - case EVENT_BATTERY_REQUESTS_HEAT: - return "COLD BATTERY! Battery requesting heating pads to activate!"; - case EVENT_BATTERY_WARMED_UP: - return "Battery requesting heating pads to stop. The battery is now warm enough."; - case EVENT_BATTERY_OVERHEAT: - return "Battery overheated. Shutting down to prevent thermal runaway!"; - case EVENT_BATTERY_OVERVOLTAGE: - return "Battery exceeding maximum design voltage. Discharge battery to prevent damage!"; - case EVENT_BATTERY_UNDERVOLTAGE: - return "Battery under minimum design voltage. Charge battery to prevent damage!"; - case EVENT_BATTERY_VALUE_UNAVAILABLE: - return "Battery measurement unavailable. Check 12V power supply and battery wiring!"; - case EVENT_BATTERY_ISOLATION: - return "Battery reports isolation error. High voltage might be leaking to ground. Check battery!"; - case EVENT_VOLTAGE_DIFFERENCE: - return "Too large voltage diff between the batteries. Second battery cannot join the DC-link"; - case EVENT_SOH_DIFFERENCE: - return "Large deviation in State of health between packs. Inspect battery."; - case EVENT_SOH_LOW: - return "State of health critically low. Battery internal resistance too high to continue. Recycle " - "battery."; - case EVENT_HVIL_FAILURE: - return "Battery interlock loop broken. Check that high voltage / low voltage connectors are seated. " - "Battery will be disabled!"; - case EVENT_PRECHARGE_FAILURE: - return "Battery failed to precharge. Check that capacitor is seated on high voltage output."; - case EVENT_AUTOMATIC_PRECHARGE_FAILURE: - return "Automatic precharge failed to reach target voltae."; - case EVENT_INTERNAL_OPEN_FAULT: - return "High voltage cable removed while battery running. Opening contactors!"; - case EVENT_INVERTER_OPEN_CONTACTOR: - return "Inverter side opened contactors. Normal operation."; - case EVENT_INTERFACE_MISSING: - return "Configuration trying to use CAN interface not baked into the software. Recompile software!"; - case EVENT_ERROR_OPEN_CONTACTOR: - return "Too much time spent in error state. Opening contactors, not safe to continue. " - "Check other active ERROR code for reason. Reboot emulator after problem is solved!"; - case EVENT_MODBUS_INVERTER_MISSING: - return "Modbus inverter has not sent any data. Inspect communication wiring!"; - case EVENT_NO_ENABLE_DETECTED: - return "Inverter Enable line has not been active for a long time. Check Wiring!"; - case EVENT_CELL_CRITICAL_UNDER_VOLTAGE: - return "CELL VOLTAGE CRITICALLY LOW! Not possible to continue. Inspect battery!"; - case EVENT_CELL_UNDER_VOLTAGE: - return "Cell undervoltage. Further discharge not possible. Check balancing of cells"; - case EVENT_CELL_OVER_VOLTAGE: - return "Cell overvoltage. Further charging not possible. Check balancing of cells"; - case EVENT_CELL_CRITICAL_OVER_VOLTAGE: - return "CELL VOLTAGE CRITICALLY HIGH! Not possible to continue. Inspect battery!"; - case EVENT_CELL_DEVIATION_HIGH: - return "Large cell voltage deviation! Check balancing of cells"; - case EVENT_UNKNOWN_EVENT_SET: - return "An unknown event was set! Review your code!"; - case EVENT_DUMMY_INFO: - return "The dummy info event was set!"; // Don't change this event message! - case EVENT_DUMMY_DEBUG: - return "The dummy debug event was set!"; // Don't change this event message! - case EVENT_DUMMY_WARNING: - return "The dummy warning event was set!"; // Don't change this event message! - case EVENT_DUMMY_ERROR: - return "The dummy error event was set!"; // Don't change this event message! - case EVENT_PERSISTENT_SAVE_INFO: - return "Failed to save user settings. Namespace full?"; - case EVENT_SERIAL_RX_WARNING: - return "Error in serial function: No data received for some time, see data for minutes"; - case EVENT_SERIAL_RX_FAILURE: - return "Error in serial function: No data for a long time!"; - case EVENT_SERIAL_TX_FAILURE: - return "Error in serial function: No ACK from receiver!"; - case EVENT_SERIAL_TRANSMITTER_FAILURE: - return "Error in serial function: Some ERROR level fault in transmitter, received by receiver"; - case EVENT_SMA_PAIRING: - return "SMA inverter trying to pair, contactors will close and open according to Enable line"; - case EVENT_OTA_UPDATE: - return "OTA update started!"; - case EVENT_OTA_UPDATE_TIMEOUT: - return "OTA update timed out!"; - case EVENT_RESET_UNKNOWN: - return "The board was reset unexpectedly, and reason can't be determined"; - case EVENT_RESET_POWERON: - return "The board was reset from a power-on event. Normal operation"; - case EVENT_RESET_EXT: - return "The board was reset from an external pin"; - case EVENT_RESET_SW: - return "The board was reset via software, webserver or OTA. Normal operation"; - case EVENT_RESET_PANIC: - return "The board was reset due to an exception or panic. Inform developers!"; - case EVENT_RESET_INT_WDT: - return "The board was reset due to an interrupt watchdog timeout. Inform developers!"; - case EVENT_RESET_TASK_WDT: - return "The board was reset due to a task watchdog timeout. Inform developers!"; - case EVENT_RESET_WDT: - return "The board was reset due to other watchdog timeout. Inform developers!"; - case EVENT_RESET_DEEPSLEEP: - return "The board was reset after exiting deep sleep mode"; - case EVENT_RESET_BROWNOUT: - return "The board was reset due to a momentary low voltage condition. This is expected during certain " - "operations like flashing via USB"; - case EVENT_RESET_SDIO: - return "The board was reset over SDIO"; - case EVENT_RESET_USB: - return "The board was reset by the USB peripheral"; - case EVENT_RESET_JTAG: - return "The board was reset by JTAG"; - case EVENT_RESET_EFUSE: - return "The board was reset due to an efuse error"; - case EVENT_RESET_PWR_GLITCH: - return "The board was reset due to a detected power glitch"; - case EVENT_RESET_CPU_LOCKUP: - return "The board was reset due to CPU lockup. Inform developers!"; - case EVENT_RJXZS_LOG: - return "Error code active in RJXZS BMS. Clear via their smartphone app!"; - case EVENT_PAUSE_BEGIN: - return "The emulator is trying to pause the battery."; - case EVENT_PAUSE_END: - return "The emulator is attempting to resume battery operation from pause."; - case EVENT_PID_FAILED: - return "Failed to write PID request to battery"; - case EVENT_WIFI_CONNECT: - return "Wifi connected."; - case EVENT_WIFI_DISCONNECT: - return "Wifi disconnected."; - case EVENT_MQTT_CONNECT: - return "MQTT connected."; - case EVENT_MQTT_DISCONNECT: - return "MQTT disconnected."; - case EVENT_EQUIPMENT_STOP: - return "User requested stop, either via equipment stop circuit or webserver Open Contactor button"; - case EVENT_SD_INIT_FAILED: - return "SD card initialization failed, check hardware. Power must be removed to reset the SD card."; - case EVENT_PERIODIC_BMS_RESET: - return "BMS Reset Event Completed."; - case EVENT_PERIODIC_BMS_RESET_AT_INIT_SUCCESS: - return "Successfully syncronised with the NTP Server. BMS will reset every 24 hours at defined time"; - case EVENT_PERIODIC_BMS_RESET_AT_INIT_FAILED: - return "Failed to syncronise with the NTP Server. BMS will reset every 24 hours from when the emulator was " - "powered on"; - case EVENT_GPIO_CONFLICT: - return "GPIO Pin Conflict: The pin used by '" + esp32hal->failed_allocator() + "' is already allocated by '" + - esp32hal->conflicting_allocator() + "'. Please check your configuration and assign different pins."; - case EVENT_GPIO_NOT_DEFINED: - return "Missing GPIO Assignment: The component '" + esp32hal->failed_allocator() + - "' requires a GPIO pin that isn't configured. Please define a valid pin number in your settings."; - default: - return ""; - } -} - -const char* get_event_enum_string(EVENTS_ENUM_TYPE event) { - // Return the event name but skip "EVENT_" that should always be first - return EVENTS_ENUM_TYPE_STRING[event] + 6; -} - -const char* get_event_level_string(EVENTS_ENUM_TYPE event) { - // Return the event level but skip "EVENT_LEVEL_" that should always be first - return EVENTS_LEVEL_TYPE_STRING[events.entries[event].level] + 12; -} - -const char* get_event_level_string(EVENTS_LEVEL_TYPE event_level) { - // Return the event level but skip "EVENT_LEVEL_TYPE_" that should always be first - return EVENTS_LEVEL_TYPE_STRING[event_level] + 17; -} - -const EVENTS_STRUCT_TYPE* get_event_pointer(EVENTS_ENUM_TYPE event) { - return &events.entries[event]; -} - -EVENTS_LEVEL_TYPE get_event_level(void) { - return events.level; -} - -EMULATOR_STATUS get_emulator_status() { - switch (events.level) { - case EVENT_LEVEL_DEBUG: - case EVENT_LEVEL_INFO: - return EMULATOR_STATUS::STATUS_OK; - case EVENT_LEVEL_WARNING: - return EMULATOR_STATUS::STATUS_WARNING; - case EVENT_LEVEL_UPDATE: - return EMULATOR_STATUS::STATUS_UPDATING; - case EVENT_LEVEL_ERROR: - return EMULATOR_STATUS::STATUS_ERROR; - default: - return EMULATOR_STATUS::STATUS_OK; - } -} - -const char* get_emulator_status_string(EMULATOR_STATUS status) { - // Return the status string but skip "STATUS_" that should always be first - return EMULATOR_STATUS_STRING[status] + 7; -} - -/* Local functions */ - -static void set_event(EVENTS_ENUM_TYPE event, uint8_t data, bool latched) { - // Just some defensive stuff if someone sets an unknown event - if (event >= EVENT_NOF_EVENTS) { - event = EVENT_UNKNOWN_EVENT_SET; + void set_event_MQTTpublished(EVENTS_ENUM_TYPE event) { + events.entries[event].MQTTpublished = true; } - // If the event is already set, no reason to continue - if ((events.entries[event].state != EVENT_STATE_ACTIVE) && - (events.entries[event].state != EVENT_STATE_ACTIVE_LATCHED)) { - events.entries[event].occurences++; - events.entries[event].MQTTpublished = false; - - DEBUG_PRINTF("Event: %s\n", get_event_message_string(event).c_str()); - } - - // We should set the event, update event info - events.entries[event].timestamp = millis64(); - events.entries[event].data = data; - // Check if the event is latching - events.entries[event].state = latched ? EVENT_STATE_ACTIVE_LATCHED : EVENT_STATE_ACTIVE; - - // Update event level, only upwards. Downward changes are done in Software.ino:loop() - events.level = (EVENTS_LEVEL_TYPE)max(events.level, events.entries[event].level); - - update_bms_status(); -} - -static void update_bms_status(void) { - switch (events.level) { - case EVENT_LEVEL_INFO: - case EVENT_LEVEL_WARNING: - case EVENT_LEVEL_DEBUG: - datalayer.battery.status.bms_status = ACTIVE; - break; - case EVENT_LEVEL_UPDATE: - datalayer.battery.status.bms_status = UPDATING; - break; - case EVENT_LEVEL_ERROR: - datalayer.battery.status.bms_status = FAULT; - break; - default: - break; - } -} - -// Function to compare events by timestamp descending -bool compareEventsByTimestampDesc(const EventData& a, const EventData& b) { - return a.event_pointer->timestamp > b.event_pointer->timestamp; -} - -// Function to compare events by timestamp ascending -bool compareEventsByTimestampAsc(const EventData& a, const EventData& b) { - return a.event_pointer->timestamp < b.event_pointer->timestamp; -} - -static void update_event_level(void) { - EVENTS_LEVEL_TYPE temporary_level = EVENT_LEVEL_INFO; - for (uint8_t i = 0u; i < EVENT_NOF_EVENTS; i++) { - if ((events.entries[i].state == EVENT_STATE_ACTIVE) || (events.entries[i].state == EVENT_STATE_ACTIVE_LATCHED)) { - temporary_level = (EVENTS_LEVEL_TYPE)max(events.entries[i].level, temporary_level); + String get_event_message_string(EVENTS_ENUM_TYPE event) { + switch (event) { + case EVENT_CANMCP2517FD_INIT_FAILURE: + return "CAN-FD initialization failed. Check hardware or bitrate settings"; + case EVENT_CANMCP2515_INIT_FAILURE: + return "CAN-MCP addon initialization failed. Check hardware"; + case EVENT_CANFD_BUFFER_FULL: + return "MCP2518FD message failed to send. Buffer full or no one on the bus to ACK the message!"; + case EVENT_CAN_BUFFER_FULL: + return "MCP2515 message failed to send. Buffer full or no one on the bus to ACK the message!"; + case EVENT_TASK_OVERRUN: + return "Task took too long to complete. CPU load might be too high. Info message, no action required."; + case EVENT_CAN_CORRUPTED_WARNING: + return "High amount of corrupted CAN messages detected. Check CAN wire shielding!"; + case EVENT_CAN_NATIVE_TX_FAILURE: + return "CAN_NATIVE failed to transmit, or no one on the bus to ACK the message!"; + case EVENT_CAN_BATTERY_MISSING: + return "Battery not sending messages via CAN for the last 60 seconds. Check wiring!"; + case EVENT_CAN_BATTERY2_MISSING: + return "Secondary battery not sending messages via CAN for the last 60 seconds. Check wiring!"; + case EVENT_CAN_CHARGER_MISSING: + return "Charger not sending messages via CAN for the last 60 seconds. Check wiring!"; + case EVENT_CAN_INVERTER_MISSING: + return "Inverter not sending messages via CAN for the last 60 seconds. Check wiring!"; + case EVENT_CONTACTOR_WELDED: + return "Contactors sticking/welded. Inspect battery with caution!"; + case EVENT_CONTACTOR_OPEN: + return "Battery decided to open contactors. Inspect battery!"; + case EVENT_CPU_OVERHEATING: + return "Battery-Emulator CPU overheating! Increase airflow/cooling to increase hardware lifespan!"; + case EVENT_CPU_OVERHEATED: + return "Battery-Emulator CPU melting! Performing controlled shutdown until temperature drops!"; + case EVENT_CHARGE_LIMIT_EXCEEDED: + return "Inverter is charging faster than battery is allowing."; + case EVENT_DISCHARGE_LIMIT_EXCEEDED: + return "Inverter is discharging faster than battery is allowing."; + case EVENT_WATER_INGRESS: + return "Water leakage inside battery detected. Operation halted. Inspect battery!"; + case EVENT_12V_LOW: + return "12V battery source below required voltage to safely close contactors. Inspect the supply/battery!"; + case EVENT_SOC_PLAUSIBILITY_ERROR: + return "SOC reported by battery not plausible. Restart battery!"; + case EVENT_SOC_UNAVAILABLE: + return "SOC not sent by BMS. Calibrate BMS via app."; + case EVENT_STALE_VALUE: + return "Important values detected as stale. System might have locked up!"; + case EVENT_KWH_PLAUSIBILITY_ERROR: + return "kWh remaining reported by battery not plausible. Battery needs cycling."; + case EVENT_BALANCING_START: + return "Balancing has started"; + case EVENT_BALANCING_END: + return "Balancing has ended"; + case EVENT_BATTERY_EMPTY: + return "Battery is completely discharged"; + case EVENT_BATTERY_FULL: + return "Battery is fully charged"; + case EVENT_BATTERY_FUSE: + return "Battery internal fuse blown. Inspect battery"; + case EVENT_BATTERY_FROZEN: + return "Battery is too cold to operate optimally. Consider warming it up!"; + case EVENT_BATTERY_CAUTION: + return "Battery has raised a general caution flag. Might want to inspect it closely."; + case EVENT_BATTERY_CHG_STOP_REQ: + return "Battery raised caution indicator AND requested charge stop. Inspect battery status!"; + case EVENT_BATTERY_DISCHG_STOP_REQ: + return "Battery raised caution indicator AND requested discharge stop. Inspect battery status!"; + case EVENT_BATTERY_CHG_DISCHG_STOP_REQ: + return "Battery raised caution indicator AND requested charge/discharge stop. Inspect battery status!"; + case EVENT_BATTERY_REQUESTS_HEAT: + return "COLD BATTERY! Battery requesting heating pads to activate!"; + case EVENT_BATTERY_WARMED_UP: + return "Battery requesting heating pads to stop. The battery is now warm enough."; + case EVENT_BATTERY_OVERHEAT: + return "Battery overheated. Shutting down to prevent thermal runaway!"; + case EVENT_BATTERY_OVERVOLTAGE: + return "Battery exceeding maximum design voltage. Discharge battery to prevent damage!"; + case EVENT_BATTERY_UNDERVOLTAGE: + return "Battery under minimum design voltage. Charge battery to prevent damage!"; + case EVENT_BATTERY_VALUE_UNAVAILABLE: + return "Battery measurement unavailable. Check 12V power supply and battery wiring!"; + case EVENT_BATTERY_ISOLATION: + return "Battery reports isolation error. High voltage might be leaking to ground. Check battery!"; + case EVENT_VOLTAGE_DIFFERENCE: + return "Too large voltage diff between the batteries. Second battery cannot join the DC-link"; + case EVENT_SOH_DIFFERENCE: + return "Large deviation in State of health between packs. Inspect battery."; + case EVENT_SOH_LOW: + return "State of health critically low. Battery internal resistance too high to continue. Recycle " + "battery."; + case EVENT_HVIL_FAILURE: + return "Battery interlock loop broken. Check that high voltage / low voltage connectors are seated. " + "Battery will be disabled!"; + case EVENT_PRECHARGE_FAILURE: + return "Battery failed to precharge. Check that capacitor is seated on high voltage output."; + case EVENT_AUTOMATIC_PRECHARGE_FAILURE: + return "Automatic precharge failed to reach target voltae."; + case EVENT_INTERNAL_OPEN_FAULT: + return "High voltage cable removed while battery running. Opening contactors!"; + case EVENT_INVERTER_OPEN_CONTACTOR: + return "Inverter side opened contactors. Normal operation."; + case EVENT_INTERFACE_MISSING: + return "Configuration trying to use CAN interface not baked into the software. Recompile software!"; + case EVENT_ERROR_OPEN_CONTACTOR: + return "Too much time spent in error state. Opening contactors, not safe to continue. " + "Check other active ERROR code for reason. Reboot emulator after problem is solved!"; + case EVENT_MODBUS_INVERTER_MISSING: + return "Modbus inverter has not sent any data. Inspect communication wiring!"; + case EVENT_NO_ENABLE_DETECTED: + return "Inverter Enable line has not been active for a long time. Check Wiring!"; + case EVENT_CELL_CRITICAL_UNDER_VOLTAGE: + return "CELL VOLTAGE CRITICALLY LOW! Not possible to continue. Inspect battery!"; + case EVENT_CELL_UNDER_VOLTAGE: + return "Cell undervoltage. Further discharge not possible. Check balancing of cells"; + case EVENT_CELL_OVER_VOLTAGE: + return "Cell overvoltage. Further charging not possible. Check balancing of cells"; + case EVENT_CELL_CRITICAL_OVER_VOLTAGE: + return "CELL VOLTAGE CRITICALLY HIGH! Not possible to continue. Inspect battery!"; + case EVENT_CELL_DEVIATION_HIGH: + return "Large cell voltage deviation! Check balancing of cells"; + case EVENT_UNKNOWN_EVENT_SET: + return "An unknown event was set! Review your code!"; + case EVENT_DUMMY_INFO: + return "The dummy info event was set!"; // Don't change this event message! + case EVENT_DUMMY_DEBUG: + return "The dummy debug event was set!"; // Don't change this event message! + case EVENT_DUMMY_WARNING: + return "The dummy warning event was set!"; // Don't change this event message! + case EVENT_DUMMY_ERROR: + return "The dummy error event was set!"; // Don't change this event message! + case EVENT_PERSISTENT_SAVE_INFO: + return "Failed to save user settings. Namespace full?"; + case EVENT_SERIAL_RX_WARNING: + return "Error in serial function: No data received for some time, see data for minutes"; + case EVENT_SERIAL_RX_FAILURE: + return "Error in serial function: No data for a long time!"; + case EVENT_SERIAL_TX_FAILURE: + return "Error in serial function: No ACK from receiver!"; + case EVENT_SERIAL_TRANSMITTER_FAILURE: + return "Error in serial function: Some ERROR level fault in transmitter, received by receiver"; + case EVENT_SMA_PAIRING: + return "SMA inverter trying to pair, contactors will close and open according to Enable line"; + case EVENT_OTA_UPDATE: + return "OTA update started!"; + case EVENT_OTA_UPDATE_TIMEOUT: + return "OTA update timed out!"; + case EVENT_RESET_UNKNOWN: + return "The board was reset unexpectedly, and reason can't be determined"; + case EVENT_RESET_POWERON: + return "The board was reset from a power-on event. Normal operation"; + case EVENT_RESET_EXT: + return "The board was reset from an external pin"; + case EVENT_RESET_SW: + return "The board was reset via software, webserver or OTA. Normal operation"; + case EVENT_RESET_PANIC: + return "The board was reset due to an exception or panic. Inform developers!"; + case EVENT_RESET_INT_WDT: + return "The board was reset due to an interrupt watchdog timeout. Inform developers!"; + case EVENT_RESET_TASK_WDT: + return "The board was reset due to a task watchdog timeout. Inform developers!"; + case EVENT_RESET_WDT: + return "The board was reset due to other watchdog timeout. Inform developers!"; + case EVENT_RESET_DEEPSLEEP: + return "The board was reset after exiting deep sleep mode"; + case EVENT_RESET_BROWNOUT: + return "The board was reset due to a momentary low voltage condition. This is expected during certain " + "operations like flashing via USB"; + case EVENT_RESET_SDIO: + return "The board was reset over SDIO"; + case EVENT_RESET_USB: + return "The board was reset by the USB peripheral"; + case EVENT_RESET_JTAG: + return "The board was reset by JTAG"; + case EVENT_RESET_EFUSE: + return "The board was reset due to an efuse error"; + case EVENT_RESET_PWR_GLITCH: + return "The board was reset due to a detected power glitch"; + case EVENT_RESET_CPU_LOCKUP: + return "The board was reset due to CPU lockup. Inform developers!"; + case EVENT_RJXZS_LOG: + return "Error code active in RJXZS BMS. Clear via their smartphone app!"; + case EVENT_PAUSE_BEGIN: + return "The emulator is trying to pause the battery."; + case EVENT_PAUSE_END: + return "The emulator is attempting to resume battery operation from pause."; + case EVENT_PID_FAILED: + return "Failed to write PID request to battery"; + case EVENT_WIFI_CONNECT: + return "Wifi connected."; + case EVENT_WIFI_DISCONNECT: + return "Wifi disconnected."; + case EVENT_MQTT_CONNECT: + return "MQTT connected."; + case EVENT_MQTT_DISCONNECT: + return "MQTT disconnected."; + case EVENT_EQUIPMENT_STOP: + return "User requested stop, either via equipment stop circuit or webserver Open Contactor button"; + case EVENT_SD_INIT_FAILED: + return "SD card initialization failed, check hardware. Power must be removed to reset the SD card."; + case EVENT_PERIODIC_BMS_RESET: + return "BMS Reset Event Completed."; + case EVENT_PERIODIC_BMS_RESET_AT_INIT_SUCCESS: + return "Successfully syncronised with the NTP Server. BMS will reset every 24 hours at defined time"; + case EVENT_PERIODIC_BMS_RESET_AT_INIT_FAILED: + return "Failed to syncronise with the NTP Server. BMS will reset every 24 hours from when the emulator was " + "powered on"; + case EVENT_GPIO_CONFLICT: + return "GPIO Pin Conflict: The pin used by '" + esp32hal->failed_allocator() + "' is already allocated by '" + + esp32hal->conflicting_allocator() + "'. Please check your configuration and assign different pins."; + case EVENT_GPIO_NOT_DEFINED: + return "Missing GPIO Assignment: The component '" + esp32hal->failed_allocator() + + "' requires a GPIO pin that isn't configured. Please define a valid pin number in your settings."; + default: + return ""; } } - events.level = temporary_level; -} + + const char* get_event_enum_string(EVENTS_ENUM_TYPE event) { + // Return the event name but skip "EVENT_" that should always be first + return EVENTS_ENUM_TYPE_STRING[event] + 6; + } + + const char* get_event_level_string(EVENTS_ENUM_TYPE event) { + // Return the event level but skip "EVENT_LEVEL_" that should always be first + return EVENTS_LEVEL_TYPE_STRING[events.entries[event].level] + 12; + } + + const char* get_event_level_string(EVENTS_LEVEL_TYPE event_level) { + // Return the event level but skip "EVENT_LEVEL_TYPE_" that should always be first + return EVENTS_LEVEL_TYPE_STRING[event_level] + 17; + } + + const EVENTS_STRUCT_TYPE* get_event_pointer(EVENTS_ENUM_TYPE event) { + return &events.entries[event]; + } + + EVENTS_LEVEL_TYPE get_event_level(void) { + return events.level; + } + + EMULATOR_STATUS get_emulator_status() { + switch (events.level) { + case EVENT_LEVEL_DEBUG: + case EVENT_LEVEL_INFO: + return EMULATOR_STATUS::STATUS_OK; + case EVENT_LEVEL_WARNING: + return EMULATOR_STATUS::STATUS_WARNING; + case EVENT_LEVEL_UPDATE: + return EMULATOR_STATUS::STATUS_UPDATING; + case EVENT_LEVEL_ERROR: + return EMULATOR_STATUS::STATUS_ERROR; + default: + return EMULATOR_STATUS::STATUS_OK; + } + } + + const char* get_emulator_status_string(EMULATOR_STATUS status) { + // Return the status string but skip "STATUS_" that should always be first + return EMULATOR_STATUS_STRING[status] + 7; + } + + /* Local functions */ + + static void set_event(EVENTS_ENUM_TYPE event, uint8_t data, bool latched) { + // Just some defensive stuff if someone sets an unknown event + if (event >= EVENT_NOF_EVENTS) { + event = EVENT_UNKNOWN_EVENT_SET; + } + + // If the event is already set, no reason to continue + if ((events.entries[event].state != EVENT_STATE_ACTIVE) && + (events.entries[event].state != EVENT_STATE_ACTIVE_LATCHED)) { + events.entries[event].occurences++; + events.entries[event].MQTTpublished = false; + + DEBUG_PRINTF("Event: %s\n", get_event_message_string(event).c_str()); + } + + // We should set the event, update event info + events.entries[event].timestamp = millis64(); + events.entries[event].data = data; + // Check if the event is latching + events.entries[event].state = latched ? EVENT_STATE_ACTIVE_LATCHED : EVENT_STATE_ACTIVE; + + // Update event level, only upwards. Downward changes are done in Software.ino:loop() + events.level = (EVENTS_LEVEL_TYPE)max(events.level, events.entries[event].level); + + update_bms_status(); + } + + static void update_bms_status(void) { + switch (events.level) { + case EVENT_LEVEL_INFO: + case EVENT_LEVEL_WARNING: + case EVENT_LEVEL_DEBUG: + datalayer.battery.status.bms_status = ACTIVE; + break; + case EVENT_LEVEL_UPDATE: + datalayer.battery.status.bms_status = UPDATING; + break; + case EVENT_LEVEL_ERROR: + datalayer.battery.status.bms_status = FAULT; + break; + default: + break; + } + } + + // Function to compare events by timestamp descending + bool compareEventsByTimestampDesc(const EventData& a, const EventData& b) { + return a.event_pointer->timestamp > b.event_pointer->timestamp; + } + + // Function to compare events by timestamp ascending + bool compareEventsByTimestampAsc(const EventData& a, const EventData& b) { + return a.event_pointer->timestamp < b.event_pointer->timestamp; + } + + static void update_event_level(void) { + EVENTS_LEVEL_TYPE temporary_level = EVENT_LEVEL_INFO; + for (uint8_t i = 0u; i < EVENT_NOF_EVENTS; i++) { + if ((events.entries[i].state == EVENT_STATE_ACTIVE) || (events.entries[i].state == EVENT_STATE_ACTIVE_LATCHED)) { + temporary_level = (EVENTS_LEVEL_TYPE)max(events.entries[i].level, temporary_level); + } + } + events.level = temporary_level; + } From 127094f9fa4fb6cff6cfcb1021f0e45202cfff40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sat, 30 Aug 2025 00:30:49 +0300 Subject: [PATCH 079/245] Fix unit test failing --- Software/src/devboard/utils/events.cpp | 653 +++++++++++++------------ 1 file changed, 327 insertions(+), 326 deletions(-) diff --git a/Software/src/devboard/utils/events.cpp b/Software/src/devboard/utils/events.cpp index 3d912124..717ae3a1 100644 --- a/Software/src/devboard/utils/events.cpp +++ b/Software/src/devboard/utils/events.cpp @@ -159,334 +159,335 @@ void reset_all_events() { } events.level = EVENT_LEVEL_INFO; update_bms_status(); +} - void set_event_MQTTpublished(EVENTS_ENUM_TYPE event) { - events.entries[event].MQTTpublished = true; +void set_event_MQTTpublished(EVENTS_ENUM_TYPE event) { + events.entries[event].MQTTpublished = true; +} + +String get_event_message_string(EVENTS_ENUM_TYPE event) { + switch (event) { + case EVENT_CANMCP2517FD_INIT_FAILURE: + return "CAN-FD initialization failed. Check hardware or bitrate settings"; + case EVENT_CANMCP2515_INIT_FAILURE: + return "CAN-MCP addon initialization failed. Check hardware"; + case EVENT_CANFD_BUFFER_FULL: + return "MCP2518FD message failed to send. Buffer full or no one on the bus to ACK the message!"; + case EVENT_CAN_BUFFER_FULL: + return "MCP2515 message failed to send. Buffer full or no one on the bus to ACK the message!"; + case EVENT_TASK_OVERRUN: + return "Task took too long to complete. CPU load might be too high. Info message, no action required."; + case EVENT_CAN_CORRUPTED_WARNING: + return "High amount of corrupted CAN messages detected. Check CAN wire shielding!"; + case EVENT_CAN_NATIVE_TX_FAILURE: + return "CAN_NATIVE failed to transmit, or no one on the bus to ACK the message!"; + case EVENT_CAN_BATTERY_MISSING: + return "Battery not sending messages via CAN for the last 60 seconds. Check wiring!"; + case EVENT_CAN_BATTERY2_MISSING: + return "Secondary battery not sending messages via CAN for the last 60 seconds. Check wiring!"; + case EVENT_CAN_CHARGER_MISSING: + return "Charger not sending messages via CAN for the last 60 seconds. Check wiring!"; + case EVENT_CAN_INVERTER_MISSING: + return "Inverter not sending messages via CAN for the last 60 seconds. Check wiring!"; + case EVENT_CONTACTOR_WELDED: + return "Contactors sticking/welded. Inspect battery with caution!"; + case EVENT_CONTACTOR_OPEN: + return "Battery decided to open contactors. Inspect battery!"; + case EVENT_CPU_OVERHEATING: + return "Battery-Emulator CPU overheating! Increase airflow/cooling to increase hardware lifespan!"; + case EVENT_CPU_OVERHEATED: + return "Battery-Emulator CPU melting! Performing controlled shutdown until temperature drops!"; + case EVENT_CHARGE_LIMIT_EXCEEDED: + return "Inverter is charging faster than battery is allowing."; + case EVENT_DISCHARGE_LIMIT_EXCEEDED: + return "Inverter is discharging faster than battery is allowing."; + case EVENT_WATER_INGRESS: + return "Water leakage inside battery detected. Operation halted. Inspect battery!"; + case EVENT_12V_LOW: + return "12V battery source below required voltage to safely close contactors. Inspect the supply/battery!"; + case EVENT_SOC_PLAUSIBILITY_ERROR: + return "SOC reported by battery not plausible. Restart battery!"; + case EVENT_SOC_UNAVAILABLE: + return "SOC not sent by BMS. Calibrate BMS via app."; + case EVENT_STALE_VALUE: + return "Important values detected as stale. System might have locked up!"; + case EVENT_KWH_PLAUSIBILITY_ERROR: + return "kWh remaining reported by battery not plausible. Battery needs cycling."; + case EVENT_BALANCING_START: + return "Balancing has started"; + case EVENT_BALANCING_END: + return "Balancing has ended"; + case EVENT_BATTERY_EMPTY: + return "Battery is completely discharged"; + case EVENT_BATTERY_FULL: + return "Battery is fully charged"; + case EVENT_BATTERY_FUSE: + return "Battery internal fuse blown. Inspect battery"; + case EVENT_BATTERY_FROZEN: + return "Battery is too cold to operate optimally. Consider warming it up!"; + case EVENT_BATTERY_CAUTION: + return "Battery has raised a general caution flag. Might want to inspect it closely."; + case EVENT_BATTERY_CHG_STOP_REQ: + return "Battery raised caution indicator AND requested charge stop. Inspect battery status!"; + case EVENT_BATTERY_DISCHG_STOP_REQ: + return "Battery raised caution indicator AND requested discharge stop. Inspect battery status!"; + case EVENT_BATTERY_CHG_DISCHG_STOP_REQ: + return "Battery raised caution indicator AND requested charge/discharge stop. Inspect battery status!"; + case EVENT_BATTERY_REQUESTS_HEAT: + return "COLD BATTERY! Battery requesting heating pads to activate!"; + case EVENT_BATTERY_WARMED_UP: + return "Battery requesting heating pads to stop. The battery is now warm enough."; + case EVENT_BATTERY_OVERHEAT: + return "Battery overheated. Shutting down to prevent thermal runaway!"; + case EVENT_BATTERY_OVERVOLTAGE: + return "Battery exceeding maximum design voltage. Discharge battery to prevent damage!"; + case EVENT_BATTERY_UNDERVOLTAGE: + return "Battery under minimum design voltage. Charge battery to prevent damage!"; + case EVENT_BATTERY_VALUE_UNAVAILABLE: + return "Battery measurement unavailable. Check 12V power supply and battery wiring!"; + case EVENT_BATTERY_ISOLATION: + return "Battery reports isolation error. High voltage might be leaking to ground. Check battery!"; + case EVENT_VOLTAGE_DIFFERENCE: + return "Too large voltage diff between the batteries. Second battery cannot join the DC-link"; + case EVENT_SOH_DIFFERENCE: + return "Large deviation in State of health between packs. Inspect battery."; + case EVENT_SOH_LOW: + return "State of health critically low. Battery internal resistance too high to continue. Recycle " + "battery."; + case EVENT_HVIL_FAILURE: + return "Battery interlock loop broken. Check that high voltage / low voltage connectors are seated. " + "Battery will be disabled!"; + case EVENT_PRECHARGE_FAILURE: + return "Battery failed to precharge. Check that capacitor is seated on high voltage output."; + case EVENT_AUTOMATIC_PRECHARGE_FAILURE: + return "Automatic precharge failed to reach target voltae."; + case EVENT_INTERNAL_OPEN_FAULT: + return "High voltage cable removed while battery running. Opening contactors!"; + case EVENT_INVERTER_OPEN_CONTACTOR: + return "Inverter side opened contactors. Normal operation."; + case EVENT_INTERFACE_MISSING: + return "Configuration trying to use CAN interface not baked into the software. Recompile software!"; + case EVENT_ERROR_OPEN_CONTACTOR: + return "Too much time spent in error state. Opening contactors, not safe to continue. " + "Check other active ERROR code for reason. Reboot emulator after problem is solved!"; + case EVENT_MODBUS_INVERTER_MISSING: + return "Modbus inverter has not sent any data. Inspect communication wiring!"; + case EVENT_NO_ENABLE_DETECTED: + return "Inverter Enable line has not been active for a long time. Check Wiring!"; + case EVENT_CELL_CRITICAL_UNDER_VOLTAGE: + return "CELL VOLTAGE CRITICALLY LOW! Not possible to continue. Inspect battery!"; + case EVENT_CELL_UNDER_VOLTAGE: + return "Cell undervoltage. Further discharge not possible. Check balancing of cells"; + case EVENT_CELL_OVER_VOLTAGE: + return "Cell overvoltage. Further charging not possible. Check balancing of cells"; + case EVENT_CELL_CRITICAL_OVER_VOLTAGE: + return "CELL VOLTAGE CRITICALLY HIGH! Not possible to continue. Inspect battery!"; + case EVENT_CELL_DEVIATION_HIGH: + return "Large cell voltage deviation! Check balancing of cells"; + case EVENT_UNKNOWN_EVENT_SET: + return "An unknown event was set! Review your code!"; + case EVENT_DUMMY_INFO: + return "The dummy info event was set!"; // Don't change this event message! + case EVENT_DUMMY_DEBUG: + return "The dummy debug event was set!"; // Don't change this event message! + case EVENT_DUMMY_WARNING: + return "The dummy warning event was set!"; // Don't change this event message! + case EVENT_DUMMY_ERROR: + return "The dummy error event was set!"; // Don't change this event message! + case EVENT_PERSISTENT_SAVE_INFO: + return "Failed to save user settings. Namespace full?"; + case EVENT_SERIAL_RX_WARNING: + return "Error in serial function: No data received for some time, see data for minutes"; + case EVENT_SERIAL_RX_FAILURE: + return "Error in serial function: No data for a long time!"; + case EVENT_SERIAL_TX_FAILURE: + return "Error in serial function: No ACK from receiver!"; + case EVENT_SERIAL_TRANSMITTER_FAILURE: + return "Error in serial function: Some ERROR level fault in transmitter, received by receiver"; + case EVENT_SMA_PAIRING: + return "SMA inverter trying to pair, contactors will close and open according to Enable line"; + case EVENT_OTA_UPDATE: + return "OTA update started!"; + case EVENT_OTA_UPDATE_TIMEOUT: + return "OTA update timed out!"; + case EVENT_RESET_UNKNOWN: + return "The board was reset unexpectedly, and reason can't be determined"; + case EVENT_RESET_POWERON: + return "The board was reset from a power-on event. Normal operation"; + case EVENT_RESET_EXT: + return "The board was reset from an external pin"; + case EVENT_RESET_SW: + return "The board was reset via software, webserver or OTA. Normal operation"; + case EVENT_RESET_PANIC: + return "The board was reset due to an exception or panic. Inform developers!"; + case EVENT_RESET_INT_WDT: + return "The board was reset due to an interrupt watchdog timeout. Inform developers!"; + case EVENT_RESET_TASK_WDT: + return "The board was reset due to a task watchdog timeout. Inform developers!"; + case EVENT_RESET_WDT: + return "The board was reset due to other watchdog timeout. Inform developers!"; + case EVENT_RESET_DEEPSLEEP: + return "The board was reset after exiting deep sleep mode"; + case EVENT_RESET_BROWNOUT: + return "The board was reset due to a momentary low voltage condition. This is expected during certain " + "operations like flashing via USB"; + case EVENT_RESET_SDIO: + return "The board was reset over SDIO"; + case EVENT_RESET_USB: + return "The board was reset by the USB peripheral"; + case EVENT_RESET_JTAG: + return "The board was reset by JTAG"; + case EVENT_RESET_EFUSE: + return "The board was reset due to an efuse error"; + case EVENT_RESET_PWR_GLITCH: + return "The board was reset due to a detected power glitch"; + case EVENT_RESET_CPU_LOCKUP: + return "The board was reset due to CPU lockup. Inform developers!"; + case EVENT_RJXZS_LOG: + return "Error code active in RJXZS BMS. Clear via their smartphone app!"; + case EVENT_PAUSE_BEGIN: + return "The emulator is trying to pause the battery."; + case EVENT_PAUSE_END: + return "The emulator is attempting to resume battery operation from pause."; + case EVENT_PID_FAILED: + return "Failed to write PID request to battery"; + case EVENT_WIFI_CONNECT: + return "Wifi connected."; + case EVENT_WIFI_DISCONNECT: + return "Wifi disconnected."; + case EVENT_MQTT_CONNECT: + return "MQTT connected."; + case EVENT_MQTT_DISCONNECT: + return "MQTT disconnected."; + case EVENT_EQUIPMENT_STOP: + return "User requested stop, either via equipment stop circuit or webserver Open Contactor button"; + case EVENT_SD_INIT_FAILED: + return "SD card initialization failed, check hardware. Power must be removed to reset the SD card."; + case EVENT_PERIODIC_BMS_RESET: + return "BMS Reset Event Completed."; + case EVENT_PERIODIC_BMS_RESET_AT_INIT_SUCCESS: + return "Successfully syncronised with the NTP Server. BMS will reset every 24 hours at defined time"; + case EVENT_PERIODIC_BMS_RESET_AT_INIT_FAILED: + return "Failed to syncronise with the NTP Server. BMS will reset every 24 hours from when the emulator was " + "powered on"; + case EVENT_GPIO_CONFLICT: + return "GPIO Pin Conflict: The pin used by '" + esp32hal->failed_allocator() + "' is already allocated by '" + + esp32hal->conflicting_allocator() + "'. Please check your configuration and assign different pins."; + case EVENT_GPIO_NOT_DEFINED: + return "Missing GPIO Assignment: The component '" + esp32hal->failed_allocator() + + "' requires a GPIO pin that isn't configured. Please define a valid pin number in your settings."; + default: + return ""; + } +} + +const char* get_event_enum_string(EVENTS_ENUM_TYPE event) { + // Return the event name but skip "EVENT_" that should always be first + return EVENTS_ENUM_TYPE_STRING[event] + 6; +} + +const char* get_event_level_string(EVENTS_ENUM_TYPE event) { + // Return the event level but skip "EVENT_LEVEL_" that should always be first + return EVENTS_LEVEL_TYPE_STRING[events.entries[event].level] + 12; +} + +const char* get_event_level_string(EVENTS_LEVEL_TYPE event_level) { + // Return the event level but skip "EVENT_LEVEL_TYPE_" that should always be first + return EVENTS_LEVEL_TYPE_STRING[event_level] + 17; +} + +const EVENTS_STRUCT_TYPE* get_event_pointer(EVENTS_ENUM_TYPE event) { + return &events.entries[event]; +} + +EVENTS_LEVEL_TYPE get_event_level(void) { + return events.level; +} + +EMULATOR_STATUS get_emulator_status() { + switch (events.level) { + case EVENT_LEVEL_DEBUG: + case EVENT_LEVEL_INFO: + return EMULATOR_STATUS::STATUS_OK; + case EVENT_LEVEL_WARNING: + return EMULATOR_STATUS::STATUS_WARNING; + case EVENT_LEVEL_UPDATE: + return EMULATOR_STATUS::STATUS_UPDATING; + case EVENT_LEVEL_ERROR: + return EMULATOR_STATUS::STATUS_ERROR; + default: + return EMULATOR_STATUS::STATUS_OK; + } +} + +const char* get_emulator_status_string(EMULATOR_STATUS status) { + // Return the status string but skip "STATUS_" that should always be first + return EMULATOR_STATUS_STRING[status] + 7; +} + +/* Local functions */ + +static void set_event(EVENTS_ENUM_TYPE event, uint8_t data, bool latched) { + // Just some defensive stuff if someone sets an unknown event + if (event >= EVENT_NOF_EVENTS) { + event = EVENT_UNKNOWN_EVENT_SET; } - String get_event_message_string(EVENTS_ENUM_TYPE event) { - switch (event) { - case EVENT_CANMCP2517FD_INIT_FAILURE: - return "CAN-FD initialization failed. Check hardware or bitrate settings"; - case EVENT_CANMCP2515_INIT_FAILURE: - return "CAN-MCP addon initialization failed. Check hardware"; - case EVENT_CANFD_BUFFER_FULL: - return "MCP2518FD message failed to send. Buffer full or no one on the bus to ACK the message!"; - case EVENT_CAN_BUFFER_FULL: - return "MCP2515 message failed to send. Buffer full or no one on the bus to ACK the message!"; - case EVENT_TASK_OVERRUN: - return "Task took too long to complete. CPU load might be too high. Info message, no action required."; - case EVENT_CAN_CORRUPTED_WARNING: - return "High amount of corrupted CAN messages detected. Check CAN wire shielding!"; - case EVENT_CAN_NATIVE_TX_FAILURE: - return "CAN_NATIVE failed to transmit, or no one on the bus to ACK the message!"; - case EVENT_CAN_BATTERY_MISSING: - return "Battery not sending messages via CAN for the last 60 seconds. Check wiring!"; - case EVENT_CAN_BATTERY2_MISSING: - return "Secondary battery not sending messages via CAN for the last 60 seconds. Check wiring!"; - case EVENT_CAN_CHARGER_MISSING: - return "Charger not sending messages via CAN for the last 60 seconds. Check wiring!"; - case EVENT_CAN_INVERTER_MISSING: - return "Inverter not sending messages via CAN for the last 60 seconds. Check wiring!"; - case EVENT_CONTACTOR_WELDED: - return "Contactors sticking/welded. Inspect battery with caution!"; - case EVENT_CONTACTOR_OPEN: - return "Battery decided to open contactors. Inspect battery!"; - case EVENT_CPU_OVERHEATING: - return "Battery-Emulator CPU overheating! Increase airflow/cooling to increase hardware lifespan!"; - case EVENT_CPU_OVERHEATED: - return "Battery-Emulator CPU melting! Performing controlled shutdown until temperature drops!"; - case EVENT_CHARGE_LIMIT_EXCEEDED: - return "Inverter is charging faster than battery is allowing."; - case EVENT_DISCHARGE_LIMIT_EXCEEDED: - return "Inverter is discharging faster than battery is allowing."; - case EVENT_WATER_INGRESS: - return "Water leakage inside battery detected. Operation halted. Inspect battery!"; - case EVENT_12V_LOW: - return "12V battery source below required voltage to safely close contactors. Inspect the supply/battery!"; - case EVENT_SOC_PLAUSIBILITY_ERROR: - return "SOC reported by battery not plausible. Restart battery!"; - case EVENT_SOC_UNAVAILABLE: - return "SOC not sent by BMS. Calibrate BMS via app."; - case EVENT_STALE_VALUE: - return "Important values detected as stale. System might have locked up!"; - case EVENT_KWH_PLAUSIBILITY_ERROR: - return "kWh remaining reported by battery not plausible. Battery needs cycling."; - case EVENT_BALANCING_START: - return "Balancing has started"; - case EVENT_BALANCING_END: - return "Balancing has ended"; - case EVENT_BATTERY_EMPTY: - return "Battery is completely discharged"; - case EVENT_BATTERY_FULL: - return "Battery is fully charged"; - case EVENT_BATTERY_FUSE: - return "Battery internal fuse blown. Inspect battery"; - case EVENT_BATTERY_FROZEN: - return "Battery is too cold to operate optimally. Consider warming it up!"; - case EVENT_BATTERY_CAUTION: - return "Battery has raised a general caution flag. Might want to inspect it closely."; - case EVENT_BATTERY_CHG_STOP_REQ: - return "Battery raised caution indicator AND requested charge stop. Inspect battery status!"; - case EVENT_BATTERY_DISCHG_STOP_REQ: - return "Battery raised caution indicator AND requested discharge stop. Inspect battery status!"; - case EVENT_BATTERY_CHG_DISCHG_STOP_REQ: - return "Battery raised caution indicator AND requested charge/discharge stop. Inspect battery status!"; - case EVENT_BATTERY_REQUESTS_HEAT: - return "COLD BATTERY! Battery requesting heating pads to activate!"; - case EVENT_BATTERY_WARMED_UP: - return "Battery requesting heating pads to stop. The battery is now warm enough."; - case EVENT_BATTERY_OVERHEAT: - return "Battery overheated. Shutting down to prevent thermal runaway!"; - case EVENT_BATTERY_OVERVOLTAGE: - return "Battery exceeding maximum design voltage. Discharge battery to prevent damage!"; - case EVENT_BATTERY_UNDERVOLTAGE: - return "Battery under minimum design voltage. Charge battery to prevent damage!"; - case EVENT_BATTERY_VALUE_UNAVAILABLE: - return "Battery measurement unavailable. Check 12V power supply and battery wiring!"; - case EVENT_BATTERY_ISOLATION: - return "Battery reports isolation error. High voltage might be leaking to ground. Check battery!"; - case EVENT_VOLTAGE_DIFFERENCE: - return "Too large voltage diff between the batteries. Second battery cannot join the DC-link"; - case EVENT_SOH_DIFFERENCE: - return "Large deviation in State of health between packs. Inspect battery."; - case EVENT_SOH_LOW: - return "State of health critically low. Battery internal resistance too high to continue. Recycle " - "battery."; - case EVENT_HVIL_FAILURE: - return "Battery interlock loop broken. Check that high voltage / low voltage connectors are seated. " - "Battery will be disabled!"; - case EVENT_PRECHARGE_FAILURE: - return "Battery failed to precharge. Check that capacitor is seated on high voltage output."; - case EVENT_AUTOMATIC_PRECHARGE_FAILURE: - return "Automatic precharge failed to reach target voltae."; - case EVENT_INTERNAL_OPEN_FAULT: - return "High voltage cable removed while battery running. Opening contactors!"; - case EVENT_INVERTER_OPEN_CONTACTOR: - return "Inverter side opened contactors. Normal operation."; - case EVENT_INTERFACE_MISSING: - return "Configuration trying to use CAN interface not baked into the software. Recompile software!"; - case EVENT_ERROR_OPEN_CONTACTOR: - return "Too much time spent in error state. Opening contactors, not safe to continue. " - "Check other active ERROR code for reason. Reboot emulator after problem is solved!"; - case EVENT_MODBUS_INVERTER_MISSING: - return "Modbus inverter has not sent any data. Inspect communication wiring!"; - case EVENT_NO_ENABLE_DETECTED: - return "Inverter Enable line has not been active for a long time. Check Wiring!"; - case EVENT_CELL_CRITICAL_UNDER_VOLTAGE: - return "CELL VOLTAGE CRITICALLY LOW! Not possible to continue. Inspect battery!"; - case EVENT_CELL_UNDER_VOLTAGE: - return "Cell undervoltage. Further discharge not possible. Check balancing of cells"; - case EVENT_CELL_OVER_VOLTAGE: - return "Cell overvoltage. Further charging not possible. Check balancing of cells"; - case EVENT_CELL_CRITICAL_OVER_VOLTAGE: - return "CELL VOLTAGE CRITICALLY HIGH! Not possible to continue. Inspect battery!"; - case EVENT_CELL_DEVIATION_HIGH: - return "Large cell voltage deviation! Check balancing of cells"; - case EVENT_UNKNOWN_EVENT_SET: - return "An unknown event was set! Review your code!"; - case EVENT_DUMMY_INFO: - return "The dummy info event was set!"; // Don't change this event message! - case EVENT_DUMMY_DEBUG: - return "The dummy debug event was set!"; // Don't change this event message! - case EVENT_DUMMY_WARNING: - return "The dummy warning event was set!"; // Don't change this event message! - case EVENT_DUMMY_ERROR: - return "The dummy error event was set!"; // Don't change this event message! - case EVENT_PERSISTENT_SAVE_INFO: - return "Failed to save user settings. Namespace full?"; - case EVENT_SERIAL_RX_WARNING: - return "Error in serial function: No data received for some time, see data for minutes"; - case EVENT_SERIAL_RX_FAILURE: - return "Error in serial function: No data for a long time!"; - case EVENT_SERIAL_TX_FAILURE: - return "Error in serial function: No ACK from receiver!"; - case EVENT_SERIAL_TRANSMITTER_FAILURE: - return "Error in serial function: Some ERROR level fault in transmitter, received by receiver"; - case EVENT_SMA_PAIRING: - return "SMA inverter trying to pair, contactors will close and open according to Enable line"; - case EVENT_OTA_UPDATE: - return "OTA update started!"; - case EVENT_OTA_UPDATE_TIMEOUT: - return "OTA update timed out!"; - case EVENT_RESET_UNKNOWN: - return "The board was reset unexpectedly, and reason can't be determined"; - case EVENT_RESET_POWERON: - return "The board was reset from a power-on event. Normal operation"; - case EVENT_RESET_EXT: - return "The board was reset from an external pin"; - case EVENT_RESET_SW: - return "The board was reset via software, webserver or OTA. Normal operation"; - case EVENT_RESET_PANIC: - return "The board was reset due to an exception or panic. Inform developers!"; - case EVENT_RESET_INT_WDT: - return "The board was reset due to an interrupt watchdog timeout. Inform developers!"; - case EVENT_RESET_TASK_WDT: - return "The board was reset due to a task watchdog timeout. Inform developers!"; - case EVENT_RESET_WDT: - return "The board was reset due to other watchdog timeout. Inform developers!"; - case EVENT_RESET_DEEPSLEEP: - return "The board was reset after exiting deep sleep mode"; - case EVENT_RESET_BROWNOUT: - return "The board was reset due to a momentary low voltage condition. This is expected during certain " - "operations like flashing via USB"; - case EVENT_RESET_SDIO: - return "The board was reset over SDIO"; - case EVENT_RESET_USB: - return "The board was reset by the USB peripheral"; - case EVENT_RESET_JTAG: - return "The board was reset by JTAG"; - case EVENT_RESET_EFUSE: - return "The board was reset due to an efuse error"; - case EVENT_RESET_PWR_GLITCH: - return "The board was reset due to a detected power glitch"; - case EVENT_RESET_CPU_LOCKUP: - return "The board was reset due to CPU lockup. Inform developers!"; - case EVENT_RJXZS_LOG: - return "Error code active in RJXZS BMS. Clear via their smartphone app!"; - case EVENT_PAUSE_BEGIN: - return "The emulator is trying to pause the battery."; - case EVENT_PAUSE_END: - return "The emulator is attempting to resume battery operation from pause."; - case EVENT_PID_FAILED: - return "Failed to write PID request to battery"; - case EVENT_WIFI_CONNECT: - return "Wifi connected."; - case EVENT_WIFI_DISCONNECT: - return "Wifi disconnected."; - case EVENT_MQTT_CONNECT: - return "MQTT connected."; - case EVENT_MQTT_DISCONNECT: - return "MQTT disconnected."; - case EVENT_EQUIPMENT_STOP: - return "User requested stop, either via equipment stop circuit or webserver Open Contactor button"; - case EVENT_SD_INIT_FAILED: - return "SD card initialization failed, check hardware. Power must be removed to reset the SD card."; - case EVENT_PERIODIC_BMS_RESET: - return "BMS Reset Event Completed."; - case EVENT_PERIODIC_BMS_RESET_AT_INIT_SUCCESS: - return "Successfully syncronised with the NTP Server. BMS will reset every 24 hours at defined time"; - case EVENT_PERIODIC_BMS_RESET_AT_INIT_FAILED: - return "Failed to syncronise with the NTP Server. BMS will reset every 24 hours from when the emulator was " - "powered on"; - case EVENT_GPIO_CONFLICT: - return "GPIO Pin Conflict: The pin used by '" + esp32hal->failed_allocator() + "' is already allocated by '" + - esp32hal->conflicting_allocator() + "'. Please check your configuration and assign different pins."; - case EVENT_GPIO_NOT_DEFINED: - return "Missing GPIO Assignment: The component '" + esp32hal->failed_allocator() + - "' requires a GPIO pin that isn't configured. Please define a valid pin number in your settings."; - default: - return ""; + // If the event is already set, no reason to continue + if ((events.entries[event].state != EVENT_STATE_ACTIVE) && + (events.entries[event].state != EVENT_STATE_ACTIVE_LATCHED)) { + events.entries[event].occurences++; + events.entries[event].MQTTpublished = false; + + DEBUG_PRINTF("Event: %s\n", get_event_message_string(event).c_str()); + } + + // We should set the event, update event info + events.entries[event].timestamp = millis64(); + events.entries[event].data = data; + // Check if the event is latching + events.entries[event].state = latched ? EVENT_STATE_ACTIVE_LATCHED : EVENT_STATE_ACTIVE; + + // Update event level, only upwards. Downward changes are done in Software.ino:loop() + events.level = (EVENTS_LEVEL_TYPE)max(events.level, events.entries[event].level); + + update_bms_status(); +} + +static void update_bms_status(void) { + switch (events.level) { + case EVENT_LEVEL_INFO: + case EVENT_LEVEL_WARNING: + case EVENT_LEVEL_DEBUG: + datalayer.battery.status.bms_status = ACTIVE; + break; + case EVENT_LEVEL_UPDATE: + datalayer.battery.status.bms_status = UPDATING; + break; + case EVENT_LEVEL_ERROR: + datalayer.battery.status.bms_status = FAULT; + break; + default: + break; + } +} + +// Function to compare events by timestamp descending +bool compareEventsByTimestampDesc(const EventData& a, const EventData& b) { + return a.event_pointer->timestamp > b.event_pointer->timestamp; +} + +// Function to compare events by timestamp ascending +bool compareEventsByTimestampAsc(const EventData& a, const EventData& b) { + return a.event_pointer->timestamp < b.event_pointer->timestamp; +} + +static void update_event_level(void) { + EVENTS_LEVEL_TYPE temporary_level = EVENT_LEVEL_INFO; + for (uint8_t i = 0u; i < EVENT_NOF_EVENTS; i++) { + if ((events.entries[i].state == EVENT_STATE_ACTIVE) || (events.entries[i].state == EVENT_STATE_ACTIVE_LATCHED)) { + temporary_level = (EVENTS_LEVEL_TYPE)max(events.entries[i].level, temporary_level); } } - - const char* get_event_enum_string(EVENTS_ENUM_TYPE event) { - // Return the event name but skip "EVENT_" that should always be first - return EVENTS_ENUM_TYPE_STRING[event] + 6; - } - - const char* get_event_level_string(EVENTS_ENUM_TYPE event) { - // Return the event level but skip "EVENT_LEVEL_" that should always be first - return EVENTS_LEVEL_TYPE_STRING[events.entries[event].level] + 12; - } - - const char* get_event_level_string(EVENTS_LEVEL_TYPE event_level) { - // Return the event level but skip "EVENT_LEVEL_TYPE_" that should always be first - return EVENTS_LEVEL_TYPE_STRING[event_level] + 17; - } - - const EVENTS_STRUCT_TYPE* get_event_pointer(EVENTS_ENUM_TYPE event) { - return &events.entries[event]; - } - - EVENTS_LEVEL_TYPE get_event_level(void) { - return events.level; - } - - EMULATOR_STATUS get_emulator_status() { - switch (events.level) { - case EVENT_LEVEL_DEBUG: - case EVENT_LEVEL_INFO: - return EMULATOR_STATUS::STATUS_OK; - case EVENT_LEVEL_WARNING: - return EMULATOR_STATUS::STATUS_WARNING; - case EVENT_LEVEL_UPDATE: - return EMULATOR_STATUS::STATUS_UPDATING; - case EVENT_LEVEL_ERROR: - return EMULATOR_STATUS::STATUS_ERROR; - default: - return EMULATOR_STATUS::STATUS_OK; - } - } - - const char* get_emulator_status_string(EMULATOR_STATUS status) { - // Return the status string but skip "STATUS_" that should always be first - return EMULATOR_STATUS_STRING[status] + 7; - } - - /* Local functions */ - - static void set_event(EVENTS_ENUM_TYPE event, uint8_t data, bool latched) { - // Just some defensive stuff if someone sets an unknown event - if (event >= EVENT_NOF_EVENTS) { - event = EVENT_UNKNOWN_EVENT_SET; - } - - // If the event is already set, no reason to continue - if ((events.entries[event].state != EVENT_STATE_ACTIVE) && - (events.entries[event].state != EVENT_STATE_ACTIVE_LATCHED)) { - events.entries[event].occurences++; - events.entries[event].MQTTpublished = false; - - DEBUG_PRINTF("Event: %s\n", get_event_message_string(event).c_str()); - } - - // We should set the event, update event info - events.entries[event].timestamp = millis64(); - events.entries[event].data = data; - // Check if the event is latching - events.entries[event].state = latched ? EVENT_STATE_ACTIVE_LATCHED : EVENT_STATE_ACTIVE; - - // Update event level, only upwards. Downward changes are done in Software.ino:loop() - events.level = (EVENTS_LEVEL_TYPE)max(events.level, events.entries[event].level); - - update_bms_status(); - } - - static void update_bms_status(void) { - switch (events.level) { - case EVENT_LEVEL_INFO: - case EVENT_LEVEL_WARNING: - case EVENT_LEVEL_DEBUG: - datalayer.battery.status.bms_status = ACTIVE; - break; - case EVENT_LEVEL_UPDATE: - datalayer.battery.status.bms_status = UPDATING; - break; - case EVENT_LEVEL_ERROR: - datalayer.battery.status.bms_status = FAULT; - break; - default: - break; - } - } - - // Function to compare events by timestamp descending - bool compareEventsByTimestampDesc(const EventData& a, const EventData& b) { - return a.event_pointer->timestamp > b.event_pointer->timestamp; - } - - // Function to compare events by timestamp ascending - bool compareEventsByTimestampAsc(const EventData& a, const EventData& b) { - return a.event_pointer->timestamp < b.event_pointer->timestamp; - } - - static void update_event_level(void) { - EVENTS_LEVEL_TYPE temporary_level = EVENT_LEVEL_INFO; - for (uint8_t i = 0u; i < EVENT_NOF_EVENTS; i++) { - if ((events.entries[i].state == EVENT_STATE_ACTIVE) || (events.entries[i].state == EVENT_STATE_ACTIVE_LATCHED)) { - temporary_level = (EVENTS_LEVEL_TYPE)max(events.entries[i].level, temporary_level); - } - } - events.level = temporary_level; - } + events.level = temporary_level; +} From 94eeccb017fe48c410b2f668109cf59241677250 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sat, 30 Aug 2025 13:05:05 +0300 Subject: [PATCH 080/245] Make USB_CAN_DEBUG toogglable via webserver --- Software/src/communication/can/comm_can.cpp | 33 ++++++++++--------- Software/src/communication/nvm/comm_nvm.cpp | 1 + Software/src/datalayer/datalayer.h | 2 ++ .../src/devboard/webserver/settings_html.cpp | 11 +++++-- Software/src/devboard/webserver/webserver.cpp | 5 +-- 5 files changed, 32 insertions(+), 20 deletions(-) diff --git a/Software/src/communication/can/comm_can.cpp b/Software/src/communication/can/comm_can.cpp index 5ba5b868..7a18eaec 100644 --- a/Software/src/communication/can/comm_can.cpp +++ b/Software/src/communication/can/comm_can.cpp @@ -320,23 +320,24 @@ void receive_frame_canfd_addon() { // This section checks if we have a complete // Support functions void print_can_frame(CAN_frame frame, frameDirection msgDir) { -#ifdef DEBUG_CAN_DATA // If enabled in user settings, print out the CAN messages via USB - uint8_t i = 0; - Serial.print("("); - Serial.print(millis() / 1000.0); - (msgDir == MSG_RX) ? Serial.print(") RX0 ") : Serial.print(") TX1 "); - Serial.print(frame.ID, HEX); - Serial.print(" ["); - Serial.print(frame.DLC); - Serial.print("] "); - for (i = 0; i < frame.DLC; i++) { - Serial.print(frame.data.u8[i] < 16 ? "0" : ""); - Serial.print(frame.data.u8[i], HEX); - if (i < frame.DLC - 1) - Serial.print(" "); + + if (datalayer.system.info.CAN_usb_logging_active) { + uint8_t i = 0; + Serial.print("("); + Serial.print(millis() / 1000.0); + (msgDir == MSG_RX) ? Serial.print(") RX0 ") : Serial.print(") TX1 "); + Serial.print(frame.ID, HEX); + Serial.print(" ["); + Serial.print(frame.DLC); + Serial.print("] "); + for (i = 0; i < frame.DLC; i++) { + Serial.print(frame.data.u8[i] < 16 ? "0" : ""); + Serial.print(frame.data.u8[i], HEX); + if (i < frame.DLC - 1) + Serial.print(" "); + } + Serial.println(""); } - Serial.println(""); -#endif // DEBUG_CAN_DATA if (datalayer.system.info.can_logging_active) { // If user clicked on CAN Logging page in webserver, start recording dump_can_frame(frame, msgDir); diff --git a/Software/src/communication/nvm/comm_nvm.cpp b/Software/src/communication/nvm/comm_nvm.cpp index 70856a57..c3eabbb0 100644 --- a/Software/src/communication/nvm/comm_nvm.cpp +++ b/Software/src/communication/nvm/comm_nvm.cpp @@ -135,6 +135,7 @@ void init_stored_settings() { remote_bms_reset = settings.getBool("REMBMSRESET", false); use_canfd_as_can = settings.getBool("CANFDASCAN", false); + datalayer.system.info.CAN_usb_logging_active = settings.getBool("CANLOGUSB", false); datalayer.system.info.usb_logging_active = settings.getBool("USBENABLED", false); datalayer.system.info.web_logging_active = settings.getBool("WEBENABLED", false); diff --git a/Software/src/datalayer/datalayer.h b/Software/src/datalayer/datalayer.h index 19154a99..581d5d59 100644 --- a/Software/src/datalayer/datalayer.h +++ b/Software/src/datalayer/datalayer.h @@ -242,6 +242,8 @@ struct DATALAYER_SYSTEM_INFO_TYPE { /** bool, determines if CAN messages should be logged for webserver */ bool can_logging_active = false; /** bool, determines if USB serial logging should occur */ + bool CAN_usb_logging_active = false; + /** bool, determines if USB serial logging should occur */ bool usb_logging_active = false; /** bool, determines if general logging should be active for webserver */ bool web_logging_active = false; diff --git a/Software/src/devboard/webserver/settings_html.cpp b/Software/src/devboard/webserver/settings_html.cpp index 753a490a..f3c32d26 100644 --- a/Software/src/devboard/webserver/settings_html.cpp +++ b/Software/src/devboard/webserver/settings_html.cpp @@ -254,6 +254,10 @@ String settings_processor(const String& var, BatteryEmulatorSettingsStore& setti return settings.getBool("WIFIAPENABLED", wifiap_enabled) ? "checked" : ""; } + if (var == "CANLOGUSB") { + return settings.getBool("CANLOGUSB") ? "checked" : ""; + } + if (var == "USBENABLED") { return settings.getBool("USBENABLED") ? "checked" : ""; } @@ -870,10 +874,13 @@ const char* getCANInterfaceName(CAN_Interface interface) { - + + + + - + diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index a227225f..18b826c5 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -413,8 +413,9 @@ void init_webserver() { }; const char* boolSettingNames[] = { - "DBLBTR", "CNTCTRL", "CNTCTRLDBL", "PWMCNTCTRL", "PERBMSRESET", "REMBMSRESET", "USBENABLED", - "WEBENABLED", "CANFDASCAN", "WIFIAPENABLED", "MQTTENABLED", "HADISC", "MQTTTOPICS", "INVICNT", + "DBLBTR", "CNTCTRL", "CNTCTRLDBL", "PWMCNTCTRL", "PERBMSRESET", + "REMBMSRESET", "USBENABLED", "CANLOGUSB", "WEBENABLED", "CANFDASCAN", + "WIFIAPENABLED", "MQTTENABLED", "HADISC", "MQTTTOPICS", "INVICNT", }; // Handles the form POST from UI to save settings of the common image From 6c8326fce17a578e720a2df458edb206a07dee88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sat, 30 Aug 2025 23:22:22 +0300 Subject: [PATCH 081/245] Make SD card configurable via webserver --- Software/Software.ino | 23 ++- Software/USER_SETTINGS.h | 6 - Software/src/communication/nvm/comm_nvm.cpp | 2 + Software/src/datalayer/datalayer.h | 4 + Software/src/devboard/sdcard/sdcard.cpp | 39 ++--- .../devboard/webserver/debug_logging_html.cpp | 12 +- .../src/devboard/webserver/settings_html.cpp | 14 ++ Software/src/devboard/webserver/webserver.cpp | 160 +++++++++--------- 8 files changed, 132 insertions(+), 128 deletions(-) diff --git a/Software/Software.ino b/Software/Software.ino index e139e0d1..ce559fbd 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -80,10 +80,10 @@ void setup() { return; } -#if defined(LOG_CAN_TO_SD) || defined(LOG_TO_SD) - xTaskCreatePinnedToCore((TaskFunction_t)&logging_loop, "logging_loop", 4096, NULL, TASK_CONNECTIVITY_PRIO, - &logging_loop_task, esp32hal->WIFICORE()); -#endif + if (datalayer.system.info.CAN_SD_logging_active || datalayer.system.info.SD_logging_active) { + xTaskCreatePinnedToCore((TaskFunction_t)&logging_loop, "logging_loop", 4096, NULL, TASK_CONNECTIVITY_PRIO, + &logging_loop_task, esp32hal->WIFICORE()); + } if (!init_contactors()) { return; @@ -167,22 +167,21 @@ void setup() { // Loop empty, all functionality runs in tasks void loop() {} -#if defined(LOG_CAN_TO_SD) || defined(LOG_TO_SD) void logging_loop(void*) { init_logging_buffers(); init_sdcard(); while (true) { -#ifdef LOG_TO_SD - write_log_to_sdcard(); -#endif -#ifdef LOG_CAN_TO_SD - write_can_frame_to_sdcard(); -#endif + if (datalayer.system.info.SD_logging_active) { + write_log_to_sdcard(); + } + + if (datalayer.system.info.CAN_SD_logging_active) { + write_can_frame_to_sdcard(); + } } } -#endif void connectivity_loop(void*) { esp_task_wdt_add(NULL); // Register this task with WDT diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index 2d07ca35..99ea52a5 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -107,8 +107,6 @@ //#define EQUIPMENT_STOP_BUTTON // Enable this to allow an equipment stop button connected to the Battery-Emulator to disengage the battery //#define LFP_CHEMISTRY //Tesla specific setting, enable this line to startup in LFP mode //#define INTERLOCK_REQUIRED //Nissan LEAF specific setting, if enabled requires both high voltage conenctors to be seated before starting -//#define LOG_TO_SD //Enable this line to log diagnostic data to SD card (WARNING, raises CPU load, do not use for production) -//#define LOG_CAN_TO_SD //Enable this line to log incoming/outgoing CAN & CAN-FD messages to SD card (WARNING, raises CPU load, do not use for production) /* CAN options */ //#define CAN_ADDON //Enable this line to activate an isolated secondary CAN Bus using add-on MCP2515 chip (Needed for some inverters / double battery) #define CRYSTAL_FREQUENCY_MHZ 8 //CAN_ADDON option, what is your MCP2515 add-on boards crystal frequency? @@ -221,10 +219,6 @@ extern IPAddress gateway; extern IPAddress subnet; #endif -#if defined(DEBUG_VIA_USB) || defined(LOG_TO_SD) -#define DEBUG_LOG -#endif - #if defined(MEB_BATTERY) #define PRECHARGE_CONTROL #endif diff --git a/Software/src/communication/nvm/comm_nvm.cpp b/Software/src/communication/nvm/comm_nvm.cpp index c3eabbb0..0d6e97b8 100644 --- a/Software/src/communication/nvm/comm_nvm.cpp +++ b/Software/src/communication/nvm/comm_nvm.cpp @@ -138,6 +138,8 @@ void init_stored_settings() { datalayer.system.info.CAN_usb_logging_active = settings.getBool("CANLOGUSB", false); datalayer.system.info.usb_logging_active = settings.getBool("USBENABLED", false); datalayer.system.info.web_logging_active = settings.getBool("WEBENABLED", false); + datalayer.system.info.CAN_SD_logging_active = settings.getBool("CANLOGSD", false); + datalayer.system.info.SD_logging_active = settings.getBool("SDLOGENABLED", false); // WIFI AP is enabled by default unless disabled in the settings wifiap_enabled = settings.getBool("WIFIAPENABLED", true); diff --git a/Software/src/datalayer/datalayer.h b/Software/src/datalayer/datalayer.h index 581d5d59..d59e2bed 100644 --- a/Software/src/datalayer/datalayer.h +++ b/Software/src/datalayer/datalayer.h @@ -244,9 +244,13 @@ struct DATALAYER_SYSTEM_INFO_TYPE { /** bool, determines if USB serial logging should occur */ bool CAN_usb_logging_active = false; /** bool, determines if USB serial logging should occur */ + bool CAN_SD_logging_active = false; + /** bool, determines if USB serial logging should occur */ bool usb_logging_active = false; /** bool, determines if general logging should be active for webserver */ bool web_logging_active = false; + /** bool, determines if general logging to SD card should be active */ + bool SD_logging_active = false; /** uint8_t, enumeration which CAN interface should be used for log playback */ uint8_t can_replay_interface = CAN_NATIVE; /** bool, determines if CAN replay should loop or not */ diff --git a/Software/src/devboard/sdcard/sdcard.cpp b/Software/src/devboard/sdcard/sdcard.cpp index 0948c1b4..5dfddebd 100644 --- a/Software/src/devboard/sdcard/sdcard.cpp +++ b/Software/src/devboard/sdcard/sdcard.cpp @@ -63,9 +63,7 @@ void add_can_frame_to_buffer(CAN_frame frame, frameDirection msgDir) { currentTime / 1000, currentTime % 1000, (msgDir == MSG_RX ? "RX0" : "TX1"), frame.ID, frame.DLC); if (xRingbufferSend(can_bufferHandle, &messagestr_buffer, size, pdMS_TO_TICKS(2)) != pdTRUE) { -#ifdef DEBUG_VIA_USB - Serial.println("Failed to send message to can ring buffer!"); -#endif // DEBUG_VIA_USB + logging.println("Failed to send message to can ring buffer!"); return; } @@ -77,9 +75,7 @@ void add_can_frame_to_buffer(CAN_frame frame, frameDirection msgDir) { size = snprintf(messagestr_buffer, sizeof(messagestr_buffer), "%02X\n", frame.data.u8[i]); if (xRingbufferSend(can_bufferHandle, &messagestr_buffer, size, pdMS_TO_TICKS(2)) != pdTRUE) { -#ifdef DEBUG_VIA_USB - Serial.println("Failed to send message to can ring buffer!"); -#endif // DEBUG_VIA_USB + logging.println("Failed to send message to can ring buffer!"); return; } } @@ -127,9 +123,7 @@ void add_log_to_buffer(const uint8_t* buffer, size_t size) { return; if (xRingbufferSend(log_bufferHandle, buffer, size, pdMS_TO_TICKS(1)) != pdTRUE) { -#ifdef DEBUG_VIA_USB - Serial.println("Failed to send message to log ring buffer!"); -#endif // DEBUG_VIA_USB + logging.println("Failed to send message to log ring buffer!"); return; } } @@ -161,21 +155,22 @@ void write_log_to_sdcard() { } void init_logging_buffers() { -#if defined(LOG_CAN_TO_SD) - can_bufferHandle = xRingbufferCreate(32 * 1024, RINGBUF_TYPE_BYTEBUF); - if (can_bufferHandle == NULL) { - logging.println("Failed to create CAN ring buffer!"); - return; - } -#endif // defined(LOG_CAN_TO_SD) -#if defined(LOG_TO_SD) - log_bufferHandle = xRingbufferCreate(1024, RINGBUF_TYPE_BYTEBUF); - if (log_bufferHandle == NULL) { - logging.println("Failed to create log ring buffer!"); - return; + if (datalayer.system.info.CAN_SD_logging_active) { + can_bufferHandle = xRingbufferCreate(32 * 1024, RINGBUF_TYPE_BYTEBUF); + if (can_bufferHandle == NULL) { + logging.println("Failed to create CAN ring buffer!"); + return; + } + } + + if (datalayer.system.info.SD_logging_active) { + log_bufferHandle = xRingbufferCreate(1024, RINGBUF_TYPE_BYTEBUF); + if (log_bufferHandle == NULL) { + logging.println("Failed to create log ring buffer!"); + return; + } } -#endif // defined(LOG_TO_SD) } bool init_sdcard() { diff --git a/Software/src/devboard/webserver/debug_logging_html.cpp b/Software/src/devboard/webserver/debug_logging_html.cpp index ae8a7fbd..9204a1fc 100644 --- a/Software/src/devboard/webserver/debug_logging_html.cpp +++ b/Software/src/devboard/webserver/debug_logging_html.cpp @@ -57,9 +57,9 @@ String debug_logger_processor(void) { content += " "; } content += " "; -#ifdef LOG_TO_SD - content += " "; -#endif + if (datalayer.system.info.SD_logging_active) { + content += " "; + } content += ""; // Start a new block for the debug log messages @@ -98,9 +98,9 @@ String debug_logger_processor(void) { content += ""; content += index_html_footer; diff --git a/Software/src/devboard/webserver/settings_html.cpp b/Software/src/devboard/webserver/settings_html.cpp index f3c32d26..023a8309 100644 --- a/Software/src/devboard/webserver/settings_html.cpp +++ b/Software/src/devboard/webserver/settings_html.cpp @@ -266,6 +266,14 @@ String settings_processor(const String& var, BatteryEmulatorSettingsStore& setti return settings.getBool("WEBENABLED") ? "checked" : ""; } + if (var == "CANLOGSD") { + return settings.getBool("CANLOGSD") ? "checked" : ""; + } + + if (var == "SDLOGENABLED") { + return settings.getBool("SDLOGENABLED") ? "checked" : ""; + } + if (var == "MQTTENABLED") { return settings.getBool("MQTTENABLED") ? "checked" : ""; } @@ -883,6 +891,12 @@ const char* getCANInterfaceName(CAN_Interface interface) { + + + + + + diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 18b826c5..fa43a89b 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -267,7 +267,7 @@ void init_webserver() { } }); - if (datalayer.system.info.web_logging_active) { //|| defined(LOG_TO_SD) + if (datalayer.system.info.web_logging_active || datalayer.system.info.SD_logging_active) { // Route for going to debug logging web page server.on("/log", HTTP_GET, [](AsyncWebServerRequest* request) { AsyncWebServerResponse* response = request->beginResponse(200, "text/html", debug_logger_processor()); @@ -289,93 +289,89 @@ void init_webserver() { }, handleFileUpload); -#ifndef LOG_CAN_TO_SD - // Define the handler to export can log - server.on("/export_can_log", HTTP_GET, [](AsyncWebServerRequest* request) { - String logs = String(datalayer.system.info.logged_can_messages); - if (logs.length() == 0) { - logs = "No logs available."; - } + if (datalayer.system.info.CAN_SD_logging_active) { + // Define the handler to export can log + server.on("/export_can_log", HTTP_GET, [](AsyncWebServerRequest* request) { + pause_can_writing(); + request->send(SD_MMC, CAN_LOG_FILE, String(), true); + resume_can_writing(); + }); - // Get the current time - time_t now = time(nullptr); - struct tm timeinfo; - localtime_r(&now, &timeinfo); + // Define the handler to delete can log + server.on("/delete_can_log", HTTP_GET, [](AsyncWebServerRequest* request) { + delete_can_log(); + request->send(200, "text/plain", "Log file deleted"); + }); + } else { + // Define the handler to export can log + server.on("/export_can_log", HTTP_GET, [](AsyncWebServerRequest* request) { + String logs = String(datalayer.system.info.logged_can_messages); + if (logs.length() == 0) { + logs = "No logs available."; + } - // Ensure time retrieval was successful - char filename[32]; - if (strftime(filename, sizeof(filename), "canlog_%H-%M-%S.txt", &timeinfo)) { - // Valid filename created - } else { - // Fallback filename if automatic timestamping failed - strcpy(filename, "battery_emulator_can_log.txt"); - } + // Get the current time + time_t now = time(nullptr); + struct tm timeinfo; + localtime_r(&now, &timeinfo); - // Use request->send with dynamic headers - AsyncWebServerResponse* response = request->beginResponse(200, "text/plain", logs); - response->addHeader("Content-Disposition", String("attachment; filename=\"") + String(filename) + "\""); - request->send(response); - }); -#endif + // Ensure time retrieval was successful + char filename[32]; + if (strftime(filename, sizeof(filename), "canlog_%H-%M-%S.txt", &timeinfo)) { + // Valid filename created + } else { + // Fallback filename if automatic timestamping failed + strcpy(filename, "battery_emulator_can_log.txt"); + } -#ifdef LOG_CAN_TO_SD - // Define the handler to export can log - server.on("/export_can_log", HTTP_GET, [](AsyncWebServerRequest* request) { - pause_can_writing(); - request->send(SD_MMC, CAN_LOG_FILE, String(), true); - resume_can_writing(); - }); + // Use request->send with dynamic headers + AsyncWebServerResponse* response = request->beginResponse(200, "text/plain", logs); + response->addHeader("Content-Disposition", String("attachment; filename=\"") + String(filename) + "\""); + request->send(response); + }); + } - // Define the handler to delete can log - server.on("/delete_can_log", HTTP_GET, [](AsyncWebServerRequest* request) { - delete_can_log(); - request->send(200, "text/plain", "Log file deleted"); - }); -#endif + if (datalayer.system.info.SD_logging_active) { + // Define the handler to delete log file + server.on("/delete_log", HTTP_GET, [](AsyncWebServerRequest* request) { + delete_log(); + request->send(200, "text/plain", "Log file deleted"); + }); -#ifdef LOG_TO_SD - // Define the handler to delete log file - server.on("/delete_log", HTTP_GET, [](AsyncWebServerRequest* request) { - delete_log(); - request->send(200, "text/plain", "Log file deleted"); - }); + // Define the handler to export debug log + server.on("/export_log", HTTP_GET, [](AsyncWebServerRequest* request) { + pause_log_writing(); + request->send(SD_MMC, LOG_FILE, String(), true); + resume_log_writing(); + }); + } else { + // Define the handler to export debug log + server.on("/export_log", HTTP_GET, [](AsyncWebServerRequest* request) { + String logs = String(datalayer.system.info.logged_can_messages); + if (logs.length() == 0) { + logs = "No logs available."; + } - // Define the handler to export debug log - server.on("/export_log", HTTP_GET, [](AsyncWebServerRequest* request) { - pause_log_writing(); - request->send(SD_MMC, LOG_FILE, String(), true); - resume_log_writing(); - }); -#endif + // Get the current time + time_t now = time(nullptr); + struct tm timeinfo; + localtime_r(&now, &timeinfo); -#ifndef LOG_TO_SD - // Define the handler to export debug log - server.on("/export_log", HTTP_GET, [](AsyncWebServerRequest* request) { - String logs = String(datalayer.system.info.logged_can_messages); - if (logs.length() == 0) { - logs = "No logs available."; - } + // Ensure time retrieval was successful + char filename[32]; + if (strftime(filename, sizeof(filename), "log_%H-%M-%S.txt", &timeinfo)) { + // Valid filename created + } else { + // Fallback filename if automatic timestamping failed + strcpy(filename, "battery_emulator_log.txt"); + } - // Get the current time - time_t now = time(nullptr); - struct tm timeinfo; - localtime_r(&now, &timeinfo); - - // Ensure time retrieval was successful - char filename[32]; - if (strftime(filename, sizeof(filename), "log_%H-%M-%S.txt", &timeinfo)) { - // Valid filename created - } else { - // Fallback filename if automatic timestamping failed - strcpy(filename, "battery_emulator_log.txt"); - } - - // Use request->send with dynamic headers - AsyncWebServerResponse* response = request->beginResponse(200, "text/plain", logs); - response->addHeader("Content-Disposition", String("attachment; filename=\"") + String(filename) + "\""); - request->send(response); - }); -#endif + // Use request->send with dynamic headers + AsyncWebServerResponse* response = request->beginResponse(200, "text/plain", logs); + response->addHeader("Content-Disposition", String("attachment; filename=\"") + String(filename) + "\""); + request->send(response); + }); + } // Route for going to cellmonitor web page def_route_with_auth("/cellmonitor", server, HTTP_GET, [](AsyncWebServerRequest* request) { @@ -413,8 +409,8 @@ void init_webserver() { }; const char* boolSettingNames[] = { - "DBLBTR", "CNTCTRL", "CNTCTRLDBL", "PWMCNTCTRL", "PERBMSRESET", - "REMBMSRESET", "USBENABLED", "CANLOGUSB", "WEBENABLED", "CANFDASCAN", + "DBLBTR", "CNTCTRL", "CNTCTRLDBL", "PWMCNTCTRL", "PERBMSRESET", "SDLOGENABLED", + "REMBMSRESET", "USBENABLED", "CANLOGUSB", "WEBENABLED", "CANFDASCAN", "CANLOGSD", "WIFIAPENABLED", "MQTTENABLED", "HADISC", "MQTTTOPICS", "INVICNT", }; @@ -1389,7 +1385,7 @@ String processor(const String& var) { content += " "; content += " "; content += " "; - if (datalayer.system.info.web_logging_active) { //|| defined(LOG_TO_SD) + if (datalayer.system.info.web_logging_active || datalayer.system.info.SD_logging_active) { content += " "; } content += " "; From 8af86d9f29b2a21d68cd25ab48b092f87f365b26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 31 Aug 2025 11:31:45 +0300 Subject: [PATCH 082/245] Add logging to tests --- test/emul/Arduino.h | 1 + test/emul/Logging.h | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 test/emul/Logging.h diff --git a/test/emul/Arduino.h b/test/emul/Arduino.h index 092d6424..5abc6edb 100644 --- a/test/emul/Arduino.h +++ b/test/emul/Arduino.h @@ -7,6 +7,7 @@ #include #include "HardwareSerial.h" +#include "Logging.h" #include "Print.h" #include "esp-hal-gpio.h" diff --git a/test/emul/Logging.h b/test/emul/Logging.h new file mode 100644 index 00000000..49388f3e --- /dev/null +++ b/test/emul/Logging.h @@ -0,0 +1,24 @@ +#ifndef LOGGING_H +#define LOGGING_H + +#include +#include + +class Logging { + public: + static void printf(const char* format, ...) { + va_list args; + va_start(args, format); + vprintf(format, args); + va_end(args); + } + + static void println(const char* message) { printf("%s\n", message); } + + static void print(const char* message) { printf("%s", message); } +}; + +// Global logging instance +extern Logging logging; + +#endif From 825db3567f6efdfb62c3ad51696557c937d57d6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 31 Aug 2025 11:37:39 +0300 Subject: [PATCH 083/245] Fix unit test to work with logging --- test/emul/Logging.cpp | 3 +++ test/emul/Logging.h | 10 ++++------ 2 files changed, 7 insertions(+), 6 deletions(-) create mode 100644 test/emul/Logging.cpp diff --git a/test/emul/Logging.cpp b/test/emul/Logging.cpp new file mode 100644 index 00000000..8c12eaab --- /dev/null +++ b/test/emul/Logging.cpp @@ -0,0 +1,3 @@ +#include "Logging.h" + +Logging logging; diff --git a/test/emul/Logging.h b/test/emul/Logging.h index 49388f3e..74088a4c 100644 --- a/test/emul/Logging.h +++ b/test/emul/Logging.h @@ -1,9 +1,9 @@ -#ifndef LOGGING_H -#define LOGGING_H +#pragma once #include #include +namespace test { class Logging { public: static void printf(const char* format, ...) { @@ -14,11 +14,9 @@ class Logging { } static void println(const char* message) { printf("%s\n", message); } - static void print(const char* message) { printf("%s", message); } }; +} // namespace test -// Global logging instance +using Logging = test::Logging; extern Logging logging; - -#endif From d0f74934af29aeb5e0c41a338665a202e197ac72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 31 Aug 2025 11:44:37 +0300 Subject: [PATCH 084/245] Update CONTRIBUTING.md with Unit tests --- CONTRIBUTING.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c87fe6ed..8136fb2e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -38,3 +38,15 @@ Or force it to check all files with ``` pre-commit run --all-files ``` + +## Local Unit test run +The Unit tests run gtest. Here is how to install this on Debian/Ubuntu and run it locally +``` +sudo apt-get install libgtest-dev +sudo apt-get install cmake +``` +Navigate to Battery-Emulator/test folder +``` +sudo cmake CMakeLists.txt +sudo make +``` From 01aa926f2649c4fa69014fa2948f83b9037de118 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 31 Aug 2025 11:48:21 +0300 Subject: [PATCH 085/245] Update gitignore for local test execution --- .gitignore | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index f0e530d2..51388b28 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,15 @@ # Ignore any files in any build folder *build/ +# Ignore any generated test files from running locally +test/CMakeCache.txt +test/CMakeFiles/ +test/CTestTestfile.cmake +test/Makefile +test/cmake_install.cmake +test/gtest/ +test/tests[1]_include.cmake + # Ignore .exe (unit tests) *.exe **/.DS_Store @@ -24,4 +33,4 @@ compile.bat *.bin # Ignore secret file -USER_SECRETS.h \ No newline at end of file +USER_SECRETS.h From 268910ef63fb774e5f92cea5af86cd271adfb98b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 31 Aug 2025 13:07:17 +0300 Subject: [PATCH 086/245] Add mock print implementation for unit tests --- Software/src/devboard/utils/logging.h | 39 ++++++++++++++++++++++++--- test/CMakeLists.txt | 1 + test/emul/Logging.cpp | 3 ++- test/emul/Logging.h | 22 ++------------- 4 files changed, 40 insertions(+), 25 deletions(-) diff --git a/Software/src/devboard/utils/logging.h b/Software/src/devboard/utils/logging.h index 61325e99..95ba6910 100644 --- a/Software/src/devboard/utils/logging.h +++ b/Software/src/devboard/utils/logging.h @@ -1,12 +1,15 @@ #ifndef __LOGGING_H__ #define __LOGGING_H__ -#include #include #include "../../../USER_SETTINGS.h" #include "../../datalayer/datalayer.h" #include "types.h" +#ifndef UNIT_TEST +// Real implementation for production +#include + class Logging : public Print { void add_timestamp(size_t size); @@ -17,9 +20,7 @@ class Logging : public Print { Logging() {} }; -extern Logging logging; - -// Replace compile-time macros with runtime checks +// Production macros #define DEBUG_PRINTF(fmt, ...) \ do { \ if (datalayer.system.info.web_logging_active || datalayer.system.info.usb_logging_active) { \ @@ -33,4 +34,34 @@ extern Logging logging; logging.println(str); \ } \ } while (0) + +#else +// Mock implementation for tests +#include +#include + +class Logging { + public: + // Mock methods that do nothing + size_t write(const uint8_t* buffer, size_t size) { return size; } + size_t write(uint8_t) { return 0; } + + static void printf(const char* fmt, ...) { + // Empty implementation - silence unused parameter warnings + (void)fmt; + } + + static void println(const char* str) { (void)str; } + + Logging() {} +}; + +// Test macros - empty implementations +#define DEBUG_PRINTF(fmt, ...) ((void)0) +#define DEBUG_PRINTLN(str) ((void)0) + +#endif + +extern Logging logging; + #endif // __LOGGING_H__ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d945a8c0..b6103027 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -28,6 +28,7 @@ ExternalProject_Get_Property(gtest source_dir binary_dir) # Create a libgtest target to be used as a dependency by test programs add_library(libgtest IMPORTED STATIC GLOBAL) add_dependencies(libgtest gtest) +add_definitions(-DUNIT_TEST) # Set libgtest properties if(WIN32) diff --git a/test/emul/Logging.cpp b/test/emul/Logging.cpp index 8c12eaab..d6cc21d6 100644 --- a/test/emul/Logging.cpp +++ b/test/emul/Logging.cpp @@ -1,3 +1,4 @@ -#include "Logging.h" +#include "../../src/devboard/utils/logging.h" +// This creates the global instance that links against the real implementation Logging logging; diff --git a/test/emul/Logging.h b/test/emul/Logging.h index 74088a4c..3de511bf 100644 --- a/test/emul/Logging.h +++ b/test/emul/Logging.h @@ -1,22 +1,4 @@ #pragma once -#include -#include - -namespace test { -class Logging { - public: - static void printf(const char* format, ...) { - va_list args; - va_start(args, format); - vprintf(format, args); - va_end(args); - } - - static void println(const char* message) { printf("%s\n", message); } - static void print(const char* message) { printf("%s", message); } -}; -} // namespace test - -using Logging = test::Logging; -extern Logging logging; +// Include the real logging header which will provide the mock implementation +#include "../../Software/src/devboard/utils/logging.h" From 083432da789ea10daa5e239ce978babe13e0c03e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 31 Aug 2025 14:07:57 +0300 Subject: [PATCH 087/245] Make LED pattern configurable in Webserver --- Software/USER_SETTINGS.h | 8 -------- Software/src/communication/nvm/comm_nvm.cpp | 1 + Software/src/datalayer/datalayer.h | 2 +- Software/src/devboard/utils/led_handler.h | 1 + Software/src/devboard/webserver/settings_html.cpp | 10 ++++++++++ Software/src/devboard/webserver/webserver.cpp | 3 +++ 6 files changed, 16 insertions(+), 9 deletions(-) diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index fe82a7fc..9a2a254d 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -173,14 +173,6 @@ //#define MAX_CUSTOM_CELL_VOLTAGE_MV 4250 // 4250 = 4.250V , Maximum cell voltage in millivolts (4250 = 4.250V) //#define MIN_CUSTOM_CELL_VOLTAGE_MV 2650 // 2650 = 2.650V , Minimum cell voltage in millivolts (2650 = 2.650V) -/* LED settings. Optional customization for how the blinking pattern on the LED should behave. -* CLASSIC - Slow up/down ramp. If CLASSIC, then a ramp up and ramp down will finish in LED_PERIOD_MS milliseconds -* FLOW - Ramp up/down depending on flow of energy -* HEARTBEAT - Heartbeat-like LED pattern that reacts to the system state with color and BPM -*/ -#define LED_MODE CLASSIC -#define LED_PERIOD_MS 3000 - /* Do not change any code below this line */ /* Only change battery specific settings above and in "USER_SETTINGS.cpp" */ typedef struct { diff --git a/Software/src/communication/nvm/comm_nvm.cpp b/Software/src/communication/nvm/comm_nvm.cpp index 9761e82d..41146ac8 100644 --- a/Software/src/communication/nvm/comm_nvm.cpp +++ b/Software/src/communication/nvm/comm_nvm.cpp @@ -146,6 +146,7 @@ void init_stored_settings() { datalayer.system.info.web_logging_active = settings.getBool("WEBENABLED", false); datalayer.system.info.CAN_SD_logging_active = settings.getBool("CANLOGSD", false); datalayer.system.info.SD_logging_active = settings.getBool("SDLOGENABLED", false); + datalayer.battery.status.led_mode = (led_mode_enum)settings.getUInt("LEDMODE", false); // WIFI AP is enabled by default unless disabled in the settings wifiap_enabled = settings.getBool("WIFIAPENABLED", true); diff --git a/Software/src/datalayer/datalayer.h b/Software/src/datalayer/datalayer.h index d59e2bed..5ead9832 100644 --- a/Software/src/datalayer/datalayer.h +++ b/Software/src/datalayer/datalayer.h @@ -110,7 +110,7 @@ struct DATALAYER_BATTERY_STATUS_TYPE { real_bms_status_enum real_bms_status = BMS_DISCONNECTED; /** LED mode, customizable by user */ - led_mode_enum led_mode = LED_MODE; + led_mode_enum led_mode = CLASSIC; }; struct DATALAYER_BATTERY_SETTINGS_TYPE { diff --git a/Software/src/devboard/utils/led_handler.h b/Software/src/devboard/utils/led_handler.h index 903267af..202cf65a 100644 --- a/Software/src/devboard/utils/led_handler.h +++ b/Software/src/devboard/utils/led_handler.h @@ -25,6 +25,7 @@ class LED { void heartbeat_run(void); uint8_t up_down(float middle_point_f); + int LED_PERIOD_MS = 3000; }; bool led_init(void); diff --git a/Software/src/devboard/webserver/settings_html.cpp b/Software/src/devboard/webserver/settings_html.cpp index 16541f17..18a1a11c 100644 --- a/Software/src/devboard/webserver/settings_html.cpp +++ b/Software/src/devboard/webserver/settings_html.cpp @@ -93,6 +93,8 @@ String options_from_map(int selected, const TMap& value_name_map) { return options; } +static const std::map led_modes = {{0, "Classic"}, {1, "Energy Flow"}, {2, "Heartbeat"}}; + static const std::map tesla_countries = { {21843, "US (USA)"}, {17217, "CA (Canada)"}, {18242, "GB (UK & N Ireland)"}, {17483, "DK (Denmark)"}, {17477, "DE (Germany)"}, {16725, "AU (Australia)"}}; @@ -571,6 +573,10 @@ String settings_processor(const String& var, BatteryEmulatorSettingsStore& setti return options_from_map(settings.getUInt("GTWPACK", 0), tesla_pack); } + if (var == "LEDMODE") { + return options_from_map(settings.getUInt("LEDMODE", 0), led_modes); + } + return String(); } @@ -972,6 +978,10 @@ const char* getCANInterfaceName(CAN_Interface interface) { + + diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index bb890bb8..91ed0c05 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -524,6 +524,9 @@ void init_webserver() { } else if (p->name() == "GTWPACK") { auto type = atoi(p->value().c_str()); settings.saveUInt("GTWPACK", type); + } else if (p->name() == "LEDMODE") { + auto type = atoi(p->value().c_str()); + settings.saveUInt("LEDMODE", type); } for (auto& boolSetting : boolSettings) { From 4574b7317c74756808e3d14982952a8a00afccb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 31 Aug 2025 14:16:52 +0300 Subject: [PATCH 088/245] Make secondary battery write correctly to balancing status --- Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp b/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp index bb75f344..44a8014b 100644 --- a/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp +++ b/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp @@ -403,7 +403,7 @@ void RenaultZoeGen2Battery::handle_incoming_can_frame(CAN_frame rx_frame) { battery_balancing_shunts[94] = (rx_frame.data.u8[1] & 0x02) >> 1; battery_balancing_shunts[95] = (rx_frame.data.u8[1] & 0x01); - memcpy(datalayer.battery.status.cell_balancing_status, battery_balancing_shunts, 96 * sizeof(bool)); + memcpy(datalayer_battery->status.cell_balancing_status, battery_balancing_shunts, 96 * sizeof(bool)); } break; case POLL_ENERGY_COMPLETE: From 7c7252694647d5c5d05b0fab0848a325aeeb6f40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 31 Aug 2025 15:08:45 +0300 Subject: [PATCH 089/245] Make tests pass --- Software/src/communication/can/comm_can.cpp | 22 +++++++++------------ test/emul/Preferences.h | 1 + 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/Software/src/communication/can/comm_can.cpp b/Software/src/communication/can/comm_can.cpp index f12ae2b8..003e1988 100644 --- a/Software/src/communication/can/comm_can.cpp +++ b/Software/src/communication/can/comm_can.cpp @@ -92,7 +92,6 @@ bool init_CAN() { const uint32_t errorCode = ACAN_ESP32::can.begin(*settingsespcan); if (errorCode == 0) { native_can_initialized = true; -#ifdef DEBUG_LOG logging.println("Native Can ok"); logging.print("Bit Rate prescaler: "); logging.println(settingsespcan->mBitRatePrescaler); @@ -112,12 +111,9 @@ bool init_CAN() { logging.print("Sample point: "); logging.print(settingsespcan->samplePointFromBitStart()); logging.println("%"); -#endif // DEBUG_LOG } else { -#ifdef DEBUG_LOG logging.print("Error Native Can: 0x"); logging.println(errorCode, HEX); -#endif // DEBUG_LOG return false; } } @@ -233,9 +229,9 @@ void transmit_can_frame_to_interface(const CAN_frame* tx_frame, int interface) { } print_can_frame(*tx_frame, frameDirection(MSG_TX)); -#ifdef LOG_CAN_TO_SD - add_can_frame_to_buffer(*tx_frame, frameDirection(MSG_TX)); -#endif + if (datalayer.system.info.CAN_SD_logging_active) { + add_can_frame_to_buffer(*tx_frame, frameDirection(MSG_TX)); + } switch (interface) { case CAN_NATIVE: { @@ -398,13 +394,13 @@ void map_can_frame_to_variable(CAN_frame* rx_frame, CAN_Interface interface) { print_can_frame(*rx_frame, frameDirection(MSG_RX)); } -#ifdef LOG_CAN_TO_SD - if (interface != - CANFD_NATIVE) { //Avoid printing twice due to receive_frame_canfd_addon sending to both FD interfaces - //TODO: This check can be removed later when refactored to use inline functions for logging - add_can_frame_to_buffer(*rx_frame, frameDirection(MSG_RX)); + if (datalayer.system.info.CAN_SD_logging_active) { + if (interface != + CANFD_NATIVE) { //Avoid printing twice due to receive_frame_canfd_addon sending to both FD interfaces + //TODO: This check can be removed later when refactored to use inline functions for logging + add_can_frame_to_buffer(*rx_frame, frameDirection(MSG_RX)); + } } -#endif // Send the frame to all the receivers registered for this interface. auto receivers = can_receivers.equal_range(interface); diff --git a/test/emul/Preferences.h b/test/emul/Preferences.h index b1091def..fa2e6019 100644 --- a/test/emul/Preferences.h +++ b/test/emul/Preferences.h @@ -1,6 +1,7 @@ #ifndef PREFERENCES #define PREFERENCES +#include #include class Preferences { From 90884998a193236829e3c3c650ec048fa65ef86a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 31 Aug 2025 15:13:08 +0300 Subject: [PATCH 090/245] Move boards folder to HAL --- .../src/devboard/hal/boards}/esp32s3_flash_16MB.json | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {boards => Software/src/devboard/hal/boards}/esp32s3_flash_16MB.json (100%) diff --git a/boards/esp32s3_flash_16MB.json b/Software/src/devboard/hal/boards/esp32s3_flash_16MB.json similarity index 100% rename from boards/esp32s3_flash_16MB.json rename to Software/src/devboard/hal/boards/esp32s3_flash_16MB.json From d7190d770b0ad438abc4832362b97f39ea69df03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 31 Aug 2025 15:37:39 +0300 Subject: [PATCH 091/245] Remove obsolete compile all hardware workflow --- .github/workflows/compile-all-hardware.yml | 93 ---------------------- 1 file changed, 93 deletions(-) delete mode 100644 .github/workflows/compile-all-hardware.yml diff --git a/.github/workflows/compile-all-hardware.yml b/.github/workflows/compile-all-hardware.yml deleted file mode 100644 index 8e1895f7..00000000 --- a/.github/workflows/compile-all-hardware.yml +++ /dev/null @@ -1,93 +0,0 @@ -# This is the name of the workflow, visible on GitHub UI. -name: 🤖 Compile All Hardware - -# Here we tell GitHub when to run the workflow. -on: - # The workflow is run when a commit is pushed or for a - # Pull Request. - - push - - pull_request - -# This is the list of jobs that will be run concurrently. -jobs: - # This pre-job is run to skip workflows in case a workflow is already run, i.e. because the workflow is triggered by both push and pull_request - skip-duplicate-actions: - runs-on: ubuntu-latest - # Map a step output to a job output - outputs: - should_skip: ${{ steps.skip_check.outputs.should_skip }} - steps: - - id: skip_check - uses: fkirc/skip-duplicate-actions@v5 - with: - # All of these options are optional, so you can remove them if you are happy with the defaults - concurrent_skipping: 'never' - skip_after_successful_duplicate: 'true' - do_not_skip: '["pull_request", "workflow_dispatch", "schedule"]' - - # Since we use a build matrix, the actual number of jobs - # started depends on how many configurations the matrix - # will produce. - - # This is the name of the job. - build-hardware: - needs: skip-duplicate-actions - if: needs.skip-duplicate-actions.outputs.should_skip != 'true' - - # Here we tell GitHub that the jobs must be determined - # dynamically depending on a matrix configuration. - strategy: - # The matrix will produce one job for each combination of parameters. - matrix: - # This is the development board hardware for which the code will be compiled. - # FBQN stands for "fully qualified board name", and is used by Arduino to define the hardware to compile for. - fqbn: - - esp32:esp32:esp32 - # further ESP32 chips - #- esp32:esp32:esp32c3 - #- esp32:esp32:esp32c2 - #- esp32:esp32:esp32c6 - #- esp32:esp32:esp32h2 - #- esp32:esp32:esp32s3 - # These are the batteries for which the code will be compiled. - battery: - - NISSAN_LEAF_BATTERY - # These are the emulated inverter communication protocols for which the code will be compiled. - inverter: - - BYD_CAN - # These are the supported hardware platforms for which the code will be compiled. - hardware: - - HW_LILYGO - - HW_STARK - - HW_3LB - - HW_DEVKIT - - # This is the platform GitHub will use to run our workflow. - runs-on: ubuntu-latest - - # This is the list of steps this job will run. - steps: - # First we clone the repo using the `checkout` action. - - name: Checkout - uses: actions/checkout@v5 - - # Copy USER_SECRETS.TEMPLATE.h to USER_SECRETS.h - - name: Copy USER_SECRETS.TEMPLATE.h to USER_SECRETS.h - run: cp ./Software/USER_SECRETS.TEMPLATE.h ./Software/USER_SECRETS.h - - # We use the `arduino/setup-arduino-cli` action to install and - # configure the Arduino CLI on the system. - - name: Setup Arduino CLI - uses: arduino/setup-arduino-cli@v2 - - # We then install the platform. - - name: Install platform - run: | - arduino-cli core update-index - arduino-cli core install esp32:esp32 - - # Finally, we compile the sketch, using the FQBN that was set - # in the build matrix, and using build flags to define the - # battery and inverter set in the build matrix. - - name: Compile Sketch - run: arduino-cli compile --fqbn ${{ matrix.fqbn }} --build-property build.partitions=min_spiffs --build-property upload.maximum_size=1966080 --build-property "build.extra_flags=-Wall -Wextra -Wpedantic -Werror -DESP32 -D${{ matrix.battery}} -D${{ matrix.inverter}} -D${{ matrix.hardware}}" ./Software From e5678d24062007f9484d953415e8553a9cc2bda3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 31 Aug 2025 15:45:15 +0300 Subject: [PATCH 092/245] Add workflow for T-2CAN --- .../compile-common-image-lilygo-2CAN.yml | 58 +++++++++++++++++++ ...l => compile-common-image-lilygo-TCAN.yml} | 2 +- .github/workflows/release-assets.yml | 14 ++++- .../boards => boards}/esp32s3_flash_16MB.json | 0 platformio.ini | 2 +- 5 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/compile-common-image-lilygo-2CAN.yml rename .github/workflows/{compile-common-image-lilygo.yml => compile-common-image-lilygo-TCAN.yml} (97%) rename {Software/src/devboard/hal/boards => boards}/esp32s3_flash_16MB.json (100%) diff --git a/.github/workflows/compile-common-image-lilygo-2CAN.yml b/.github/workflows/compile-common-image-lilygo-2CAN.yml new file mode 100644 index 00000000..9972c4c4 --- /dev/null +++ b/.github/workflows/compile-common-image-lilygo-2CAN.yml @@ -0,0 +1,58 @@ +name: 🔋 Compile Common Image for Lilygo 2CAN + +on: + # The workflow is run when a commit is pushed or for a + # Pull Request. + - push + - pull_request + +# This is the list of jobs that will be run concurrently. +jobs: + # This pre-job is run to skip workflows in case a workflow is already run, i.e. because the workflow is triggered by both push and pull_request + skip-duplicate-actions: + runs-on: ubuntu-latest + # Map a step output to a job output + outputs: + should_skip: ${{ steps.skip_check.outputs.should_skip }} + steps: + - id: skip_check + uses: fkirc/skip-duplicate-actions@v5 + with: + # All of these options are optional, so you can remove them if you are happy with the defaults + concurrent_skipping: 'never' + skip_after_successful_duplicate: 'true' + do_not_skip: '["pull_request", "workflow_dispatch", "schedule"]' + + build-common-image: + # This is the platform GitHub will use to run our workflow. + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v5 + name: Checkout code + + - uses: actions/cache@v4 + with: + path: | + ~/.cache/pip + ~/.platformio/.cache + key: ${{ runner.os }}-pio + + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + - name: Install PlatformIO Core + run: pip install --upgrade platformio + + # Copy USER_SECRETS.TEMPLATE.h to USER_SECRETS.h + - name: Copy USER_SECRETS.TEMPLATE.h to USER_SECRETS.h + run: cp ./Software/USER_SECRETS.TEMPLATE.h ./Software/USER_SECRETS.h + + - name: Build image for Lilygo + run: pio run -e lilygo_2CAN_330 + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: battery-emulator-lilygo.bin + path: .pio/build/lilygo_2CAN_330/firmware.bin diff --git a/.github/workflows/compile-common-image-lilygo.yml b/.github/workflows/compile-common-image-lilygo-TCAN.yml similarity index 97% rename from .github/workflows/compile-common-image-lilygo.yml rename to .github/workflows/compile-common-image-lilygo-TCAN.yml index 43c0cd44..aee0a9bc 100644 --- a/.github/workflows/compile-common-image-lilygo.yml +++ b/.github/workflows/compile-common-image-lilygo-TCAN.yml @@ -1,4 +1,4 @@ -name: 🔋 Compile Common Image for Lilygo +name: 🔋 Compile Common Image for Lilygo T-CAN on: # The workflow is run when a commit is pushed or for a diff --git a/.github/workflows/release-assets.yml b/.github/workflows/release-assets.yml index c87b029b..fcbf4684 100644 --- a/.github/workflows/release-assets.yml +++ b/.github/workflows/release-assets.yml @@ -51,16 +51,26 @@ jobs: - name: Copy USER_SECRETS.TEMPLATE.h to USER_SECRETS.h run: cp ./Software/USER_SECRETS.TEMPLATE.h ./Software/USER_SECRETS.h - - name: 🛠 Build ota image for Lilygo + - name: 🛠 Build ota image for Lilygo T-CAN run: | pio run -e lilygo_330 cp .pio/build/lilygo_330/firmware.bin output/BE_${{ steps.vars.outputs.tag }}_LilygoT-CAN485.ota.bin - - name: 🛠 Build factory image for Lilygo + - name: 🛠 Build factory image for Lilygo T-CAN run: | esptool --chip esp32 merge-bin -o .pio/build/lilygo_330/factory.bin --flash-mode dio --flash-freq 40m --flash-size 4MB 0x1000 .pio/build/lilygo_330/bootloader.bin 0x8000 .pio/build/lilygo_330/partitions.bin 0xe000 ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin 0x10000 .pio/build/lilygo_330/firmware.bin mv .pio/build/lilygo_330/factory.bin output/BE_${{ steps.vars.outputs.tag }}_LilygoT-CAN485.factory.bin + - name: 🛠 Build ota image for Lilygo 2-CAN + run: | + pio run -e lilygo_2CAN_330 + cp .pio/build/lilygo_2CAN_330/firmware.bin output/BE_${{ steps.vars.outputs.tag }}_LilygoT-2CAN.ota.bin + + - name: 🛠 Build factory image for Lilygo 2-CAN + run: | + esptool --chip esp32 merge-bin -o .pio/build/lilygo_2CAN_330/factory.bin --flash-mode dio --flash-freq 40m --flash-size 4MB 0x1000 .pio/build/lilygo_2CAN_330/bootloader.bin 0x8000 .pio/build/lilygo_2CAN_330/partitions.bin 0xe000 ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin 0x10000 .pio/build/lilygo_2CAN_330/firmware.bin + mv .pio/build/lilygo_2CAN_330/factory.bin output/BE_${{ steps.vars.outputs.tag }}_LilygoT-2CAN.factory.bin + - name: 🛠 Build ota image for Stark run: | pio run -e stark_330 diff --git a/Software/src/devboard/hal/boards/esp32s3_flash_16MB.json b/boards/esp32s3_flash_16MB.json similarity index 100% rename from Software/src/devboard/hal/boards/esp32s3_flash_16MB.json rename to boards/esp32s3_flash_16MB.json diff --git a/platformio.ini b/platformio.ini index 16aaf9ab..b411b41c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -51,7 +51,7 @@ framework = arduino build_flags = -I include -DHW_STARK -DCOMMON_IMAGE -DDEBUG_VIA_USB lib_deps = -[env:lilygo_2can_330] +[env:lilygo_2CAN_330] platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.30/platform-espressif32.zip board = esp32s3_flash_16MB monitor_speed = 115200 From a6f406a92833ff71300aae709c491a4979ee9631 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 31 Aug 2025 16:48:07 +0300 Subject: [PATCH 093/245] Fix crash on Common Image for Geely Geometry C --- Software/src/battery/DALY-BMS.cpp | 4 +-- .../src/battery/GEELY-GEOMETRY-C-BATTERY.cpp | 2 +- .../src/battery/GEELY-GEOMETRY-C-BATTERY.h | 25 ++++++++++++++----- Software/src/battery/GEELY-GEOMETRY-C-HTML.h | 4 --- 4 files changed, 22 insertions(+), 13 deletions(-) diff --git a/Software/src/battery/DALY-BMS.cpp b/Software/src/battery/DALY-BMS.cpp index 38c8bd1e..da93f6a4 100644 --- a/Software/src/battery/DALY-BMS.cpp +++ b/Software/src/battery/DALY-BMS.cpp @@ -15,8 +15,8 @@ static int16_t current_dA = 0; static uint16_t voltage_dV = 0; static uint32_t remaining_capacity_mAh = 0; static uint16_t cellvoltages_mV[48] = {0}; -static uint16_t cellvoltage_min_mV = 0; -static uint16_t cellvoltage_max_mV = 0; +static uint16_t cellvoltage_min_mV = 3700; +static uint16_t cellvoltage_max_mV = 3700; static uint16_t cell_count = 0; static uint16_t SOC = 0; static bool has_fault = false; diff --git a/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.cpp b/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.cpp index 40c047f4..6400f077 100644 --- a/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.cpp +++ b/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.cpp @@ -1,7 +1,7 @@ #include "GEELY-GEOMETRY-C-BATTERY.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" -#include "../datalayer/datalayer_extended.h" //For "More battery info" webpage +#include "../datalayer/datalayer_extended.h" #include "../devboard/utils/events.h" /* TODO diff --git a/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.h b/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.h index 31accda6..adee75e4 100644 --- a/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.h +++ b/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.h @@ -1,7 +1,6 @@ #ifndef GEELY_GEOMETRY_C_BATTERY_H #define GEELY_GEOMETRY_C_BATTERY_H #include "../datalayer/datalayer.h" -#include "../datalayer/datalayer_extended.h" #include "CanBattery.h" #include "GEELY-GEOMETRY-C-HTML.h" @@ -11,6 +10,20 @@ class GeelyGeometryCBattery : public CanBattery { public: + // Use this constructor for the second battery. + GeelyGeometryCBattery(DATALAYER_BATTERY_TYPE* datalayer_ptr, DATALAYER_INFO_GEELY_GEOMETRY_C* extended, + CAN_Interface targetCan) + : CanBattery(targetCan) { + datalayer_battery = datalayer_ptr; + + battery_voltage = 0; + } + // Use the default constructor to create the first or single battery. + GeelyGeometryCBattery() { + datalayer_battery = &datalayer.battery; + datalayer_geometryc = &datalayer_extended.geometryC; + } + virtual void setup(void); virtual void handle_incoming_can_frame(CAN_frame rx_frame); virtual void update_values(); @@ -20,6 +33,11 @@ class GeelyGeometryCBattery : public CanBattery { BatteryHtmlRenderer& get_status_renderer() { return renderer; } private: + GeelyGeometryCHtmlRenderer renderer; + + DATALAYER_BATTERY_TYPE* datalayer_battery; + DATALAYER_INFO_GEELY_GEOMETRY_C* datalayer_geometryc; + static const int POLL_SOC = 0x4B35; static const int POLL_CC2_VOLTAGE = 0x4BCF; static const int POLL_CELL_MAX_VOLTAGE_NUMBER = 0x4B1E; @@ -41,8 +59,6 @@ class GeelyGeometryCBattery : public CanBattery { static const int POLL_MULTI_HARDWARE_VERSION = 0x4B6B; static const int POLL_MULTI_SOFTWARE_VERSION = 0x4B6C; - GeelyGeometryCHtmlRenderer renderer; - static const int MAX_PACK_VOLTAGE_70_DV = 4420; //70kWh static const int MIN_PACK_VOLTAGE_70_DV = 2860; static const int MAX_PACK_VOLTAGE_53_DV = 4160; //53kWh @@ -51,9 +67,6 @@ class GeelyGeometryCBattery : public CanBattery { static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value - DATALAYER_BATTERY_TYPE* datalayer_battery; - DATALAYER_INFO_GEELY_GEOMETRY_C* datalayer_geometryc; - CAN_frame GEELY_191 = {.FD = false, //PAS_APA_Status , 10ms .ext_ID = false, .DLC = 8, diff --git a/Software/src/battery/GEELY-GEOMETRY-C-HTML.h b/Software/src/battery/GEELY-GEOMETRY-C-HTML.h index fc6275b3..7a51f488 100644 --- a/Software/src/battery/GEELY-GEOMETRY-C-HTML.h +++ b/Software/src/battery/GEELY-GEOMETRY-C-HTML.h @@ -1,8 +1,6 @@ #ifndef _GEELY_GEOMETRY_C_HTML_H #define _GEELY_GEOMETRY_C_HTML_H -#include -#include "../datalayer/datalayer.h" #include "../datalayer/datalayer_extended.h" #include "../devboard/webserver/BatteryHtmlRenderer.h" @@ -10,7 +8,6 @@ class GeelyGeometryCHtmlRenderer : public BatteryHtmlRenderer { public: String get_status_html() { String content; - char readableSerialNumber[29]; // One extra space for null terminator memcpy(readableSerialNumber, datalayer_extended.geometryC.BatterySerialNumber, sizeof(datalayer_extended.geometryC.BatterySerialNumber)); @@ -52,7 +49,6 @@ class GeelyGeometryCHtmlRenderer : public BatteryHtmlRenderer { "

Module 5 temperature: " + String(datalayer_extended.geometryC.ModuleTemperatures[4]) + " °C

"; content += "

Module 6 temperature: " + String(datalayer_extended.geometryC.ModuleTemperatures[5]) + " °C

"; - return content; } }; From bdcf4e788c35657d1f24f476ef55b0dec191615c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 31 Aug 2025 17:02:06 +0300 Subject: [PATCH 094/245] Fix crash on Common Image for Ioniq 28 --- Software/src/battery/HYUNDAI-IONIQ-28-BATTERY.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Software/src/battery/HYUNDAI-IONIQ-28-BATTERY.h b/Software/src/battery/HYUNDAI-IONIQ-28-BATTERY.h index 313915d5..0b639a74 100644 --- a/Software/src/battery/HYUNDAI-IONIQ-28-BATTERY.h +++ b/Software/src/battery/HYUNDAI-IONIQ-28-BATTERY.h @@ -12,7 +12,8 @@ class HyundaiIoniq28Battery : public CanBattery { public: - HyundaiIoniq28Battery() : renderer(*this) {} + // Use the default constructor to create the first or single battery. + HyundaiIoniq28Battery() : renderer(*this) { datalayer_battery = &datalayer.battery; } BatteryHtmlRenderer& get_status_renderer() { return renderer; } From b0330dc5f5d07beec32666817586ad533c303b3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 31 Aug 2025 17:10:02 +0300 Subject: [PATCH 095/245] Fix crash on Common Image for MEB --- Software/src/battery/MEB-BATTERY.cpp | 32 ++++++++++++++-------------- Software/src/battery/MEB-BATTERY.h | 17 +++++++++++++++ 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/Software/src/battery/MEB-BATTERY.cpp b/Software/src/battery/MEB-BATTERY.cpp index 0fe468f7..6969ad9d 100644 --- a/Software/src/battery/MEB-BATTERY.cpp +++ b/Software/src/battery/MEB-BATTERY.cpp @@ -200,15 +200,15 @@ uint8_t vw_crc_calc(uint8_t* inputBytes, uint8_t length, uint32_t address) { void MebBattery:: update_values() { //This function maps all the values fetched via CAN to the correct parameters used for modbus - datalayer.battery.status.real_soc = battery_SOC * 5; //*0.05*100 + datalayer_battery->status.real_soc = battery_SOC * 5; //*0.05*100 - datalayer.battery.status.voltage_dV = BMS_voltage * 2.5; // *0.25*10 + datalayer_battery->status.voltage_dV = BMS_voltage * 2.5; // *0.25*10 - datalayer.battery.status.current_dA = (BMS_current - 16300); // 0.1 * 10 + datalayer_battery->status.current_dA = (BMS_current - 16300); // 0.1 * 10 if (nof_cells_determined) { - datalayer.battery.info.total_capacity_Wh = - ((float)datalayer.battery.info.number_of_cells) * 3.67 * ((float)BMS_capacity_ah) * 0.2 * 1.02564; + datalayer_battery->info.total_capacity_Wh = + ((float)datalayer_battery->info.number_of_cells) * 3.67 * ((float)BMS_capacity_ah) * 0.2 * 1.02564; // The factor 1.02564 = 1/0.975 is to correct for bottom 2.5% which is reported by the remaining_capacity_Wh, // but which is not actually usable, but if we do not include it, the remaining_capacity_Wh can be larger than // the total_capacity_Wh. @@ -216,32 +216,32 @@ void MebBattery:: // total_capacity_Wh calculated above. int Wh_max = 61832 * 0.935; // 108 cells - if (datalayer.battery.info.number_of_cells <= 84) + if (datalayer_battery->info.number_of_cells <= 84) Wh_max = 48091 * 0.9025; - else if (datalayer.battery.info.number_of_cells <= 96) + else if (datalayer_battery->info.number_of_cells <= 96) Wh_max = 82442 * 0.9025; if (BMS_capacity_ah > 0) - datalayer.battery.status.soh_pptt = 10000 * datalayer.battery.info.total_capacity_Wh / (Wh_max * 1.02564); + datalayer_battery->status.soh_pptt = 10000 * datalayer_battery->info.total_capacity_Wh / (Wh_max * 1.02564); } - datalayer.battery.status.remaining_capacity_Wh = usable_energy_amount_Wh * 5; + datalayer_battery->status.remaining_capacity_Wh = usable_energy_amount_Wh * 5; - datalayer.battery.status.max_charge_power_W = (max_charge_power_watt * 100); + datalayer_battery->status.max_charge_power_W = (max_charge_power_watt * 100); - datalayer.battery.status.max_discharge_power_W = (max_discharge_power_watt * 100); + datalayer_battery->status.max_discharge_power_W = (max_discharge_power_watt * 100); //Power in watts, Negative = charging batt - datalayer.battery.status.active_power_W = - ((datalayer.battery.status.voltage_dV * datalayer.battery.status.current_dA) / 100); + datalayer_battery->status.active_power_W = + ((datalayer_battery->status.voltage_dV * datalayer_battery->status.current_dA) / 100); // datalayer.battery.status.temperature_min_dC = actual_temperature_lowest_C*5 -400; // We use the value below, because it has better accuracy - datalayer.battery.status.temperature_min_dC = (battery_min_temp * 10) / 64; + datalayer_battery->status.temperature_min_dC = (battery_min_temp * 10) / 64; // datalayer.battery.status.temperature_max_dC = actual_temperature_highest_C*5 -400; // We use the value below, because it has better accuracy - datalayer.battery.status.temperature_max_dC = (battery_max_temp * 10) / 64; + datalayer_battery->status.temperature_max_dC = (battery_max_temp * 10) / 64; //Map all cell voltages to the global array - memcpy(datalayer.battery.status.cell_voltages_mV, cellvoltages_polled, 108 * sizeof(uint16_t)); + memcpy(datalayer_battery->status.cell_voltages_mV, cellvoltages_polled, 108 * sizeof(uint16_t)); if (service_disconnect_switch_missing) { set_event(EVENT_HVIL_FAILURE, 1); diff --git a/Software/src/battery/MEB-BATTERY.h b/Software/src/battery/MEB-BATTERY.h index ae484576..0751fe6e 100644 --- a/Software/src/battery/MEB-BATTERY.h +++ b/Software/src/battery/MEB-BATTERY.h @@ -9,6 +9,19 @@ class MebBattery : public CanBattery { public: + // Use this constructor for the second battery. + MebBattery(DATALAYER_BATTERY_TYPE* datalayer_ptr, DATALAYER_INFO_MEB* extended, CAN_Interface targetCan) + : CanBattery(targetCan) { + datalayer_battery = datalayer_ptr; + + BMS_voltage = 0; + } + // Use the default constructor to create the first or single battery. + MebBattery() { + datalayer_battery = &datalayer.battery; + datalayer_meb = &datalayer_extended.meb; + } + virtual void setup(void); virtual void handle_incoming_can_frame(CAN_frame rx_frame); virtual void update_values(); @@ -21,6 +34,10 @@ class MebBattery : public CanBattery { private: MebHtmlRenderer renderer; + + DATALAYER_BATTERY_TYPE* datalayer_battery; + DATALAYER_INFO_MEB* datalayer_meb; + static const int MAX_PACK_VOLTAGE_84S_DV = 3528; //5000 = 500.0V static const int MIN_PACK_VOLTAGE_84S_DV = 2520; static const int MAX_PACK_VOLTAGE_96S_DV = 4032; From 6c25a669ec983913d0cb134349522a9aeb336e1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 31 Aug 2025 17:20:28 +0300 Subject: [PATCH 096/245] Update Software.ino --- Software/Software.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/Software.ino b/Software/Software.ino index 92c32305..9367bedf 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -41,7 +41,7 @@ volatile unsigned long long bmsResetTimeOffset = 0; // The current software version, shown on webserver -const char* version_number = "9.0.RC3experimental"; +const char* version_number = "9.0.RC4"; // Interval timers volatile unsigned long currentMillis = 0; From 36493a89ccd69ef72dc7290695eb62bfc84d2e83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 31 Aug 2025 17:50:26 +0300 Subject: [PATCH 097/245] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c6872409..e18215ba 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ![GitHub release (with filter)](https://img.shields.io/github/v/release/dalathegreat/BYD-Battery-Emulator-For-Gen24?color=%23008000) ![GitHub Repo stars](https://img.shields.io/github/stars/dalathegreat/Battery-Emulator?style=flat&color=%23128512) ![GitHub forks](https://img.shields.io/github/forks/dalathegreat/Battery-Emulator?style=flat&color=%23128512) -![GitHub actions](https://img.shields.io/github/actions/workflow/status/dalathegreat/BYD-Battery-Emulator-For-Gen24/compile-common-image-lilygo.yml?color=0E810E) +![GitHub actions](https://img.shields.io/github/actions/workflow/status/dalathegreat/BYD-Battery-Emulator-For-Gen24/compile-common-image-lilygo-TCAN.yml?color=0E810E) ![Static Badge](https://img.shields.io/badge/made-with_love-blue?color=%23008000) ## What is Battery Emulator? From d38a8d6655fdd039568c788d431a3c5eded7ed44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 31 Aug 2025 17:50:51 +0300 Subject: [PATCH 098/245] Update Software.ino --- Software/Software.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/Software.ino b/Software/Software.ino index 9367bedf..71cf186c 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -41,7 +41,7 @@ volatile unsigned long long bmsResetTimeOffset = 0; // The current software version, shown on webserver -const char* version_number = "9.0.RC4"; +const char* version_number = "9.0.RC5experimental"; // Interval timers volatile unsigned long currentMillis = 0; From dc4aa951098032d01d5753687c620e24da265241 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 31 Aug 2025 21:57:41 +0300 Subject: [PATCH 099/245] Remove broken NTP BMS reset functionality --- Software/Software.ino | 14 -- Software/USER_SETTINGS.cpp | 8 - Software/USER_SETTINGS.h | 2 - Software/src/devboard/utils/events.cpp | 7 - Software/src/devboard/utils/events.h | 204 +++++++++++------------ Software/src/devboard/utils/ntp_time.cpp | 64 ------- Software/src/devboard/utils/ntp_time.h | 12 -- 7 files changed, 101 insertions(+), 210 deletions(-) delete mode 100644 Software/src/devboard/utils/ntp_time.cpp delete mode 100644 Software/src/devboard/utils/ntp_time.h diff --git a/Software/Software.ino b/Software/Software.ino index 71cf186c..bfd3ceab 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -35,11 +35,6 @@ #error You must select a target hardware in the USER_SETTINGS.h file! #endif -#ifdef PERIODIC_BMS_RESET_AT -#include "src/devboard/utils/ntp_time.h" -#endif -volatile unsigned long long bmsResetTimeOffset = 0; - // The current software version, shown on webserver const char* version_number = "9.0.RC5experimental"; @@ -152,15 +147,6 @@ void setup() { xTaskCreatePinnedToCore((TaskFunction_t)&core_loop, "core_loop", 4096, NULL, TASK_CORE_PRIO, &main_loop_task, esp32hal->CORE_FUNCTION_CORE()); -#ifdef PERIODIC_BMS_RESET_AT - bmsResetTimeOffset = getTimeOffsetfromNowUntil(PERIODIC_BMS_RESET_AT); - if (bmsResetTimeOffset == 0) { - set_event(EVENT_PERIODIC_BMS_RESET_AT_INIT_FAILED, 0); - } else { - set_event(EVENT_PERIODIC_BMS_RESET_AT_INIT_SUCCESS, 0); - } -#endif - DEBUG_PRINTF("setup() complete\n"); } diff --git a/Software/USER_SETTINGS.cpp b/Software/USER_SETTINGS.cpp index edf41a9a..1bc74643 100644 --- a/Software/USER_SETTINGS.cpp +++ b/Software/USER_SETTINGS.cpp @@ -71,11 +71,3 @@ volatile float CHARGER_MIN_HV = 200; // Min permissible output (VDC) of cha volatile float CHARGER_MAX_POWER = 3300; // Max power capable of charger, as a ceiling for validating config volatile float CHARGER_MAX_A = 11.5; // Max current output (amps) of charger volatile float CHARGER_END_A = 1.0; // Current at which charging is considered complete - -#ifdef PERIODIC_BMS_RESET_AT -// A list of rules for your zone can be obtained from https://github.com/esp8266/Arduino/blob/master/cores/esp8266/TZ.h -const char* time_zone = - "GMT0BST,M3.5.0/1,M10.5.0"; // TimeZone rule for Europe/London including daylight adjustment rules (optional) -const char* ntpServer1 = "pool.ntp.org"; -const char* ntpServer2 = "time.nist.gov"; -#endif diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index 9a2a254d..bdef00da 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -90,8 +90,6 @@ //#define NC_CONTACTORS //Enable this line to control normally closed contactors. CONTACTOR_CONTROL must be enabled for this option. Extremely rare setting! //#define PERIODIC_BMS_RESET //Enable to have the emulator powercycle the connected battery every 24hours via GPIO. Useful for some batteries like Nissan LEAF //#define REMOTE_BMS_RESET //Enable to allow the emulator to remotely trigger a powercycle of the battery via MQTT. Useful for some batteries like Nissan LEAF -// PERIODIC_BMS_RESET_AT Uses NTP server, internet required. In 24 Hour format WITHOUT leading 0. e.g 0230 should be 230. Time Zone is set in USER_SETTINGS.cpp -//#define PERIODIC_BMS_RESET_AT 525 /* Shunt/Contactor settings (Optional) */ //#define BMW_SBOX // SBOX relay control & battery current/voltage measurement diff --git a/Software/src/devboard/utils/events.cpp b/Software/src/devboard/utils/events.cpp index 5085ff8a..19e877b3 100644 --- a/Software/src/devboard/utils/events.cpp +++ b/Software/src/devboard/utils/events.cpp @@ -125,8 +125,6 @@ void init_events(void) { events.entries[EVENT_EQUIPMENT_STOP].level = EVENT_LEVEL_ERROR; events.entries[EVENT_SD_INIT_FAILED].level = EVENT_LEVEL_WARNING; events.entries[EVENT_PERIODIC_BMS_RESET].level = EVENT_LEVEL_INFO; - events.entries[EVENT_PERIODIC_BMS_RESET_AT_INIT_SUCCESS].level = EVENT_LEVEL_INFO; - events.entries[EVENT_PERIODIC_BMS_RESET_AT_INIT_FAILED].level = EVENT_LEVEL_WARNING; events.entries[EVENT_BATTERY_TEMP_DEVIATION_HIGH].level = EVENT_LEVEL_WARNING; events.entries[EVENT_GPIO_CONFLICT].level = EVENT_LEVEL_ERROR; events.entries[EVENT_GPIO_NOT_DEFINED].level = EVENT_LEVEL_ERROR; @@ -365,11 +363,6 @@ String get_event_message_string(EVENTS_ENUM_TYPE event) { return "SD card initialization failed, check hardware. Power must be removed to reset the SD card."; case EVENT_PERIODIC_BMS_RESET: return "BMS Reset Event Completed."; - case EVENT_PERIODIC_BMS_RESET_AT_INIT_SUCCESS: - return "Successfully syncronised with the NTP Server. BMS will reset every 24 hours at defined time"; - case EVENT_PERIODIC_BMS_RESET_AT_INIT_FAILED: - return "Failed to syncronise with the NTP Server. BMS will reset every 24 hours from when the emulator was " - "powered on"; case EVENT_GPIO_CONFLICT: return "GPIO Pin Conflict: The pin used by '" + esp32hal->failed_allocator() + "' is already allocated by '" + esp32hal->conflicting_allocator() + "'. Please check your configuration and assign different pins."; diff --git a/Software/src/devboard/utils/events.h b/Software/src/devboard/utils/events.h index 6b90cf44..8f85b13b 100644 --- a/Software/src/devboard/utils/events.h +++ b/Software/src/devboard/utils/events.h @@ -9,109 +9,107 @@ #define GENERATE_ENUM(ENUM) ENUM, #define GENERATE_STRING(STRING) #STRING, -#define EVENTS_ENUM_TYPE(XX) \ - XX(EVENT_CANMCP2517FD_INIT_FAILURE) \ - XX(EVENT_CANMCP2515_INIT_FAILURE) \ - XX(EVENT_CANFD_BUFFER_FULL) \ - XX(EVENT_CAN_BUFFER_FULL) \ - XX(EVENT_CAN_CORRUPTED_WARNING) \ - XX(EVENT_CAN_BATTERY_MISSING) \ - XX(EVENT_CAN_BATTERY2_MISSING) \ - XX(EVENT_CAN_CHARGER_MISSING) \ - XX(EVENT_CAN_INVERTER_MISSING) \ - XX(EVENT_CAN_NATIVE_TX_FAILURE) \ - XX(EVENT_CHARGE_LIMIT_EXCEEDED) \ - XX(EVENT_CONTACTOR_WELDED) \ - XX(EVENT_CONTACTOR_OPEN) \ - XX(EVENT_CPU_OVERHEATING) \ - XX(EVENT_CPU_OVERHEATED) \ - XX(EVENT_DISCHARGE_LIMIT_EXCEEDED) \ - XX(EVENT_WATER_INGRESS) \ - XX(EVENT_12V_LOW) \ - XX(EVENT_SOC_PLAUSIBILITY_ERROR) \ - XX(EVENT_SOC_UNAVAILABLE) \ - XX(EVENT_STALE_VALUE) \ - XX(EVENT_KWH_PLAUSIBILITY_ERROR) \ - XX(EVENT_BALANCING_START) \ - XX(EVENT_BALANCING_END) \ - XX(EVENT_BATTERY_EMPTY) \ - XX(EVENT_BATTERY_FULL) \ - XX(EVENT_BATTERY_FUSE) \ - XX(EVENT_BATTERY_FROZEN) \ - XX(EVENT_BATTERY_CAUTION) \ - XX(EVENT_BATTERY_CHG_STOP_REQ) \ - XX(EVENT_BATTERY_DISCHG_STOP_REQ) \ - XX(EVENT_BATTERY_CHG_DISCHG_STOP_REQ) \ - XX(EVENT_BATTERY_OVERHEAT) \ - XX(EVENT_BATTERY_OVERVOLTAGE) \ - XX(EVENT_BATTERY_UNDERVOLTAGE) \ - XX(EVENT_BATTERY_VALUE_UNAVAILABLE) \ - XX(EVENT_BATTERY_ISOLATION) \ - XX(EVENT_BATTERY_REQUESTS_HEAT) \ - XX(EVENT_BATTERY_WARMED_UP) \ - XX(EVENT_VOLTAGE_DIFFERENCE) \ - XX(EVENT_SOH_DIFFERENCE) \ - XX(EVENT_SOH_LOW) \ - XX(EVENT_HVIL_FAILURE) \ - XX(EVENT_PRECHARGE_FAILURE) \ - XX(EVENT_INTERNAL_OPEN_FAULT) \ - XX(EVENT_INVERTER_OPEN_CONTACTOR) \ - XX(EVENT_INTERFACE_MISSING) \ - XX(EVENT_MODBUS_INVERTER_MISSING) \ - XX(EVENT_NO_ENABLE_DETECTED) \ - XX(EVENT_ERROR_OPEN_CONTACTOR) \ - XX(EVENT_CELL_CRITICAL_UNDER_VOLTAGE) \ - XX(EVENT_CELL_CRITICAL_OVER_VOLTAGE) \ - XX(EVENT_CELL_UNDER_VOLTAGE) \ - XX(EVENT_CELL_OVER_VOLTAGE) \ - XX(EVENT_CELL_DEVIATION_HIGH) \ - XX(EVENT_UNKNOWN_EVENT_SET) \ - XX(EVENT_OTA_UPDATE) \ - XX(EVENT_OTA_UPDATE_TIMEOUT) \ - XX(EVENT_DUMMY_INFO) \ - XX(EVENT_DUMMY_DEBUG) \ - XX(EVENT_DUMMY_WARNING) \ - XX(EVENT_DUMMY_ERROR) \ - XX(EVENT_PERSISTENT_SAVE_INFO) \ - XX(EVENT_SERIAL_RX_WARNING) \ - XX(EVENT_SERIAL_RX_FAILURE) \ - XX(EVENT_SERIAL_TX_FAILURE) \ - XX(EVENT_SERIAL_TRANSMITTER_FAILURE) \ - XX(EVENT_SMA_PAIRING) \ - XX(EVENT_TASK_OVERRUN) \ - XX(EVENT_RESET_UNKNOWN) \ - XX(EVENT_RESET_POWERON) \ - XX(EVENT_RESET_EXT) \ - XX(EVENT_RESET_SW) \ - XX(EVENT_RESET_PANIC) \ - XX(EVENT_RESET_INT_WDT) \ - XX(EVENT_RESET_TASK_WDT) \ - XX(EVENT_RESET_WDT) \ - XX(EVENT_RESET_DEEPSLEEP) \ - XX(EVENT_RESET_BROWNOUT) \ - XX(EVENT_RESET_SDIO) \ - XX(EVENT_RESET_USB) \ - XX(EVENT_RESET_JTAG) \ - XX(EVENT_RESET_EFUSE) \ - XX(EVENT_RESET_PWR_GLITCH) \ - XX(EVENT_RESET_CPU_LOCKUP) \ - XX(EVENT_RJXZS_LOG) \ - XX(EVENT_PAUSE_BEGIN) \ - XX(EVENT_PAUSE_END) \ - XX(EVENT_PID_FAILED) \ - XX(EVENT_WIFI_CONNECT) \ - XX(EVENT_WIFI_DISCONNECT) \ - XX(EVENT_MQTT_CONNECT) \ - XX(EVENT_MQTT_DISCONNECT) \ - XX(EVENT_EQUIPMENT_STOP) \ - XX(EVENT_AUTOMATIC_PRECHARGE_FAILURE) \ - XX(EVENT_SD_INIT_FAILED) \ - XX(EVENT_PERIODIC_BMS_RESET) \ - XX(EVENT_PERIODIC_BMS_RESET_AT_INIT_SUCCESS) \ - XX(EVENT_PERIODIC_BMS_RESET_AT_INIT_FAILED) \ - XX(EVENT_BATTERY_TEMP_DEVIATION_HIGH) \ - XX(EVENT_GPIO_NOT_DEFINED) \ - XX(EVENT_GPIO_CONFLICT) \ +#define EVENTS_ENUM_TYPE(XX) \ + XX(EVENT_CANMCP2517FD_INIT_FAILURE) \ + XX(EVENT_CANMCP2515_INIT_FAILURE) \ + XX(EVENT_CANFD_BUFFER_FULL) \ + XX(EVENT_CAN_BUFFER_FULL) \ + XX(EVENT_CAN_CORRUPTED_WARNING) \ + XX(EVENT_CAN_BATTERY_MISSING) \ + XX(EVENT_CAN_BATTERY2_MISSING) \ + XX(EVENT_CAN_CHARGER_MISSING) \ + XX(EVENT_CAN_INVERTER_MISSING) \ + XX(EVENT_CAN_NATIVE_TX_FAILURE) \ + XX(EVENT_CHARGE_LIMIT_EXCEEDED) \ + XX(EVENT_CONTACTOR_WELDED) \ + XX(EVENT_CONTACTOR_OPEN) \ + XX(EVENT_CPU_OVERHEATING) \ + XX(EVENT_CPU_OVERHEATED) \ + XX(EVENT_DISCHARGE_LIMIT_EXCEEDED) \ + XX(EVENT_WATER_INGRESS) \ + XX(EVENT_12V_LOW) \ + XX(EVENT_SOC_PLAUSIBILITY_ERROR) \ + XX(EVENT_SOC_UNAVAILABLE) \ + XX(EVENT_STALE_VALUE) \ + XX(EVENT_KWH_PLAUSIBILITY_ERROR) \ + XX(EVENT_BALANCING_START) \ + XX(EVENT_BALANCING_END) \ + XX(EVENT_BATTERY_EMPTY) \ + XX(EVENT_BATTERY_FULL) \ + XX(EVENT_BATTERY_FUSE) \ + XX(EVENT_BATTERY_FROZEN) \ + XX(EVENT_BATTERY_CAUTION) \ + XX(EVENT_BATTERY_CHG_STOP_REQ) \ + XX(EVENT_BATTERY_DISCHG_STOP_REQ) \ + XX(EVENT_BATTERY_CHG_DISCHG_STOP_REQ) \ + XX(EVENT_BATTERY_OVERHEAT) \ + XX(EVENT_BATTERY_OVERVOLTAGE) \ + XX(EVENT_BATTERY_UNDERVOLTAGE) \ + XX(EVENT_BATTERY_VALUE_UNAVAILABLE) \ + XX(EVENT_BATTERY_ISOLATION) \ + XX(EVENT_BATTERY_REQUESTS_HEAT) \ + XX(EVENT_BATTERY_WARMED_UP) \ + XX(EVENT_VOLTAGE_DIFFERENCE) \ + XX(EVENT_SOH_DIFFERENCE) \ + XX(EVENT_SOH_LOW) \ + XX(EVENT_HVIL_FAILURE) \ + XX(EVENT_PRECHARGE_FAILURE) \ + XX(EVENT_INTERNAL_OPEN_FAULT) \ + XX(EVENT_INVERTER_OPEN_CONTACTOR) \ + XX(EVENT_INTERFACE_MISSING) \ + XX(EVENT_MODBUS_INVERTER_MISSING) \ + XX(EVENT_NO_ENABLE_DETECTED) \ + XX(EVENT_ERROR_OPEN_CONTACTOR) \ + XX(EVENT_CELL_CRITICAL_UNDER_VOLTAGE) \ + XX(EVENT_CELL_CRITICAL_OVER_VOLTAGE) \ + XX(EVENT_CELL_UNDER_VOLTAGE) \ + XX(EVENT_CELL_OVER_VOLTAGE) \ + XX(EVENT_CELL_DEVIATION_HIGH) \ + XX(EVENT_UNKNOWN_EVENT_SET) \ + XX(EVENT_OTA_UPDATE) \ + XX(EVENT_OTA_UPDATE_TIMEOUT) \ + XX(EVENT_DUMMY_INFO) \ + XX(EVENT_DUMMY_DEBUG) \ + XX(EVENT_DUMMY_WARNING) \ + XX(EVENT_DUMMY_ERROR) \ + XX(EVENT_PERSISTENT_SAVE_INFO) \ + XX(EVENT_SERIAL_RX_WARNING) \ + XX(EVENT_SERIAL_RX_FAILURE) \ + XX(EVENT_SERIAL_TX_FAILURE) \ + XX(EVENT_SERIAL_TRANSMITTER_FAILURE) \ + XX(EVENT_SMA_PAIRING) \ + XX(EVENT_TASK_OVERRUN) \ + XX(EVENT_RESET_UNKNOWN) \ + XX(EVENT_RESET_POWERON) \ + XX(EVENT_RESET_EXT) \ + XX(EVENT_RESET_SW) \ + XX(EVENT_RESET_PANIC) \ + XX(EVENT_RESET_INT_WDT) \ + XX(EVENT_RESET_TASK_WDT) \ + XX(EVENT_RESET_WDT) \ + XX(EVENT_RESET_DEEPSLEEP) \ + XX(EVENT_RESET_BROWNOUT) \ + XX(EVENT_RESET_SDIO) \ + XX(EVENT_RESET_USB) \ + XX(EVENT_RESET_JTAG) \ + XX(EVENT_RESET_EFUSE) \ + XX(EVENT_RESET_PWR_GLITCH) \ + XX(EVENT_RESET_CPU_LOCKUP) \ + XX(EVENT_RJXZS_LOG) \ + XX(EVENT_PAUSE_BEGIN) \ + XX(EVENT_PAUSE_END) \ + XX(EVENT_PID_FAILED) \ + XX(EVENT_WIFI_CONNECT) \ + XX(EVENT_WIFI_DISCONNECT) \ + XX(EVENT_MQTT_CONNECT) \ + XX(EVENT_MQTT_DISCONNECT) \ + XX(EVENT_EQUIPMENT_STOP) \ + XX(EVENT_AUTOMATIC_PRECHARGE_FAILURE) \ + XX(EVENT_SD_INIT_FAILED) \ + XX(EVENT_PERIODIC_BMS_RESET) \ + XX(EVENT_BATTERY_TEMP_DEVIATION_HIGH) \ + XX(EVENT_GPIO_NOT_DEFINED) \ + XX(EVENT_GPIO_CONFLICT) \ XX(EVENT_NOF_EVENTS) typedef enum { EVENTS_ENUM_TYPE(GENERATE_ENUM) } EVENTS_ENUM_TYPE; diff --git a/Software/src/devboard/utils/ntp_time.cpp b/Software/src/devboard/utils/ntp_time.cpp deleted file mode 100644 index 5221a36a..00000000 --- a/Software/src/devboard/utils/ntp_time.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include "ntp_time.h" -#include -#include "../../devboard/utils/logging.h" -#include "time.h" - -const unsigned long millisInADay = 24 * 60 * 60 * 1000; // 24 hours in milliseconds - -unsigned long long getNtpTimeInMillis() { - configTzTime(time_zone, ntpServer1, ntpServer2); - struct tm timeinfo; - - // Wait for time to be set - for (int i = 0; i < 5; i++) { - if (getLocalTime(&timeinfo)) { - logging.println("Time retrieved from NTP Server"); - break; - } - logging.println("Waiting for NTP time..."); - } - - if (!getLocalTime(&timeinfo)) { - logging.println("Failed to obtain time"); - return 0; - } - - // Convert to milliseconds - time_t epochTime = mktime(&timeinfo); - return static_cast(epochTime) * 1000; -} - -// Function to calculate the difference in milliseconds to the next target time -unsigned long long millisToNextTargetTime(unsigned long long currentMillis, int targetTime) { - int hour = targetTime / 100; - int minute = targetTime % 100; - - time_t currentTime = currentMillis / 1000; // Convert milliseconds to seconds - struct tm* timeinfo = localtime(¤tTime); - - // Set timeinfo to the target time on the next day - timeinfo->tm_hour = hour; - timeinfo->tm_min = minute; - timeinfo->tm_sec = 0; - - // Increment day if the current time is past the target time - if (mktime(timeinfo) <= currentTime) { - timeinfo->tm_mday += 1; - } - time_t nextTargetTime = mktime(timeinfo); - unsigned long long nextTargetMillis = static_cast(nextTargetTime) * 1000; - return nextTargetMillis - currentMillis; -} - -unsigned long long getTimeOffsetfromNowUntil(int targetTime) { - logging.println("Getting time offset from now until " + String(targetTime)); - unsigned long long timeinMillis = getNtpTimeInMillis(); - if (timeinMillis != 0) { - logging.println("Time in millis: " + String(timeinMillis)); - unsigned long long timeOffsetUntilTargetTime = millisInADay - (millisToNextTargetTime(timeinMillis, targetTime)); - logging.println("Time offset until target time: " + String(timeOffsetUntilTargetTime)); - return timeOffsetUntilTargetTime; - } else - - return 0; -} diff --git a/Software/src/devboard/utils/ntp_time.h b/Software/src/devboard/utils/ntp_time.h deleted file mode 100644 index d4946104..00000000 --- a/Software/src/devboard/utils/ntp_time.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef __NTPTIME_H__ -#define __NTPTIME_H__ - -extern const char* ntpServer1; -extern const char* ntpServer2; -extern const char* time_zone; - -unsigned long long getNtpTimeInMillis(); -unsigned long long millisToNextTargetTime(unsigned long long currentMillis, int targetTime); -unsigned long long getTimeOffsetfromNowUntil(int targetTime); - -#endif From 13df59f80687b845d103776fd8fb624b827bd383 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 31 Aug 2025 22:54:57 +0300 Subject: [PATCH 100/245] Make performance profiling configurable from webserver --- Software/Software.ino | 87 +++++++++---------- Software/USER_SETTINGS.h | 1 - .../comm_contactorcontrol.cpp | 3 +- Software/src/communication/nvm/comm_nvm.cpp | 1 + Software/src/datalayer/datalayer.h | 6 +- Software/src/devboard/utils/time_meas.h | 6 -- .../src/devboard/webserver/settings_html.cpp | 7 ++ Software/src/devboard/webserver/webserver.cpp | 37 ++++---- 8 files changed, 74 insertions(+), 74 deletions(-) diff --git a/Software/Software.ino b/Software/Software.ino index bfd3ceab..98fe5f8e 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -42,10 +42,11 @@ const char* version_number = "9.0.RC5experimental"; volatile unsigned long currentMillis = 0; unsigned long previousMillis10ms = 0; unsigned long previousMillisUpdateVal = 0; -#ifdef FUNCTION_TIME_MEASUREMENT // Task time measurement for debugging MyTimer core_task_timer_10s(INTERVAL_10_S); -#endif +uint64_t start_time_10ms = 0; +uint64_t start_time_values = 0; +uint64_t start_time_cantx = 0; TaskHandle_t main_loop_task; TaskHandle_t connectivity_loop_task; TaskHandle_t logging_loop_task; @@ -248,24 +249,24 @@ void core_loop(void*) { set_event(EVENT_TASK_OVERRUN, (currentMillis - previousMillis10ms)); } previousMillis10ms = currentMillis; -#ifdef FUNCTION_TIME_MEASUREMENT - START_TIME_MEASUREMENT(time_10ms); -#endif + if (datalayer.system.info.performance_measurement_active) { + START_TIME_MEASUREMENT(10ms); + } led_exe(); handle_contactors(); // Take care of startup precharge/contactor closing #ifdef PRECHARGE_CONTROL handle_precharge_control(currentMillis); #endif // PRECHARGE_CONTROL -#ifdef FUNCTION_TIME_MEASUREMENT - END_TIME_MEASUREMENT_MAX(time_10ms, datalayer.system.status.time_10ms_us); -#endif + if (datalayer.system.info.performance_measurement_active) { + END_TIME_MEASUREMENT_MAX(10ms, datalayer.system.status.time_10ms_us); + } } if (currentMillis - previousMillisUpdateVal >= INTERVAL_1_S) { previousMillisUpdateVal = currentMillis; // Order matters on the update_loop! -#ifdef FUNCTION_TIME_MEASUREMENT - START_TIME_MEASUREMENT(time_values); -#endif + if (datalayer.system.info.performance_measurement_active) { + START_TIME_MEASUREMENT(values); + } update_pause_state(); // Check if we are OK to send CAN or need to pause // Fetch battery values @@ -285,48 +286,46 @@ void core_loop(void*) { inverter->update_values(); } -#ifdef FUNCTION_TIME_MEASUREMENT - END_TIME_MEASUREMENT_MAX(time_values, datalayer.system.status.time_values_us); -#endif + if (datalayer.system.info.performance_measurement_active) { + END_TIME_MEASUREMENT_MAX(values, datalayer.system.status.time_values_us); + } + } + if (datalayer.system.info.performance_measurement_active) { + START_TIME_MEASUREMENT(cantx); } -#ifdef FUNCTION_TIME_MEASUREMENT - START_TIME_MEASUREMENT(cantx); -#endif // Let all transmitter objects send their messages for (auto& transmitter : transmitters) { transmitter->transmit(currentMillis); } -#ifdef FUNCTION_TIME_MEASUREMENT - END_TIME_MEASUREMENT_MAX(cantx, datalayer.system.status.time_cantx_us); - END_TIME_MEASUREMENT_MAX(all, datalayer.system.status.core_task_10s_max_us); -#endif -#ifdef FUNCTION_TIME_MEASUREMENT - if (datalayer.system.status.core_task_10s_max_us > datalayer.system.status.core_task_max_us) { - // Update worst case total time - datalayer.system.status.core_task_max_us = datalayer.system.status.core_task_10s_max_us; - // Record snapshots of task times - datalayer.system.status.time_snap_comm_us = datalayer.system.status.time_comm_us; - datalayer.system.status.time_snap_10ms_us = datalayer.system.status.time_10ms_us; - datalayer.system.status.time_snap_values_us = datalayer.system.status.time_values_us; - datalayer.system.status.time_snap_cantx_us = datalayer.system.status.time_cantx_us; - datalayer.system.status.time_snap_ota_us = datalayer.system.status.time_ota_us; - } + if (datalayer.system.info.performance_measurement_active) { + END_TIME_MEASUREMENT_MAX(cantx, datalayer.system.status.time_cantx_us); + END_TIME_MEASUREMENT_MAX(all, datalayer.system.status.core_task_10s_max_us); + if (datalayer.system.status.core_task_10s_max_us > datalayer.system.status.core_task_max_us) { + // Update worst case total time + datalayer.system.status.core_task_max_us = datalayer.system.status.core_task_10s_max_us; + // Record snapshots of task times + datalayer.system.status.time_snap_comm_us = datalayer.system.status.time_comm_us; + datalayer.system.status.time_snap_10ms_us = datalayer.system.status.time_10ms_us; + datalayer.system.status.time_snap_values_us = datalayer.system.status.time_values_us; + datalayer.system.status.time_snap_cantx_us = datalayer.system.status.time_cantx_us; + datalayer.system.status.time_snap_ota_us = datalayer.system.status.time_ota_us; + } - datalayer.system.status.core_task_max_us = - MAX(datalayer.system.status.core_task_10s_max_us, datalayer.system.status.core_task_max_us); - if (core_task_timer_10s.elapsed()) { - datalayer.system.status.time_ota_us = 0; - datalayer.system.status.time_comm_us = 0; - datalayer.system.status.time_10ms_us = 0; - datalayer.system.status.time_values_us = 0; - datalayer.system.status.time_cantx_us = 0; - datalayer.system.status.core_task_10s_max_us = 0; - datalayer.system.status.wifi_task_10s_max_us = 0; - datalayer.system.status.mqtt_task_10s_max_us = 0; + datalayer.system.status.core_task_max_us = + MAX(datalayer.system.status.core_task_10s_max_us, datalayer.system.status.core_task_max_us); + if (core_task_timer_10s.elapsed()) { + datalayer.system.status.time_ota_us = 0; + datalayer.system.status.time_comm_us = 0; + datalayer.system.status.time_10ms_us = 0; + datalayer.system.status.time_values_us = 0; + datalayer.system.status.time_cantx_us = 0; + datalayer.system.status.core_task_10s_max_us = 0; + datalayer.system.status.wifi_task_10s_max_us = 0; + datalayer.system.status.mqtt_task_10s_max_us = 0; + } } -#endif // FUNCTION_TIME_MEASUREMENT esp_task_wdt_reset(); // Reset watchdog to prevent reset vTaskDelayUntil(&xLastWakeTime, xFrequency); } diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index bdef00da..fdf4340b 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -123,7 +123,6 @@ #define WIFIAP //When enabled, the emulator will broadcast its own access point Wifi. Can be used at the same time as a normal Wifi connection to a router. #define MDNSRESPONDER //Enable this line to enable MDNS, allows battery monitor te be found by .local address. Requires WEBSERVER to be enabled. #define LOAD_SAVED_SETTINGS_ON_BOOT // Enable this line to read settings stored via the webserver on boot (overrides Wifi credentials set here) -//#define FUNCTION_TIME_MEASUREMENT // Enable this to record execution times and present them in the web UI (WARNING, raises CPU load, do not use for production) /* MQTT options */ // #define MQTT // Enable this line to enable MQTT diff --git a/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp b/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp index 8983807e..c7a0e877 100644 --- a/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp +++ b/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp @@ -297,7 +297,7 @@ void handle_BMSpower() { if (periodic_bms_reset) { // Check if 24 hours have passed since the last power removal - if ((currentTime + bmsResetTimeOffset) - lastPowerRemovalTime >= powerRemovalInterval) { + if (currentTime - lastPowerRemovalTime >= powerRemovalInterval) { start_bms_reset(); } } @@ -329,7 +329,6 @@ void start_bms_reset() { if (!datalayer.system.status.BMS_reset_in_progress) { lastPowerRemovalTime = currentTime; // Record the time when BMS reset was started // we are now resetting at the correct time. We don't need to offset anymore - bmsResetTimeOffset = 0; // Set a flag to let the rest of the system know we are cutting power to the BMS. // The battery CAN sending routine will then know not to try guto send anything towards battery while active datalayer.system.status.BMS_reset_in_progress = true; diff --git a/Software/src/communication/nvm/comm_nvm.cpp b/Software/src/communication/nvm/comm_nvm.cpp index 3c83f9c2..bd8a8802 100644 --- a/Software/src/communication/nvm/comm_nvm.cpp +++ b/Software/src/communication/nvm/comm_nvm.cpp @@ -144,6 +144,7 @@ void init_stored_settings() { remote_bms_reset = settings.getBool("REMBMSRESET", false); use_canfd_as_can = settings.getBool("CANFDASCAN", false); + datalayer.system.info.performance_measurement_active = settings.getBool("PERFPROFILE", false); datalayer.system.info.CAN_usb_logging_active = settings.getBool("CANLOGUSB", false); datalayer.system.info.usb_logging_active = settings.getBool("USBENABLED", false); datalayer.system.info.web_logging_active = settings.getBool("WEBENABLED", false); diff --git a/Software/src/datalayer/datalayer.h b/Software/src/datalayer/datalayer.h index 5ead9832..83649469 100644 --- a/Software/src/datalayer/datalayer.h +++ b/Software/src/datalayer/datalayer.h @@ -259,12 +259,13 @@ struct DATALAYER_SYSTEM_INFO_TYPE { bool can_native_send_fail = false; /** bool, MCP2515 CAN failed to send flag */ bool can_2515_send_fail = false; - /** uint16_t, MCP2518 CANFD failed to send flag */ + /** bool, MCP2518 CANFD failed to send flag */ bool can_2518_send_fail = false; + /** bool, determines if detailed performance measurement should be shown on webserver */ + bool performance_measurement_active = false; }; struct DATALAYER_SYSTEM_STATUS_TYPE { -#ifdef FUNCTION_TIME_MEASUREMENT /** Core task measurement variable */ int64_t core_task_max_us = 0; /** Core task measurement variable, reset each 10 seconds */ @@ -305,7 +306,6 @@ struct DATALAYER_SYSTEM_STATUS_TYPE { * This will show the performance of CAN TX when the total time reached a new worst case */ int64_t time_snap_cantx_us = 0; -#endif /** uint8_t */ /** A counter set each time a new message comes from inverter. * This value then gets decremented every second. Incase we reach 0 diff --git a/Software/src/devboard/utils/time_meas.h b/Software/src/devboard/utils/time_meas.h index 30f1e5eb..df2c8b0c 100644 --- a/Software/src/devboard/utils/time_meas.h +++ b/Software/src/devboard/utils/time_meas.h @@ -7,7 +7,6 @@ /** Start time measurement in microseconds * Input parameter must be a unique "tag", e.g: START_TIME_MEASUREMENT(wifi); */ -#ifdef FUNCTION_TIME_MEASUREMENT #define START_TIME_MEASUREMENT(x) int64_t start_time_##x = esp_timer_get_time() /** End time measurement in microseconds * Input parameters are the unique tag and the name of the ALREADY EXISTING @@ -21,10 +20,5 @@ * This will log the maximum value in the destination variable. */ #define END_TIME_MEASUREMENT_MAX(x, y) y = MAX(y, esp_timer_get_time() - start_time_##x) -#else -#define START_TIME_MEASUREMENT(x) ; -#define END_TIME_MEASUREMENT(x, y) ; -#define END_TIME_MEASUREMENT_MAX(x, y) ; -#endif #endif diff --git a/Software/src/devboard/webserver/settings_html.cpp b/Software/src/devboard/webserver/settings_html.cpp index 0a9d4163..62060cfb 100644 --- a/Software/src/devboard/webserver/settings_html.cpp +++ b/Software/src/devboard/webserver/settings_html.cpp @@ -283,6 +283,10 @@ String settings_processor(const String& var, BatteryEmulatorSettingsStore& setti return settings.getBool("WIFIAPENABLED", wifiap_enabled) ? "checked" : ""; } + if (var == "PERFPROFILE") { + return settings.getBool("PERFPROFILE") ? "checked" : ""; + } + if (var == "CANLOGUSB") { return settings.getBool("CANLOGUSB") ? "checked" : ""; } @@ -970,6 +974,9 @@ const char* getCANInterfaceName(CAN_Interface interface) { + + + diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index d9ab1ad3..f0b5c633 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -411,7 +411,7 @@ void init_webserver() { const char* boolSettingNames[] = { "DBLBTR", "CNTCTRL", "CNTCTRLDBL", "PWMCNTCTRL", "PERBMSRESET", "SDLOGENABLED", "REMBMSRESET", "USBENABLED", "CANLOGUSB", "WEBENABLED", "CANFDASCAN", "CANLOGSD", "WIFIAPENABLED", "MQTTENABLED", - "HADISC", "MQTTTOPICS", "INVICNT", "GTWRHD", "DIGITALHVIL", + "HADISC", "MQTTTOPICS", "INVICNT", "GTWRHD", "DIGITALHVIL", "PERFPROFILE", }; // Handles the form POST from UI to save settings of the common image @@ -914,23 +914,24 @@ String processor(const String& var) { #endif // HW_STARK content += " @ " + String(datalayer.system.info.CPU_temperature, 1) + " °C

"; content += "

Uptime: " + get_uptime() + "

"; -#ifdef FUNCTION_TIME_MEASUREMENT - // Load information - content += "

Core task max load: " + String(datalayer.system.status.core_task_max_us) + " us

"; - content += "

Core task max load last 10 s: " + String(datalayer.system.status.core_task_10s_max_us) + " us

"; - content += - "

MQTT function (MQTT task) max load last 10 s: " + String(datalayer.system.status.mqtt_task_10s_max_us) + - " us

"; - content += - "

WIFI function (MQTT task) max load last 10 s: " + String(datalayer.system.status.wifi_task_10s_max_us) + - " us

"; - content += "

Max load @ worst case execution of core task:

"; - content += "

10ms function timing: " + String(datalayer.system.status.time_snap_10ms_us) + " us

"; - content += "

Values function timing: " + String(datalayer.system.status.time_snap_values_us) + " us

"; - content += "

CAN/serial RX function timing: " + String(datalayer.system.status.time_snap_comm_us) + " us

"; - content += "

CAN TX function timing: " + String(datalayer.system.status.time_snap_cantx_us) + " us

"; - content += "

OTA function timing: " + String(datalayer.system.status.time_snap_ota_us) + " us

"; -#endif // FUNCTION_TIME_MEASUREMENT + if (datalayer.system.info.performance_measurement_active) { + // Load information + content += "

Core task max load: " + String(datalayer.system.status.core_task_max_us) + " us

"; + content += + "

Core task max load last 10 s: " + String(datalayer.system.status.core_task_10s_max_us) + " us

"; + content += + "

MQTT function (MQTT task) max load last 10 s: " + String(datalayer.system.status.mqtt_task_10s_max_us) + + " us

"; + content += + "

WIFI function (MQTT task) max load last 10 s: " + String(datalayer.system.status.wifi_task_10s_max_us) + + " us

"; + content += "

Max load @ worst case execution of core task:

"; + content += "

10ms function timing: " + String(datalayer.system.status.time_snap_10ms_us) + " us

"; + content += "

Values function timing: " + String(datalayer.system.status.time_snap_values_us) + " us

"; + content += "

CAN/serial RX function timing: " + String(datalayer.system.status.time_snap_comm_us) + " us

"; + content += "

CAN TX function timing: " + String(datalayer.system.status.time_snap_cantx_us) + " us

"; + content += "

OTA function timing: " + String(datalayer.system.status.time_snap_ota_us) + " us

"; + } wl_status_t status = WiFi.status(); // Display ssid of network connected to and, if connected to the WiFi, its own IP From 7ded4f16622a1dfa9decd64938a3206305ec884d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 31 Aug 2025 23:52:44 +0300 Subject: [PATCH 101/245] Remove user voltage set ability from USER_SETTINGS, now common --- Software/USER_SETTINGS.h | 78 +----------------------------- Software/src/battery/BATTERIES.cpp | 22 ++------- 2 files changed, 6 insertions(+), 94 deletions(-) diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index fdf4340b..8c450453 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -4,77 +4,9 @@ #include #include "src/devboard/utils/types.h" -/* This file contains all the battery/inverter protocol settings Battery-Emulator software */ -/* To switch between batteries/inverters, uncomment a line to enable, comment out to disable. */ -/* There are also some options for battery limits and extra functionality */ -/* To edit battery specific limits, see also the USER_SETTINGS.cpp file*/ +/* This file is being transitioned towards COMMON_IMAGE. Use v8.16 if you are taking this software into use! */ -/* Select battery used */ -//#define BMW_I3_BATTERY -//#define BMW_IX_BATTERY -//#define BMW_PHEV_BATTERY -//#define BOLT_AMPERA_BATTERY -//#define BYD_ATTO_3_BATTERY -//#define FOXESS_BATTERY -//#define CELLPOWER_BMS -//#define CHADEMO_BATTERY //NOTE: inherently enables CONTACTOR_CONTROL below -//#define GEELY_GEOMETRY_C_BATTERY -//#define HYUNDAI_IONIQ_28_BATTERY -//#define CMFA_EV_BATTERY -//#define IMIEV_CZERO_ION_BATTERY -//#define JAGUAR_IPACE_BATTERY -//#define KIA_E_GMP_BATTERY -//#define KIA_HYUNDAI_64_BATTERY -//#define KIA_HYUNDAI_64_FD_BATTERY -//#define KIA_HYUNDAI_HYBRID_BATTERY -//#define MEB_BATTERY -//#define MG_5_BATTERY -//#define MG_HS_PHEV_BATTERY -//#define NISSAN_LEAF_BATTERY -//#define ORION_BMS -//#define PYLON_BATTERY -//#define DALY_BMS -//#define RJXZS_BMS -//#define RANGE_ROVER_PHEV_BATTERY -//#define RELION_BATTERY -//#define RENAULT_KANGOO_BATTERY -//#define RENAULT_TWIZY_BATTERY -//#define RENAULT_ZOE_GEN1_BATTERY -//#define RENAULT_ZOE_GEN2_BATTERY -//#define SONO_BATTERY -//#define SAMSUNG_SDI_LV_BATTERY -//#define SANTA_FE_PHEV_BATTERY -//#define SIMPBMS_BATTERY -//#define STELLANTIS_ECMP_BATTERY -//#define TESLA_MODEL_3Y_BATTERY -//#define TESLA_MODEL_SX_BATTERY -//#define VOLVO_SPA_BATTERY -//#define VOLVO_SPA_HYBRID_BATTERY -//#define TEST_FAKE_BATTERY -//#define DOUBLE_BATTERY //Enable this line if you use two identical batteries at the same time (requires separate CAN setup) - -/* Select inverter communication protocol. See Wiki for which to use with your inverter: https://github.com/dalathegreat/Battery-Emulator/wiki */ -//#define AFORE_CAN //Enable this line to emulate an "Afore battery" over CAN bus -//#define BYD_CAN //Enable this line to emulate a "BYD Battery-Box Premium HVS" over CAN Bus -//#define BYD_CAN_DEYE //Enable this line to emulate a "BYD Battery-Box Premium HVS" over CAN Bus, with Deye specific fixes -//#define BYD_KOSTAL_RS485 //Enable this line to emulate a "BYD 11kWh HVM battery" over Kostal RS485 -//#define BYD_MODBUS //Enable this line to emulate a "BYD 11kWh HVM battery" over Modbus RTU -//#define FERROAMP_CAN //Enable this line to emulate a "Pylon 4x96V Force H2" over CAN Bus -//#define FOXESS_CAN //Enable this line to emulate a "HV2600/ECS4100 battery" over CAN bus -//#define GROWATT_HV_CAN //Enable this line to emulate a "Growatt High Voltage v1.10 battery" over CAN bus -//#define GROWATT_LV_CAN //Enable this line to emulate a "48V Growatt Low Voltage battery" over CAN bus -//#define PYLON_LV_CAN //Enable this line to emulate a "48V Pylontech battery" over CAN bus -//#define PYLON_CAN //Enable this line to emulate a "High Voltage Pylontech battery" over CAN bus -//#define SCHNEIDER_CAN //Enable this line to emulate a "Schneider Version 2: SE BMS" over CAN bus -//#define SMA_BYD_H_CAN //Enable this line to emulate a "BYD Battery-Box H 8.9kWh, 7 mod" (SMA compatible) over CAN bus -//#define SMA_BYD_HVS_CAN //Enable this line to emulate a "BYD Battery-Box HVS 10.2KW battery" (SMA compatible) over CAN bus -//#define SMA_LV_CAN //Enable this line to emulate a "SMA Sunny Island 48V battery" over CAN bus -//#define SMA_TRIPOWER_CAN //Enable this line to emulate a "SMA Home Storage battery" over CAN bus -//#define SOFAR_CAN //Enable this line to emulate a "Sofar Energy Storage Inverter High Voltage BMS General Protocol (Extended Frame)" over CAN bus -//#define SOLAX_CAN //Enable this line to emulate a "SolaX Triple Power LFP" over CAN bus -//#define SOLXPOW_CAN //Enable this line to emulate a "Solxpow compatible battery" over CAN bus -//#define SOL_ARK_LV_CAN //Enable this line to emulate a "Sol-Ark compatible LV battery" over CAN bus -//#define SUNGROW_CAN //Enable this line to emulate a "Sungrow SBR064" over CAN bus +//#define COMMON_IMAGE /* Select hardware used for Battery-Emulator */ //#define HW_LILYGO @@ -164,12 +96,6 @@ // 3000 = 300.0V, Target discharge voltage (Value can be tuned on the fly via webserver). Not used unless BATTERY_USE_VOLTAGE_LIMITS = true #define BATTERY_MAX_DISCHARGE_VOLTAGE 3000 -/* Pack/cell voltage limits for custom-BMS batteries (RJXZS, Daly, etc.) */ -//#define MAX_CUSTOM_PACK_VOLTAGE_DV 5000 // 5000 = 500.0V , Maximum pack voltage in decivolts -//#define MIN_CUSTOM_PACK_VOLTAGE_DV 2500 // 2500 = 250.0V , Minimum pack voltage in decivolts -//#define MAX_CUSTOM_CELL_VOLTAGE_MV 4250 // 4250 = 4.250V , Maximum cell voltage in millivolts (4250 = 4.250V) -//#define MIN_CUSTOM_CELL_VOLTAGE_MV 2650 // 2650 = 2.650V , Minimum cell voltage in millivolts (2650 = 2.650V) - /* Do not change any code below this line */ /* Only change battery specific settings above and in "USER_SETTINGS.cpp" */ typedef struct { diff --git a/Software/src/battery/BATTERIES.cpp b/Software/src/battery/BATTERIES.cpp index 10134bf2..e76f35cf 100644 --- a/Software/src/battery/BATTERIES.cpp +++ b/Software/src/battery/BATTERIES.cpp @@ -329,22 +329,8 @@ uint16_t user_selected_tesla_GTW_mapRegion = 2; uint16_t user_selected_tesla_GTW_chassisType = 2; uint16_t user_selected_tesla_GTW_packEnergy = 1; -/* User-selected voltages used for custom-BMS batteries (RJXZS etc.) */ -#if defined(MAX_CUSTOM_PACK_VOLTAGE_DV) && defined(MIN_CUSTOM_PACK_VOLTAGE_DV) && \ - defined(MAX_CUSTOM_CELL_VOLTAGE_MV) && defined(MIN_CUSTOM_CELL_VOLTAGE_MV) -// Use USER_SETTINGS.h values for cell/pack voltage defaults -uint16_t user_selected_max_pack_voltage_default_dV = MAX_CUSTOM_PACK_VOLTAGE_DV; -uint16_t user_selected_min_pack_voltage_default_dV = MIN_CUSTOM_PACK_VOLTAGE_DV; -uint16_t user_selected_max_cell_voltage_default_mV = MAX_CUSTOM_CELL_VOLTAGE_MV; -uint16_t user_selected_min_cell_voltage_default_mV = MIN_CUSTOM_CELL_VOLTAGE_MV; -#else // Use 0V for user selected cell/pack voltage defaults (COMMON_IMAGE will replace with saved values from NVM) -uint16_t user_selected_max_pack_voltage_default_dV = 0; -uint16_t user_selected_min_pack_voltage_default_dV = 0; -uint16_t user_selected_max_cell_voltage_default_mV = 0; -uint16_t user_selected_min_cell_voltage_default_mV = 0; -#endif -uint16_t user_selected_max_pack_voltage_dV = user_selected_max_pack_voltage_default_dV; -uint16_t user_selected_min_pack_voltage_dV = user_selected_min_pack_voltage_default_dV; -uint16_t user_selected_max_cell_voltage_mV = user_selected_max_cell_voltage_default_mV; -uint16_t user_selected_min_cell_voltage_mV = user_selected_min_cell_voltage_default_mV; +uint16_t user_selected_max_pack_voltage_dV = 0; +uint16_t user_selected_min_pack_voltage_dV = 0; +uint16_t user_selected_max_cell_voltage_mV = 0; +uint16_t user_selected_min_cell_voltage_mV = 0; From 9031d3e5cf4ea591ae8db9f9673d5c79cf413e8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Mon, 1 Sep 2025 13:25:59 +0300 Subject: [PATCH 102/245] Move more settings to common-image --- Software/USER_SETTINGS.h | 9 --- Software/src/battery/CHADEMO-BATTERY.h | 3 - .../comm_contactorcontrol.cpp | 61 +++++-------------- .../contactorcontrol/comm_contactorcontrol.h | 3 + Software/src/communication/nvm/comm_nvm.cpp | 3 + .../src/devboard/webserver/settings_html.cpp | 56 +++++++++++++---- Software/src/devboard/webserver/webserver.cpp | 9 +++ 7 files changed, 76 insertions(+), 68 deletions(-) diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index 8c450453..08cb36b6 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -14,15 +14,6 @@ //#define HW_3LB //#define HW_DEVKIT -/* Contactor settings. If you have a battery that does not activate contactors via CAN, configure this section */ -#define PRECHARGE_TIME_MS 500 //Precharge time in milliseconds. Modify to suit your inverter (See wiki for more info) -//#define CONTACTOR_CONTROL //Enable this line to have the emulator handle automatic precharge/contactor+/contactor- closing sequence (See wiki for pins) -//#define CONTACTOR_CONTROL_DOUBLE_BATTERY //Enable this line to have the emulator hardware control secondary set of contactors for double battery setups (See wiki for pins) -//#define PWM_CONTACTOR_CONTROL //Enable this line to use PWM for CONTACTOR_CONTROL, which lowers power consumption and heat generation. CONTACTOR_CONTROL must be enabled. -//#define NC_CONTACTORS //Enable this line to control normally closed contactors. CONTACTOR_CONTROL must be enabled for this option. Extremely rare setting! -//#define PERIODIC_BMS_RESET //Enable to have the emulator powercycle the connected battery every 24hours via GPIO. Useful for some batteries like Nissan LEAF -//#define REMOTE_BMS_RESET //Enable to allow the emulator to remotely trigger a powercycle of the battery via MQTT. Useful for some batteries like Nissan LEAF - /* Shunt/Contactor settings (Optional) */ //#define BMW_SBOX // SBOX relay control & battery current/voltage measurement diff --git a/Software/src/battery/CHADEMO-BATTERY.h b/Software/src/battery/CHADEMO-BATTERY.h index 8e603f4e..613a8057 100644 --- a/Software/src/battery/CHADEMO-BATTERY.h +++ b/Software/src/battery/CHADEMO-BATTERY.h @@ -9,9 +9,6 @@ #ifdef CHADEMO_BATTERY #define SELECTED_BATTERY_CLASS ChademoBattery - -//Contactor control is required for CHADEMO support -#define CONTACTOR_CONTROL #endif class ChademoBattery : public CanBattery { diff --git a/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp b/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp index c7a0e877..a88ff9d0 100644 --- a/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp +++ b/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp @@ -3,45 +3,16 @@ #include "../../devboard/safety/safety.h" #include "../../inverter/INVERTERS.h" -#ifdef CONTACTOR_CONTROL -const bool contactor_control_enabled_default = true; -#else -const bool contactor_control_enabled_default = false; -#endif -bool contactor_control_enabled = contactor_control_enabled_default; - -#ifdef PWM_CONTACTOR_CONTROL -const bool pwn_contactor_control_default = true; -#else -const bool pwn_contactor_control_default = false; -#endif -bool pwm_contactor_control = pwn_contactor_control_default; - -#ifdef PERIODIC_BMS_RESET -const bool periodic_bms_reset_default = true; -#else -const bool periodic_bms_reset_default = false; -#endif -bool periodic_bms_reset = periodic_bms_reset_default; - -#ifdef REMOTE_BMS_RESET -const bool remote_bms_reset_default = true; -#else -const bool remote_bms_reset_default = false; -#endif -bool remote_bms_reset = remote_bms_reset_default; - -#ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY -const bool contactor_control_enabled_double_battery_default = true; -#else -const bool contactor_control_enabled_double_battery_default = false; -#endif -bool contactor_control_enabled_double_battery = contactor_control_enabled_double_battery_default; - // TODO: Ensure valid values at run-time +// User can update all these values via Settings page +bool contactor_control_enabled = false; //Should GPIO contactor control be performed? +uint16_t precharge_time_ms = 100; //Precharge time in ms. Adjust depending on capacitance in inverter +bool pwm_contactor_control = false; //Should the contactors be economized via PWM after they are engaged? +bool contactor_control_enabled_double_battery = false; //Should a contactor for the secondary battery be operated? +bool remote_bms_reset = false; //Is it possible to actuate BMS reset via MQTT? +bool periodic_bms_reset = false; //Should periodic BMS reset be performed each 24h? // Parameters - enum State { DISCONNECTED, START_PRECHARGE, PRECHARGE, POSITIVE, PRECHARGE_OFF, COMPLETED, SHUTDOWN_REQUESTED }; State contactorStatus = DISCONNECTED; @@ -60,11 +31,11 @@ const int OFF = 0; 500 // Time after negative contactor is turned on, to start precharge (not actual precharge time!) #define PRECHARGE_COMPLETED_TIME_MS \ 1000 // After successful precharge, resistor is turned off after this delay (and contactors are economized if PWM enabled) -#define PWM_Freq 20000 // 20 kHz frequency, beyond audible range -#define PWM_Res 10 // 10 Bit resolution 0 to 1023, maps 'nicely' to 0% 100% -#define PWM_HOLD_DUTY 250 -#define PWM_OFF_DUTY 0 +uint16_t pwm_frequency = 20000; +uint16_t pwm_hold_duty = 250; #define PWM_ON_DUTY 1023 +#define PWM_RESOLUTION 10 +#define PWM_OFF_DUTY 0 //No need to have this userconfigurable #define PWM_Positive_Channel 0 #define PWM_Negative_Channel 1 static unsigned long prechargeStartTime = 0; @@ -108,8 +79,8 @@ bool init_contactors() { if (pwm_contactor_control) { // Setup PWM Channel Frequency and Resolution - ledcAttachChannel(posPin, PWM_Freq, PWM_Res, PWM_Positive_Channel); - ledcAttachChannel(negPin, PWM_Freq, PWM_Res, PWM_Negative_Channel); + ledcAttachChannel(posPin, pwm_frequency, PWM_RESOLUTION, PWM_Positive_Channel); + ledcAttachChannel(negPin, pwm_frequency, PWM_RESOLUTION, PWM_Negative_Channel); // Set all pins OFF (0% PWM) ledcWrite(posPin, PWM_OFF_DUTY); ledcWrite(negPin, PWM_OFF_DUTY); @@ -245,7 +216,7 @@ void handle_contactors() { break; case POSITIVE: - if (currentTime - negativeStartTime >= PRECHARGE_TIME_MS) { + if (currentTime - negativeStartTime >= precharge_time_ms) { set(posPin, ON, PWM_ON_DUTY); dbg_contactors("POSITIVE"); prechargeCompletedTime = currentTime; @@ -256,8 +227,8 @@ void handle_contactors() { case PRECHARGE_OFF: if (currentTime - prechargeCompletedTime >= PRECHARGE_COMPLETED_TIME_MS) { set(prechargePin, OFF); - set(negPin, ON, PWM_HOLD_DUTY); - set(posPin, ON, PWM_HOLD_DUTY); + set(negPin, ON, pwm_hold_duty); + set(posPin, ON, pwm_hold_duty); dbg_contactors("PRECHARGE_OFF"); contactorStatus = COMPLETED; datalayer.system.status.contactors_engaged = 1; diff --git a/Software/src/communication/contactorcontrol/comm_contactorcontrol.h b/Software/src/communication/contactorcontrol/comm_contactorcontrol.h index 45551610..9441c121 100644 --- a/Software/src/communication/contactorcontrol/comm_contactorcontrol.h +++ b/Software/src/communication/contactorcontrol/comm_contactorcontrol.h @@ -10,6 +10,9 @@ extern bool contactor_control_enabled_double_battery; extern bool pwm_contactor_control; extern bool periodic_bms_reset; extern bool remote_bms_reset; +extern uint16_t precharge_time_ms; +extern uint16_t pwm_frequency; +extern uint16_t pwm_hold_duty; /** * @brief Handle BMS power output diff --git a/Software/src/communication/nvm/comm_nvm.cpp b/Software/src/communication/nvm/comm_nvm.cpp index bd8a8802..83d2b6c1 100644 --- a/Software/src/communication/nvm/comm_nvm.cpp +++ b/Software/src/communication/nvm/comm_nvm.cpp @@ -138,8 +138,11 @@ void init_stored_settings() { equipment_stop_behavior = (STOP_BUTTON_BEHAVIOR)settings.getUInt("EQSTOP", (int)STOP_BUTTON_BEHAVIOR::NOT_CONNECTED); user_selected_second_battery = settings.getBool("DBLBTR", false); contactor_control_enabled = settings.getBool("CNTCTRL", false); + precharge_time_ms = settings.getUInt("PRECHGMS", 100); contactor_control_enabled_double_battery = settings.getBool("CNTCTRLDBL", false); pwm_contactor_control = settings.getBool("PWMCNTCTRL", false); + pwm_frequency = settings.getUInt("PWMFREQ", 20000); + pwm_hold_duty = settings.getUInt("PWMHOLD", 250); periodic_bms_reset = settings.getBool("PERBMSRESET", false); remote_bms_reset = settings.getBool("REMBMSRESET", false); use_canfd_as_can = settings.getBool("CANFDASCAN", false); diff --git a/Software/src/devboard/webserver/settings_html.cpp b/Software/src/devboard/webserver/settings_html.cpp index 62060cfb..cc4ffc14 100644 --- a/Software/src/devboard/webserver/settings_html.cpp +++ b/Software/src/devboard/webserver/settings_html.cpp @@ -557,6 +557,18 @@ String settings_processor(const String& var, BatteryEmulatorSettingsStore& setti return String(settings.getUInt("CANFREQ", 8)); } + if (var == "PRECHGMS") { + return String(settings.getUInt("PRECHGMS", 100)); + } + + if (var == "PWMFREQ") { + return String(settings.getUInt("PWMFREQ", 20000)); + } + + if (var == "PWMHOLD") { + return String(settings.getUInt("PWMHOLD", 250)); + } + if (var == "DIGITALHVIL") { return settings.getBool("DIGITALHVIL") ? "checked" : ""; } @@ -781,6 +793,16 @@ const char* getCANInterfaceName(CAN_Interface interface) { display: contents; } + form .if-pwmcntctrl { display: none; } + form[data-pwmcntctrl="true"] .if-pwmcntctrl { + display: contents; + } + + form .if-cntctrl { display: none; } + form[data-cntctrl="true"] .if-cntctrl { + display: contents; + } + form .if-sofar { display: none; } form[data-inverter="17"] .if-sofar { display: contents; @@ -932,7 +954,7 @@ const char* getCANInterfaceName(CAN_Interface interface) { - +
- + + + +
- + -
- - -
+
+ + - - + + + +
+ + + + + +
+ +
diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index f0b5c633..6b6102e4 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -515,6 +515,15 @@ void init_webserver() { } else if (p->name() == "CANFREQ") { auto type = atoi(p->value().c_str()); settings.saveUInt("CANFREQ", type); + } else if (p->name() == "PRECHGMS") { + auto type = atoi(p->value().c_str()); + settings.saveUInt("PRECHGMS", type); + } else if (p->name() == "PWMFREQ") { + auto type = atoi(p->value().c_str()); + settings.saveUInt("PWMFREQ", type); + } else if (p->name() == "PWMHOLD") { + auto type = atoi(p->value().c_str()); + settings.saveUInt("PWMHOLD", type); } else if (p->name() == "GTWCOUNTRY") { auto type = atoi(p->value().c_str()); settings.saveUInt("GTWCOUNTRY", type); From 7ee0eb5e8887a1ed76b3a2da14edfaf247b544d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Mon, 1 Sep 2025 15:03:56 +0300 Subject: [PATCH 103/245] Remove old method to compile, all is now Common Image --- Software/USER_SETTINGS.cpp | 16 ------- Software/USER_SETTINGS.h | 2 - Software/src/battery/BATTERIES.cpp | 44 +------------------ Software/src/battery/BMW-I3-BATTERY.h | 4 -- Software/src/battery/BMW-IX-BATTERY.h | 4 -- Software/src/battery/BMW-PHEV-BATTERY.h | 4 -- Software/src/battery/BOLT-AMPERA-BATTERY.h | 4 -- Software/src/battery/BYD-ATTO-3-BATTERY.h | 3 -- Software/src/battery/CELLPOWER-BMS.h | 4 -- Software/src/battery/CHADEMO-BATTERY.h | 4 -- Software/src/battery/CMFA-EV-BATTERY.h | 4 -- Software/src/battery/DALY-BMS.h | 4 -- Software/src/battery/ECMP-BATTERY.h | 4 -- Software/src/battery/FOXESS-BATTERY.h | 4 -- .../src/battery/GEELY-GEOMETRY-C-BATTERY.h | 4 -- .../src/battery/HYUNDAI-IONIQ-28-BATTERY.h | 4 -- .../src/battery/IMIEV-CZERO-ION-BATTERY.h | 4 -- Software/src/battery/JAGUAR-IPACE-BATTERY.h | 4 -- Software/src/battery/KIA-64FD-BATTERY.h | 4 -- Software/src/battery/KIA-E-GMP-BATTERY.h | 4 -- Software/src/battery/KIA-HYUNDAI-64-BATTERY.h | 4 -- .../src/battery/KIA-HYUNDAI-HYBRID-BATTERY.h | 4 -- Software/src/battery/MEB-BATTERY.h | 4 -- Software/src/battery/MG-5-BATTERY.h | 4 -- Software/src/battery/MG-HS-PHEV-BATTERY.h | 4 -- Software/src/battery/NISSAN-LEAF-BATTERY.h | 4 -- Software/src/battery/ORION-BMS.h | 4 -- Software/src/battery/PYLON-BATTERY.h | 4 -- .../src/battery/RANGE-ROVER-PHEV-BATTERY.h | 4 -- Software/src/battery/RELION-LV-BATTERY.h | 4 -- Software/src/battery/RENAULT-KANGOO-BATTERY.h | 4 -- Software/src/battery/RENAULT-TWIZY.h | 4 -- .../src/battery/RENAULT-ZOE-GEN1-BATTERY.h | 4 -- .../src/battery/RENAULT-ZOE-GEN2-BATTERY.h | 4 -- Software/src/battery/RJXZS-BMS.h | 4 -- Software/src/battery/SAMSUNG-SDI-LV-BATTERY.h | 4 -- Software/src/battery/SANTA-FE-PHEV-BATTERY.h | 4 -- Software/src/battery/SIMPBMS-BATTERY.h | 4 -- Software/src/battery/SONO-BATTERY.h | 4 -- Software/src/battery/Shunts.cpp | 20 --------- Software/src/battery/TESLA-BATTERY.h | 7 --- Software/src/battery/TEST-FAKE-BATTERY.h | 4 -- Software/src/battery/VOLVO-SPA-BATTERY.h | 4 -- .../src/battery/VOLVO-SPA-HYBRID-BATTERY.h | 4 -- Software/src/charger/CHARGERS.cpp | 7 +-- Software/src/communication/nvm/comm_nvm.cpp | 3 -- Software/src/devboard/mqtt/mqtt.cpp | 31 ++----------- .../src/devboard/webserver/settings_html.cpp | 8 +--- Software/src/devboard/webserver/webserver.cpp | 22 ++-------- Software/src/devboard/wifi/wifi.cpp | 12 +---- Software/src/inverter/INVERTERS.cpp | 26 ----------- Software/src/inverter/INVERTERS.h | 5 --- platformio.ini | 15 +------ 53 files changed, 12 insertions(+), 361 deletions(-) diff --git a/Software/USER_SETTINGS.cpp b/Software/USER_SETTINGS.cpp index 1bc74643..134c50d3 100644 --- a/Software/USER_SETTINGS.cpp +++ b/Software/USER_SETTINGS.cpp @@ -21,25 +21,14 @@ volatile CAN_Configuration can_config = { .shunt = CAN_NATIVE // (OPTIONAL) Which CAN is your shunt connected to? }; -#ifdef COMMON_IMAGE std::string ssid; std::string password; std::string passwordAP; -#else -std::string ssid = WIFI_SSID; // Set in USER_SECRETS.h -std::string password = WIFI_PASSWORD; // Set in USER_SECRETS.h -std::string passwordAP = AP_PASSWORD; // Set in USER_SECRETS.h -#endif const uint8_t wifi_channel = 0; // Set to 0 for automatic channel selection -#ifdef COMMON_IMAGE std::string http_username; std::string http_password; -#else -std::string http_username = HTTP_USERNAME; // Set in USER_SECRETS.h -std::string http_password = HTTP_PASSWORD; // Set in USER_SECRETS.h -#endif // Set your Static IP address. Only used incase WIFICONFIG is set in USER_SETTINGS.h IPAddress local_IP(192, 168, 10, 150); @@ -47,13 +36,8 @@ IPAddress gateway(192, 168, 10, 1); IPAddress subnet(255, 255, 255, 0); // MQTT -#ifdef COMMON_IMAGE std::string mqtt_user; std::string mqtt_password; -#else -std::string mqtt_user = MQTT_USER; // Set in USER_SECRETS.h -std::string mqtt_password = MQTT_PASSWORD; // Set in USER_SECRETS.h -#endif const char* mqtt_topic_name = "BE"; // Custom MQTT topic name. Previously, the name was automatically set to "battery-emulator_esp32-XXXXXX" diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index 08cb36b6..9e8283a0 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -6,8 +6,6 @@ /* This file is being transitioned towards COMMON_IMAGE. Use v8.16 if you are taking this software into use! */ -//#define COMMON_IMAGE - /* Select hardware used for Battery-Emulator */ //#define HW_LILYGO //#define HW_STARK diff --git a/Software/src/battery/BATTERIES.cpp b/Software/src/battery/BATTERIES.cpp index e76f35cf..b24751ab 100644 --- a/Software/src/battery/BATTERIES.cpp +++ b/Software/src/battery/BATTERIES.cpp @@ -3,10 +3,6 @@ #include "CanBattery.h" #include "RS485Battery.h" -#if !defined(COMMON_IMAGE) && !defined(SELECTED_BATTERY_CLASS) -#error No battery selected! Choose one from the USER_SETTINGS.h file or build COMMON_IMAGE. -#endif - Battery* battery = nullptr; Battery* battery2 = nullptr; @@ -149,11 +145,6 @@ const battery_chemistry_enum battery_chemistry_default = battery_chemistry_enum: battery_chemistry_enum user_selected_battery_chemistry = battery_chemistry_default; -#ifdef COMMON_IMAGE -#ifdef SELECTED_BATTERY_CLASS -#error "Compile time SELECTED_BATTERY_CLASS should not be defined with COMMON_IMAGE" -#endif - BatteryType user_selected_battery_type = BatteryType::NissanLeaf; bool user_selected_second_battery = false; @@ -287,39 +278,6 @@ void setup_battery() { } } } -#else // Battery selection has been made at build-time - -void setup_battery() { - // Instantiate the battery only once just in case this function gets called multiple times. - if (battery == nullptr) { -#ifdef TESLA_MODEL_3Y_BATTERY - battery = new SELECTED_BATTERY_CLASS(user_selected_battery_chemistry); -#else - battery = new SELECTED_BATTERY_CLASS(); -#endif - } - battery->setup(); - -#ifdef DOUBLE_BATTERY - if (battery2 == nullptr) { -#if defined(BMW_I3_BATTERY) - battery2 = - new SELECTED_BATTERY_CLASS(&datalayer.battery2, &datalayer.system.status.battery2_allowed_contactor_closing, - can_config.battery_double, esp32hal->WUP_PIN2()); -#elif defined(KIA_HYUNDAI_64_BATTERY) - battery2 = new SELECTED_BATTERY_CLASS(&datalayer.battery2, &datalayer_extended.KiaHyundai64_2, - &datalayer.system.status.battery2_allowed_contactor_closing, - can_config.battery_double); -#elif defined(SANTA_FE_PHEV_BATTERY) || defined(TEST_FAKE_BATTERY) - battery2 = new SELECTED_BATTERY_CLASS(&datalayer.battery2, can_config.battery_double); -#else - battery2 = new SELECTED_BATTERY_CLASS(&datalayer.battery2, nullptr, can_config.battery_double); -#endif - } - battery2->setup(); -#endif -} -#endif /* User-selected Tesla settings */ bool user_selected_tesla_digital_HVIL = false; @@ -329,7 +287,7 @@ uint16_t user_selected_tesla_GTW_mapRegion = 2; uint16_t user_selected_tesla_GTW_chassisType = 2; uint16_t user_selected_tesla_GTW_packEnergy = 1; -// Use 0V for user selected cell/pack voltage defaults (COMMON_IMAGE will replace with saved values from NVM) +// Use 0V for user selected cell/pack voltage defaults (On boot will be replaced with saved values from NVM) uint16_t user_selected_max_pack_voltage_dV = 0; uint16_t user_selected_min_pack_voltage_dV = 0; uint16_t user_selected_max_cell_voltage_mV = 0; diff --git a/Software/src/battery/BMW-I3-BATTERY.h b/Software/src/battery/BMW-I3-BATTERY.h index 436c2604..adb42908 100644 --- a/Software/src/battery/BMW-I3-BATTERY.h +++ b/Software/src/battery/BMW-I3-BATTERY.h @@ -6,10 +6,6 @@ #include "BMW-I3-HTML.h" #include "CanBattery.h" -#ifdef BMW_I3_BATTERY -#define SELECTED_BATTERY_CLASS BmwI3Battery -#endif - class BmwI3Battery : public CanBattery { public: // Use this constructor for the second battery. diff --git a/Software/src/battery/BMW-IX-BATTERY.h b/Software/src/battery/BMW-IX-BATTERY.h index 074b4286..7bc54095 100644 --- a/Software/src/battery/BMW-IX-BATTERY.h +++ b/Software/src/battery/BMW-IX-BATTERY.h @@ -4,10 +4,6 @@ #include "BMW-IX-HTML.h" #include "CanBattery.h" -#ifdef BMW_IX_BATTERY -#define SELECTED_BATTERY_CLASS BmwIXBattery -#endif - class BmwIXBattery : public CanBattery { public: BmwIXBattery() : renderer(*this) {} diff --git a/Software/src/battery/BMW-PHEV-BATTERY.h b/Software/src/battery/BMW-PHEV-BATTERY.h index b35c9edc..553e514e 100644 --- a/Software/src/battery/BMW-PHEV-BATTERY.h +++ b/Software/src/battery/BMW-PHEV-BATTERY.h @@ -3,10 +3,6 @@ #include "BMW-PHEV-HTML.h" #include "CanBattery.h" -#ifdef BMW_PHEV_BATTERY -#define SELECTED_BATTERY_CLASS BmwPhevBattery -#endif - class BmwPhevBattery : public CanBattery { public: virtual void setup(void); diff --git a/Software/src/battery/BOLT-AMPERA-BATTERY.h b/Software/src/battery/BOLT-AMPERA-BATTERY.h index d7d08da4..05b28f80 100644 --- a/Software/src/battery/BOLT-AMPERA-BATTERY.h +++ b/Software/src/battery/BOLT-AMPERA-BATTERY.h @@ -3,10 +3,6 @@ #include "BOLT-AMPERA-HTML.h" #include "CanBattery.h" -#ifdef BOLT_AMPERA_BATTERY -#define SELECTED_BATTERY_CLASS BoltAmperaBattery -#endif - class BoltAmperaBattery : public CanBattery { public: virtual void setup(void); diff --git a/Software/src/battery/BYD-ATTO-3-BATTERY.h b/Software/src/battery/BYD-ATTO-3-BATTERY.h index 9e8f7497..a85c70ca 100644 --- a/Software/src/battery/BYD-ATTO-3-BATTERY.h +++ b/Software/src/battery/BYD-ATTO-3-BATTERY.h @@ -24,9 +24,6 @@ static const int RAMPDOWN_POWER_ALLOWED = 10000; // Power to start ramp down from, set a lower value to limit the power even further as SOC decreases /* Do not modify the rows below */ -#ifdef BYD_ATTO_3_BATTERY -#define SELECTED_BATTERY_CLASS BydAttoBattery -#endif class BydAttoBattery : public CanBattery { public: diff --git a/Software/src/battery/CELLPOWER-BMS.h b/Software/src/battery/CELLPOWER-BMS.h index ba8be709..75251848 100644 --- a/Software/src/battery/CELLPOWER-BMS.h +++ b/Software/src/battery/CELLPOWER-BMS.h @@ -3,10 +3,6 @@ #include "CELLPOWER-HTML.h" #include "CanBattery.h" -#ifdef CELLPOWER_BMS -#define SELECTED_BATTERY_CLASS CellPowerBms -#endif - class CellPowerBms : public CanBattery { public: CellPowerBms() : CanBattery(CAN_Speed::CAN_SPEED_250KBPS) {} diff --git a/Software/src/battery/CHADEMO-BATTERY.h b/Software/src/battery/CHADEMO-BATTERY.h index 613a8057..fa0eaa5a 100644 --- a/Software/src/battery/CHADEMO-BATTERY.h +++ b/Software/src/battery/CHADEMO-BATTERY.h @@ -7,10 +7,6 @@ #include "CHADEMO-BATTERY-HTML.h" #include "CanBattery.h" -#ifdef CHADEMO_BATTERY -#define SELECTED_BATTERY_CLASS ChademoBattery -#endif - class ChademoBattery : public CanBattery { public: ChademoBattery() { diff --git a/Software/src/battery/CMFA-EV-BATTERY.h b/Software/src/battery/CMFA-EV-BATTERY.h index 048e386a..3f649240 100644 --- a/Software/src/battery/CMFA-EV-BATTERY.h +++ b/Software/src/battery/CMFA-EV-BATTERY.h @@ -4,10 +4,6 @@ #include "CMFA-EV-HTML.h" #include "CanBattery.h" -#ifdef CMFA_EV_BATTERY -#define SELECTED_BATTERY_CLASS CmfaEvBattery -#endif - class CmfaEvBattery : public CanBattery { public: // Use this constructor for the second battery. diff --git a/Software/src/battery/DALY-BMS.h b/Software/src/battery/DALY-BMS.h index 4b9c6998..1bb7acb8 100644 --- a/Software/src/battery/DALY-BMS.h +++ b/Software/src/battery/DALY-BMS.h @@ -3,10 +3,6 @@ #include "RS485Battery.h" -#ifdef DALY_BMS -#define SELECTED_BATTERY_CLASS DalyBms -#endif - class DalyBms : public RS485Battery { public: void setup(); diff --git a/Software/src/battery/ECMP-BATTERY.h b/Software/src/battery/ECMP-BATTERY.h index 63284818..fe4aa58a 100644 --- a/Software/src/battery/ECMP-BATTERY.h +++ b/Software/src/battery/ECMP-BATTERY.h @@ -3,10 +3,6 @@ #include "CanBattery.h" #include "ECMP-HTML.h" -#ifdef STELLANTIS_ECMP_BATTERY -#define SELECTED_BATTERY_CLASS EcmpBattery -#endif - class EcmpBattery : public CanBattery { public: virtual void setup(void); diff --git a/Software/src/battery/FOXESS-BATTERY.h b/Software/src/battery/FOXESS-BATTERY.h index c3471d09..a10d62fd 100644 --- a/Software/src/battery/FOXESS-BATTERY.h +++ b/Software/src/battery/FOXESS-BATTERY.h @@ -2,10 +2,6 @@ #define FOXESS_BATTERY_H #include "CanBattery.h" -#ifdef FOXESS_BATTERY -#define SELECTED_BATTERY_CLASS FoxessBattery -#endif - class FoxessBattery : public CanBattery { public: virtual void setup(void); diff --git a/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.h b/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.h index adee75e4..0de25c99 100644 --- a/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.h +++ b/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.h @@ -4,10 +4,6 @@ #include "CanBattery.h" #include "GEELY-GEOMETRY-C-HTML.h" -#ifdef GEELY_GEOMETRY_C_BATTERY -#define SELECTED_BATTERY_CLASS GeelyGeometryCBattery -#endif - class GeelyGeometryCBattery : public CanBattery { public: // Use this constructor for the second battery. diff --git a/Software/src/battery/HYUNDAI-IONIQ-28-BATTERY.h b/Software/src/battery/HYUNDAI-IONIQ-28-BATTERY.h index a6cff594..613e3628 100644 --- a/Software/src/battery/HYUNDAI-IONIQ-28-BATTERY.h +++ b/Software/src/battery/HYUNDAI-IONIQ-28-BATTERY.h @@ -6,10 +6,6 @@ #include "CanBattery.h" #include "HYUNDAI-IONIQ-28-BATTERY-HTML.h" -#ifdef HYUNDAI_IONIQ_28_BATTERY -#define SELECTED_BATTERY_CLASS HyundaiIoniq28Battery -#endif - class HyundaiIoniq28Battery : public CanBattery { public: // Use the default constructor to create the first or single battery. diff --git a/Software/src/battery/IMIEV-CZERO-ION-BATTERY.h b/Software/src/battery/IMIEV-CZERO-ION-BATTERY.h index d905aa30..90a3b6b1 100644 --- a/Software/src/battery/IMIEV-CZERO-ION-BATTERY.h +++ b/Software/src/battery/IMIEV-CZERO-ION-BATTERY.h @@ -2,10 +2,6 @@ #define IMIEV_CZERO_ION_BATTERY_H #include "CanBattery.h" -#ifdef IMIEV_CZERO_ION_BATTERY -#define SELECTED_BATTERY_CLASS ImievCZeroIonBattery -#endif - class ImievCZeroIonBattery : public CanBattery { public: virtual void setup(void); diff --git a/Software/src/battery/JAGUAR-IPACE-BATTERY.h b/Software/src/battery/JAGUAR-IPACE-BATTERY.h index aae6e428..20cc25e1 100644 --- a/Software/src/battery/JAGUAR-IPACE-BATTERY.h +++ b/Software/src/battery/JAGUAR-IPACE-BATTERY.h @@ -3,10 +3,6 @@ #include "CanBattery.h" -#ifdef JAGUAR_IPACE_BATTERY -#define SELECTED_BATTERY_CLASS JaguarIpaceBattery -#endif - class JaguarIpaceBattery : public CanBattery { public: virtual void setup(void); diff --git a/Software/src/battery/KIA-64FD-BATTERY.h b/Software/src/battery/KIA-64FD-BATTERY.h index 2b2806c9..5a8638a6 100644 --- a/Software/src/battery/KIA-64FD-BATTERY.h +++ b/Software/src/battery/KIA-64FD-BATTERY.h @@ -5,10 +5,6 @@ #define ESTIMATE_SOC_FROM_CELLVOLTAGE -#ifdef KIA_HYUNDAI_64_FD_BATTERY -#define SELECTED_BATTERY_CLASS Kia64FDBattery -#endif - class Kia64FDBattery : public CanBattery { public: virtual void setup(void); diff --git a/Software/src/battery/KIA-E-GMP-BATTERY.h b/Software/src/battery/KIA-E-GMP-BATTERY.h index 461f7d4a..2e71c6ff 100644 --- a/Software/src/battery/KIA-E-GMP-BATTERY.h +++ b/Software/src/battery/KIA-E-GMP-BATTERY.h @@ -5,10 +5,6 @@ #define ESTIMATE_SOC_FROM_CELLVOLTAGE -#ifdef KIA_E_GMP_BATTERY -#define SELECTED_BATTERY_CLASS KiaEGmpBattery -#endif - class KiaEGmpBattery : public CanBattery { public: KiaEGmpBattery() : renderer(*this) {} diff --git a/Software/src/battery/KIA-HYUNDAI-64-BATTERY.h b/Software/src/battery/KIA-HYUNDAI-64-BATTERY.h index 1e991465..832c0e9a 100644 --- a/Software/src/battery/KIA-HYUNDAI-64-BATTERY.h +++ b/Software/src/battery/KIA-HYUNDAI-64-BATTERY.h @@ -5,10 +5,6 @@ #include "CanBattery.h" #include "KIA-HYUNDAI-64-HTML.h" -#ifdef KIA_HYUNDAI_64_BATTERY -#define SELECTED_BATTERY_CLASS KiaHyundai64Battery -#endif - class KiaHyundai64Battery : public CanBattery { public: // Use this constructor for the second battery. diff --git a/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.h b/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.h index 95e84e45..3bd9f499 100644 --- a/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.h +++ b/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.h @@ -2,10 +2,6 @@ #define KIA_HYUNDAI_HYBRID_BATTERY_H #include "CanBattery.h" -#ifdef KIA_HYUNDAI_HYBRID_BATTERY -#define SELECTED_BATTERY_CLASS KiaHyundaiHybridBattery -#endif - class KiaHyundaiHybridBattery : public CanBattery { public: virtual void setup(void); diff --git a/Software/src/battery/MEB-BATTERY.h b/Software/src/battery/MEB-BATTERY.h index 0751fe6e..d4ea6c60 100644 --- a/Software/src/battery/MEB-BATTERY.h +++ b/Software/src/battery/MEB-BATTERY.h @@ -3,10 +3,6 @@ #include "CanBattery.h" #include "MEB-HTML.h" -#ifdef MEB_BATTERY -#define SELECTED_BATTERY_CLASS MebBattery -#endif - class MebBattery : public CanBattery { public: // Use this constructor for the second battery. diff --git a/Software/src/battery/MG-5-BATTERY.h b/Software/src/battery/MG-5-BATTERY.h index 21db48ab..3f8b8650 100644 --- a/Software/src/battery/MG-5-BATTERY.h +++ b/Software/src/battery/MG-5-BATTERY.h @@ -2,10 +2,6 @@ #define MG_5_BATTERY_H #include "CanBattery.h" -#ifdef MG_5_BATTERY -#define SELECTED_BATTERY_CLASS Mg5Battery -#endif - class Mg5Battery : public CanBattery { public: virtual void setup(void); diff --git a/Software/src/battery/MG-HS-PHEV-BATTERY.h b/Software/src/battery/MG-HS-PHEV-BATTERY.h index 81ef6dc9..7a9ade7b 100644 --- a/Software/src/battery/MG-HS-PHEV-BATTERY.h +++ b/Software/src/battery/MG-HS-PHEV-BATTERY.h @@ -3,10 +3,6 @@ #include "CanBattery.h" -#ifdef MG_HS_PHEV_BATTERY -#define SELECTED_BATTERY_CLASS MgHsPHEVBattery -#endif - class MgHsPHEVBattery : public CanBattery { public: virtual void setup(void); diff --git a/Software/src/battery/NISSAN-LEAF-BATTERY.h b/Software/src/battery/NISSAN-LEAF-BATTERY.h index 034b7b78..99a5691b 100644 --- a/Software/src/battery/NISSAN-LEAF-BATTERY.h +++ b/Software/src/battery/NISSAN-LEAF-BATTERY.h @@ -6,10 +6,6 @@ #include "CanBattery.h" #include "NISSAN-LEAF-HTML.h" -#ifdef NISSAN_LEAF_BATTERY -#define SELECTED_BATTERY_CLASS NissanLeafBattery -#endif - class NissanLeafBattery : public CanBattery { public: // Use this constructor for the second battery. diff --git a/Software/src/battery/ORION-BMS.h b/Software/src/battery/ORION-BMS.h index eaf03c59..45d267d6 100644 --- a/Software/src/battery/ORION-BMS.h +++ b/Software/src/battery/ORION-BMS.h @@ -4,10 +4,6 @@ #include "../system_settings.h" #include "CanBattery.h" -#ifdef ORION_BMS -#define SELECTED_BATTERY_CLASS OrionBms -#endif - class OrionBms : public CanBattery { public: virtual void setup(void); diff --git a/Software/src/battery/PYLON-BATTERY.h b/Software/src/battery/PYLON-BATTERY.h index bc88ba99..f6b5d75b 100644 --- a/Software/src/battery/PYLON-BATTERY.h +++ b/Software/src/battery/PYLON-BATTERY.h @@ -4,10 +4,6 @@ #include "../datalayer/datalayer.h" #include "CanBattery.h" -#ifdef PYLON_BATTERY -#define SELECTED_BATTERY_CLASS PylonBattery -#endif - class PylonBattery : public CanBattery { public: // Use this constructor for the second battery. diff --git a/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.h b/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.h index 2207188a..82dc4f01 100644 --- a/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.h +++ b/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.h @@ -3,10 +3,6 @@ #include "CanBattery.h" -#ifdef RANGE_ROVER_PHEV_BATTERY -#define SELECTED_BATTERY_CLASS RangeRoverPhevBattery -#endif - class RangeRoverPhevBattery : public CanBattery { public: virtual void setup(void); diff --git a/Software/src/battery/RELION-LV-BATTERY.h b/Software/src/battery/RELION-LV-BATTERY.h index 53c59431..bee6f09b 100644 --- a/Software/src/battery/RELION-LV-BATTERY.h +++ b/Software/src/battery/RELION-LV-BATTERY.h @@ -4,10 +4,6 @@ #include "../system_settings.h" #include "CanBattery.h" -#ifdef RELION_BATTERY -#define SELECTED_BATTERY_CLASS RelionBattery -#endif - class RelionBattery : public CanBattery { public: RelionBattery() : CanBattery(CAN_Speed::CAN_SPEED_250KBPS) {} diff --git a/Software/src/battery/RENAULT-KANGOO-BATTERY.h b/Software/src/battery/RENAULT-KANGOO-BATTERY.h index 77eb0b3f..0fc8c8cf 100644 --- a/Software/src/battery/RENAULT-KANGOO-BATTERY.h +++ b/Software/src/battery/RENAULT-KANGOO-BATTERY.h @@ -3,10 +3,6 @@ #include "CanBattery.h" -#ifdef RENAULT_KANGOO_BATTERY -#define SELECTED_BATTERY_CLASS RenaultKangooBattery -#endif - class RenaultKangooBattery : public CanBattery { public: virtual void setup(void); diff --git a/Software/src/battery/RENAULT-TWIZY.h b/Software/src/battery/RENAULT-TWIZY.h index d9c71f7d..00891cb4 100644 --- a/Software/src/battery/RENAULT-TWIZY.h +++ b/Software/src/battery/RENAULT-TWIZY.h @@ -2,10 +2,6 @@ #define RENAULT_TWIZY_BATTERY_H #include "CanBattery.h" -#ifdef RENAULT_TWIZY_BATTERY -#define SELECTED_BATTERY_CLASS RenaultTwizyBattery -#endif - class RenaultTwizyBattery : public CanBattery { public: virtual void setup(void); diff --git a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.h b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.h index d004f373..1e5d7631 100644 --- a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.h +++ b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.h @@ -4,10 +4,6 @@ #include "CanBattery.h" #include "RENAULT-ZOE-GEN1-HTML.h" -#ifdef RENAULT_ZOE_GEN1_BATTERY -#define SELECTED_BATTERY_CLASS RenaultZoeGen1Battery -#endif - class RenaultZoeGen1Battery : public CanBattery { public: // Use this constructor for the second battery. diff --git a/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.h b/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.h index 0c540877..04a60465 100644 --- a/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.h +++ b/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.h @@ -4,10 +4,6 @@ #include "CanBattery.h" #include "RENAULT-ZOE-GEN2-HTML.h" -#ifdef RENAULT_ZOE_GEN2_BATTERY -#define SELECTED_BATTERY_CLASS RenaultZoeGen2Battery -#endif - class RenaultZoeGen2Battery : public CanBattery { public: // Use this constructor for the second battery. diff --git a/Software/src/battery/RJXZS-BMS.h b/Software/src/battery/RJXZS-BMS.h index d616f31a..6d549752 100644 --- a/Software/src/battery/RJXZS-BMS.h +++ b/Software/src/battery/RJXZS-BMS.h @@ -4,10 +4,6 @@ #include "../system_settings.h" #include "CanBattery.h" -#ifdef RJXZS_BMS -#define SELECTED_BATTERY_CLASS RjxzsBms -#endif - class RjxzsBms : public CanBattery { public: RjxzsBms() : CanBattery(CAN_Speed::CAN_SPEED_250KBPS) {} diff --git a/Software/src/battery/SAMSUNG-SDI-LV-BATTERY.h b/Software/src/battery/SAMSUNG-SDI-LV-BATTERY.h index 61da5b9f..b46a22fe 100644 --- a/Software/src/battery/SAMSUNG-SDI-LV-BATTERY.h +++ b/Software/src/battery/SAMSUNG-SDI-LV-BATTERY.h @@ -4,10 +4,6 @@ #include "../datalayer/datalayer.h" #include "CanBattery.h" -#ifdef SAMSUNG_SDI_LV_BATTERY -#define SELECTED_BATTERY_CLASS SamsungSdiLVBattery -#endif - class SamsungSdiLVBattery : public CanBattery { public: virtual void setup(void); diff --git a/Software/src/battery/SANTA-FE-PHEV-BATTERY.h b/Software/src/battery/SANTA-FE-PHEV-BATTERY.h index 9e9ec2bf..5fbabaff 100644 --- a/Software/src/battery/SANTA-FE-PHEV-BATTERY.h +++ b/Software/src/battery/SANTA-FE-PHEV-BATTERY.h @@ -3,10 +3,6 @@ #include "../datalayer/datalayer.h" #include "CanBattery.h" -#ifdef SANTA_FE_PHEV_BATTERY -#define SELECTED_BATTERY_CLASS SantaFePhevBattery -#endif - class SantaFePhevBattery : public CanBattery { public: // Use this constructor for the second battery. diff --git a/Software/src/battery/SIMPBMS-BATTERY.h b/Software/src/battery/SIMPBMS-BATTERY.h index 9295c635..3d7cce8b 100644 --- a/Software/src/battery/SIMPBMS-BATTERY.h +++ b/Software/src/battery/SIMPBMS-BATTERY.h @@ -3,10 +3,6 @@ #include "CanBattery.h" -#ifdef SIMPBMS_BATTERY -#define SELECTED_BATTERY_CLASS SimpBmsBattery -#endif - class SimpBmsBattery : public CanBattery { public: virtual void setup(void); diff --git a/Software/src/battery/SONO-BATTERY.h b/Software/src/battery/SONO-BATTERY.h index 6721a301..1544c02a 100644 --- a/Software/src/battery/SONO-BATTERY.h +++ b/Software/src/battery/SONO-BATTERY.h @@ -3,10 +3,6 @@ #include "CanBattery.h" -#ifdef SONO_BATTERY -#define SELECTED_BATTERY_CLASS SonoBattery -#endif - class SonoBattery : public CanBattery { public: virtual void setup(void); diff --git a/Software/src/battery/Shunts.cpp b/Software/src/battery/Shunts.cpp index 45578f2a..b8f5fcc7 100644 --- a/Software/src/battery/Shunts.cpp +++ b/Software/src/battery/Shunts.cpp @@ -4,11 +4,6 @@ CanShunt* shunt = nullptr; ShuntType user_selected_shunt_type = ShuntType::None; -#ifdef COMMON_IMAGE -#ifdef SELECTED_SHUNT_CLASS -#error "Compile time SELECTED_SHUNT_CLASS should not be defined with COMMON_IMAGE" -#endif - void setup_can_shunt() { if (shunt) { return; @@ -30,21 +25,6 @@ void setup_can_shunt() { } } -#else -void setup_can_shunt() { - if (shunt) { - return; - } - -#if defined(SELECTED_SHUNT_CLASS) - shunt = new SELECTED_SHUNT_CLASS(); - if (shunt) { - shunt->setup(); - } -#endif -} -#endif - extern std::vector supported_shunt_types() { std::vector types; diff --git a/Software/src/battery/TESLA-BATTERY.h b/Software/src/battery/TESLA-BATTERY.h index fca5a83b..6276cf3c 100644 --- a/Software/src/battery/TESLA-BATTERY.h +++ b/Software/src/battery/TESLA-BATTERY.h @@ -4,13 +4,6 @@ #include "CanBattery.h" #include "TESLA-HTML.h" -#ifdef TESLA_MODEL_3Y_BATTERY -#define SELECTED_BATTERY_CLASS TeslaModel3YBattery -#endif -#ifdef TESLA_MODEL_SX_BATTERY -#define SELECTED_BATTERY_CLASS TeslaModelSXBattery -#endif - // 0x7FF gateway config, "Gen3" vehicles only, not applicable to Gen2 "classic" Model S and Model X // These are user configurable from the Webserver UI extern bool user_selected_tesla_digital_HVIL; diff --git a/Software/src/battery/TEST-FAKE-BATTERY.h b/Software/src/battery/TEST-FAKE-BATTERY.h index 012a0363..b2d6bae5 100644 --- a/Software/src/battery/TEST-FAKE-BATTERY.h +++ b/Software/src/battery/TEST-FAKE-BATTERY.h @@ -3,10 +3,6 @@ #include "../datalayer/datalayer.h" #include "CanBattery.h" -#ifdef TEST_FAKE_BATTERY -#define SELECTED_BATTERY_CLASS TestFakeBattery -#endif - class TestFakeBattery : public CanBattery { public: // Use this constructor for the second battery. diff --git a/Software/src/battery/VOLVO-SPA-BATTERY.h b/Software/src/battery/VOLVO-SPA-BATTERY.h index b5d4fcad..3c5dbbce 100644 --- a/Software/src/battery/VOLVO-SPA-BATTERY.h +++ b/Software/src/battery/VOLVO-SPA-BATTERY.h @@ -4,10 +4,6 @@ #include "CanBattery.h" #include "VOLVO-SPA-HTML.h" -#ifdef VOLVO_SPA_BATTERY -#define SELECTED_BATTERY_CLASS VolvoSpaBattery -#endif - class VolvoSpaBattery : public CanBattery { public: virtual void setup(void); diff --git a/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.h b/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.h index 62339c0a..4e44baef 100644 --- a/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.h +++ b/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.h @@ -3,10 +3,6 @@ #include "CanBattery.h" #include "VOLVO-SPA-HYBRID-HTML.h" -#ifdef VOLVO_SPA_HYBRID_BATTERY -#define SELECTED_BATTERY_CLASS VolvoSpaHybridBattery -#endif - class VolvoSpaHybridBattery : public CanBattery { public: virtual void setup(void); diff --git a/Software/src/charger/CHARGERS.cpp b/Software/src/charger/CHARGERS.cpp index 2ab85110..22e7d8c5 100644 --- a/Software/src/charger/CHARGERS.cpp +++ b/Software/src/charger/CHARGERS.cpp @@ -30,16 +30,11 @@ extern const char* name_for_charger_type(ChargerType type) { } void setup_charger() { -#ifdef COMMON_IMAGE + switch (user_selected_charger_type) { case ChargerType::ChevyVolt: charger = new ChevyVoltCharger(); case ChargerType::NissanLeaf: charger = new NissanLeafCharger(); } -#else -#ifdef SELECTED_CHARGER_CLASS - charger = new SELECTED_CHARGER_CLASS(); -#endif -#endif } diff --git a/Software/src/communication/nvm/comm_nvm.cpp b/Software/src/communication/nvm/comm_nvm.cpp index 83d2b6c1..9e72d8cc 100644 --- a/Software/src/communication/nvm/comm_nvm.cpp +++ b/Software/src/communication/nvm/comm_nvm.cpp @@ -87,7 +87,6 @@ void init_stored_settings() { datalayer.battery.settings.user_set_bms_reset_duration_ms = temp; } -#ifdef COMMON_IMAGE user_selected_battery_type = (BatteryType)settings.getUInt("BATTTYPE", (int)BatteryType::None); user_selected_battery_chemistry = (battery_chemistry_enum)settings.getUInt("BATTCHEM", (int)battery_chemistry_enum::NCA); @@ -168,8 +167,6 @@ void init_stored_settings() { mqtt_user = settings.getString("MQTTUSER").c_str(); mqtt_password = settings.getString("MQTTPASSWORD").c_str(); -#endif - settings.end(); } diff --git a/Software/src/devboard/mqtt/mqtt.cpp b/Software/src/devboard/mqtt/mqtt.cpp index f23d3be4..36452c84 100644 --- a/Software/src/devboard/mqtt/mqtt.cpp +++ b/Software/src/devboard/mqtt/mqtt.cpp @@ -15,29 +15,11 @@ #include "mqtt.h" #include "mqtt_client.h" -#ifdef MQTT -const bool mqtt_enabled_default = true; -#else -const bool mqtt_enabled_default = false; -#endif +bool mqtt_enabled = false; +bool ha_autodiscovery_enabled = false; -bool mqtt_enabled = mqtt_enabled_default; - -#ifdef HA_AUTODISCOVERY -const bool ha_autodiscovery_enabled_default = true; -#else -const bool ha_autodiscovery_enabled_default = false; -#endif - -bool ha_autodiscovery_enabled = ha_autodiscovery_enabled_default; - -#ifdef COMMON_IMAGE const int mqtt_port_default = 0; const char* mqtt_server_default = ""; -#else -const int mqtt_port_default = MQTT_PORT; -const char* mqtt_server_default = MQTT_SERVER; -#endif int mqtt_port = mqtt_port_default; std::string mqtt_server = mqtt_server_default; @@ -626,7 +608,7 @@ bool init_mqtt(void) { } if (mqtt_manual_topic_object_name) { -#ifdef COMMON_IMAGE + BatteryEmulatorSettingsStore settings; topic_name = settings.getString("MQTTTOPIC", mqtt_topic_name); object_id_prefix = settings.getString("MQTTOBJIDPREFIX", mqtt_object_id_prefix); @@ -649,13 +631,6 @@ bool init_mqtt(void) { device_id = ha_device_id; } -#else - // Use custom topic name, object ID prefix, and device name from user settings - topic_name = mqtt_topic_name; - object_id_prefix = mqtt_object_id_prefix; - device_name = mqtt_device_name; - device_id = ha_device_id; -#endif } else { // Use default naming based on WiFi hostname for topic, object ID prefix, and device name topic_name = "battery-emulator_" + String(WiFi.getHostname()); diff --git a/Software/src/devboard/webserver/settings_html.cpp b/Software/src/devboard/webserver/settings_html.cpp index cc4ffc14..7414d53f 100644 --- a/Software/src/devboard/webserver/settings_html.cpp +++ b/Software/src/devboard/webserver/settings_html.cpp @@ -135,12 +135,6 @@ String settings_processor(const String& var, BatteryEmulatorSettingsStore& setti return String(ssid.c_str()); } -#ifndef COMMON_IMAGE - if (var == "COMMONIMAGEDIVCLASS") { - return "hidden"; - } -#endif - if (var == "SAVEDCLASS") { if (!settingsUpdated) { return "hidden"; @@ -839,7 +833,7 @@ const char* getCANInterfaceName(CAN_Interface interface) {

SSID: %SSID%

Password: ########

-
diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 6b6102e4..8ef745fa 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -23,21 +23,10 @@ extern std::string http_username; extern std::string http_password; -#ifdef WEBSERVER -const bool webserver_enabled_default = true; -#else -const bool webserver_enabled_default = false; -#endif +bool webserver_enabled = + true; // Global flag to enable or disable the webserver //Old method to disable was with #ifdef WEBSERVER -bool webserver_enabled = webserver_enabled_default; // Global flag to enable or disable the webserver - -#ifndef COMMON_IMAGE -const bool webserver_auth_default = WEBSERVER_AUTH_REQUIRED; -#else -const bool webserver_auth_default = false; -#endif - -bool webserver_auth = webserver_auth_default; +bool webserver_auth = false; // Create AsyncWebServer object on port 80 AsyncWebServer server(80); @@ -401,7 +390,6 @@ void init_webserver() { request->send(200, "text/html", "OK"); }); -#ifdef COMMON_IMAGE struct BoolSetting { const char* name; bool existingValue; @@ -557,7 +545,6 @@ void init_webserver() { settingsUpdated = settings.were_settings_updated(); request->redirect("/settings"); }); -#endif // Route for editing SSID def_route_with_auth("/updateSSID", server, HTTP_GET, [](AsyncWebServerRequest* request) { @@ -908,9 +895,6 @@ String processor(const String& var) { content += "
"; content += "

Software: " + String(version_number); -#ifdef COMMON_IMAGE - content += " (Common image) "; -#endif // Show hardware used: #ifdef HW_LILYGO content += " Hardware: LilyGo T-CAN485"; diff --git a/Software/src/devboard/wifi/wifi.cpp b/Software/src/devboard/wifi/wifi.cpp index 53425b0d..44adc907 100644 --- a/Software/src/devboard/wifi/wifi.cpp +++ b/Software/src/devboard/wifi/wifi.cpp @@ -12,17 +12,7 @@ const bool wifi_enabled_default = false; bool wifi_enabled = wifi_enabled_default; -#ifdef COMMON_IMAGE -const bool wifiap_enabled_default = true; -#else -#ifdef WIFIAP -const bool wifiap_enabled_default = true; -#else -const bool wifiap_enabled_default = false; -#endif -#endif - -bool wifiap_enabled = wifiap_enabled_default; +bool wifiap_enabled = true; //Old method was with ifdef #ifdef MDNSRESPONDER const bool mdns_enabled_default = true; diff --git a/Software/src/inverter/INVERTERS.cpp b/Software/src/inverter/INVERTERS.cpp index cbd64f4b..37b88d2d 100644 --- a/Software/src/inverter/INVERTERS.cpp +++ b/Software/src/inverter/INVERTERS.cpp @@ -96,11 +96,6 @@ extern const char* name_for_inverter_type(InverterProtocolType type) { return nullptr; } -#ifdef COMMON_IMAGE -#ifdef SELECTED_INVERTER_CLASS -#error "Compile time SELECTED_INVERTER_CLASS should not be defined with COMMON_IMAGE" -#endif - bool setup_inverter() { if (inverter) { return true; @@ -205,24 +200,3 @@ bool setup_inverter() { return false; } - -#else -bool setup_inverter() { - if (inverter) { - // The inverter is setup only once. - return true; - } - -#ifdef SELECTED_INVERTER_CLASS - inverter = new SELECTED_INVERTER_CLASS(); - - if (inverter) { - return inverter->setup(); - } - - return false; -#else - return true; -#endif -} -#endif diff --git a/Software/src/inverter/INVERTERS.h b/Software/src/inverter/INVERTERS.h index b8207f7d..08d411ef 100644 --- a/Software/src/inverter/INVERTERS.h +++ b/Software/src/inverter/INVERTERS.h @@ -7,11 +7,6 @@ extern InverterProtocol* inverter; #include "../../USER_SETTINGS.h" #include "AFORE-CAN.h" - -#ifdef BYD_CAN_DEYE -#define BYD_CAN -#endif - #include "BYD-CAN.h" #include "BYD-MODBUS.h" #include "FERROAMP-CAN.h" diff --git a/platformio.ini b/platformio.ini index b411b41c..f4a37b8f 100644 --- a/platformio.ini +++ b/platformio.ini @@ -28,7 +28,7 @@ monitor_speed = 115200 monitor_filters = default, time, log2file board_build.partitions = min_spiffs.csv framework = arduino -build_flags = -I include -DHW_LILYGO -DCOMMON_IMAGE +build_flags = -I include -DHW_LILYGO lib_deps = [env:stark_330] @@ -38,17 +38,7 @@ monitor_speed = 115200 monitor_filters = default, time, log2file, esp32_exception_decoder board_build.partitions = min_spiffs.csv framework = arduino -build_flags = -I include -DHW_STARK -DCOMMON_IMAGE -lib_deps = - -[env:stark_330_debuglog] -platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.30/platform-espressif32.zip -board = esp32dev -monitor_speed = 115200 -monitor_filters = default, time, log2file, esp32_exception_decoder -board_build.partitions = min_spiffs.csv -framework = arduino -build_flags = -I include -DHW_STARK -DCOMMON_IMAGE -DDEBUG_VIA_USB +build_flags = -I include -DHW_STARK lib_deps = [env:lilygo_2CAN_330] @@ -62,7 +52,6 @@ framework = arduino build_flags = -I include -D HW_LILYGO2CAN - -D COMMON_IMAGE -D BOARD_HAS_PSRAM -D ARDUINO_USB_MODE=1 -D ARDUINO_USB_CDC_ON_BOOT=1 ;1 is to use the USB port as a serial port From 21b2b06dabf31760001c2f863d4a6aebecaa6bae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Mon, 1 Sep 2025 15:23:02 +0300 Subject: [PATCH 104/245] Remove USER_SECRETS --- .../compile-common-image-lilygo-2CAN.yml | 4 ---- .../compile-common-image-lilygo-TCAN.yml | 4 ---- .../workflows/compile-common-image-stark.yml | 4 ---- Software/Software.ino | 1 - Software/USER_SECRETS.TEMPLATE.h | 22 ------------------- Software/USER_SETTINGS.cpp | 1 - Software/src/devboard/mqtt/mqtt.cpp | 1 - Software/src/devboard/webserver/webserver.cpp | 1 - 8 files changed, 38 deletions(-) delete mode 100644 Software/USER_SECRETS.TEMPLATE.h diff --git a/.github/workflows/compile-common-image-lilygo-2CAN.yml b/.github/workflows/compile-common-image-lilygo-2CAN.yml index 9972c4c4..d6083cd7 100644 --- a/.github/workflows/compile-common-image-lilygo-2CAN.yml +++ b/.github/workflows/compile-common-image-lilygo-2CAN.yml @@ -44,10 +44,6 @@ jobs: - name: Install PlatformIO Core run: pip install --upgrade platformio - # Copy USER_SECRETS.TEMPLATE.h to USER_SECRETS.h - - name: Copy USER_SECRETS.TEMPLATE.h to USER_SECRETS.h - run: cp ./Software/USER_SECRETS.TEMPLATE.h ./Software/USER_SECRETS.h - - name: Build image for Lilygo run: pio run -e lilygo_2CAN_330 diff --git a/.github/workflows/compile-common-image-lilygo-TCAN.yml b/.github/workflows/compile-common-image-lilygo-TCAN.yml index aee0a9bc..e5908572 100644 --- a/.github/workflows/compile-common-image-lilygo-TCAN.yml +++ b/.github/workflows/compile-common-image-lilygo-TCAN.yml @@ -44,10 +44,6 @@ jobs: - name: Install PlatformIO Core run: pip install --upgrade platformio - # Copy USER_SECRETS.TEMPLATE.h to USER_SECRETS.h - - name: Copy USER_SECRETS.TEMPLATE.h to USER_SECRETS.h - run: cp ./Software/USER_SECRETS.TEMPLATE.h ./Software/USER_SECRETS.h - - name: Build image for Lilygo run: pio run -e lilygo_330 diff --git a/.github/workflows/compile-common-image-stark.yml b/.github/workflows/compile-common-image-stark.yml index 8f763dc0..eba9d656 100644 --- a/.github/workflows/compile-common-image-stark.yml +++ b/.github/workflows/compile-common-image-stark.yml @@ -44,10 +44,6 @@ jobs: - name: Install PlatformIO Core run: pip install --upgrade platformio - # Copy USER_SECRETS.TEMPLATE.h to USER_SECRETS.h - - name: Copy USER_SECRETS.TEMPLATE.h to USER_SECRETS.h - run: cp ./Software/USER_SECRETS.TEMPLATE.h ./Software/USER_SECRETS.h - - name: Build image for Stark CMR run: pio run -e stark_330 diff --git a/Software/Software.ino b/Software/Software.ino index 98fe5f8e..e26fc42f 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -1,7 +1,6 @@ /* Do not change any code below this line unless you are sure what you are doing */ /* Only change battery specific settings in "USER_SETTINGS.h" */ #include "HardwareSerial.h" -#include "USER_SECRETS.h" #include "USER_SETTINGS.h" #include "esp_system.h" #include "esp_task_wdt.h" diff --git a/Software/USER_SECRETS.TEMPLATE.h b/Software/USER_SECRETS.TEMPLATE.h deleted file mode 100644 index d2faf71f..00000000 --- a/Software/USER_SECRETS.TEMPLATE.h +++ /dev/null @@ -1,22 +0,0 @@ -/* This file should be renamed to USER_SECRETS.h to be able to use the software! -It contains all the credentials that should never be made public */ -#ifndef COMMON_IMAGE - -//Password to the access point generated by the Battery-Emulator -#define AP_PASSWORD "123456789" // Minimum of 8 characters; set to blank if you want the access point to be open - -//Name and password of Wifi network you want the emulator to connect to -#define WIFI_SSID "REPLACE_WITH_YOUR_SSID" // Maximum of 63 characters -#define WIFI_PASSWORD "REPLACE_WITH_YOUR_PASSWORD" // Minimum of 8 characters - -//Set WEBSERVER_AUTH_REQUIRED to true to require a password when accessing the webserver homepage. Improves cybersecurity. -#define WEBSERVER_AUTH_REQUIRED false -#define HTTP_USERNAME "admin" // Username for webserver authentication -#define HTTP_PASSWORD "admin" // Password for webserver authentication - -//MQTT credentials -#define MQTT_SERVER "192.168.xxx.yyy" // MQTT server address -#define MQTT_PORT 1883 // MQTT server port -#define MQTT_USER "" // MQTT username, leave blank for no authentication -#define MQTT_PASSWORD "" // MQTT password, leave blank for no authentication -#endif diff --git a/Software/USER_SETTINGS.cpp b/Software/USER_SETTINGS.cpp index 134c50d3..48cf2c9b 100644 --- a/Software/USER_SETTINGS.cpp +++ b/Software/USER_SETTINGS.cpp @@ -1,6 +1,5 @@ #include "USER_SETTINGS.h" #include -#include "USER_SECRETS.h" #include "src/devboard/hal/hal.h" /* This file contains all the battery settings and limits */ diff --git a/Software/src/devboard/mqtt/mqtt.cpp b/Software/src/devboard/mqtt/mqtt.cpp index 36452c84..108baf6d 100644 --- a/Software/src/devboard/mqtt/mqtt.cpp +++ b/Software/src/devboard/mqtt/mqtt.cpp @@ -4,7 +4,6 @@ #include #include #include -#include "../../../USER_SECRETS.h" #include "../../../USER_SETTINGS.h" #include "../../battery/BATTERIES.h" #include "../../communication/contactorcontrol/comm_contactorcontrol.h" diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 8ef745fa..70c862e4 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -2,7 +2,6 @@ #include #include #include -#include "../../../USER_SECRETS.h" #include "../../battery/BATTERIES.h" #include "../../battery/Battery.h" #include "../../charger/CHARGERS.h" From 024ff58965cf43f70fa1caa5c5c61898a2261920 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Mon, 1 Sep 2025 15:34:28 +0300 Subject: [PATCH 105/245] Remove secrets copy from tests --- .github/workflows/unit-tests.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 3b385bc8..081538cc 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -13,7 +13,6 @@ jobs: - name: Configure and build with CMake run: | - cp Software/USER_SECRETS.TEMPLATE.h Software/USER_SECRETS.h cd test mkdir build cd build From cf68489bf862ef32aebbb7eb6089f7081e0947ed Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 1 Sep 2025 16:29:53 +0000 Subject: [PATCH 106/245] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-clang-format: v20.1.8 → v21.1.0](https://github.com/pre-commit/mirrors-clang-format/compare/v20.1.8...v21.1.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3563e969..cc342f1e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,7 +10,7 @@ ci: repos: - repo: https://github.com/pre-commit/mirrors-clang-format - rev: v20.1.8 + rev: v21.1.0 hooks: - id: clang-format args: [-Werror] # change formatting warnings to errors, hook includes -i (Inplace edit) by default From 5c3aaf0a22b4ac9a30c0fa10cc45fa9c350d7932 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 1 Sep 2025 16:31:01 +0000 Subject: [PATCH 107/245] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../webserver/advanced_battery_html.cpp | 75 ++++--------------- Software/src/inverter/KOSTAL-RS485.h | 2 +- Software/src/inverter/PYLON-CAN.cpp | 2 +- Software/src/inverter/SOLXPOW-CAN.cpp | 2 +- 4 files changed, 19 insertions(+), 62 deletions(-) diff --git a/Software/src/devboard/webserver/advanced_battery_html.cpp b/Software/src/devboard/webserver/advanced_battery_html.cpp index c3ea86d1..2f9e6ebe 100644 --- a/Software/src/devboard/webserver/advanced_battery_html.cpp +++ b/Software/src/devboard/webserver/advanced_battery_html.cpp @@ -8,83 +8,40 @@ // Available generic battery commands that are taken into use based on what the selected battery supports. std::vector battery_commands = { {"clearIsolation", "Clear isolation fault", "clear any active isolation fault?", - [](Battery* b) { return b && b->supports_clear_isolation(); }, - [](Battery* b) { - b->clear_isolation(); - }}, + [](Battery* b) { return b && b->supports_clear_isolation(); }, [](Battery* b) { b->clear_isolation(); }}, {"chademoRestart", "Restart", "restart the V2X session?", - [](Battery* b) { return b && b->supports_chademo_restart(); }, - [](Battery* b) { - b->chademo_restart(); - }}, + [](Battery* b) { return b && b->supports_chademo_restart(); }, [](Battery* b) { b->chademo_restart(); }}, {"chademoStop", "Stop", "stop V2X?", [](Battery* b) { return b && b->supports_chademo_restart(); }, - [](Battery* b) { - b->chademo_restart(); - }}, + [](Battery* b) { b->chademo_restart(); }}, {"resetBMS", "BMS reset", "reset the BMS?", [](Battery* b) { return b && b->supports_reset_BMS(); }, - [](Battery* b) { - b->reset_BMS(); - }}, + [](Battery* b) { b->reset_BMS(); }}, {"resetSOC", "SOC reset", "reset SOC?", [](Battery* b) { return b && b->supports_reset_SOC(); }, - [](Battery* b) { - b->reset_SOC(); - }}, + [](Battery* b) { b->reset_SOC(); }}, {"resetCrash", "Unlock crashed BMS", "reset crash data? Note this will unlock your BMS and enable contactor closing and SOC calculation.", - [](Battery* b) { return b && b->supports_reset_crash(); }, - [](Battery* b) { - b->reset_crash(); - }}, + [](Battery* b) { return b && b->supports_reset_crash(); }, [](Battery* b) { b->reset_crash(); }}, {"resetNVROL", "Perform NVROL reset", "trigger an NVROL reset? Battery will be unavailable for 30 seconds while this is active!", - [](Battery* b) { return b && b->supports_reset_NVROL(); }, - [](Battery* b) { - b->reset_NVROL(); - }}, + [](Battery* b) { return b && b->supports_reset_NVROL(); }, [](Battery* b) { b->reset_NVROL(); }}, {"resetContactor", "Perform contactor reset", "reset contactors?", - [](Battery* b) { return b && b->supports_contactor_reset(); }, - [](Battery* b) { - b->reset_contactor(); - }}, + [](Battery* b) { return b && b->supports_contactor_reset(); }, [](Battery* b) { b->reset_contactor(); }}, {"resetDTC", "Erase DTC", "erase DTCs?", [](Battery* b) { return b && b->supports_reset_DTC(); }, - [](Battery* b) { - b->reset_DTC(); - }}, + [](Battery* b) { b->reset_DTC(); }}, {"readDTC", "Read DTC (result must be checked in CANlog)", nullptr, - [](Battery* b) { return b && b->supports_read_DTC(); }, - [](Battery* b) { - b->read_DTC(); - }}, + [](Battery* b) { return b && b->supports_read_DTC(); }, [](Battery* b) { b->read_DTC(); }}, {"resetBECM", "Restart BECM module", "restart BECM??", [](Battery* b) { return b && b->supports_reset_DTC(); }, - [](Battery* b) { - b->reset_DTC(); - }}, + [](Battery* b) { b->reset_DTC(); }}, {"contactorClose", "Close Contactors", "a contactor close request?", - [](Battery* b) { return b && b->supports_contactor_close(); }, - [](Battery* b) { - b->request_close_contactors(); - }}, + [](Battery* b) { return b && b->supports_contactor_close(); }, [](Battery* b) { b->request_close_contactors(); }}, {"contactorOpen", "Open Contactors", "a contactor open request?", - [](Battery* b) { return b && b->supports_contactor_close(); }, - [](Battery* b) { - b->request_open_contactors(); - }}, + [](Battery* b) { return b && b->supports_contactor_close(); }, [](Battery* b) { b->request_open_contactors(); }}, {"resetSOH", "Reset degradation data", "reset degradation data?", - [](Battery* b) { return b && b->supports_reset_SOH(); }, - [](Battery* b) { - b->reset_SOH(); - }}, + [](Battery* b) { return b && b->supports_reset_SOH(); }, [](Battery* b) { b->reset_SOH(); }}, {"setFactoryMode", "Set Factory Mode", "set factory mode and disable isolation measurement?", - [](Battery* b) { return b && b->supports_factory_mode_method(); }, - [](Battery* b) { - b->set_factory_mode(); - }}, + [](Battery* b) { return b && b->supports_factory_mode_method(); }, [](Battery* b) { b->set_factory_mode(); }}, {"toggleSOC", "Toggle SOC method", "toggle SOC method? This will toggle between ESTIMATED and MEASURED SOC methods.", - [](Battery* b) { return b && b->supports_toggle_SOC_method(); }, - [](Battery* b) { - b->toggle_SOC_method(); - }}, + [](Battery* b) { return b && b->supports_toggle_SOC_method(); }, [](Battery* b) { b->toggle_SOC_method(); }}, }; String advanced_battery_processor(const String& var) { diff --git a/Software/src/inverter/KOSTAL-RS485.h b/Software/src/inverter/KOSTAL-RS485.h index 48c2bc34..aecc3620 100644 --- a/Software/src/inverter/KOSTAL-RS485.h +++ b/Software/src/inverter/KOSTAL-RS485.h @@ -22,7 +22,7 @@ class KostalInverterProtocol : public Rs485InverterProtocol { bool check_kostal_frame_crc(int len); // How many value updates we can go without inverter gets reported as missing \ - // e.g. value set to 12, 12*5sec=60seconds without comm before event is raised + // e.g. value set to 12, 12*5sec=60seconds without comm before event is raised const int RS485_HEALTHY = 12; const uint8_t KOSTAL_FRAMEHEADER[5] = {0x62, 0xFF, 0x02, 0xFF, 0x29}; diff --git a/Software/src/inverter/PYLON-CAN.cpp b/Software/src/inverter/PYLON-CAN.cpp index dabd778f..fa7fde5b 100644 --- a/Software/src/inverter/PYLON-CAN.cpp +++ b/Software/src/inverter/PYLON-CAN.cpp @@ -6,7 +6,7 @@ #define SEND_0 //If defined, the messages will have ID ending with 0 (useful for some inverters) //#define SEND_1 //If defined, the messages will have ID ending with 1 (useful for some inverters) #define INVERT_LOW_HIGH_BYTES //If defined, certain frames will have inverted low/high bytes \ - //useful for some inverters like Sofar that report the voltages incorrect otherwise + //useful for some inverters like Sofar that report the voltages incorrect otherwise //#define SET_30K_OFFSET //If defined, current values are sent with a 30k offest (useful for ferroamp) void PylonInverter:: diff --git a/Software/src/inverter/SOLXPOW-CAN.cpp b/Software/src/inverter/SOLXPOW-CAN.cpp index b29946f1..e54c0dfe 100644 --- a/Software/src/inverter/SOLXPOW-CAN.cpp +++ b/Software/src/inverter/SOLXPOW-CAN.cpp @@ -6,7 +6,7 @@ #define SEND_0 //If defined, the messages will have ID ending with 0 (useful for some inverters) //#define SEND_1 //If defined, the messages will have ID ending with 1 (useful for some inverters) #define INVERT_LOW_HIGH_BYTES //If defined, certain frames will have inverted low/high bytes \ - //useful for some inverters like Sofar that report the voltages incorrect otherwise + //useful for some inverters like Sofar that report the voltages incorrect otherwise //#define SET_30K_OFFSET //If defined, current values are sent with a 30k offest (useful for ferroamp) void SolxpowInverter:: From 12cc542a42d8338a2f739991e344a8b97603b894 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Mon, 1 Sep 2025 21:27:09 +0300 Subject: [PATCH 108/245] Make tests pass --- Software/src/battery/BMW-IX-BATTERY.cpp | 12 +-- Software/src/battery/BMW-PHEV-BATTERY.cpp | 34 +++----- Software/src/battery/BMW-PHEV-BATTERY.h | 2 + Software/src/battery/BOLT-AMPERA-BATTERY.cpp | 1 + Software/src/battery/BYD-ATTO-3-BATTERY.cpp | 1 + Software/src/battery/CHADEMO-BATTERY.cpp | 12 +-- Software/src/battery/CHADEMO-SHUNTS.cpp | 22 ++--- Software/src/battery/CMFA-EV-BATTERY.cpp | 1 + Software/src/battery/DALY-BMS.cpp | 12 +-- Software/src/battery/FOXESS-BATTERY.cpp | 1 + .../src/battery/GEELY-GEOMETRY-C-BATTERY.cpp | 1 + Software/src/battery/GEELY-GEOMETRY-C-HTML.h | 1 + .../src/battery/IMIEV-CZERO-ION-BATTERY.cpp | 12 +-- Software/src/battery/JAGUAR-IPACE-BATTERY.cpp | 7 +- Software/src/battery/KIA-E-GMP-BATTERY.cpp | 77 +---------------- .../src/battery/KIA-HYUNDAI-64-BATTERY.cpp | 1 + Software/src/battery/KIA-HYUNDAI-64-HTML.h | 1 + .../battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp | 1 + Software/src/battery/MEB-BATTERY.cpp | 3 +- Software/src/battery/MG-5-BATTERY.cpp | 3 +- Software/src/battery/MG-HS-PHEV-BATTERY.cpp | 3 +- Software/src/battery/NISSAN-LEAF-BATTERY.cpp | 6 +- .../src/battery/RANGE-ROVER-PHEV-BATTERY.cpp | 2 +- Software/src/battery/RENAULT-TWIZY.cpp | 1 + .../src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp | 3 +- .../src/battery/SANTA-FE-PHEV-BATTERY.cpp | 2 +- Software/src/battery/SIMPBMS-BATTERY.cpp | 2 +- Software/src/battery/SONO-BATTERY.cpp | 1 + Software/src/battery/TESLA-BATTERY.cpp | 57 ++++++------- Software/src/battery/TEST-FAKE-BATTERY.cpp | 6 -- Software/src/battery/VOLVO-SPA-BATTERY.cpp | 2 +- .../src/battery/VOLVO-SPA-HYBRID-BATTERY.cpp | 2 +- Software/src/devboard/utils/logging.h | 50 ++++++++++- Software/src/inverter/KOSTAL-RS485.cpp | 8 +- Software/src/inverter/KOSTAL-RS485.h | 1 - Software/src/inverter/SMA-TRIPOWER-CAN.cpp | 58 ++++--------- Software/src/inverter/SMA-TRIPOWER-CAN.h | 9 -- Software/src/inverter/SOFAR-CAN.cpp | 5 +- test/CMakeLists.txt | 71 +++++++++++++++- test/emul/Arduino.cpp | 18 ++++ test/emul/Arduino.h | 82 +++++++++++++++++++ test/emul/HardwareSerial.h | 19 ++++- test/emul/Print.h | 2 +- test/emul/Stream.h | 6 +- 44 files changed, 362 insertions(+), 259 deletions(-) diff --git a/Software/src/battery/BMW-IX-BATTERY.cpp b/Software/src/battery/BMW-IX-BATTERY.cpp index be16b640..93596ca8 100644 --- a/Software/src/battery/BMW-IX-BATTERY.cpp +++ b/Software/src/battery/BMW-IX-BATTERY.cpp @@ -37,7 +37,7 @@ uint8_t BmwIXBattery::increment_alive_counter(uint8_t counter) { return counter; } -static byte increment_C0_counter(byte counter) { +static uint8_t increment_C0_counter(uint8_t counter) { counter++; // Reset to 0xF0 if it exceeds 0xFE if (counter > 0xFE) { @@ -459,10 +459,12 @@ void BmwIXBattery::setup(void) { // Performs one time setup at startup void BmwIXBattery::HandleIncomingUserRequest(void) { // Debug user request to open or close the contactors - logging.print("User request: contactor close: "); - logging.print(userRequestContactorClose); - logging.print(" User request: contactor open: "); - logging.println(userRequestContactorOpen); + if (userRequestContactorClose) { + logging.printf("User request: contactor close"); + } + if (userRequestContactorOpen) { + logging.printf("User request: contactor open"); + } if ((userRequestContactorClose == false) && (userRequestContactorOpen == false)) { // do nothing } else if ((userRequestContactorClose == true) && (userRequestContactorOpen == false)) { diff --git a/Software/src/battery/BMW-PHEV-BATTERY.cpp b/Software/src/battery/BMW-PHEV-BATTERY.cpp index 8d76e34b..6e40142e 100644 --- a/Software/src/battery/BMW-PHEV-BATTERY.cpp +++ b/Software/src/battery/BMW-PHEV-BATTERY.cpp @@ -1,5 +1,6 @@ #include "BMW-PHEV-BATTERY.h" #include +#include //For unit test #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../datalayer/datalayer_extended.h" @@ -149,15 +150,6 @@ uint8_t BmwPhevBattery::increment_alive_counter(uint8_t counter) { return counter; } -static byte increment_0C0_counter(byte counter) { - counter++; - // Reset to 0xF0 if it exceeds 0xFE - if (counter > 0xFE) { - counter = 0xF0; - } - return counter; -} - void BmwPhevBattery::processCellVoltages() { const int startByte = 3; // Start reading at byte 3 const int numVoltages = 96; // Number of cell voltage values to process @@ -469,7 +461,7 @@ void BmwPhevBattery::handle_incoming_can_frame(CAN_frame rx_frame) { #endif // DEBUG_LOG && UDS_LOG transmit_can_frame(&BMW_6F1_REQUEST_CONTINUE_MULTIFRAME); gUDSContext.receivedInBatch = 0; // Reset batch count - Serial.println("Sent FC for next batch of 3 frames."); + logging.println("Sent FC for next batch of 3 frames."); } break; @@ -513,18 +505,18 @@ void BmwPhevBattery::handle_incoming_can_frame(CAN_frame rx_frame) { battery_current = ((int32_t)((gUDSContext.UDS_buffer[3] << 24) | (gUDSContext.UDS_buffer[4] << 16) | (gUDSContext.UDS_buffer[5] << 8) | gUDSContext.UDS_buffer[6])) * 0.1; - logging.print("Received current/amps measurement data: "); + logging.printf("Received current/amps measurement data: "); logging.print(battery_current); - logging.print(" - "); + logging.printf(" - "); for (uint16_t i = 0; i < gUDSContext.UDS_bytesReceived; i++) { // Optional leading zero for single-digit hex if (gUDSContext.UDS_buffer[i] < 0x10) { - logging.print("0"); + logging.printf("0"); } - logging.print(gUDSContext.UDS_buffer[i], HEX); - logging.print(" "); + logging.print(gUDSContext.UDS_buffer[i]); + logging.printf(" "); } - logging.println(); // new line at the end + logging.println(""); // new line at the end } //Cell Min/Max @@ -540,14 +532,14 @@ void BmwPhevBattery::handle_incoming_can_frame(CAN_frame rx_frame) { max_cell_voltage = (gUDSContext.UDS_buffer[11] << 8 | gUDSContext.UDS_buffer[12]) / 10; } else { logging.println("Cell Min Max Invalid 65535 or 0..."); - logging.print("Received data: "); + logging.printf("Received data: "); for (uint16_t i = 0; i < gUDSContext.UDS_bytesReceived; i++) { // Optional leading zero for single-digit hex if (gUDSContext.UDS_buffer[i] < 0x10) { - logging.print("0"); + logging.printf("0"); } - logging.print(gUDSContext.UDS_buffer[i], HEX); - logging.print(" "); + logging.print(gUDSContext.UDS_buffer[i]); + logging.printf(" "); } logging.println(); // new line at the end } @@ -678,7 +670,7 @@ void BmwPhevBattery::transmit_can(unsigned long currentMillis) { } void BmwPhevBattery::setup(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "BMW PHEV Battery", 63); + strncpy(datalayer.system.info.battery_protocol, Name, 63); datalayer.system.info.battery_protocol[63] = '\0'; //Wakeup the SME wake_battery_via_canbus(); diff --git a/Software/src/battery/BMW-PHEV-BATTERY.h b/Software/src/battery/BMW-PHEV-BATTERY.h index 553e514e..0c6baa39 100644 --- a/Software/src/battery/BMW-PHEV-BATTERY.h +++ b/Software/src/battery/BMW-PHEV-BATTERY.h @@ -10,6 +10,8 @@ class BmwPhevBattery : public CanBattery { virtual void update_values(); virtual void transmit_can(unsigned long currentMillis); + static constexpr const char* Name = "BMW PHEV Battery"; + BatteryHtmlRenderer& get_status_renderer() { return renderer; } private: diff --git a/Software/src/battery/BOLT-AMPERA-BATTERY.cpp b/Software/src/battery/BOLT-AMPERA-BATTERY.cpp index 7fec034d..b740c11b 100644 --- a/Software/src/battery/BOLT-AMPERA-BATTERY.cpp +++ b/Software/src/battery/BOLT-AMPERA-BATTERY.cpp @@ -1,4 +1,5 @@ #include "BOLT-AMPERA-BATTERY.h" +#include //For unit test #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../datalayer/datalayer_extended.h" diff --git a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp index 14688bf9..7e4137fc 100644 --- a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp +++ b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp @@ -1,4 +1,5 @@ #include "BYD-ATTO-3-BATTERY.h" +#include //For unit test #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../datalayer/datalayer_extended.h" diff --git a/Software/src/battery/CHADEMO-BATTERY.cpp b/Software/src/battery/CHADEMO-BATTERY.cpp index 07257708..40205fbe 100644 --- a/Software/src/battery/CHADEMO-BATTERY.cpp +++ b/Software/src/battery/CHADEMO-BATTERY.cpp @@ -120,7 +120,7 @@ void ChademoBattery::process_vehicle_charging_session(CAN_frame rx_frame) { uint8_t chargingrate = 0; if (x100_chg_lim.ConstantOfChargingRateIndication > 0) { chargingrate = x102_chg_session.StateOfCharge / x100_chg_lim.ConstantOfChargingRateIndication * 100; - logging.print("Charge Rate (kW): "); + logging.printf("Charge Rate (kW): "); logging.println(chargingrate); } @@ -220,9 +220,9 @@ void ChademoBattery::process_vehicle_charging_limits(CAN_frame rx_frame) { if (get_measured_voltage() <= x200_discharge_limits.MinimumDischargeVoltage && CHADEMO_Status > CHADEMO_NEGOTIATE) { logging.println("x200 minimum discharge voltage met or exceeded, stopping."); - logging.print("Measured: "); + logging.printf("Measured: "); logging.print(get_measured_voltage()); - logging.print("Minimum voltage: "); + logging.printf("Minimum voltage: "); logging.print(x200_discharge_limits.MinimumDischargeVoltage); CHADEMO_Status = CHADEMO_STOP; } @@ -240,9 +240,9 @@ void ChademoBattery::process_vehicle_discharge_estimate(CAN_frame rx_frame) { if (currentMillis - previousMillis5000 >= INTERVAL_5_S) { previousMillis5000 = currentMillis; - logging.print("x201 availabile vehicle energy, completion time: "); + logging.printf("x201 availabile vehicle energy, completion time: "); logging.println(x201_discharge_estimate.AvailableVehicleEnergy); - logging.print("x201 approx vehicle completion time: "); + logging.printf("x201 approx vehicle completion time: "); logging.println(x201_discharge_estimate.ApproxDischargeCompletionTime); } } @@ -766,7 +766,7 @@ void ChademoBattery::handle_chademo_sequence() { /* check whether contactors ready, because externally dependent upon inverter allow during discharge */ if (contactors_ready) { logging.println("Contactors ready"); - logging.print("Voltage: "); + logging.printf("Voltage: "); logging.println(get_measured_voltage()); /* transition to POWERFLOW state if discharge compatible on both sides */ if (x109_evse_state.discharge_compatible && x102_chg_session.s.status.StatusVehicleDischargeCompatible && diff --git a/Software/src/battery/CHADEMO-SHUNTS.cpp b/Software/src/battery/CHADEMO-SHUNTS.cpp index aa1503f7..b539f315 100644 --- a/Software/src/battery/CHADEMO-SHUNTS.cpp +++ b/Software/src/battery/CHADEMO-SHUNTS.cpp @@ -22,7 +22,6 @@ #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" #include "CHADEMO-BATTERY.h" -#include "src/devboard/utils/logging.h" /* Initial frames received from ISA shunts provide invalid during initialization */ static int framecount = 0; @@ -87,17 +86,6 @@ void ISA_handleFrame(CAN_frame* frame) { case 0x510: case 0x511: - logging.print(millis()); // Example printout, time, ID, length, data: 7553 1DB 8 FF C0 B9 EA 0 0 2 5D - logging.print(" "); - logging.print(frame->ID, HEX); - logging.print(" "); - logging.print(frame->DLC); - logging.print(" "); - for (int i = 0; i < frame->DLC; ++i) { - logging.print(frame->data.u8[i], HEX); - logging.print(" "); - } - logging.println(""); break; case 0x521: @@ -245,7 +233,7 @@ void ISA_initialize() { ISA_STOP(); delay(500); for (int i = 0; i < 8; i++) { - logging.print("ISA Initialization "); + logging.printf("ISA Initialization "); logging.println(i); outframe.data.u8[0] = (0x20 + i); @@ -382,7 +370,7 @@ void ISA_initCurrent() { } void ISA_getCONFIG(uint8_t i) { - logging.print("ISA Get Config "); + logging.printf("ISA Get Config "); logging.println(i); outframe.data.u8[0] = (0x60 + i); @@ -398,7 +386,7 @@ void ISA_getCONFIG(uint8_t i) { } void ISA_getCAN_ID(uint8_t i) { - logging.print("ISA Get CAN ID "); + logging.printf("ISA Get CAN ID "); logging.println(i); outframe.data.u8[0] = (0x50 + i); @@ -418,8 +406,8 @@ void ISA_getCAN_ID(uint8_t i) { } void ISA_getINFO(uint8_t i) { - logging.print("ISA Get INFO "); - logging.println(i, HEX); + logging.printf("ISA Get INFO "); + logging.println(i); outframe.data.u8[0] = (0x70 + i); outframe.data.u8[1] = 0x00; diff --git a/Software/src/battery/CMFA-EV-BATTERY.cpp b/Software/src/battery/CMFA-EV-BATTERY.cpp index 6923c29e..c4856e27 100644 --- a/Software/src/battery/CMFA-EV-BATTERY.cpp +++ b/Software/src/battery/CMFA-EV-BATTERY.cpp @@ -1,4 +1,5 @@ #include "CMFA-EV-BATTERY.h" +#include //unit tests memcpy #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../datalayer/datalayer_extended.h" diff --git a/Software/src/battery/DALY-BMS.cpp b/Software/src/battery/DALY-BMS.cpp index da93f6a4..bf9e00b8 100644 --- a/Software/src/battery/DALY-BMS.cpp +++ b/Software/src/battery/DALY-BMS.cpp @@ -16,7 +16,7 @@ static uint16_t voltage_dV = 0; static uint32_t remaining_capacity_mAh = 0; static uint16_t cellvoltages_mV[48] = {0}; static uint16_t cellvoltage_min_mV = 3700; -static uint16_t cellvoltage_max_mV = 3700; +static uint16_t cellvoltage_max_mV = 0; static uint16_t cell_count = 0; static uint16_t SOC = 0; static bool has_fault = false; @@ -109,12 +109,12 @@ uint32_t decode_uint32be(uint8_t data[8], uint8_t offset) { } void dump_buff(const char* msg, uint8_t* buff, uint8_t len) { - logging.print("[DALY-BMS] "); - logging.print(msg); + logging.printf("[DALY-BMS] "); + logging.printf(msg); for (int i = 0; i < len; i++) { - logging.print(buff[i] >> 4, HEX); - logging.print(buff[i] & 0xf, HEX); - logging.print(" "); + logging.print(buff[i] >> 4); + logging.print(buff[i] & 0xf); + logging.printf(" "); } logging.println(); } diff --git a/Software/src/battery/FOXESS-BATTERY.cpp b/Software/src/battery/FOXESS-BATTERY.cpp index 6ec0509e..22a9d3b8 100644 --- a/Software/src/battery/FOXESS-BATTERY.cpp +++ b/Software/src/battery/FOXESS-BATTERY.cpp @@ -1,4 +1,5 @@ #include "FOXESS-BATTERY.h" +#include //For unit test #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" diff --git a/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.cpp b/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.cpp index 6400f077..35b45829 100644 --- a/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.cpp +++ b/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.cpp @@ -1,4 +1,5 @@ #include "GEELY-GEOMETRY-C-BATTERY.h" +#include //For unit test #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../datalayer/datalayer_extended.h" diff --git a/Software/src/battery/GEELY-GEOMETRY-C-HTML.h b/Software/src/battery/GEELY-GEOMETRY-C-HTML.h index 7a51f488..0a5dff34 100644 --- a/Software/src/battery/GEELY-GEOMETRY-C-HTML.h +++ b/Software/src/battery/GEELY-GEOMETRY-C-HTML.h @@ -1,6 +1,7 @@ #ifndef _GEELY_GEOMETRY_C_HTML_H #define _GEELY_GEOMETRY_C_HTML_H +#include //For unit test #include "../datalayer/datalayer_extended.h" #include "../devboard/webserver/BatteryHtmlRenderer.h" diff --git a/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp b/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp index 694a6d09..d77820a9 100644 --- a/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp +++ b/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp @@ -1,4 +1,5 @@ #include "IMIEV-CZERO-ION-BATTERY.h" +#include //for unit tests #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" @@ -76,17 +77,8 @@ void ImievCZeroIonBattery:: if (!BMU_Detected) { logging.println("BMU not detected, check wiring!"); + //TODO: Raise event } - - logging.println("Battery Values"); - logging.print("BMU SOC: "); - logging.print(BMU_SOC); - logging.print(" BMU Current: "); - logging.print(BMU_Current); - logging.print(" BMU Battery Voltage: "); - logging.print(BMU_PackVoltage); - logging.print(" BMU_Power: "); - logging.print(BMU_Power); } void ImievCZeroIonBattery::handle_incoming_can_frame(CAN_frame rx_frame) { diff --git a/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp b/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp index 2f43438f..4bda3f0c 100644 --- a/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp +++ b/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp @@ -1,4 +1,5 @@ #include "JAGUAR-IPACE-BATTERY.h" +#include //for unit tests #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" @@ -57,9 +58,9 @@ CAN_frame ipace_keep_alive = {.FD = false, .data = {0x9E, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}};*/ static void print_units(const char* header, int value, const char* units) { - logging.print(header); - logging.print(value); - logging.print(units); + logging.printf(header); + logging.printf("%d", value); + logging.printf(units); } void JaguarIpaceBattery::update_values() { diff --git a/Software/src/battery/KIA-E-GMP-BATTERY.cpp b/Software/src/battery/KIA-E-GMP-BATTERY.cpp index ba6b1c06..3da4f096 100644 --- a/Software/src/battery/KIA-E-GMP-BATTERY.cpp +++ b/Software/src/battery/KIA-E-GMP-BATTERY.cpp @@ -55,18 +55,6 @@ uint16_t KiaEGmpBattery::estimateSOC(uint16_t packVoltage, uint16_t cellCount, i // Calculate average cell voltage in millivolts uint16_t avgCellVoltage = compensatedPackVoltageMv / cellCount; - logging.print("Pack: "); - logging.print(packVoltage / 10.0); - logging.print("V, Current: "); - logging.print(currentAmps / 10.0); - logging.print("A, Drop: "); - logging.print(voltageDrop / 1000.0); - logging.print("V, Comp Pack: "); - logging.print(compensatedPackVoltageMv / 1000.0); - logging.print("V, Avg Cell: "); - logging.print(avgCellVoltage); - logging.println("mV"); - // Use the cell voltage lookup table to estimate SOC return estimateSOCFromCell(avgCellVoltage); } @@ -182,65 +170,6 @@ void KiaEGmpBattery::update_values() { if (leadAcidBatteryVoltage < 110) { set_event(EVENT_12V_LOW, leadAcidBatteryVoltage); } - - /* Safeties verified. Perform USB serial printout if configured to do so */ - - logging.println(); //sepatator - logging.println("Values from battery: "); - logging.print("SOC BMS: "); - logging.print((uint16_t)SOC_BMS / 10.0, 1); - logging.print("% | SOC Display: "); - logging.print((uint16_t)SOC_Display / 10.0, 1); - logging.print("% | SOH "); - logging.print((uint16_t)batterySOH / 10.0, 1); - logging.println("%"); - logging.print((int16_t)batteryAmps / 10.0, 1); - logging.print(" Amps | "); - logging.print((uint16_t)batteryVoltage / 10.0, 1); - logging.print(" Volts | "); - logging.print((int16_t)datalayer.battery.status.active_power_W); - logging.println(" Watts"); - logging.print("Allowed Charge "); - logging.print((uint16_t)allowedChargePower * 10); - logging.print(" W | Allowed Discharge "); - logging.print((uint16_t)allowedDischargePower * 10); - logging.println(" W"); - logging.print("MaxCellVolt "); - logging.print(CellVoltMax_mV); - logging.print(" mV No "); - logging.print(CellVmaxNo); - logging.print(" | MinCellVolt "); - logging.print(CellVoltMin_mV); - logging.print(" mV No "); - logging.println(CellVminNo); - logging.print("TempHi "); - logging.print((int16_t)temperatureMax); - logging.print("°C TempLo "); - logging.print((int16_t)temperatureMin); - logging.print("°C WaterInlet "); - logging.print((int8_t)temperature_water_inlet); - logging.print("°C PowerRelay "); - logging.print((int8_t)powerRelayTemperature * 2); - logging.println("°C"); - logging.print("Aux12volt: "); - logging.print((int16_t)leadAcidBatteryVoltage / 10.0, 1); - logging.println("V | "); - logging.print("BmsManagementMode "); - logging.print((uint8_t)batteryManagementMode, BIN); - if (bitRead((uint8_t)BMS_ign, 2) == 1) { - logging.print(" | BmsIgnition ON"); - } else { - logging.print(" | BmsIgnition OFF"); - } - - if (bitRead((uint8_t)batteryRelay, 0) == 1) { - logging.print(" | PowerRelay ON"); - } else { - logging.print(" | PowerRelay OFF"); - } - logging.print(" | Inverter "); - logging.print(inverterVoltage); - logging.println(" Volts"); } // Getter implementations for HTML renderer @@ -321,14 +250,10 @@ void KiaEGmpBattery::handle_incoming_can_frame(CAN_frame rx_frame) { datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; break; case 0x7EC: - // print_canfd_frame(frame); switch (rx_frame.data.u8[0]) { case 0x10: //"PID Header" - // logging.println ("Send ack"); poll_data_pid = rx_frame.data.u8[4]; - // if (rx_frame.data.u8[4] == poll_data_pid) { - transmit_can_frame(&EGMP_7E4_ack); //Send ack to BMS if the same frame is sent as polled - // } + transmit_can_frame(&EGMP_7E4_ack); //Send ack to BMS break; case 0x21: //First frame in PID group if (poll_data_pid == 1) { diff --git a/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp b/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp index bc499862..af7ce7c4 100644 --- a/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp +++ b/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp @@ -1,4 +1,5 @@ #include "KIA-HYUNDAI-64-BATTERY.h" +#include //For unit test #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../datalayer/datalayer_extended.h" diff --git a/Software/src/battery/KIA-HYUNDAI-64-HTML.h b/Software/src/battery/KIA-HYUNDAI-64-HTML.h index a982f1e6..bb6f12fa 100644 --- a/Software/src/battery/KIA-HYUNDAI-64-HTML.h +++ b/Software/src/battery/KIA-HYUNDAI-64-HTML.h @@ -1,6 +1,7 @@ #ifndef _KIA_HYUNDAI_64_HTML_H #define _KIA_HYUNDAI_64_HTML_H +#include //For unit test #include "../datalayer/datalayer.h" #include "../datalayer/datalayer_extended.h" #include "../devboard/webserver/BatteryHtmlRenderer.h" diff --git a/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp b/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp index ff87dc4d..8c8d4ecb 100644 --- a/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp +++ b/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp @@ -1,4 +1,5 @@ #include "KIA-HYUNDAI-HYBRID-BATTERY.h" +#include //For unit test #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" diff --git a/Software/src/battery/MEB-BATTERY.cpp b/Software/src/battery/MEB-BATTERY.cpp index 6969ad9d..e0237756 100644 --- a/Software/src/battery/MEB-BATTERY.cpp +++ b/Software/src/battery/MEB-BATTERY.cpp @@ -1,6 +1,7 @@ #include "MEB-BATTERY.h" #include #include // For std::min and std::max +#include //For unit test #include "../communication/can/comm_can.h" #include "../communication/can/obd.h" #include "../datalayer/datalayer.h" @@ -1262,8 +1263,6 @@ void MebBattery::handle_incoming_can_frame(CAN_frame rx_frame) { handle_obd_frame(rx_frame); break; default: - logging.printf("Unknown CAN frame received:\n"); - dump_can_frame(rx_frame, MSG_RX); break; } datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; diff --git a/Software/src/battery/MG-5-BATTERY.cpp b/Software/src/battery/MG-5-BATTERY.cpp index 90c486ca..f2e5f991 100644 --- a/Software/src/battery/MG-5-BATTERY.cpp +++ b/Software/src/battery/MG-5-BATTERY.cpp @@ -1,4 +1,5 @@ #include "MG-5-BATTERY.h" +#include //For unit test #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" @@ -112,7 +113,7 @@ void Mg5Battery::transmit_can(unsigned long currentMillis) { } void Mg5Battery::setup(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "MG 5 battery", 63); + strncpy(datalayer.system.info.battery_protocol, Name, 63); datalayer.system.info.battery_protocol[63] = '\0'; datalayer.system.status.battery_allows_contactor_closing = true; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/MG-HS-PHEV-BATTERY.cpp b/Software/src/battery/MG-HS-PHEV-BATTERY.cpp index a8395067..57d8476a 100644 --- a/Software/src/battery/MG-HS-PHEV-BATTERY.cpp +++ b/Software/src/battery/MG-HS-PHEV-BATTERY.cpp @@ -1,10 +1,11 @@ #include "MG-HS-PHEV-BATTERY.h" +#include //For unit test +#include //For unit test #include "../communication/can/comm_can.h" #include "../communication/contactorcontrol/comm_contactorcontrol.h" #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" #include "../devboard/utils/logging.h" - /* MG HS PHEV 16.6kWh battery integration diff --git a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp index be9900d9..17e4f3d3 100644 --- a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp +++ b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp @@ -1,13 +1,13 @@ #include "NISSAN-LEAF-BATTERY.h" +#include //For unit test +#include "../charger/CHARGERS.h" +#include "../charger/CanCharger.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../datalayer/datalayer_extended.h" //For "More battery info" webpage #include "../devboard/utils/events.h" #include "../devboard/utils/logging.h" -#include "../charger/CHARGERS.h" -#include "../charger/CanCharger.h" - uint16_t Temp_fromRAW_to_F(uint16_t temperature); //Cryptographic functions void decodeChallengeData(unsigned int SeedInput, unsigned char* Crypt_Output_Buffer); diff --git a/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.cpp b/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.cpp index e34cd9ef..7e31bad4 100644 --- a/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.cpp +++ b/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.cpp @@ -1,8 +1,8 @@ #include "RANGE-ROVER-PHEV-BATTERY.h" +#include //For unit test #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" - /* TODO - LOG files from vehicle needed to determine CAN content needed to send towards battery! - BCCM_PMZ_A (0x18B 50ms) diff --git a/Software/src/battery/RENAULT-TWIZY.cpp b/Software/src/battery/RENAULT-TWIZY.cpp index 3fa74fb1..fd214296 100644 --- a/Software/src/battery/RENAULT-TWIZY.cpp +++ b/Software/src/battery/RENAULT-TWIZY.cpp @@ -1,5 +1,6 @@ #include "RENAULT-TWIZY.h" #include +#include //For unit test #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" diff --git a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp index 095fb6a1..71fc5e85 100644 --- a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp +++ b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp @@ -1,10 +1,9 @@ #include "RENAULT-ZOE-GEN1-BATTERY.h" +#include //For unit test #include "../datalayer/datalayer.h" #include "../datalayer/datalayer_extended.h" #include "../devboard/utils/events.h" -void transmit_can_frame(CAN_frame* tx_frame, int interface); - /* Information in this file is based of the OVMS V3 vehicle_renaultzoe.cpp component https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/blob/master/vehicle/OVMS.V3/components/vehicle_renaultzoe/src/vehicle_renaultzoe.cpp The Zoe BMS apparently does not send total pack voltage, so we use the polled 96x cellvoltages summed up as total voltage diff --git a/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp b/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp index 7cad62fd..e68829c5 100644 --- a/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp +++ b/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp @@ -1,8 +1,8 @@ #include "SANTA-FE-PHEV-BATTERY.h" +#include //For unit test #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" - /* Credits go to maciek16c for these findings! https://github.com/maciek16c/hyundai-santa-fe-phev-battery https://openinverter.org/forum/viewtopic.php?p=62256 diff --git a/Software/src/battery/SIMPBMS-BATTERY.cpp b/Software/src/battery/SIMPBMS-BATTERY.cpp index bc58c580..27af92be 100644 --- a/Software/src/battery/SIMPBMS-BATTERY.cpp +++ b/Software/src/battery/SIMPBMS-BATTERY.cpp @@ -1,8 +1,8 @@ #include "SIMPBMS-BATTERY.h" +#include //For unit test #include "../battery/BATTERIES.h" #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" - void SimpBmsBattery::update_values() { datalayer.battery.status.real_soc = (SOC * 100); //increase SOC range from 0-100 -> 100.00 diff --git a/Software/src/battery/SONO-BATTERY.cpp b/Software/src/battery/SONO-BATTERY.cpp index c2243720..d99405d2 100644 --- a/Software/src/battery/SONO-BATTERY.cpp +++ b/Software/src/battery/SONO-BATTERY.cpp @@ -1,4 +1,5 @@ #include "SONO-BATTERY.h" +#include //For unit test #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" diff --git a/Software/src/battery/TESLA-BATTERY.cpp b/Software/src/battery/TESLA-BATTERY.cpp index 8407eed9..3f338ee4 100644 --- a/Software/src/battery/TESLA-BATTERY.cpp +++ b/Software/src/battery/TESLA-BATTERY.cpp @@ -1,4 +1,5 @@ #include "TESLA-BATTERY.h" +#include //For unit test #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../datalayer/datalayer_extended.h" //For Advanced Battery Insights webpage @@ -995,52 +996,52 @@ void TeslaBattery:: } printFaultCodesIfActive(); - logging.print("BMS Contactors State: "); - logging.print(getBMSContactorState(battery_contactor)); // Display what state the BMS thinks the contactors are in - logging.print(", HVIL: "); - logging.print(getHvilStatusState(battery_hvil_status)); - logging.print(", NegativeState: "); - logging.print(getContactorState(battery_packContNegativeState)); - logging.print(", PositiveState: "); + logging.printf("BMS Contactors State: "); + logging.printf(getBMSContactorState(battery_contactor)); // Display what state the BMS thinks the contactors are in + logging.printf(", HVIL: "); + logging.printf(getHvilStatusState(battery_hvil_status)); + logging.printf(", NegativeState: "); + logging.printf(getContactorState(battery_packContNegativeState)); + logging.printf(", PositiveState: "); logging.println(getContactorState(battery_packContPositiveState)); - logging.print("HVP Contactors setState: "); - logging.print( + logging.printf("HVP Contactors setState: "); + logging.printf( getContactorText(battery_packContactorSetState)); // Display what state the HVP has set the contactors to be in - logging.print(", Closing blocked: "); - logging.print(getNoYes(battery_packCtrsClosingBlocked)); + logging.printf(", Closing blocked: "); + logging.printf(getNoYes(battery_packCtrsClosingBlocked)); if (battery_packContactorSetState == 5) { - logging.print(" (already CLOSED)"); + logging.printf(" (already CLOSED)"); } - logging.print(", Pyrotest: "); + logging.printf(", Pyrotest: "); logging.println(getNoYes(battery_pyroTestInProgress)); - logging.print("Battery values: "); - logging.print("Real SOC: "); + logging.printf("Battery values: "); + logging.printf("Real SOC: "); logging.print(battery_soc_ui / 10.0, 1); - logging.print(", Battery voltage: "); + logging.printf(", Battery voltage: "); logging.print(battery_volts / 10.0, 1); - logging.print("V"); - logging.print(", Battery HV current: "); + logging.printf("V"); + logging.printf(", Battery HV current: "); logging.print(battery_amps / 10.0, 1); - logging.print("A"); - logging.print(", Fully charged?: "); + logging.printf("A"); + logging.printf(", Fully charged?: "); if (battery_full_charge_complete) - logging.print("YES, "); + logging.printf("YES, "); else - logging.print("NO, "); + logging.printf("NO, "); if (datalayer.battery.info.chemistry == battery_chemistry_enum::LFP) { - logging.print("LFP chemistry detected!"); + logging.printf("LFP chemistry detected!"); } logging.println(""); - logging.print("Cellstats, Max: "); + logging.printf("Cellstats, Max: "); logging.print(battery_cell_max_v); - logging.print("mV (cell "); + logging.printf("mV (cell "); logging.print(battery_BrickVoltageMaxNum); - logging.print("), Min: "); + logging.printf("), Min: "); logging.print(battery_cell_min_v); - logging.print("mV (cell "); + logging.printf("mV (cell "); logging.print(battery_BrickVoltageMinNum); - logging.print("), Imbalance: "); + logging.printf("), Imbalance: "); logging.print(battery_cell_deviation_mV); logging.println("mV."); diff --git a/Software/src/battery/TEST-FAKE-BATTERY.cpp b/Software/src/battery/TEST-FAKE-BATTERY.cpp index b7b18bc8..9803e7ee 100644 --- a/Software/src/battery/TEST-FAKE-BATTERY.cpp +++ b/Software/src/battery/TEST-FAKE-BATTERY.cpp @@ -3,12 +3,6 @@ #include "../datalayer/datalayer.h" #include "../devboard/utils/logging.h" -static void print_units(const char* header, int value, const char* units) { - logging.print(header); - logging.print(value); - logging.print(units); -} - void TestFakeBattery:: update_values() { /* This function puts fake values onto the parameters sent towards the inverter */ diff --git a/Software/src/battery/VOLVO-SPA-BATTERY.cpp b/Software/src/battery/VOLVO-SPA-BATTERY.cpp index e251f9c7..5efc48b8 100644 --- a/Software/src/battery/VOLVO-SPA-BATTERY.cpp +++ b/Software/src/battery/VOLVO-SPA-BATTERY.cpp @@ -1,10 +1,10 @@ #include "VOLVO-SPA-BATTERY.h" +#include //For unit test #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../datalayer/datalayer_extended.h" //For "More battery info" webpage #include "../devboard/utils/events.h" #include "../devboard/utils/logging.h" - void VolvoSpaBattery:: update_values() { //This function maps all the values fetched via CAN to the correct parameters used for the inverter diff --git a/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.cpp b/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.cpp index 7afc2ada..d99bc995 100644 --- a/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.cpp +++ b/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.cpp @@ -1,10 +1,10 @@ #include "VOLVO-SPA-HYBRID-BATTERY.h" +#include //For unit test #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../datalayer/datalayer_extended.h" //For "More battery info" webpage #include "../devboard/utils/events.h" #include "../devboard/utils/logging.h" - void VolvoSpaHybridBattery:: update_values() { //This function maps all the values fetched via CAN to the correct parameters used for the inverter uint8_t cnt = 0; diff --git a/Software/src/devboard/utils/logging.h b/Software/src/devboard/utils/logging.h index 95ba6910..69a1f170 100644 --- a/Software/src/devboard/utils/logging.h +++ b/Software/src/devboard/utils/logging.h @@ -8,7 +8,6 @@ #ifndef UNIT_TEST // Real implementation for production -#include class Logging : public Print { void add_timestamp(size_t size); @@ -51,12 +50,61 @@ class Logging { (void)fmt; } + // Overloaded print methods for different data types + static void print(const char* str) { (void)str; } + static void print(char c) { (void)c; } + static void print(int8_t num) { (void)num; } + static void print(uint8_t num) { (void)num; } + static void print(int16_t num) { (void)num; } + static void print(uint16_t num) { (void)num; } + static void print(int32_t num) { (void)num; } + static void print(uint32_t num) { (void)num; } + static void print(int64_t num) { (void)num; } + static void print(uint64_t num) { (void)num; } + static void print(float num) { (void)num; } + static void print(double num) { (void)num; } + static void print(bool b) { (void)b; } + + static void print(double num, int precision) { + (void)num; + (void)precision; + } + + static void print(float num, int precision) { + (void)num; + (void)precision; + } + + static void print(int32_t num, int base) { + (void)num; + (void)base; + } + + static void print(uint32_t num, int base) { + (void)num; + (void)base; + } + static void println(const char* str) { (void)str; } + static void println(char c) { (void)c; } + static void println(int8_t num) { (void)num; } + static void println(uint8_t num) { (void)num; } + static void println(int16_t num) { (void)num; } + static void println(uint16_t num) { (void)num; } + static void println(int32_t num) { (void)num; } + static void println(uint32_t num) { (void)num; } + static void println(int64_t num) { (void)num; } + static void println(uint64_t num) { (void)num; } + static void println(float num) { (void)num; } + static void println(double num) { (void)num; } + static void println(bool b) { (void)b; } + static void println() {} // Empty println Logging() {} }; // Test macros - empty implementations +#define DEBUG_PRINT(fmt, ...) ((void)0) #define DEBUG_PRINTF(fmt, ...) ((void)0) #define DEBUG_PRINTLN(str) ((void)0) diff --git a/Software/src/inverter/KOSTAL-RS485.cpp b/Software/src/inverter/KOSTAL-RS485.cpp index 045733e8..7e88f67f 100644 --- a/Software/src/inverter/KOSTAL-RS485.cpp +++ b/Software/src/inverter/KOSTAL-RS485.cpp @@ -14,18 +14,18 @@ void KostalInverterProtocol::float2frame(uint8_t* arr, float value, uint8_t fram } static void dbg_timestamp(void) { - logging.print("["); + logging.printf("["); logging.print(millis()); - logging.print(" ms] "); + logging.printf(" ms] "); } static void dbg_frame(uint8_t* frame, int len, const char* prefix) { dbg_timestamp(); logging.print(prefix); - logging.print(": "); + logging.printf(": "); for (uint8_t i = 0; i < len; i++) { if (frame[i] < 0x10) { - logging.print("0"); + logging.printf("0"); } logging.print(frame[i], HEX); logging.print(" "); diff --git a/Software/src/inverter/KOSTAL-RS485.h b/Software/src/inverter/KOSTAL-RS485.h index 48c2bc34..5301aaed 100644 --- a/Software/src/inverter/KOSTAL-RS485.h +++ b/Software/src/inverter/KOSTAL-RS485.h @@ -20,7 +20,6 @@ class KostalInverterProtocol : public Rs485InverterProtocol { int baud_rate() { return 57600; } void float2frame(uint8_t* arr, float value, uint8_t framepointer); bool check_kostal_frame_crc(int len); - // How many value updates we can go without inverter gets reported as missing \ // e.g. value set to 12, 12*5sec=60seconds without comm before event is raised const int RS485_HEALTHY = 12; diff --git a/Software/src/inverter/SMA-TRIPOWER-CAN.cpp b/Software/src/inverter/SMA-TRIPOWER-CAN.cpp index 172cd4c0..213c530b 100644 --- a/Software/src/inverter/SMA-TRIPOWER-CAN.cpp +++ b/Software/src/inverter/SMA-TRIPOWER-CAN.cpp @@ -137,17 +137,6 @@ void SmaTripowerInverter::map_can_frame_to_variable(CAN_frame rx_frame) { } } -void SmaTripowerInverter::pushFrame(CAN_frame* frame, std::function callback) { - if (listLength >= 20) { - return; //TODO: scream. - } - framesToSend[listLength] = { - .frame = frame, - .callback = callback, - }; - listLength++; -} - void SmaTripowerInverter::transmit_can(unsigned long currentMillis) { // Send CAN Message only if we're enabled by inverter @@ -155,18 +144,6 @@ void SmaTripowerInverter::transmit_can(unsigned long currentMillis) { return; } - if (listLength > 0 && currentMillis - previousMillis250ms >= INTERVAL_250_MS) { - previousMillis250ms = currentMillis; - // Send next frame. - Frame frame = framesToSend[0]; - transmit_can_frame(frame.frame); - frame.callback(); - for (int i = 0; i < listLength - 1; i++) { - framesToSend[i] = framesToSend[i + 1]; - } - listLength--; - } - if (!pairing_completed) { return; } @@ -174,19 +151,19 @@ void SmaTripowerInverter::transmit_can(unsigned long currentMillis) { // Send CAN Message every 2s if (currentMillis - previousMillis2s >= INTERVAL_2_S) { previousMillis2s = currentMillis; - pushFrame(&SMA_358); + transmit_can_frame(&SMA_358); } // Send CAN Message every 10s if (currentMillis - previousMillis10s >= INTERVAL_10_S) { previousMillis10s = currentMillis; - pushFrame(&SMA_518); - pushFrame(&SMA_4D8); - pushFrame(&SMA_3D8); + transmit_can_frame(&SMA_518); + transmit_can_frame(&SMA_4D8); + transmit_can_frame(&SMA_3D8); } // Send CAN Message every 60s (potentially SMA_458 is not required for stable operation) if (currentMillis - previousMillis60s >= INTERVAL_60_S) { previousMillis60s = currentMillis; - pushFrame(&SMA_458); + transmit_can_frame(&SMA_458); } } @@ -195,18 +172,17 @@ void SmaTripowerInverter::completePairing() { } void SmaTripowerInverter::transmit_can_init() { - listLength = 0; // clear all frames - pushFrame(&SMA_558); //Pairing start - Vendor - pushFrame(&SMA_598); //Serial - pushFrame(&SMA_5D8); //BYD - pushFrame(&SMA_618_0); //BATTERY - pushFrame(&SMA_618_1); //-Box Pr - pushFrame(&SMA_618_2); //emium H - pushFrame(&SMA_618_3); //VS - pushFrame(&SMA_358); - pushFrame(&SMA_3D8); - pushFrame(&SMA_458); - pushFrame(&SMA_4D8); - pushFrame(&SMA_518, [this]() { this->completePairing(); }); + transmit_can_frame(&SMA_558); //Pairing start - Vendor + transmit_can_frame(&SMA_598); //Serial + transmit_can_frame(&SMA_5D8); //BYD + transmit_can_frame(&SMA_618_0); //BATTERY + transmit_can_frame(&SMA_618_1); //-Box Pr + transmit_can_frame(&SMA_618_2); //emium H + transmit_can_frame(&SMA_618_3); //VS + transmit_can_frame(&SMA_358); + transmit_can_frame(&SMA_3D8); + transmit_can_frame(&SMA_458); + transmit_can_frame(&SMA_4D8); + transmit_can_frame(&SMA_518); } diff --git a/Software/src/inverter/SMA-TRIPOWER-CAN.h b/Software/src/inverter/SMA-TRIPOWER-CAN.h index 09397370..ace38bf9 100644 --- a/Software/src/inverter/SMA-TRIPOWER-CAN.h +++ b/Software/src/inverter/SMA-TRIPOWER-CAN.h @@ -24,7 +24,6 @@ class SmaTripowerInverter : public SmaInverterBase { const int THIRTY_MINUTES = 1200; void transmit_can_init(); - void pushFrame(CAN_frame* frame, std::function callback = []() {}); void completePairing(); unsigned long previousMillis250ms = 0; // will store last time a 250ms CAN Message was send @@ -33,14 +32,6 @@ class SmaTripowerInverter : public SmaInverterBase { unsigned long previousMillis10s = 0; // will store last time a 10s CAN Message was send unsigned long previousMillis60s = 0; // will store last time a 60s CAN Message was send - typedef struct { - CAN_frame* frame; - std::function callback; - } Frame; - - unsigned short listLength = 0; - Frame framesToSend[20]; - uint32_t inverter_time = 0; uint16_t inverter_voltage = 0; int16_t inverter_current = 0; diff --git a/Software/src/inverter/SOFAR-CAN.cpp b/Software/src/inverter/SOFAR-CAN.cpp index 6779a3a2..23369be7 100644 --- a/Software/src/inverter/SOFAR-CAN.cpp +++ b/Software/src/inverter/SOFAR-CAN.cpp @@ -292,9 +292,8 @@ bool SofarInverter::setup() { // Performs one time setup at startup over CAN bu init_frame(SOFAR_783, 0x783); init_frame(SOFAR_784, 0x784); - String tempStr(datalayer.battery.settings.sofar_user_specified_battery_id); - strncpy(datalayer.system.info.inverter_brand, tempStr.c_str(), 7); - datalayer.system.info.inverter_brand[7] = '\0'; + snprintf(datalayer.system.info.inverter_brand, sizeof(datalayer.system.info.inverter_brand), "%s", + datalayer.battery.settings.sofar_user_specified_battery_id); return true; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index b6103027..8d16116c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -85,11 +85,80 @@ add_executable(tests ../Software/USER_SETTINGS.cpp ../Software/src/battery/BATTERIES.cpp ../Software/src/battery/Battery.cpp + ../Software/src/battery/BMW-I3-BATTERY.cpp + ../Software/src/battery/BMW-I3-HTML.cpp + ../Software/src/battery/BMW-IX-BATTERY.cpp + ../Software/src/battery/BMW-IX-HTML.cpp + ../Software/src/battery/BMW-PHEV-BATTERY.cpp + ../Software/src/battery/BMW-SBOX.cpp + ../Software/src/battery/BOLT-AMPERA-BATTERY.cpp + ../Software/src/battery/BYD-ATTO-3-BATTERY.cpp ../Software/src/battery/CanBattery.cpp + ../Software/src/battery/CELLPOWER-BMS.cpp + ../Software/src/battery/CHADEMO-BATTERY.cpp + ../Software/src/battery/CHADEMO-SHUNTS.cpp + ../Software/src/battery/CMFA-EV-BATTERY.cpp + ../Software/src/battery/DALY-BMS.cpp + ../Software/src/battery/ECMP-BATTERY.cpp + ../Software/src/battery/FOXESS-BATTERY.cpp + ../Software/src/battery/GEELY-GEOMETRY-C-BATTERY.cpp + ../Software/src/battery/HYUNDAI-IONIQ-28-BATTERY-HTML.cpp + ../Software/src/battery/HYUNDAI-IONIQ-28-BATTERY.cpp + ../Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp + ../Software/src/battery/JAGUAR-IPACE-BATTERY.cpp + ../Software/src/battery/KIA-E-GMP-BATTERY.cpp + ../Software/src/battery/KIA-E-GMP-HTML.cpp + ../Software/src/battery/KIA-64FD-BATTERY.cpp + ../Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp + ../Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp + ../Software/src/battery/MEB-BATTERY.cpp + ../Software/src/battery/MG-5-BATTERY.cpp + ../Software/src/battery/MG-HS-PHEV-BATTERY.cpp ../Software/src/battery/NISSAN-LEAF-BATTERY.cpp - ../Software/src/inverter/INVERTERS.cpp + ../Software/src/battery/ORION-BMS.cpp + ../Software/src/battery/PYLON-BATTERY.cpp + ../Software/src/battery/RANGE-ROVER-PHEV-BATTERY.cpp + ../Software/src/battery/RELION-LV-BATTERY.cpp + ../Software/src/battery/RENAULT-KANGOO-BATTERY.cpp + ../Software/src/battery/RENAULT-TWIZY.cpp + ../Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp + ../Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp + ../Software/src/battery/RJXZS-BMS.cpp + ../Software/src/battery/SAMSUNG-SDI-LV-BATTERY.cpp + ../Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp + ../Software/src/battery/Shunts.cpp + ../Software/src/battery/SIMPBMS-BATTERY.cpp + ../Software/src/battery/SONO-BATTERY.cpp + ../Software/src/battery/TESLA-BATTERY.cpp + ../Software/src/battery/TEST-FAKE-BATTERY.cpp + ../Software/src/battery/VOLVO-SPA-BATTERY.cpp + ../Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.cpp + ../Software/src/inverter/AFORE-CAN.cpp + ../Software/src/inverter/BYD-CAN.cpp ../Software/src/inverter/BYD-MODBUS.cpp + ../Software/src/inverter/FERROAMP-CAN.cpp + ../Software/src/inverter/FOXESS-CAN.cpp + ../Software/src/inverter/GROWATT-HV-CAN.cpp + ../Software/src/inverter/GROWATT-LV-CAN.cpp + ../Software/src/inverter/GROWATT-WIT-CAN.cpp + ../Software/src/inverter/INVERTERS.cpp + ../Software/src/inverter/KOSTAL-RS485.cpp + ../Software/src/inverter/ModbusInverterProtocol.cpp + ../Software/src/inverter/PYLON-CAN.cpp + ../Software/src/inverter/PYLON-LV-CAN.cpp + ../Software/src/inverter/SCHNEIDER-CAN.cpp + ../Software/src/inverter/SMA-BYD-H-CAN.cpp + ../Software/src/inverter/SMA-BYD-HVS-CAN.cpp + ../Software/src/inverter/SMA-LV-CAN.cpp + ../Software/src/inverter/SMA-TRIPOWER-CAN.cpp + ../Software/src/inverter/SOFAR-CAN.cpp + ../Software/src/inverter/SOL-ARK-LV-CAN.cpp + ../Software/src/inverter/SOLAX-CAN.cpp + ../Software/src/inverter/SOLXPOW-CAN.cpp + ../Software/src/inverter/SUNGROW-CAN.cpp ../Software/src/charger/CHARGERS.cpp + ../Software/src/charger/CHEVY-VOLT-CHARGER.cpp + ../Software/src/charger/NISSAN-LEAF-CHARGER.cpp emul/can.cpp emul/time.cpp emul/serial.cpp diff --git a/test/emul/Arduino.cpp b/test/emul/Arduino.cpp index bdaa38d8..aa0997e0 100644 --- a/test/emul/Arduino.cpp +++ b/test/emul/Arduino.cpp @@ -15,4 +15,22 @@ int max(int a, int b) { return (a > b) ? a : b; } +// Mock implementation for OBD +#include "../../Software/src/communication/can/obd.h" +void handle_obd_frame(CAN_frame& frame) { + (void)frame; +} +void transmit_obd_can_frame(unsigned int address, int interface, bool canFD) { + (void)interface; +} + +void start_bms_reset() {} + +#include "../../Software/src/communication/rs485/comm_rs485.h" + +// Mock implementation +void register_receiver(Rs485Receiver* receiver) { + (void)receiver; // Silence unused parameter warning +} + ESPClass ESP; diff --git a/test/emul/Arduino.h b/test/emul/Arduino.h index 5abc6edb..149191af 100644 --- a/test/emul/Arduino.h +++ b/test/emul/Arduino.h @@ -6,15 +6,97 @@ #include #include +#include +#include +#include +#include +#include + #include "HardwareSerial.h" #include "Logging.h" #include "Print.h" #include "esp-hal-gpio.h" +// Arduino base constants for print formatting +constexpr int BIN = 2; +constexpr int OCT = 8; +constexpr int DEC = 10; +constexpr int HEX = 16; +// Arduino type aliases +using byte = uint8_t; +#define boolean bool +// Arduino random functions +inline long random(long max) { + (void)max; + return 0; // Return a predictable value for testing +} + +inline long random(long min, long max) { + (void)min; + (void)max; + return min; // Return the minimum value for predictability +} + +// Also add randomSeed for completeness +inline void randomSeed(unsigned long seed) { + (void)seed; +} + +inline uint16_t word(uint8_t highByte, uint8_t lowByte) { + return (static_cast(highByte) << 8) | lowByte; +} + +inline uint16_t word(uint16_t w) { + return w; +} + +// Bit manipulation functions +inline uint8_t bitRead(uint8_t value, uint8_t bit) { + return (value >> bit) & 0x01; +} + +inline void bitSet(uint8_t& value, uint8_t bit) { + value |= (1UL << bit); +} + +inline void bitClear(uint8_t& value, uint8_t bit) { + value &= ~(1UL << bit); +} + +inline void bitWrite(uint8_t& value, uint8_t bit, uint8_t bitvalue) { + if (bitvalue) { + bitSet(value, bit); + } else { + bitClear(value, bit); + } +} + +// Byte extraction functions +inline uint8_t lowByte(uint16_t w) { + return static_cast(w & 0xFF); +} + +inline uint8_t highByte(uint16_t w) { + return static_cast(w >> 8); +} + +template +inline const T& min(const T& a, const T& b) { + return (a < b) ? a : b; +} + +template +inline const T& max(const T& a, const T& b) { + return (a > b) ? a : b; +} void pinMode(uint8_t pin, uint8_t mode); void digitalWrite(uint8_t pin, uint8_t val); int digitalRead(uint8_t pin); +inline int analogRead(uint8_t pin) { + (void)pin; + return 0; // Return 0 for predictable tests +} unsigned long micros(); // Can be previously declared as a macro in stupid eModbus diff --git a/test/emul/HardwareSerial.h b/test/emul/HardwareSerial.h index e20000a2..d53f9d5a 100644 --- a/test/emul/HardwareSerial.h +++ b/test/emul/HardwareSerial.h @@ -35,17 +35,28 @@ enum SerialConfig { class HardwareSerial : public Stream { public: - int available() { return 0; } + // Implement ALL pure virtual functions from base classes + int available() override { return 0; } + int read() override { return -1; } + int peek() override { return -1; } + void flush() override {} // Implement flush from Print + size_t write(uint8_t) override { return 0; } // Implement write from Print + + // Your existing methods uint32_t baudRate() { return 9600; } void begin(unsigned long baud, uint32_t config = SERIAL_8N1, int8_t rxPin = -1, int8_t txPin = -1, bool invert = false, unsigned long timeout_ms = 20000UL, uint8_t rxfifo_full_thrhd = 120) {} - int read() { return 0; } void setTxBufferSize(uint16_t size) {} void setRxBufferSize(uint16_t size) {} bool setRxFIFOFull(uint8_t fifoBytes) { return false; } - size_t write(uint8_t) { return 0; } -}; + // Add the buffer write method + size_t write(const uint8_t* buffer, size_t size) override { + (void)buffer; + (void)size; + return 0; + } +}; extern HardwareSerial Serial; extern HardwareSerial Serial1; extern HardwareSerial Serial2; diff --git a/test/emul/Print.h b/test/emul/Print.h index eb715272..256f82d8 100644 --- a/test/emul/Print.h +++ b/test/emul/Print.h @@ -5,7 +5,7 @@ class Print { public: virtual void flush() {} void printf(const char* format, ...) {} - virtual size_t write(uint8_t) = 0; + virtual size_t write(uint8_t) { return 0; } virtual size_t write(const char* s) { return 0; } virtual size_t write(const uint8_t* buffer, size_t size) { return 0; } }; diff --git a/test/emul/Stream.h b/test/emul/Stream.h index c1847603..774bca43 100644 --- a/test/emul/Stream.h +++ b/test/emul/Stream.h @@ -5,8 +5,10 @@ class Stream : public Print { public: - virtual int available() = 0; - virtual int read() = 0; + virtual int available() { return 0; } + virtual int read() { return -1; } + virtual int peek() { return -1; } + // flush() is already inherited from Print }; #endif From 6f76bdfeb08cbf7133426f667fdb726c1cdebd94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Mon, 1 Sep 2025 21:30:48 +0300 Subject: [PATCH 109/245] Restore HEX printing --- Software/src/battery/BMW-PHEV-BATTERY.cpp | 4 ++-- Software/src/battery/DALY-BMS.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Software/src/battery/BMW-PHEV-BATTERY.cpp b/Software/src/battery/BMW-PHEV-BATTERY.cpp index 6e40142e..d199ed13 100644 --- a/Software/src/battery/BMW-PHEV-BATTERY.cpp +++ b/Software/src/battery/BMW-PHEV-BATTERY.cpp @@ -513,7 +513,7 @@ void BmwPhevBattery::handle_incoming_can_frame(CAN_frame rx_frame) { if (gUDSContext.UDS_buffer[i] < 0x10) { logging.printf("0"); } - logging.print(gUDSContext.UDS_buffer[i]); + logging.print(gUDSContext.UDS_buffer[i], HEX); logging.printf(" "); } logging.println(""); // new line at the end @@ -538,7 +538,7 @@ void BmwPhevBattery::handle_incoming_can_frame(CAN_frame rx_frame) { if (gUDSContext.UDS_buffer[i] < 0x10) { logging.printf("0"); } - logging.print(gUDSContext.UDS_buffer[i]); + logging.print(gUDSContext.UDS_buffer[i], HEX); logging.printf(" "); } logging.println(); // new line at the end diff --git a/Software/src/battery/DALY-BMS.cpp b/Software/src/battery/DALY-BMS.cpp index bf9e00b8..e42b222a 100644 --- a/Software/src/battery/DALY-BMS.cpp +++ b/Software/src/battery/DALY-BMS.cpp @@ -112,8 +112,8 @@ void dump_buff(const char* msg, uint8_t* buff, uint8_t len) { logging.printf("[DALY-BMS] "); logging.printf(msg); for (int i = 0; i < len; i++) { - logging.print(buff[i] >> 4); - logging.print(buff[i] & 0xf); + logging.print(buff[i] >> 4, HEX); + logging.print(buff[i] & 0xf, HEX); logging.printf(" "); } logging.println(); From dabbcd8bcdafd76d4968dcaeebb18b1c0bdd3b3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Mon, 1 Sep 2025 22:03:00 +0300 Subject: [PATCH 110/245] Make LEAF interlock setting configurable --- Software/USER_SETTINGS.h | 6 ------ Software/src/battery/BATTERIES.cpp | 2 ++ Software/src/battery/BATTERIES.h | 1 + Software/src/battery/NISSAN-LEAF-BATTERY.cpp | 13 +++++++------ Software/src/battery/NISSAN-LEAF-BATTERY.h | 2 ++ Software/src/communication/nvm/comm_nvm.cpp | 1 + Software/src/devboard/utils/types.h | 2 +- .../src/devboard/webserver/settings_html.cpp | 19 +++++++++++++++++-- Software/src/devboard/webserver/webserver.cpp | 2 +- 9 files changed, 32 insertions(+), 16 deletions(-) diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index 9e8283a0..099842bd 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -15,18 +15,12 @@ /* Shunt/Contactor settings (Optional) */ //#define BMW_SBOX // SBOX relay control & battery current/voltage measurement -/* Select charger used (Optional) */ -//#define CHEVYVOLT_CHARGER //Enable this line to control a Chevrolet Volt charger connected to battery - for example, when generator charging or using an inverter without a charging function. -//#define NISSANLEAF_CHARGER //Enable this line to control a Nissan LEAF PDM connected to battery - for example, when generator charging - /* Automatic Precharge settings (Optional) If you have a battery that expects an external voltage applied before opening contactors (within the battery), configure this section */ //#define PRECHARGE_CONTROL //Enable this line to control a modified HIA4V1 via PWM on the HIA4V1_PIN (see Wiki and HAL for pin definition) //#define INVERTER_DISCONNECT_CONTACTOR_IS_NORMALLY_OPEN //Enable this line if you use a normally open contactor instead of normally closed /* Other options */ //#define EQUIPMENT_STOP_BUTTON // Enable this to allow an equipment stop button connected to the Battery-Emulator to disengage the battery -//#define LFP_CHEMISTRY //Tesla specific setting, enable this line to startup in LFP mode -//#define INTERLOCK_REQUIRED //Nissan LEAF specific setting, if enabled requires both high voltage conenctors to be seated before starting /* CAN options */ //#define CAN_ADDON //Enable this line to activate an isolated secondary CAN Bus using add-on MCP2515 chip (Needed for some inverters / double battery) #define CRYSTAL_FREQUENCY_MHZ 8 //CAN_ADDON option, what is your MCP2515 add-on boards crystal frequency? diff --git a/Software/src/battery/BATTERIES.cpp b/Software/src/battery/BATTERIES.cpp index b24751ab..f4639e26 100644 --- a/Software/src/battery/BATTERIES.cpp +++ b/Software/src/battery/BATTERIES.cpp @@ -279,6 +279,8 @@ void setup_battery() { } } +/* User-selected Nissan LEAF settings */ +bool user_selected_LEAF_interlock_mandatory = false; /* User-selected Tesla settings */ bool user_selected_tesla_digital_HVIL = false; uint16_t user_selected_tesla_GTW_country = 17477; diff --git a/Software/src/battery/BATTERIES.h b/Software/src/battery/BATTERIES.h index 78e55398..22741186 100644 --- a/Software/src/battery/BATTERIES.h +++ b/Software/src/battery/BATTERIES.h @@ -62,6 +62,7 @@ extern uint16_t user_selected_min_pack_voltage_dV; extern uint16_t user_selected_max_cell_voltage_mV; extern uint16_t user_selected_min_cell_voltage_mV; +extern bool user_selected_LEAF_interlock_mandatory; extern bool user_selected_tesla_digital_HVIL; extern uint16_t user_selected_tesla_GTW_country; extern bool user_selected_tesla_GTW_rightHandDrive; diff --git a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp index 17e4f3d3..2bebb92f 100644 --- a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp +++ b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp @@ -150,13 +150,14 @@ void NissanLeafBattery:: clear_event(EVENT_BATTERY_CHG_DISCHG_STOP_REQ); } -#ifdef INTERLOCK_REQUIRED - if (!battery_Interlock) { - set_event(EVENT_HVIL_FAILURE, 0); - } else { - clear_event(EVENT_HVIL_FAILURE); + if (user_selected_LEAF_interlock_mandatory) { + //If user requires both large 80kW and small 6kW interlock to be seated for operation + if (!battery_Interlock) { + set_event(EVENT_HVIL_FAILURE, 0); + } else { + clear_event(EVENT_HVIL_FAILURE); + } } -#endif if (battery_HeatExist) { if (battery_Heating_Stop) { diff --git a/Software/src/battery/NISSAN-LEAF-BATTERY.h b/Software/src/battery/NISSAN-LEAF-BATTERY.h index 99a5691b..c6a5f868 100644 --- a/Software/src/battery/NISSAN-LEAF-BATTERY.h +++ b/Software/src/battery/NISSAN-LEAF-BATTERY.h @@ -6,6 +6,8 @@ #include "CanBattery.h" #include "NISSAN-LEAF-HTML.h" +extern bool user_selected_LEAF_interlock_mandatory; + class NissanLeafBattery : public CanBattery { public: // Use this constructor for the second battery. diff --git a/Software/src/communication/nvm/comm_nvm.cpp b/Software/src/communication/nvm/comm_nvm.cpp index 9e72d8cc..58ca7745 100644 --- a/Software/src/communication/nvm/comm_nvm.cpp +++ b/Software/src/communication/nvm/comm_nvm.cpp @@ -105,6 +105,7 @@ void init_stored_settings() { user_selected_inverter_battery_type = settings.getUInt("INVBTYPE", 0); user_selected_inverter_ignore_contactors = settings.getBool("INVICNT", false); user_selected_can_addon_crystal_frequency_mhz = settings.getUInt("CANFREQ", 8); + user_selected_LEAF_interlock_mandatory = settings.getBool("INTERLOCKREQ", false); user_selected_tesla_digital_HVIL = settings.getBool("DIGITALHVIL", false); user_selected_tesla_GTW_country = settings.getUInt("GTWCOUNTRY", 0); user_selected_tesla_GTW_rightHandDrive = settings.getBool("GTWRHD", false); diff --git a/Software/src/devboard/utils/types.h b/Software/src/devboard/utils/types.h index 846d6c43..fab3d4d9 100644 --- a/Software/src/devboard/utils/types.h +++ b/Software/src/devboard/utils/types.h @@ -9,7 +9,7 @@ using duration = std::chrono::duration>; enum bms_status_enum { STANDBY = 0, INACTIVE = 1, DARKSTART = 2, ACTIVE = 3, FAULT = 4, UPDATING = 5 }; enum real_bms_status_enum { BMS_DISCONNECTED = 0, BMS_STANDBY = 1, BMS_ACTIVE = 2, BMS_FAULT = 3 }; -enum battery_chemistry_enum { NCA = 1, NMC = 2, LFP = 3, Highest }; +enum battery_chemistry_enum { Autodetect = 0, NCA = 1, NMC = 2, LFP = 3, Highest }; enum class comm_interface { Modbus = 1, diff --git a/Software/src/devboard/webserver/settings_html.cpp b/Software/src/devboard/webserver/settings_html.cpp index 7414d53f..0517962b 100644 --- a/Software/src/devboard/webserver/settings_html.cpp +++ b/Software/src/devboard/webserver/settings_html.cpp @@ -203,8 +203,9 @@ String settings_processor(const String& var, BatteryEmulatorSettingsStore& setti name_for_comm_interface); } if (var == "BATTCHEM") { - return options_for_enum((battery_chemistry_enum)settings.getUInt("BATTCHEM", (int)battery_chemistry_enum::NCA), - name_for_chemistry); + return options_for_enum( + (battery_chemistry_enum)settings.getUInt("BATTCHEM", (int)battery_chemistry_enum::Autodetect), + name_for_chemistry); } if (var == "INVTYPE") { return options_for_enum_with_none( @@ -563,6 +564,10 @@ String settings_processor(const String& var, BatteryEmulatorSettingsStore& setti return String(settings.getUInt("PWMHOLD", 250)); } + if (var == "INTERLOCKREQ") { + return settings.getBool("INTERLOCKREQ") ? "checked" : ""; + } + if (var == "DIGITALHVIL") { return settings.getBool("DIGITALHVIL") ? "checked" : ""; } @@ -777,6 +782,11 @@ const char* getCANInterfaceName(CAN_Interface interface) { display: contents; } + form .if-nissan { display: none; } + form[data-battery="21"] .if-nissan { + display: contents; + } + form .if-tesla { display: none; } form[data-battery="32"] .if-tesla, form[data-battery="33"] .if-tesla { display: contents; @@ -841,6 +851,11 @@ const char* getCANInterfaceName(CAN_Interface interface) { %BATTTYPE% +
+ + +
+
diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 70c862e4..fe71aa61 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -398,7 +398,7 @@ void init_webserver() { const char* boolSettingNames[] = { "DBLBTR", "CNTCTRL", "CNTCTRLDBL", "PWMCNTCTRL", "PERBMSRESET", "SDLOGENABLED", "REMBMSRESET", "USBENABLED", "CANLOGUSB", "WEBENABLED", "CANFDASCAN", "CANLOGSD", "WIFIAPENABLED", "MQTTENABLED", - "HADISC", "MQTTTOPICS", "INVICNT", "GTWRHD", "DIGITALHVIL", "PERFPROFILE", + "HADISC", "MQTTTOPICS", "INVICNT", "GTWRHD", "DIGITALHVIL", "PERFPROFILE", "INTERLOCKREQ", }; // Handles the form POST from UI to save settings of the common image From 688ba0388c043ffd9f35cb30880a3158fe9314a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Mon, 1 Sep 2025 23:12:21 +0300 Subject: [PATCH 111/245] Remove more USER_SETTINGS --- Software/USER_SETTINGS.h | 27 +-------------------- Software/src/battery/DALY-BMS.cpp | 1 - Software/src/battery/MG-HS-PHEV-BATTERY.cpp | 1 - Software/src/communication/can/comm_can.cpp | 16 +++--------- Software/src/communication/can/comm_can.h | 4 +++ Software/src/datalayer/datalayer.h | 23 ++++++++++-------- Software/src/inverter/BYD-CAN.cpp | 3 +++ Software/src/inverter/BYD-CAN.h | 6 +++-- 8 files changed, 28 insertions(+), 53 deletions(-) diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index 099842bd..f6c06dd7 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -7,7 +7,7 @@ /* This file is being transitioned towards COMMON_IMAGE. Use v8.16 if you are taking this software into use! */ /* Select hardware used for Battery-Emulator */ -//#define HW_LILYGO +#define HW_LILYGO //#define HW_STARK //#define HW_3LB //#define HW_DEVKIT @@ -19,18 +19,7 @@ //#define PRECHARGE_CONTROL //Enable this line to control a modified HIA4V1 via PWM on the HIA4V1_PIN (see Wiki and HAL for pin definition) //#define INVERTER_DISCONNECT_CONTACTOR_IS_NORMALLY_OPEN //Enable this line if you use a normally open contactor instead of normally closed -/* Other options */ -//#define EQUIPMENT_STOP_BUTTON // Enable this to allow an equipment stop button connected to the Battery-Emulator to disengage the battery -/* CAN options */ -//#define CAN_ADDON //Enable this line to activate an isolated secondary CAN Bus using add-on MCP2515 chip (Needed for some inverters / double battery) -#define CRYSTAL_FREQUENCY_MHZ 8 //CAN_ADDON option, what is your MCP2515 add-on boards crystal frequency? -//#define CANFD_ADDON //Enable this line to activate an isolated secondary CAN-FD bus using add-on MCP2518FD chip / Native CANFD on Stark board -#define CANFD_ADDON_CRYSTAL_FREQUENCY_MHZ \ - ACAN2517FDSettings::OSC_40MHz //CANFD_ADDON option, what is your MCP2518 add-on boards crystal frequency? -//#define USE_CANFD_INTERFACE_AS_CLASSIC_CAN // Enable this line if you intend to use the CANFD as normal CAN - /* Connectivity options */ -#define WIFI //#define WIFICONFIG //Enable this line to set a static IP address / gateway /subnet mask for the device. see USER_SETTINGS.cpp for the settings //#define CUSTOM_HOSTNAME \ "battery-emulator" //Enable this line to use a custom hostname for the device, if disabled the default naming format 'esp32-XXXXXX' will be used. @@ -50,18 +39,6 @@ // This naming convention was in place until version 7.5.0. Users should check the version from which they are updating, as this change // may break compatibility with previous versions of MQTT naming. Please refer to USER_SETTINGS.cpp for configuration options. -/* Home Assistant options */ -#define HA_AUTODISCOVERY // Enable this line to send Home Assistant autodiscovery messages. If not enabled manual configuration of Home Assitant is required - -/* Battery settings */ -// Predefined total energy capacity of the battery in Watt-hours (updates automatically from battery data when available) -#define BATTERY_WH_MAX 30000 -// Increases battery life. If true will rescale SOC between the configured min/max-percentage -#define BATTERY_USE_SCALED_SOC true -// 8000 = 80.0% , Max percentage the battery will charge to (Inverter gets 100% when reached) -#define BATTERY_MAXPERCENTAGE 8000 -// 2000 = 20.0% , Min percentage the battery will discharge to (Inverter gets 0% when reached) -#define BATTERY_MINPERCENTAGE 2000 // 500 = 50.0 °C , Max temperature (Will produce a battery overheat event if above) #define BATTERY_MAXTEMPERATURE 500 // -250 = -25.0 °C , Min temperature (Will produce a battery frozen event if below) @@ -98,8 +75,6 @@ extern volatile float CHARGER_MAX_POWER; extern volatile float CHARGER_MAX_A; extern volatile float CHARGER_END_A; -extern volatile unsigned long long bmsResetTimeOffset; - #include "src/communication/equipmentstopbutton/comm_equipmentstopbutton.h" // Equipment stop button behavior. Use NC button for safety reasons. diff --git a/Software/src/battery/DALY-BMS.cpp b/Software/src/battery/DALY-BMS.cpp index e42b222a..9cbeb794 100644 --- a/Software/src/battery/DALY-BMS.cpp +++ b/Software/src/battery/DALY-BMS.cpp @@ -72,7 +72,6 @@ void DalyBms::setup(void) { // Performs one time setup at startup datalayer.battery.info.min_design_voltage_dV = user_selected_min_pack_voltage_dV; datalayer.battery.info.max_cell_voltage_mV = user_selected_max_cell_voltage_mV; datalayer.battery.info.min_cell_voltage_mV = user_selected_min_cell_voltage_mV; - datalayer.battery.info.total_capacity_Wh = BATTERY_WH_MAX; datalayer.system.status.battery_allows_contactor_closing = true; auto rx_pin = esp32hal->RS485_RX_PIN(); diff --git a/Software/src/battery/MG-HS-PHEV-BATTERY.cpp b/Software/src/battery/MG-HS-PHEV-BATTERY.cpp index 57d8476a..daf7485f 100644 --- a/Software/src/battery/MG-HS-PHEV-BATTERY.cpp +++ b/Software/src/battery/MG-HS-PHEV-BATTERY.cpp @@ -373,6 +373,5 @@ void MgHsPHEVBattery::setup(void) { // Performs one time setup at startup datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; - datalayer.battery.info.total_capacity_Wh = BATTERY_WH_MAX; datalayer.battery.info.number_of_cells = 90; } diff --git a/Software/src/communication/can/comm_can.cpp b/Software/src/communication/can/comm_can.cpp index 003e1988..eadb75d8 100644 --- a/Software/src/communication/can/comm_can.cpp +++ b/Software/src/communication/can/comm_can.cpp @@ -19,18 +19,9 @@ struct CanReceiverRegistration { static std::multimap can_receivers; -const uint8_t rx_queue_size = 10; // Receive Queue size volatile bool send_ok_native = 0; volatile bool send_ok_2515 = 0; volatile bool send_ok_2518 = 0; -static unsigned long previousMillis10 = 0; - -#ifdef USE_CANFD_INTERFACE_AS_CLASSIC_CAN -const bool use_canfd_as_can_default = true; -#else -const bool use_canfd_as_can_default = false; -#endif -bool use_canfd_as_can = use_canfd_as_can_default; void map_can_frame_to_variable(CAN_frame* rx_frame, CAN_Interface interface); @@ -41,10 +32,9 @@ void register_can_receiver(CanReceiver* receiver, CAN_Interface interface, CAN_S ACAN_ESP32_Settings* settingsespcan; -uint8_t user_selected_can_addon_crystal_frequency_mhz = 0; static uint32_t QUARTZ_FREQUENCY; SPIClass SPI2515; - +uint8_t user_selected_can_addon_crystal_frequency_mhz = 0; ACAN2515* can2515; ACAN2515Settings* settings2515; @@ -53,7 +43,7 @@ static ACAN2515_Buffer16 gBuffer; SPIClass SPI2517; ACAN2517FD* canfd; ACAN2517FDSettings* settings2517; - +bool use_canfd_as_can = false; // Initialization functions bool native_can_initialized = false; @@ -190,7 +180,7 @@ bool init_CAN() { CANFD_ADDON_CRYSTAL_FREQUENCY_MHZ, bitRate, DataBitRateFactor::x4); // Arbitration bit rate: 250/500 kbit/s, data bit rate: 1/2 Mbit/s - // ListenOnly / Normal20B / NormalFD + // ListenOnly / Normal20B / NormalFDs settings2517->mRequestedMode = use_canfd_as_can ? ACAN2517FDSettings::Normal20B : ACAN2517FDSettings::NormalFD; const uint32_t errorCode2517 = canfd->begin(*settings2517, [] { canfd->isr(); }); diff --git a/Software/src/communication/can/comm_can.h b/Software/src/communication/can/comm_can.h index 2b7ece97..91e58017 100644 --- a/Software/src/communication/can/comm_can.h +++ b/Software/src/communication/can/comm_can.h @@ -9,6 +9,10 @@ extern uint8_t user_selected_can_addon_crystal_frequency_mhz; void dump_can_frame(CAN_frame& frame, frameDirection msgDir); void transmit_can_frame_to_interface(const CAN_frame* tx_frame, int interface); +//These defines are not used if user updates values via Settings page +#define CRYSTAL_FREQUENCY_MHZ 8 +#define CANFD_ADDON_CRYSTAL_FREQUENCY_MHZ ACAN2517FDSettings::OSC_40MHz + class CanReceiver; enum class CAN_Speed { diff --git a/Software/src/datalayer/datalayer.h b/Software/src/datalayer/datalayer.h index 83649469..6ac56088 100644 --- a/Software/src/datalayer/datalayer.h +++ b/Software/src/datalayer/datalayer.h @@ -7,9 +7,11 @@ struct DATALAYER_BATTERY_INFO_TYPE { /** uint32_t */ - /** Total energy capacity in Watt-hours */ - uint32_t total_capacity_Wh = BATTERY_WH_MAX; - uint32_t reported_total_capacity_Wh = BATTERY_WH_MAX; + /** Total energy capacity in Watt-hours + * Automatically updates depending on battery integration OR from settings page + */ + uint32_t total_capacity_Wh = 30000; + uint32_t reported_total_capacity_Wh = 30000; /** uint16_t */ /** The maximum intended packvoltage, in deciVolt. 4900 = 490.0 V */ @@ -28,7 +30,7 @@ struct DATALAYER_BATTERY_INFO_TYPE { uint8_t number_of_cells; /** Other */ - /** Chemistry of the pack. NCA, NMC or LFP (so far) */ + /** Chemistry of the pack. Autodetect, or force specific chemistry */ battery_chemistry_enum chemistry = battery_chemistry_enum::NCA; }; @@ -114,16 +116,17 @@ struct DATALAYER_BATTERY_STATUS_TYPE { }; struct DATALAYER_BATTERY_SETTINGS_TYPE { - /** SOC scaling setting. Set to true to use SOC scaling */ - bool soc_scaling_active = BATTERY_USE_SCALED_SOC; + /** SOC scaling setting. Increases battery life. + * If true will rescale SOC between the configured min/max-percentage */ + bool soc_scaling_active = true; /** Minimum percentage setting. Set this value to the lowest real SOC * you want the inverter to be able to use. At this real SOC, the inverter - * will "see" 0% */ - int16_t min_percentage = BATTERY_MINPERCENTAGE; + * will "see" 0% , Example 2000 = 20.0%*/ + uint16_t min_percentage = 2000; /** Maximum percentage setting. Set this value to the highest real SOC * you want the inverter to be able to use. At this real SOC, the inverter - * will "see" 100% */ - uint16_t max_percentage = BATTERY_MAXPERCENTAGE; + * will "see" 100% Example 8000 = 80.0%*/ + uint16_t max_percentage = 8000; /** The user specified maximum allowed charge rate, in deciAmpere. 300 = 30.0 A */ uint16_t max_user_set_charge_dA = BATTERY_MAX_CHARGE_AMP; diff --git a/Software/src/inverter/BYD-CAN.cpp b/Software/src/inverter/BYD-CAN.cpp index 0e9687b2..a779a270 100644 --- a/Software/src/inverter/BYD-CAN.cpp +++ b/Software/src/inverter/BYD-CAN.cpp @@ -85,6 +85,9 @@ void BydCanInverter:: //Temperature min BYD_210.data.u8[2] = (datalayer.battery.status.temperature_min_dC >> 8); BYD_210.data.u8[3] = (datalayer.battery.status.temperature_min_dC & 0x00FF); + //Capacity + BYD_250.data.u8[4] = (uint8_t)((datalayer.battery.info.reported_total_capacity_Wh / 100) >> 8); + BYD_250.data.u8[5] = (uint8_t)(datalayer.battery.info.reported_total_capacity_Wh / 100); } void BydCanInverter::map_can_frame_to_variable(CAN_frame rx_frame) { diff --git a/Software/src/inverter/BYD-CAN.h b/Software/src/inverter/BYD-CAN.h index 41f7f644..b61d0a5c 100644 --- a/Software/src/inverter/BYD-CAN.h +++ b/Software/src/inverter/BYD-CAN.h @@ -6,6 +6,7 @@ #endif #include "../../USER_SETTINGS.h" +#include "../datalayer/datalayer.h" #include "CanInverterProtocol.h" class BydCanInverter : public CanInverterProtocol { @@ -30,8 +31,9 @@ class BydCanInverter : public CanInverterProtocol { .ext_ID = false, .DLC = 8, .ID = 0x250, - .data = {FW_MAJOR_VERSION, FW_MINOR_VERSION, 0x00, 0x66, (uint8_t)((BATTERY_WH_MAX / 100) >> 8), - (uint8_t)(BATTERY_WH_MAX / 100), 0x02, + .data = {FW_MAJOR_VERSION, FW_MINOR_VERSION, 0x00, 0x66, + (uint8_t)((datalayer.battery.info.reported_total_capacity_Wh / 100) >> 8), + (uint8_t)(datalayer.battery.info.reported_total_capacity_Wh / 100), 0x02, 0x09}}; //0-1 FW version , Capacity kWh byte4&5 (example 24kWh = 240) CAN_frame BYD_290 = {.FD = false, .ext_ID = false, From 7328f7b99b1b195ad7f45e24582129096a5e81ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Mon, 1 Sep 2025 23:22:34 +0300 Subject: [PATCH 112/245] Remove even more USER_SETTINGS --- Software/USER_SETTINGS.h | 13 +---------- Software/src/communication/nvm/comm_nvm.cpp | 7 +----- Software/src/devboard/mqtt/mqtt.cpp | 12 ++++------ Software/src/devboard/wifi/wifi.cpp | 26 ++++----------------- 4 files changed, 11 insertions(+), 47 deletions(-) diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index f6c06dd7..3495ae44 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -7,7 +7,7 @@ /* This file is being transitioned towards COMMON_IMAGE. Use v8.16 if you are taking this software into use! */ /* Select hardware used for Battery-Emulator */ -#define HW_LILYGO +//#define HW_LILYGO //#define HW_STARK //#define HW_3LB //#define HW_DEVKIT @@ -21,23 +21,12 @@ /* Connectivity options */ //#define WIFICONFIG //Enable this line to set a static IP address / gateway /subnet mask for the device. see USER_SETTINGS.cpp for the settings -//#define CUSTOM_HOSTNAME \ - "battery-emulator" //Enable this line to use a custom hostname for the device, if disabled the default naming format 'esp32-XXXXXX' will be used. -#define WEBSERVER //Enable this line to enable WiFi, and to run the webserver. See USER_SETTINGS.cpp for the Wifi settings. -#define WIFIAP //When enabled, the emulator will broadcast its own access point Wifi. Can be used at the same time as a normal Wifi connection to a router. -#define MDNSRESPONDER //Enable this line to enable MDNS, allows battery monitor te be found by .local address. Requires WEBSERVER to be enabled. -#define LOAD_SAVED_SETTINGS_ON_BOOT // Enable this line to read settings stored via the webserver on boot (overrides Wifi credentials set here) /* MQTT options */ // #define MQTT // Enable this line to enable MQTT #define MQTT_QOS 0 // MQTT Quality of Service (0, 1, or 2) #define MQTT_PUBLISH_CELL_VOLTAGES // Enable this line to publish cell voltages to MQTT #define MQTT_TIMEOUT 2000 // MQTT timeout in milliseconds -#define MQTT_MANUAL_TOPIC_OBJECT_NAME -// Enable MQTT_MANUAL_TOPIC_OBJECT_NAME to use custom MQTT topic, object ID prefix, and device name. -// WARNING: If this is not defined, the previous default naming format 'battery-emulator_esp32-XXXXXX' (based on hardware ID) will be used. -// This naming convention was in place until version 7.5.0. Users should check the version from which they are updating, as this change -// may break compatibility with previous versions of MQTT naming. Please refer to USER_SETTINGS.cpp for configuration options. // 500 = 50.0 °C , Max temperature (Will produce a battery overheat event if above) #define BATTERY_MAXTEMPERATURE 500 diff --git a/Software/src/communication/nvm/comm_nvm.cpp b/Software/src/communication/nvm/comm_nvm.cpp index 58ca7745..8869fc15 100644 --- a/Software/src/communication/nvm/comm_nvm.cpp +++ b/Software/src/communication/nvm/comm_nvm.cpp @@ -26,12 +26,7 @@ void init_stored_settings() { set_event(EVENT_EQUIPMENT_STOP, 1); } -#ifndef LOAD_SAVED_SETTINGS_ON_BOOT - settings.clear(); // If this clear function is executed, no settings will be read from storage - - //always save the equipment stop status - settings.putBool("EQUIPMENT_STOP", datalayer.system.settings.equipment_stop_active); -#endif // LOAD_SAVED_SETTINGS_ON_BOOT + //settings.clear(); // If this clear function is executed, no settings will be read from storage. For dev esp32hal->set_default_configuration_values(); diff --git a/Software/src/devboard/mqtt/mqtt.cpp b/Software/src/devboard/mqtt/mqtt.cpp index 108baf6d..a031f1a5 100644 --- a/Software/src/devboard/mqtt/mqtt.cpp +++ b/Software/src/devboard/mqtt/mqtt.cpp @@ -23,13 +23,11 @@ const char* mqtt_server_default = ""; int mqtt_port = mqtt_port_default; std::string mqtt_server = mqtt_server_default; -#ifdef MQTT_MANUAL_TOPIC_OBJECT_NAME -const bool mqtt_manual_topic_object_name_default = true; -#else -const bool mqtt_manual_topic_object_name_default = false; -#endif - -bool mqtt_manual_topic_object_name = mqtt_manual_topic_object_name_default; +bool mqtt_manual_topic_object_name = + true; //TODO, should this be configurable from webserver? Or legacy option removed? +// If this is not true, the previous default naming format 'battery-emulator_esp32-XXXXXX' (based on hardware ID) will be used. +// This naming convention was in place until version 7.5.0. Users should check the version from which they are updating, as this change +// may break compatibility with previous versions of MQTT naming esp_mqtt_client_config_t mqtt_cfg; esp_mqtt_client_handle_t client; diff --git a/Software/src/devboard/wifi/wifi.cpp b/Software/src/devboard/wifi/wifi.cpp index 44adc907..869d863f 100644 --- a/Software/src/devboard/wifi/wifi.cpp +++ b/Software/src/devboard/wifi/wifi.cpp @@ -4,29 +4,11 @@ #include "../utils/logging.h" #include "USER_SETTINGS.h" -#if defined(WIFI) || defined(WEBSERVER) -const bool wifi_enabled_default = true; -#else -const bool wifi_enabled_default = false; -#endif - -bool wifi_enabled = wifi_enabled_default; - -bool wifiap_enabled = true; //Old method was with ifdef - -#ifdef MDNSRESPONDER -const bool mdns_enabled_default = true; -#else -const bool mdns_enabled_default = false; -#endif -bool mdns_enabled = mdns_enabled_default; - -#ifdef CUSTOM_HOSTNAME -std::string custom_hostname = CUSTOM_HOSTNAME; -#else -std::string custom_hostname; -#endif +bool wifi_enabled = true; +bool wifiap_enabled = true; +bool mdns_enabled = true; //If true, allows battery monitor te be found by .local address +std::string custom_hostname; //If not set, the default naming format 'esp32-XXXXXX' will be used std::string ssidAP; // Configuration Parameters From aba193ca85ce39ba67aaa9dafe561d9e4079f253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Mon, 1 Sep 2025 23:41:18 +0300 Subject: [PATCH 113/245] Remove more USER_SETTINGS --- Software/Software.ino | 8 ++++++++ Software/USER_SETTINGS.h | 19 ------------------- Software/src/battery/DALY-BMS.cpp | 5 +++-- Software/src/datalayer/datalayer.h | 18 +++++++++++------- Software/src/devboard/safety/safety.h | 4 +++- 5 files changed, 25 insertions(+), 29 deletions(-) diff --git a/Software/Software.ino b/Software/Software.ino index e26fc42f..9d9f2c62 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -51,6 +51,14 @@ TaskHandle_t connectivity_loop_task; TaskHandle_t logging_loop_task; TaskHandle_t mqtt_loop_task; +/* Charger settings (Optional, when using generator charging) */ +volatile float CHARGER_SET_HV = 384; // Reasonably appropriate 4.0v per cell charging of a 96s pack +volatile float CHARGER_MAX_HV = 420; // Max permissible output (VDC) of charger +volatile float CHARGER_MIN_HV = 200; // Min permissible output (VDC) of charger +volatile float CHARGER_MAX_POWER = 3300; // Max power capable of charger, as a ceiling for validating config +volatile float CHARGER_MAX_A = 11.5; // Max current output (amps) of charger +volatile float CHARGER_END_A = 1.0; // Current at which charging is considered complete + Logging logging; // Initialization diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index 3495ae44..6b199614 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -22,29 +22,10 @@ /* Connectivity options */ //#define WIFICONFIG //Enable this line to set a static IP address / gateway /subnet mask for the device. see USER_SETTINGS.cpp for the settings -/* MQTT options */ -// #define MQTT // Enable this line to enable MQTT #define MQTT_QOS 0 // MQTT Quality of Service (0, 1, or 2) #define MQTT_PUBLISH_CELL_VOLTAGES // Enable this line to publish cell voltages to MQTT #define MQTT_TIMEOUT 2000 // MQTT timeout in milliseconds -// 500 = 50.0 °C , Max temperature (Will produce a battery overheat event if above) -#define BATTERY_MAXTEMPERATURE 500 -// -250 = -25.0 °C , Min temperature (Will produce a battery frozen event if below) -#define BATTERY_MINTEMPERATURE -250 -// 150 = 15.0 °C , Max difference between min and max temperature (Will produce a battery temperature deviation event if greater) -#define BATTERY_MAX_TEMPERATURE_DEVIATION 150 -// 300 = 30.0A , Max charge in Amp (Some inverters needs to be limited) -#define BATTERY_MAX_CHARGE_AMP 300 -// 300 = 30.0A , Max discharge in Amp (Some inverters needs to be limited) -#define BATTERY_MAX_DISCHARGE_AMP 300 -// Enable this to manually set voltage limits on how much battery can be discharged/charged. Normally not used. -#define BATTERY_USE_VOLTAGE_LIMITS false -// 5000 = 500.0V , Target charge voltage (Value can be tuned on the fly via webserver). Not used unless BATTERY_USE_VOLTAGE_LIMITS = true -#define BATTERY_MAX_CHARGE_VOLTAGE 5000 -// 3000 = 300.0V, Target discharge voltage (Value can be tuned on the fly via webserver). Not used unless BATTERY_USE_VOLTAGE_LIMITS = true -#define BATTERY_MAX_DISCHARGE_VOLTAGE 3000 - /* Do not change any code below this line */ /* Only change battery specific settings above and in "USER_SETTINGS.cpp" */ typedef struct { diff --git a/Software/src/battery/DALY-BMS.cpp b/Software/src/battery/DALY-BMS.cpp index 9cbeb794..74f92703 100644 --- a/Software/src/battery/DALY-BMS.cpp +++ b/Software/src/battery/DALY-BMS.cpp @@ -27,8 +27,9 @@ void DalyBms::update_values() { datalayer.battery.status.current_dA = current_dA; //value is *10 (150 = 15.0) datalayer.battery.status.remaining_capacity_Wh = (remaining_capacity_mAh * (uint32_t)voltage_dV) / 10000; - datalayer.battery.status.max_charge_power_W = (BATTERY_MAX_CHARGE_AMP * voltage_dV) / 100; - datalayer.battery.status.max_discharge_power_W = (BATTERY_MAX_DISCHARGE_AMP * voltage_dV) / 100; + datalayer.battery.status.max_charge_power_W = (datalayer.battery.settings.max_user_set_charge_dA * voltage_dV) / 100; + datalayer.battery.status.max_discharge_power_W = + (datalayer.battery.settings.max_user_set_discharge_dA * voltage_dV) / 100; // limit power when SoC is low or high uint32_t adaptive_power_limit = 999999; diff --git a/Software/src/datalayer/datalayer.h b/Software/src/datalayer/datalayer.h index 6ac56088..fddc057c 100644 --- a/Software/src/datalayer/datalayer.h +++ b/Software/src/datalayer/datalayer.h @@ -128,18 +128,22 @@ struct DATALAYER_BATTERY_SETTINGS_TYPE { * will "see" 100% Example 8000 = 80.0%*/ uint16_t max_percentage = 8000; - /** The user specified maximum allowed charge rate, in deciAmpere. 300 = 30.0 A */ - uint16_t max_user_set_charge_dA = BATTERY_MAX_CHARGE_AMP; - /** The user specified maximum allowed discharge rate, in deciAmpere. 300 = 30.0 A */ - uint16_t max_user_set_discharge_dA = BATTERY_MAX_DISCHARGE_AMP; + /** The user specified maximum allowed charge rate, in deciAmpere. 300 = 30.0 A + * Updates later on via Settings + */ + uint16_t max_user_set_charge_dA = 300; + /** The user specified maximum allowed discharge rate, in deciAmpere. 300 = 30.0 A + * Updates later on via Settings + */ + uint16_t max_user_set_discharge_dA = 300; /** User specified discharge/charge voltages in use. Set to true to use user specified values */ /** Some inverters like to see a specific target voltage for charge/discharge. Use these values to override automatic voltage limits*/ - bool user_set_voltage_limits_active = BATTERY_USE_VOLTAGE_LIMITS; + bool user_set_voltage_limits_active = false; /** The user specified maximum allowed charge voltage, in deciVolt. 4000 = 400.0 V */ - uint16_t max_user_set_charge_voltage_dV = BATTERY_MAX_CHARGE_VOLTAGE; + uint16_t max_user_set_charge_voltage_dV = 4500; /** The user specified maximum allowed discharge voltage, in deciVolt. 3000 = 300.0 V */ - uint16_t max_user_set_discharge_voltage_dV = BATTERY_MAX_DISCHARGE_VOLTAGE; + uint16_t max_user_set_discharge_voltage_dV = 3000; /** The user specified BMS reset period. Keeps track on how many milliseconds should we keep power off during daily BMS reset */ uint16_t user_set_bms_reset_duration_ms = 30000; diff --git a/Software/src/devboard/safety/safety.h b/Software/src/devboard/safety/safety.h index c402176c..432e05ce 100644 --- a/Software/src/devboard/safety/safety.h +++ b/Software/src/devboard/safety/safety.h @@ -3,7 +3,9 @@ #include #define MAX_CAN_FAILURES 50 - +#define BATTERY_MAX_TEMPERATURE_DEVIATION 150 // 150 = 15.0 °C +#define BATTERY_MAXTEMPERATURE 500 +#define BATTERY_MINTEMPERATURE -250 #define MAX_CHARGE_DISCHARGE_LIMIT_FAILURES 5 //battery pause status begin From 1cc4a021c261ca92e8a03f005bc9b0f4d37d4376 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Mon, 1 Sep 2025 23:50:17 +0300 Subject: [PATCH 114/245] Convert .ino to .cpp file --- Software/{Software.ino => Software.cpp} | 503 ++++++++++++------------ 1 file changed, 247 insertions(+), 256 deletions(-) rename Software/{Software.ino => Software.cpp} (96%) diff --git a/Software/Software.ino b/Software/Software.cpp similarity index 96% rename from Software/Software.ino rename to Software/Software.cpp index 9d9f2c62..70fd5609 100644 --- a/Software/Software.ino +++ b/Software/Software.cpp @@ -1,5 +1,4 @@ -/* Do not change any code below this line unless you are sure what you are doing */ -/* Only change battery specific settings in "USER_SETTINGS.h" */ +#include #include "HardwareSerial.h" #include "USER_SETTINGS.h" #include "esp_system.h" @@ -51,130 +50,20 @@ TaskHandle_t connectivity_loop_task; TaskHandle_t logging_loop_task; TaskHandle_t mqtt_loop_task; -/* Charger settings (Optional, when using generator charging) */ -volatile float CHARGER_SET_HV = 384; // Reasonably appropriate 4.0v per cell charging of a 96s pack -volatile float CHARGER_MAX_HV = 420; // Max permissible output (VDC) of charger -volatile float CHARGER_MIN_HV = 200; // Min permissible output (VDC) of charger -volatile float CHARGER_MAX_POWER = 3300; // Max power capable of charger, as a ceiling for validating config -volatile float CHARGER_MAX_A = 11.5; // Max current output (amps) of charger -volatile float CHARGER_END_A = 1.0; // Current at which charging is considered complete - Logging logging; -// Initialization -void setup() { - init_hal(); +static std::list transmitters; - init_serial(); - - // We print this after setting up serial, so that is also printed if configured to do so - logging.printf("Battery emulator %s build " __DATE__ " " __TIME__ "\n", version_number); - - init_events(); - - init_stored_settings(); - - if (wifi_enabled) { - xTaskCreatePinnedToCore((TaskFunction_t)&connectivity_loop, "connectivity_loop", 4096, NULL, TASK_CONNECTIVITY_PRIO, - &connectivity_loop_task, esp32hal->WIFICORE()); - } - - if (!led_init()) { - return; - } - - if (datalayer.system.info.CAN_SD_logging_active || datalayer.system.info.SD_logging_active) { - xTaskCreatePinnedToCore((TaskFunction_t)&logging_loop, "logging_loop", 4096, NULL, TASK_CONNECTIVITY_PRIO, - &logging_loop_task, esp32hal->WIFICORE()); - } - - if (!init_contactors()) { - return; - } - - if (!init_precharge_control()) { - return; - } - - setup_charger(); - - if (!setup_inverter()) { - return; - } - setup_battery(); - setup_can_shunt(); - - // Init CAN only after any CAN receivers have had a chance to register. - if (!init_CAN()) { - return; - } - - if (!init_rs485()) { - return; - } - - if (!init_equipment_stop_button()) { - return; - } - - // BOOT button at runtime is used as an input for various things - pinMode(0, INPUT_PULLUP); - - check_reset_reason(); - - // Initialize Task Watchdog for subscribed tasks - esp_task_wdt_config_t wdt_config = {// 5s should be enough for the connectivity tasks (which are all contending - // for the same core) to yield to each other and reset their watchdogs. - .timeout_ms = INTERVAL_5_S, - // We don't benefit from idle task watchdogs, our critical loops have their - // own. The idle watchdogs can cause nuisance reboots under heavy load. - .idle_core_mask = 0, - // Panic (and reboot) on timeout - .trigger_panic = true}; -#ifdef CONFIG_ESP_TASK_WDT - // ESP-IDF will have already initialized it, so reconfigure. - // Arduino and PlatformIO have different watchdog defaults, so we reconfigure - // for consistency. - esp_task_wdt_reconfigure(&wdt_config); -#else - // Otherwise initialize it for the first time. - esp_task_wdt_init(&wdt_config); -#endif - - // Start tasks - - if (mqtt_enabled) { - if (!init_mqtt()) { - return; - } - - xTaskCreatePinnedToCore((TaskFunction_t)&mqtt_loop, "mqtt_loop", 4096, NULL, TASK_MQTT_PRIO, &mqtt_loop_task, - esp32hal->WIFICORE()); - } - - xTaskCreatePinnedToCore((TaskFunction_t)&core_loop, "core_loop", 4096, NULL, TASK_CORE_PRIO, &main_loop_task, - esp32hal->CORE_FUNCTION_CORE()); - - DEBUG_PRINTF("setup() complete\n"); +void register_transmitter(Transmitter* transmitter) { + transmitters.push_back(transmitter); + DEBUG_PRINTF("transmitter registered, total: %d\n", transmitters.size()); } -// Loop empty, all functionality runs in tasks -void loop() {} - -void logging_loop(void*) { - - init_logging_buffers(); - init_sdcard(); - - while (true) { - if (datalayer.system.info.SD_logging_active) { - write_log_to_sdcard(); - } - - if (datalayer.system.info.CAN_SD_logging_active) { - write_can_frame_to_sdcard(); - } - } +// Initialization functions +void init_serial() { + // Init Serial monitor + Serial.begin(115200); + while (!Serial) {} } void connectivity_loop(void*) { @@ -205,146 +94,22 @@ void connectivity_loop(void*) { } } -void mqtt_loop(void*) { - esp_task_wdt_add(NULL); // Register this task with WDT +void logging_loop(void*) { + + init_logging_buffers(); + init_sdcard(); while (true) { - START_TIME_MEASUREMENT(mqtt); - mqtt_loop(); - END_TIME_MEASUREMENT_MAX(mqtt, datalayer.system.status.mqtt_task_10s_max_us); - esp_task_wdt_reset(); // Reset watchdog - delay(1); + if (datalayer.system.info.SD_logging_active) { + write_log_to_sdcard(); + } + + if (datalayer.system.info.CAN_SD_logging_active) { + write_can_frame_to_sdcard(); + } } } -static std::list transmitters; - -void register_transmitter(Transmitter* transmitter) { - transmitters.push_back(transmitter); - DEBUG_PRINTF("transmitter registered, total: %d\n", transmitters.size()); -} - -void core_loop(void*) { - esp_task_wdt_add(NULL); // Register this task with WDT - TickType_t xLastWakeTime = xTaskGetTickCount(); - const TickType_t xFrequency = pdMS_TO_TICKS(1); // Convert 1ms to ticks - - while (true) { - - START_TIME_MEASUREMENT(all); - START_TIME_MEASUREMENT(comm); - - monitor_equipment_stop_button(); - - // Input, Runs as fast as possible - receive_can(); // Receive CAN messages - receive_rs485(); // Process serial2 RS485 interface - - END_TIME_MEASUREMENT_MAX(comm, datalayer.system.status.time_comm_us); - - if (webserver_enabled) { - START_TIME_MEASUREMENT(ota); - ElegantOTA.loop(); - END_TIME_MEASUREMENT_MAX(ota, datalayer.system.status.time_ota_us); - } - - // Process - currentMillis = millis(); - if (currentMillis - previousMillis10ms >= INTERVAL_10_MS) { - if ((currentMillis - previousMillis10ms >= INTERVAL_10_MS_DELAYED) && - (milliseconds(currentMillis) > esp32hal->BOOTUP_TIME())) { - set_event(EVENT_TASK_OVERRUN, (currentMillis - previousMillis10ms)); - } - previousMillis10ms = currentMillis; - if (datalayer.system.info.performance_measurement_active) { - START_TIME_MEASUREMENT(10ms); - } - led_exe(); - handle_contactors(); // Take care of startup precharge/contactor closing -#ifdef PRECHARGE_CONTROL - handle_precharge_control(currentMillis); -#endif // PRECHARGE_CONTROL - if (datalayer.system.info.performance_measurement_active) { - END_TIME_MEASUREMENT_MAX(10ms, datalayer.system.status.time_10ms_us); - } - } - - if (currentMillis - previousMillisUpdateVal >= INTERVAL_1_S) { - previousMillisUpdateVal = currentMillis; // Order matters on the update_loop! - if (datalayer.system.info.performance_measurement_active) { - START_TIME_MEASUREMENT(values); - } - update_pause_state(); // Check if we are OK to send CAN or need to pause - - // Fetch battery values - if (battery) { - battery->update_values(); - } - - if (battery2) { - battery2->update_values(); - check_interconnect_available(); - } - update_calculated_values(); - update_machineryprotection(); // Check safeties - - // Update values heading towards inverter - if (inverter) { - inverter->update_values(); - } - - if (datalayer.system.info.performance_measurement_active) { - END_TIME_MEASUREMENT_MAX(values, datalayer.system.status.time_values_us); - } - } - if (datalayer.system.info.performance_measurement_active) { - START_TIME_MEASUREMENT(cantx); - } - - // Let all transmitter objects send their messages - for (auto& transmitter : transmitters) { - transmitter->transmit(currentMillis); - } - - if (datalayer.system.info.performance_measurement_active) { - END_TIME_MEASUREMENT_MAX(cantx, datalayer.system.status.time_cantx_us); - END_TIME_MEASUREMENT_MAX(all, datalayer.system.status.core_task_10s_max_us); - if (datalayer.system.status.core_task_10s_max_us > datalayer.system.status.core_task_max_us) { - // Update worst case total time - datalayer.system.status.core_task_max_us = datalayer.system.status.core_task_10s_max_us; - // Record snapshots of task times - datalayer.system.status.time_snap_comm_us = datalayer.system.status.time_comm_us; - datalayer.system.status.time_snap_10ms_us = datalayer.system.status.time_10ms_us; - datalayer.system.status.time_snap_values_us = datalayer.system.status.time_values_us; - datalayer.system.status.time_snap_cantx_us = datalayer.system.status.time_cantx_us; - datalayer.system.status.time_snap_ota_us = datalayer.system.status.time_ota_us; - } - - datalayer.system.status.core_task_max_us = - MAX(datalayer.system.status.core_task_10s_max_us, datalayer.system.status.core_task_max_us); - if (core_task_timer_10s.elapsed()) { - datalayer.system.status.time_ota_us = 0; - datalayer.system.status.time_comm_us = 0; - datalayer.system.status.time_10ms_us = 0; - datalayer.system.status.time_values_us = 0; - datalayer.system.status.time_cantx_us = 0; - datalayer.system.status.core_task_10s_max_us = 0; - datalayer.system.status.wifi_task_10s_max_us = 0; - datalayer.system.status.mqtt_task_10s_max_us = 0; - } - } - esp_task_wdt_reset(); // Reset watchdog to prevent reset - vTaskDelayUntil(&xLastWakeTime, xFrequency); - } -} - -// Initialization functions -void init_serial() { - // Init Serial monitor - Serial.begin(115200); - while (!Serial) {} -} - void check_interconnect_available() { if (datalayer.battery.status.voltage_dV == 0 || datalayer.battery2.status.voltage_dV == 0) { return; // Both voltage values need to be available to start check @@ -589,3 +354,229 @@ void check_reset_reason() { break; } } + +void core_loop(void*) { + esp_task_wdt_add(NULL); // Register this task with WDT + TickType_t xLastWakeTime = xTaskGetTickCount(); + const TickType_t xFrequency = pdMS_TO_TICKS(1); // Convert 1ms to ticks + + while (true) { + + START_TIME_MEASUREMENT(all); + START_TIME_MEASUREMENT(comm); + + monitor_equipment_stop_button(); + + // Input, Runs as fast as possible + receive_can(); // Receive CAN messages + receive_rs485(); // Process serial2 RS485 interface + + END_TIME_MEASUREMENT_MAX(comm, datalayer.system.status.time_comm_us); + + if (webserver_enabled) { + START_TIME_MEASUREMENT(ota); + ElegantOTA.loop(); + END_TIME_MEASUREMENT_MAX(ota, datalayer.system.status.time_ota_us); + } + + // Process + currentMillis = millis(); + if (currentMillis - previousMillis10ms >= INTERVAL_10_MS) { + if ((currentMillis - previousMillis10ms >= INTERVAL_10_MS_DELAYED) && + (milliseconds(currentMillis) > esp32hal->BOOTUP_TIME())) { + set_event(EVENT_TASK_OVERRUN, (currentMillis - previousMillis10ms)); + } + previousMillis10ms = currentMillis; + if (datalayer.system.info.performance_measurement_active) { + START_TIME_MEASUREMENT(10ms); + } + led_exe(); + handle_contactors(); // Take care of startup precharge/contactor closing +#ifdef PRECHARGE_CONTROL + handle_precharge_control(currentMillis); +#endif // PRECHARGE_CONTROL + if (datalayer.system.info.performance_measurement_active) { + END_TIME_MEASUREMENT_MAX(10ms, datalayer.system.status.time_10ms_us); + } + } + + if (currentMillis - previousMillisUpdateVal >= INTERVAL_1_S) { + previousMillisUpdateVal = currentMillis; // Order matters on the update_loop! + if (datalayer.system.info.performance_measurement_active) { + START_TIME_MEASUREMENT(values); + } + update_pause_state(); // Check if we are OK to send CAN or need to pause + + // Fetch battery values + if (battery) { + battery->update_values(); + } + + if (battery2) { + battery2->update_values(); + check_interconnect_available(); + } + update_calculated_values(); + update_machineryprotection(); // Check safeties + + // Update values heading towards inverter + if (inverter) { + inverter->update_values(); + } + + if (datalayer.system.info.performance_measurement_active) { + END_TIME_MEASUREMENT_MAX(values, datalayer.system.status.time_values_us); + } + } + if (datalayer.system.info.performance_measurement_active) { + START_TIME_MEASUREMENT(cantx); + } + + // Let all transmitter objects send their messages + for (auto& transmitter : transmitters) { + transmitter->transmit(currentMillis); + } + + if (datalayer.system.info.performance_measurement_active) { + END_TIME_MEASUREMENT_MAX(cantx, datalayer.system.status.time_cantx_us); + END_TIME_MEASUREMENT_MAX(all, datalayer.system.status.core_task_10s_max_us); + if (datalayer.system.status.core_task_10s_max_us > datalayer.system.status.core_task_max_us) { + // Update worst case total time + datalayer.system.status.core_task_max_us = datalayer.system.status.core_task_10s_max_us; + // Record snapshots of task times + datalayer.system.status.time_snap_comm_us = datalayer.system.status.time_comm_us; + datalayer.system.status.time_snap_10ms_us = datalayer.system.status.time_10ms_us; + datalayer.system.status.time_snap_values_us = datalayer.system.status.time_values_us; + datalayer.system.status.time_snap_cantx_us = datalayer.system.status.time_cantx_us; + datalayer.system.status.time_snap_ota_us = datalayer.system.status.time_ota_us; + } + + datalayer.system.status.core_task_max_us = + MAX(datalayer.system.status.core_task_10s_max_us, datalayer.system.status.core_task_max_us); + if (core_task_timer_10s.elapsed()) { + datalayer.system.status.time_ota_us = 0; + datalayer.system.status.time_comm_us = 0; + datalayer.system.status.time_10ms_us = 0; + datalayer.system.status.time_values_us = 0; + datalayer.system.status.time_cantx_us = 0; + datalayer.system.status.core_task_10s_max_us = 0; + datalayer.system.status.wifi_task_10s_max_us = 0; + datalayer.system.status.mqtt_task_10s_max_us = 0; + } + } + esp_task_wdt_reset(); // Reset watchdog to prevent reset + vTaskDelayUntil(&xLastWakeTime, xFrequency); + } +} + +// Initialization +void setup() { + init_hal(); + + init_serial(); + + // We print this after setting up serial, so that is also printed if configured to do so + logging.printf("Battery emulator %s build " __DATE__ " " __TIME__ "\n", version_number); + + init_events(); + + init_stored_settings(); + + if (wifi_enabled) { + xTaskCreatePinnedToCore((TaskFunction_t)&connectivity_loop, "connectivity_loop", 4096, NULL, TASK_CONNECTIVITY_PRIO, + &connectivity_loop_task, esp32hal->WIFICORE()); + } + + if (!led_init()) { + return; + } + + if (datalayer.system.info.CAN_SD_logging_active || datalayer.system.info.SD_logging_active) { + xTaskCreatePinnedToCore((TaskFunction_t)&logging_loop, "logging_loop", 4096, NULL, TASK_CONNECTIVITY_PRIO, + &logging_loop_task, esp32hal->WIFICORE()); + } + + if (!init_contactors()) { + return; + } + + if (!init_precharge_control()) { + return; + } + + setup_charger(); + + if (!setup_inverter()) { + return; + } + setup_battery(); + setup_can_shunt(); + + // Init CAN only after any CAN receivers have had a chance to register. + if (!init_CAN()) { + return; + } + + if (!init_rs485()) { + return; + } + + if (!init_equipment_stop_button()) { + return; + } + + // BOOT button at runtime is used as an input for various things + pinMode(0, INPUT_PULLUP); + + check_reset_reason(); + + // Initialize Task Watchdog for subscribed tasks + esp_task_wdt_config_t wdt_config = {// 5s should be enough for the connectivity tasks (which are all contending + // for the same core) to yield to each other and reset their watchdogs. + .timeout_ms = INTERVAL_5_S, + // We don't benefit from idle task watchdogs, our critical loops have their + // own. The idle watchdogs can cause nuisance reboots under heavy load. + .idle_core_mask = 0, + // Panic (and reboot) on timeout + .trigger_panic = true}; +#ifdef CONFIG_ESP_TASK_WDT + // ESP-IDF will have already initialized it, so reconfigure. + // Arduino and PlatformIO have different watchdog defaults, so we reconfigure + // for consistency. + esp_task_wdt_reconfigure(&wdt_config); +#else + // Otherwise initialize it for the first time. + esp_task_wdt_init(&wdt_config); +#endif + + // Start tasks + + if (mqtt_enabled) { + if (!init_mqtt()) { + return; + } + + xTaskCreatePinnedToCore((TaskFunction_t)&mqtt_loop, "mqtt_loop", 4096, NULL, TASK_MQTT_PRIO, &mqtt_loop_task, + esp32hal->WIFICORE()); + } + + xTaskCreatePinnedToCore((TaskFunction_t)&core_loop, "core_loop", 4096, NULL, TASK_CORE_PRIO, &main_loop_task, + esp32hal->CORE_FUNCTION_CORE()); + + DEBUG_PRINTF("setup() complete\n"); +} + +// Loop empty, all functionality runs in tasks +void loop() {} + +void mqtt_loop(void*) { + esp_task_wdt_add(NULL); // Register this task with WDT + + while (true) { + START_TIME_MEASUREMENT(mqtt); + mqtt_loop(); + END_TIME_MEASUREMENT_MAX(mqtt, datalayer.system.status.mqtt_task_10s_max_us); + esp_task_wdt_reset(); // Reset watchdog + delay(1); + } +} From fb62b2cdb15c1a3b999c9da2070d8aa9ba8995e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 2 Sep 2025 18:13:24 +0300 Subject: [PATCH 115/245] Progress --- Software/Software.cpp | 6 +++++- Software/USER_SETTINGS.cpp | 18 ++++++++---------- Software/USER_SETTINGS.h | 12 ------------ Software/src/devboard/mqtt/mqtt.cpp | 7 ++++++- 4 files changed, 19 insertions(+), 24 deletions(-) diff --git a/Software/Software.cpp b/Software/Software.cpp index 70fd5609..dde1c9f3 100644 --- a/Software/Software.cpp +++ b/Software/Software.cpp @@ -52,8 +52,12 @@ TaskHandle_t mqtt_loop_task; Logging logging; -static std::list transmitters; +std::string mqtt_user; //TODO, move? +std::string mqtt_password; //TODO, move? +std::string http_username; //TODO, move? +std::string http_password; //TODO, move? +static std::list transmitters; void register_transmitter(Transmitter* transmitter) { transmitters.push_back(transmitter); DEBUG_PRINTF("transmitter registered, total: %d\n", transmitters.size()); diff --git a/Software/USER_SETTINGS.cpp b/Software/USER_SETTINGS.cpp index 48cf2c9b..d79a90ae 100644 --- a/Software/USER_SETTINGS.cpp +++ b/Software/USER_SETTINGS.cpp @@ -20,23 +20,21 @@ volatile CAN_Configuration can_config = { .shunt = CAN_NATIVE // (OPTIONAL) Which CAN is your shunt connected to? }; -std::string ssid; -std::string password; -std::string passwordAP; - -const uint8_t wifi_channel = 0; // Set to 0 for automatic channel selection - -std::string http_username; -std::string http_password; // Set your Static IP address. Only used incase WIFICONFIG is set in USER_SETTINGS.h IPAddress local_IP(192, 168, 10, 150); IPAddress gateway(192, 168, 10, 1); IPAddress subnet(255, 255, 255, 0); +std::string ssid; +std::string password; +std::string passwordAP; + +const uint8_t wifi_channel = 0; // Set to 0 for automatic channel selection + + // MQTT -std::string mqtt_user; -std::string mqtt_password; + const char* mqtt_topic_name = "BE"; // Custom MQTT topic name. Previously, the name was automatically set to "battery-emulator_esp32-XXXXXX" diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index 6b199614..30e2a80a 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -4,14 +4,6 @@ #include #include "src/devboard/utils/types.h" -/* This file is being transitioned towards COMMON_IMAGE. Use v8.16 if you are taking this software into use! */ - -/* Select hardware used for Battery-Emulator */ -//#define HW_LILYGO -//#define HW_STARK -//#define HW_3LB -//#define HW_DEVKIT - /* Shunt/Contactor settings (Optional) */ //#define BMW_SBOX // SBOX relay control & battery current/voltage measurement @@ -22,10 +14,6 @@ /* Connectivity options */ //#define WIFICONFIG //Enable this line to set a static IP address / gateway /subnet mask for the device. see USER_SETTINGS.cpp for the settings -#define MQTT_QOS 0 // MQTT Quality of Service (0, 1, or 2) -#define MQTT_PUBLISH_CELL_VOLTAGES // Enable this line to publish cell voltages to MQTT -#define MQTT_TIMEOUT 2000 // MQTT timeout in milliseconds - /* Do not change any code below this line */ /* Only change battery specific settings above and in "USER_SETTINGS.cpp" */ typedef struct { diff --git a/Software/src/devboard/mqtt/mqtt.cpp b/Software/src/devboard/mqtt/mqtt.cpp index a031f1a5..525df501 100644 --- a/Software/src/devboard/mqtt/mqtt.cpp +++ b/Software/src/devboard/mqtt/mqtt.cpp @@ -24,11 +24,16 @@ int mqtt_port = mqtt_port_default; std::string mqtt_server = mqtt_server_default; bool mqtt_manual_topic_object_name = - true; //TODO, should this be configurable from webserver? Or legacy option removed? + true; //TODO: should this be configurable from webserver? Or legacy option removed? // If this is not true, the previous default naming format 'battery-emulator_esp32-XXXXXX' (based on hardware ID) will be used. // This naming convention was in place until version 7.5.0. Users should check the version from which they are updating, as this change // may break compatibility with previous versions of MQTT naming +#define MQTT_PUBLISH_CELL_VOLTAGES // Enable this line to publish cell voltages to MQTT +#define MQTT_QOS 0 // MQTT Quality of Service (0, 1, or 2) +#define MQTT_TIMEOUT 2000 // MQTT timeout in milliseconds + //TODO: Should all these defines be a configurable option? + esp_mqtt_client_config_t mqtt_cfg; esp_mqtt_client_handle_t client; char mqtt_msg[MQTT_MSG_BUFFER_SIZE]; From a0b0fbd5d6b977d0ab41f8b8f99a814466bde632 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 2 Sep 2025 18:27:37 +0300 Subject: [PATCH 116/245] Restore CAN-FD library to v2.1.4 --- .../pierremolinaro-ACAN2517FD/ACAN2517FD.cpp | 250 +++++++++--------- .../pierremolinaro-ACAN2517FD/ACAN2517FD.h | 168 ++++++------ .../ACAN2517FDFilters.h | 58 ++-- .../ACAN2517FDSettings.cpp | 239 ++++++++++------- .../ACAN2517FDSettings.h | 182 ++++++------- .../ACAN2517FD_ACANFDBuffer.h | 122 --------- .../pierremolinaro-ACAN2517FD/ACANFDBuffer.h | 119 +++++++++ ...ateFactor.h => ACANFD_DataBitRateFactor.h} | 10 +- ...AN2517FD_CANFDMessage.h => CANFDMessage.h} | 46 ++-- .../{ACAN2517FD_CANMessage.h => CANMessage.h} | 12 +- .../lib/pierremolinaro-ACAN2517FD/README.md | 0 .../library.properties | 9 - 12 files changed, 624 insertions(+), 591 deletions(-) delete mode 100644 Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD_ACANFDBuffer.h create mode 100644 Software/src/lib/pierremolinaro-ACAN2517FD/ACANFDBuffer.h rename Software/src/lib/pierremolinaro-ACAN2517FD/{ACAN2517FD_DataBitRateFactor.h => ACANFD_DataBitRateFactor.h} (73%) rename Software/src/lib/pierremolinaro-ACAN2517FD/{ACAN2517FD_CANFDMessage.h => CANFDMessage.h} (65%) rename Software/src/lib/pierremolinaro-ACAN2517FD/{ACAN2517FD_CANMessage.h => CANMessage.h} (84%) mode change 100755 => 100644 Software/src/lib/pierremolinaro-ACAN2517FD/README.md delete mode 100644 Software/src/lib/pierremolinaro-ACAN2517FD/library.properties diff --git a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.cpp b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.cpp index 2f190f98..ec2526c2 100644 --- a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.cpp +++ b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.cpp @@ -1,20 +1,20 @@ -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- // A CAN driver for MCP2517FD, CANFD mode // by Pierre Molinaro // https://github.com/pierremolinaro/acan2517FD // -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- #include "ACAN2517FD.h" -#include "../../system_settings.h" //Contains task priority //Modded by Battery-Emulator +#include "../../system_settings.h" //Contains task priority -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- static const uint8_t TXBWS = 0 ; -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- // Note about ESP32 -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- // // It appears that Arduino ESP32 interrupts are managed in a completely different way from "usual" Arduino: // - SPI.usingInterrupt is not implemented; @@ -43,7 +43,7 @@ static const uint8_t TXBWS = 0 ; // #endif // mSPI.endTransaction () ; // -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- #ifdef ARDUINO_ARCH_ESP32 static void myESP32Task (void * pData) { @@ -55,9 +55,9 @@ static const uint8_t TXBWS = 0 ; } #endif -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- // ACAN2517FD register addresses -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- static const uint16_t CON_REGISTER = 0x000 ; static const uint16_t NBTCFG_REGISTER = 0x004 ; @@ -68,85 +68,85 @@ static const uint16_t TREC_REGISTER = 0x034 ; static const uint16_t BDIAG0_REGISTER = 0x038 ; static const uint16_t BDIAG1_REGISTER = 0x03C ; -//------------------------------------------------------------------------------ +//······················································································································ // TXQ REGISTERS -//------------------------------------------------------------------------------ +//······················································································································ static const uint16_t TXQCON_REGISTER = 0x050 ; static const uint16_t TXQSTA_REGISTER = 0x054 ; static const uint16_t TXQUA_REGISTER = 0x058 ; -//------------------------------------------------------------------------------ +//······················································································································ // INTERRUPT REGISTERS -//------------------------------------------------------------------------------ +//······················································································································ static const uint16_t INT_REGISTER = 0x01C ; -//------------------------------------------------------------------------------ +//······················································································································ // FIFO REGISTERS -//------------------------------------------------------------------------------ +//······················································································································ static uint16_t FIFOCON_REGISTER (const uint16_t inFIFOIndex) { // 1 ... 31 return 0x05C + 12 * (inFIFOIndex - 1) ; } -//------------------------------------------------------------------------------ +//······················································································································ static uint16_t FIFOSTA_REGISTER (const uint16_t inFIFOIndex) { // 1 ... 31 return 0x060 + 12 * (inFIFOIndex - 1) ; } -//------------------------------------------------------------------------------ +//······················································································································ static uint16_t FIFOUA_REGISTER (const uint16_t inFIFOIndex) { // 1 ... 31 return 0x064 + 12 * (inFIFOIndex - 1) ; } -//------------------------------------------------------------------------------ +//······················································································································ // FILTER REGISTERS -//------------------------------------------------------------------------------ +//······················································································································ static uint16_t FLTCON_REGISTER (const uint16_t inFilterIndex) { // 0 ... 31 (DS20005688B, page 58) return 0x1D0 + inFilterIndex ; } -//------------------------------------------------------------------------------ +//······················································································································ static uint16_t FLTOBJ_REGISTER (const uint16_t inFilterIndex) { // 0 ... 31 (DS20005688B, page 60) return 0x1F0 + 8 * inFilterIndex ; } -//------------------------------------------------------------------------------ +//······················································································································ static uint16_t MASK_REGISTER (const uint16_t inFilterIndex) { // 0 ... 31 (DS20005688B, page 61) return 0x1F4 + 8 * inFilterIndex ; } -//------------------------------------------------------------------------------ +//······················································································································ // OSCILLATOR REGISTER -//------------------------------------------------------------------------------ +//······················································································································ static const uint16_t OSC_REGISTER = 0xE00 ; -//------------------------------------------------------------------------------ +//······················································································································ // INPUT / OUPUT CONTROL REGISTER -//------------------------------------------------------------------------------ +//······················································································································ static const uint16_t IOCON_REGISTER_00_07 = 0xE04 ; static const uint16_t IOCON_REGISTER_08_15 = 0xE05 ; static const uint16_t IOCON_REGISTER_16_23 = 0xE06 ; static const uint16_t IOCON_REGISTER_24_31 = 0xE07 ; -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- // RECEIVE FIFO INDEX -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- static const uint8_t RECEIVE_FIFO_INDEX = 1 ; static const uint8_t TRANSMIT_FIFO_INDEX = 2 ; -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- // BYTE BUFFER UTILITY FUNCTIONS -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- static void enterU32InBufferAtIndex (const uint32_t inValue, uint8_t ioBuffer [], const uint8_t inIndex) { ioBuffer [inIndex + 0] = (uint8_t) inValue ; @@ -155,7 +155,7 @@ static void enterU32InBufferAtIndex (const uint32_t inValue, uint8_t ioBuffer [] ioBuffer [inIndex + 3] = (uint8_t) (inValue >> 24) ; } -//------------------------------------------------------------------------------ +//······················································································································ static uint32_t u32FromBufferAtIndex (uint8_t ioBuffer [], const uint8_t inIndex) { uint32_t result = (uint32_t) ioBuffer [inIndex + 0] ; @@ -165,7 +165,7 @@ static uint32_t u32FromBufferAtIndex (uint8_t ioBuffer [], const uint8_t inIndex return result ; } -//------------------------------------------------------------------------------ +//······················································································································ static uint16_t u16FromBufferAtIndex (uint8_t ioBuffer [], const uint8_t inIndex) { uint16_t result = (uint16_t) ioBuffer [inIndex + 0] ; @@ -173,7 +173,8 @@ static uint16_t u16FromBufferAtIndex (uint8_t ioBuffer [], const uint8_t inIndex return result ; } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- + static inline void turnOffInterrupts () { #ifndef DISABLEMCP2517FDCOMPAT #ifdef ARDUINO_ARCH_ESP32 @@ -208,6 +209,7 @@ mINT (inINT), mUsesTXQ (false), mHardwareTxFIFOFull (false), mRxInterruptEnabled (true), +mHasDataBitRate (false), mTransmitFIFOPayload (0), mTXQBufferPayload (0), mReceiveFIFOPayload (0), @@ -221,7 +223,7 @@ mDriverTransmitBuffer () { } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- uint32_t ACAN2517FD::begin (const ACAN2517FDSettings & inSettings, void (* inInterruptServiceRoutine) (void)) { @@ -232,7 +234,7 @@ uint32_t ACAN2517FD::begin (const ACAN2517FDSettings & inSettings, return begin (inSettings, inInterruptServiceRoutine, filters) ; } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- uint32_t ACAN2517FD::begin (const ACAN2517FDSettings & inSettings, void (* inInterruptServiceRoutine) (void), @@ -501,14 +503,17 @@ uint32_t ACAN2517FD::begin (const ACAN2517FDSettings & inSettings, // bits 11-8: TSEG2 - 1 // bits 7-4: unused // bits 3-0: SJW - 1 - data = inSettings.mBitRatePrescaler - 1 ; - data <<= 8 ; - data |= inSettings.mDataPhaseSegment1 - 1 ; - data <<= 8 ; - data |= inSettings.mDataPhaseSegment2 - 1 ; - data <<= 8 ; - data |= inSettings.mDataSJW - 1 ; - writeRegister32 (DBTCFG_REGISTER, data) ; + mHasDataBitRate = inSettings.mDataBitRateFactor != ::DataBitRateFactor::x1 ; + if (mHasDataBitRate) { + data = inSettings.mBitRatePrescaler - 1 ; + data <<= 8 ; + data |= inSettings.mDataPhaseSegment1 - 1 ; + data <<= 8 ; + data |= inSettings.mDataPhaseSegment2 - 1 ; + data <<= 8 ; + data |= inSettings.mDataSJW - 1 ; + writeRegister32 (DBTCFG_REGISTER, data) ; + } //----------------------------------- Request mode (CON_REGISTER + 3, DS20005688B, page 24) // bits 7-4: Transmit Bandwith Sharing Bits ---> 0 // bit 3: Abort All Pending Transmissions bit --> 0 @@ -526,14 +531,14 @@ uint32_t ACAN2517FD::begin (const ACAN2517FDSettings & inSettings, } } #ifdef ARDUINO_ARCH_ESP32 - xTaskCreate (myESP32Task, "ACAN2517Handler", 1024, this, TASK_ACAN2517FD_PRIORITY, &mESP32TaskHandle) ; //Modded by Battery-Emulator + xTaskCreate (myESP32Task, "ACAN2517Handler", 1024, this, TASK_ACAN2517FD_PRIORITY, &mESP32TaskHandle) ; #endif if (mINT != 255) { // 255 means interrupt is not used #ifdef ARDUINO_ARCH_ESP32 attachInterrupt (itPin, inInterruptServiceRoutine, FALLING) ; #else - mSPI.usingInterrupt (itPin) ; // usingInterrupt is not implemented in Arduino ESP32 attachInterrupt (itPin, inInterruptServiceRoutine, LOW) ; // Thank to Flole998 + mSPI.usingInterrupt (itPin) ; // usingInterrupt is not implemented in Arduino ESP32 #endif } // If you begin() multiple times without constructor, @@ -545,13 +550,13 @@ uint32_t ACAN2517FD::begin (const ACAN2517FDSettings & inSettings, return errorCode ; } -//------------------------------------------------------------------------------ +//······················································································································ // end method (resets the MCP2517FD, deallocate buffers, and detach interrupt pin) -//------------------------------------------------------------------------------ +//······················································································································ bool ACAN2517FD::end (void) { mSPI.beginTransaction (mSPISettings) ; - turnOffInterrupts () ; + turnOffInterrupts () ; //--- Detach interrupt pin if (mINT != 255) { // 255 means interrupt is not used const int8_t itPin = digitalPinToInterrupt (mINT) ; @@ -592,19 +597,15 @@ bool ACAN2517FD::end (void) { return ok ; } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- // SEND FRAME -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- bool ACAN2517FD::tryToSend (const CANFDMessage & inMessage) { bool ok = inMessage.isValid () ; if (ok) { mSPI.beginTransaction (mSPISettings) ; - #ifdef ARDUINO_ARCH_ESP32 - taskDISABLE_INTERRUPTS () ; - #else - noInterrupts () ; - #endif + turnOffInterrupts () ; if (inMessage.idx == 0) { ok = inMessage.len <= mTransmitFIFOPayload ; if (ok) { @@ -616,13 +617,13 @@ bool ACAN2517FD::tryToSend (const CANFDMessage & inMessage) { ok = sendViaTXQ (inMessage) ; } } - turnOnInterrupts () ; + turnOnInterrupts(); mSPI.endTransaction () ; } return ok ; } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- bool ACAN2517FD::enterInTransmitBuffer (const CANFDMessage & inMessage) { bool result ; @@ -644,7 +645,7 @@ bool ACAN2517FD::enterInTransmitBuffer (const CANFDMessage & inMessage) { return result ; } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- static uint32_t lengthCodeForLength (const uint8_t inLength) { uint32_t result = inLength & 0x0F ; @@ -660,7 +661,7 @@ static uint32_t lengthCodeForLength (const uint8_t inLength) { return result ; } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- void ACAN2517FD::appendInControllerTxFIFO (const CANFDMessage & inMessage) { const uint16_t ramAddr = uint16_t (0x400 + readRegister32Assume_SPI_transaction (FIFOUA_REGISTER (TRANSMIT_FIFO_INDEX))) ; @@ -685,7 +686,9 @@ void ACAN2517FD::appendInControllerTxFIFO (const CANFDMessage & inMessage) { break ; case CANFDMessage::CANFD_WITH_BIT_RATE_SWITCH : flags |= 1 << 7 ; // Set FDF bit - flags |= 1 << 6 ; // Set BRS bit + if (mHasDataBitRate) { + flags |= 1 << 6 ; // Set BRS bit + } break ; } //--- Word count @@ -711,7 +714,7 @@ void ACAN2517FD::appendInControllerTxFIFO (const CANFDMessage & inMessage) { writeRegister8Assume_SPI_transaction (FIFOCON_REGISTER (TRANSMIT_FIFO_INDEX) + 1, data8); } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- bool ACAN2517FD::sendViaTXQ (const CANFDMessage & inMessage) { bool ok = mUsesTXQ ; @@ -748,7 +751,9 @@ bool ACAN2517FD::sendViaTXQ (const CANFDMessage & inMessage) { break ; case CANFDMessage::CANFD_WITH_BIT_RATE_SWITCH : flags |= 1 << 7 ; // Set FDF bit - flags |= 1 << 6 ; // Set BRS bit + if (mHasDataBitRate) { + flags |= 1 << 6 ; // Set BRS bit + } break ; } //--- Word count @@ -777,48 +782,41 @@ bool ACAN2517FD::sendViaTXQ (const CANFDMessage & inMessage) { return ok ; } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- // RECEIVE FRAME -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- bool ACAN2517FD::available (void) { mSPI.beginTransaction (mSPISettings) ; - turnOffInterrupts () ; + turnOffInterrupts () ; const bool hasReceivedMessage = mDriverReceiveBuffer.count () > 0 ; - turnOnInterrupts () ; + turnOnInterrupts(); mSPI.endTransaction () ; return hasReceivedMessage ; } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- bool ACAN2517FD::receive (CANFDMessage & outMessage) { + mSPI.beginTransaction (mSPISettings) ; + turnOffInterrupts () ; const bool hasReceivedMessage = mDriverReceiveBuffer.remove (outMessage) ; //--- If receive interrupt is disabled, enable it (added in release 2.17) if (mINT == 255) { // No interrupt pin mRxInterruptEnabled = true ; isr_poll_core () ; // Perform polling }else if (!mRxInterruptEnabled) { - - mSPI.beginTransaction (mSPISettings) ; - #ifdef ARDUINO_ARCH_ESP32 - taskDISABLE_INTERRUPTS () ; - #else - noInterrupts () ; - #endif - mRxInterruptEnabled = true ; uint8_t data8 = readRegister8Assume_SPI_transaction (INT_REGISTER + 2) ; data8 |= (1 << 1) ; // Receive FIFO Interrupt Enable writeRegister8Assume_SPI_transaction (INT_REGISTER + 2, data8) ; - - turnOnInterrupts () ; - mSPI.endTransaction () ; } + turnOnInterrupts(); + mSPI.endTransaction () ; return hasReceivedMessage ; } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- bool ACAN2517FD::dispatchReceivedMessage (const tFilterMatchCallBack inFilterMatchCallBack) { CANFDMessage receivedMessage ; @@ -836,9 +834,9 @@ bool ACAN2517FD::dispatchReceivedMessage (const tFilterMatchCallBack inFilterMat return hasReceived ; } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- // POLLING (ESP32) -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- #ifdef ARDUINO_ARCH_ESP32 void ACAN2517FD::poll (void) { @@ -848,22 +846,22 @@ bool ACAN2517FD::dispatchReceivedMessage (const tFilterMatchCallBack inFilterMat } #endif -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- // POLLING (other than ESP32) -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- #ifndef ARDUINO_ARCH_ESP32 void ACAN2517FD::poll (void) { - noInterrupts () ; + turnOffInterrupts () ; isr_poll_core () ; - interrupts () ; + turnOnInterrupts () ; } #endif -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- // INTERRUPT SERVICE ROUTINE (ESP32) // https://stackoverflow.com/questions/51750377/how-to-disable-interrupt-watchdog-in-esp32-or-increase-isr-time-limit -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- #ifdef ARDUINO_ARCH_ESP32 void ACAN2517FD::isr (void) { @@ -873,9 +871,9 @@ bool ACAN2517FD::dispatchReceivedMessage (const tFilterMatchCallBack inFilterMat } #endif -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- // INTERRUPT SERVICE ROUTINE (other than ESP32) -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- #ifndef ARDUINO_ARCH_ESP32 void ACAN2517FD::isr (void) { @@ -883,9 +881,9 @@ bool ACAN2517FD::dispatchReceivedMessage (const tFilterMatchCallBack inFilterMat } #endif -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- // INTERRUPT SERVICE ROUTINES (common) -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- void ACAN2517FD::isr_poll_core (void) { mSPI.beginTransaction (mSPISettings) ; @@ -935,7 +933,7 @@ void ACAN2517FD::isr_poll_core (void) { mSPI.endTransaction () ; } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- void ACAN2517FD::transmitInterrupt (void) { // Generated if hardware transmit FIFO is not full CANFDMessage message ; @@ -950,7 +948,7 @@ void ACAN2517FD::transmitInterrupt (void) { // Generated if hardware transmit FI } } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- void ACAN2517FD::receiveInterrupt (void) { const uint16_t ramAddress = uint16_t (0x400 + readRegister32Assume_SPI_transaction (FIFOUA_REGISTER (RECEIVE_FIFO_INDEX))) ; @@ -1009,9 +1007,9 @@ void ACAN2517FD::receiveInterrupt (void) { } } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- // MCP2517FD REGISTER ACCESS, SECOND LEVEL FUNCTIONS (HANDLE CS, ASSUME WITHIN SPI TRANSACTION) -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- void ACAN2517FD::writeRegister32Assume_SPI_transaction (const uint16_t inRegisterAddress, const uint32_t inValue) { @@ -1029,7 +1027,7 @@ void ACAN2517FD::writeRegister32Assume_SPI_transaction (const uint16_t inRegiste deassertCS () ; } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- void ACAN2517FD::writeRegister8Assume_SPI_transaction (const uint16_t inRegisterAddress, const uint8_t inValue) { @@ -1044,7 +1042,7 @@ void ACAN2517FD::writeRegister8Assume_SPI_transaction (const uint16_t inRegister deassertCS () ; } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- uint32_t ACAN2517FD::readRegister32Assume_SPI_transaction (const uint16_t inRegisterAddress) { //--- Read word register via 6-byte buffer (speed enhancement, thanks to thomasfla) @@ -1063,7 +1061,7 @@ uint32_t ACAN2517FD::readRegister32Assume_SPI_transaction (const uint16_t inRegi return result ; } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- uint16_t ACAN2517FD::readRegister16Assume_SPI_transaction (const uint16_t inRegisterAddress) { //--- Read half-word register via 4-byte buffer (speed enhancement, thanks to thomasfla) @@ -1082,7 +1080,7 @@ uint16_t ACAN2517FD::readRegister16Assume_SPI_transaction (const uint16_t inRegi return result ; } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- uint8_t ACAN2517FD::readRegister8Assume_SPI_transaction (const uint16_t inRegisterAddress) { //--- Read byte register via 3-byte buffer (speed enhancement, thanks to thomasfla) @@ -1096,71 +1094,71 @@ uint8_t ACAN2517FD::readRegister8Assume_SPI_transaction (const uint16_t inRegist return buffer [2] ; } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- // MCP2517FD REGISTER ACCESS, THIRD LEVEL FUNCTIONS (HANDLE CS AND SPI TRANSACTION) -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- void ACAN2517FD::writeRegister8 (const uint16_t inRegisterAddress, const uint8_t inValue) { mSPI.beginTransaction (mSPISettings) ; turnOffInterrupts () ; writeRegister8Assume_SPI_transaction (inRegisterAddress, inValue) ; - turnOnInterrupts () ; + turnOnInterrupts () ; mSPI.endTransaction () ; } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- uint8_t ACAN2517FD::readRegister8 (const uint16_t inRegisterAddress) { mSPI.beginTransaction (mSPISettings) ; turnOffInterrupts () ; const uint8_t result = readRegister8Assume_SPI_transaction (inRegisterAddress) ; -turnOnInterrupts () ; + turnOnInterrupts () ; mSPI.endTransaction () ; return result ; } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- uint16_t ACAN2517FD::readRegister16 (const uint16_t inRegisterAddress) { mSPI.beginTransaction (mSPISettings) ; turnOffInterrupts () ; const uint16_t result = readRegister16Assume_SPI_transaction (inRegisterAddress) ; -turnOnInterrupts () ; + turnOnInterrupts () ; mSPI.endTransaction () ; return result ; } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- void ACAN2517FD::writeRegister32 (const uint16_t inRegisterAddress, const uint32_t inValue) { mSPI.beginTransaction (mSPISettings) ; turnOffInterrupts () ; writeRegister32Assume_SPI_transaction (inRegisterAddress, inValue) ; -turnOnInterrupts () ; + turnOnInterrupts () ; mSPI.endTransaction () ; } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- uint32_t ACAN2517FD::readRegister32 (const uint16_t inRegisterAddress) { mSPI.beginTransaction (mSPISettings) ; turnOffInterrupts () ; const uint32_t result = readRegister32Assume_SPI_transaction (inRegisterAddress) ; -turnOnInterrupts () ; + turnOnInterrupts () ; mSPI.endTransaction () ; return result ; } -//------------------------------------------------------------------------------ +//······················································································································ // Current MCP2517FD Operation Mode -//------------------------------------------------------------------------------ +//······················································································································ ACAN2517FDSettings::OperationMode ACAN2517FD::currentOperationMode (void) { const uint8_t mode = readRegister8 (CON_REGISTER + 2) >> 5 ; return ACAN2517FDSettings::OperationMode (mode) ; } -//------------------------------------------------------------------------------ +//······················································································································ bool ACAN2517FD::recoverFromRestrictedOperationMode (void) { bool recoveryDone = false ; @@ -1184,9 +1182,9 @@ bool ACAN2517FD::recoverFromRestrictedOperationMode (void) { return recoveryDone ; } -//------------------------------------------------------------------------------ +//······················································································································ // Set MCP2517FD Operation Mode -//------------------------------------------------------------------------------ +//······················································································································ void ACAN2517FD::setOperationMode (const ACAN2517FDSettings::OperationMode inOperationMode) { // bits 7-4: Transmit Bandwith Sharing Bits ---> 0 @@ -1194,7 +1192,7 @@ void ACAN2517FD::setOperationMode (const ACAN2517FDSettings::OperationMode inOpe writeRegister8 (CON_REGISTER + 3, uint8_t (inOperationMode)); } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- void ACAN2517FD::reset2517FD (void) { mSPI.beginTransaction (mSPISettings) ; // Check RESET is performed with 800 kHz clock @@ -1202,14 +1200,14 @@ void ACAN2517FD::reset2517FD (void) { assertCS () ; mSPI.transfer16 (0x00) ; // Reset instruction: 0x0000 deassertCS () ; -turnOnInterrupts () ; + turnOnInterrupts () ; mSPI.endTransaction () ; } -//------------------------------------------------------------------------------ +//······················································································································ // Sleep Mode to Configuration Mode // (returns true if MCP2517FD was in sleep mode) -//------------------------------------------------------------------------------ +//······················································································································ // The device exits Sleep mode due to a dominant edge on RXCAN or by enabling the oscillator (clearing OSC.OSCDIS). // The module will transition automatically to Configuration mode. @@ -1228,21 +1226,21 @@ bool ACAN2517FD::performSleepModeToConfigurationMode (void) { return inSleepMode ; } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- uint32_t ACAN2517FD::errorCounters (void) { return readRegister32 (TREC_REGISTER) ; } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- uint32_t ACAN2517FD::diagInfos (const int inIndex) { // thanks to Flole998 and turmary return readRegister32 (inIndex ? BDIAG1_REGISTER: BDIAG0_REGISTER) ; } -//------------------------------------------------------------------------------ +//······················································································································ // GPIO -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- void ACAN2517FD::gpioSetMode (const uint8_t inPin, const uint8_t inMode) { if (inPin <= 1) { @@ -1262,7 +1260,7 @@ void ACAN2517FD::gpioSetMode (const uint8_t inPin, const uint8_t inMode) { } } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- void ACAN2517FD::gpioWrite (const uint8_t inPin, const uint8_t inLevel) { if (inPin <= 1) { @@ -1276,14 +1274,14 @@ void ACAN2517FD::gpioWrite (const uint8_t inPin, const uint8_t inLevel) { } } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- bool ACAN2517FD::gpioRead (const uint8_t inPin) { const uint8_t value = readRegister8 (IOCON_REGISTER_16_23) ; return (value >> inPin) & 1 ; } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- void ACAN2517FD::configureGPIO0AsXSTBY (void) { uint8_t value = readRegister8 (IOCON_REGISTER_00_07) ; @@ -1291,4 +1289,4 @@ void ACAN2517FD::configureGPIO0AsXSTBY (void) { writeRegister8 (IOCON_REGISTER_00_07, value) ; } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- diff --git a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h index ec77b11c..d9f85db7 100644 --- a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h +++ b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h @@ -1,16 +1,17 @@ -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- // A CAN driver for MCP2517FD, CANFD mode // by Pierre Molinaro // https://github.com/pierremolinaro/acan2517FD -//------------------------------------------------------------------------------ +// +//---------------------------------------------------------------------------------------------------------------------- #pragma once -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- #include "ACAN2517FDSettings.h" -#include "ACAN2517FD_ACANFDBuffer.h" -#include "ACAN2517FD_CANMessage.h" +#include "ACANFDBuffer.h" +#include "CANMessage.h" #include "ACAN2517FDFilters.h" #include @@ -24,23 +25,23 @@ // #define DISABLEMCP2517FDCOMPAT -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- // ACAN2517FD class -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- class ACAN2517FD { - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // CONSTRUCTOR - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// CONSTRUCTOR +//······················································································································ public: ACAN2517FD (const uint8_t inCS, // CS input of MCP2517FD SPIClass & inSPI, // Hardware SPI object const uint8_t inINT) ; // INT output of MCP2517FD - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // begin method (returns 0 if no error) - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// begin method (returns 0 if no error) +//······················································································································ public: uint32_t begin (const ACAN2517FDSettings & inSettings, void (* inInterruptServiceRoutine) (void)) ; @@ -72,22 +73,22 @@ class ACAN2517FD { public: static const uint32_t kISRNotNullAndNoIntPin = uint32_t (1) << 19 ; public: static const uint32_t kInvalidTDCO = uint32_t (1) << 20 ; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // end method (resets the MCP2517FD, deallocate buffers, and detach interrupt pin) - // Return true if end method succeeds, and false otherwise - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// end method (resets the MCP2517FD, deallocate buffers, and detach interrupt pin) +// Return true if end method succeeds, and false otherwise +//······················································································································ public: bool end (void) ; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Send a message - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// Send a message +//······················································································································ public: bool tryToSend (const CANFDMessage & inMessage) ; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Receive a message - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// Receive a message +//······················································································································ public: bool receive (CANFDMessage & outMessage) ; public: bool available (void) ; @@ -97,44 +98,44 @@ class ACAN2517FD { //--- Call back function array private: ACANFDCallBackRoutine * mCallBackFunctionArray = NULL ; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Get error counters - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// Get error counters +//······················································································································ public: uint32_t errorCounters (void) ; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Get diagnostic information (thanks to Flole998 and turmary) - // inIndex == 0 returns BDIAG0_REGISTER - // inIndex != 0 returns BDIAG1_REGISTER - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// Get diagnostic information (thanks to Flole998 and turmary) +// inIndex == 0 returns BDIAG0_REGISTER +// inIndex != 0 returns BDIAG1_REGISTER +//······················································································································ public: uint32_t diagInfos (const int inIndex = 1) ; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Operation Mode - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// Operation Mode +//······················································································································ public: ACAN2517FDSettings::OperationMode currentOperationMode (void) ; public: void setOperationMode (const ACAN2517FDSettings::OperationMode inMode) ; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Recovery from Restricted Operation Mode - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// Recovery from Restricted Operation Mode +//······················································································································ public: bool recoverFromRestrictedOperationMode (void) ; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Sleep Mode to Configuration Mode - // (returns true if MCP2517FD was in sleep mode) - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// Sleep Mode to Configuration Mode +// (returns true if MCP2517FD was in sleep mode) +//······················································································································ public: bool performSleepModeToConfigurationMode (void) ; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Private properties - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// Private properties +//······················································································································ #ifdef ARDUINO_ARCH_ESP32 private: TaskHandle_t mESP32TaskHandle = nullptr ; @@ -146,51 +147,40 @@ class ACAN2517FD { private: bool mUsesTXQ ; private: bool mHardwareTxFIFOFull ; private: bool mRxInterruptEnabled ; // Added in 2.1.7 + private: bool mHasDataBitRate ; private: uint8_t mTransmitFIFOPayload ; // in byte count private: uint8_t mTXQBufferPayload ; // in byte count private: uint8_t mReceiveFIFOPayload ; // in byte count private: uint8_t mTXBWS_RequestedMode ; private: uint8_t mHardwareReceiveBufferOverflowCount ; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Receive buffer - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// Receive buffer +//······················································································································ private: ACANFDBuffer mDriverReceiveBuffer ; - public: uint32_t driverReceiveBufferPeakCount (void) const { - return mDriverReceiveBuffer.peakCount () ; - } + public: uint32_t driverReceiveBufferPeakCount (void) const { return mDriverReceiveBuffer.peakCount () ; } - public: uint8_t hardwareReceiveBufferOverflowCount (void) const { - return mHardwareReceiveBufferOverflowCount ; - } + public: uint8_t hardwareReceiveBufferOverflowCount (void) const { return mHardwareReceiveBufferOverflowCount ; } - public: void resetHardwareReceiveBufferOverflowCount (void) { - mHardwareReceiveBufferOverflowCount = 0 ; - } + public: void resetHardwareReceiveBufferOverflowCount (void) { mHardwareReceiveBufferOverflowCount = 0 ; } - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Transmit buffer - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// Transmit buffer +//······················································································································ private: ACANFDBuffer mDriverTransmitBuffer ; - public: uint32_t driverTransmitBufferSize (void) const { - return mDriverTransmitBuffer.size () ; - } + public: uint32_t driverTransmitBufferSize (void) const { return mDriverTransmitBuffer.size () ; } - public: uint32_t driverTransmitBufferCount (void) const { - return mDriverTransmitBuffer.count () ; - } + public: uint32_t driverTransmitBufferCount (void) const { return mDriverTransmitBuffer.count () ; } - public: uint32_t driverTransmitBufferPeakCount (void) const { - return mDriverTransmitBuffer.peakCount () ; - } + public: uint32_t driverTransmitBufferPeakCount (void) const { return mDriverTransmitBuffer.peakCount () ; } - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Private methods - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// Private methods +//······················································································································ private: void writeRegister32Assume_SPI_transaction (const uint16_t inRegisterAddress, const uint32_t inValue) ; private: void writeRegister8Assume_SPI_transaction (const uint16_t inRegisterAddress, const uint8_t inValue) ; @@ -212,15 +202,15 @@ class ACAN2517FD { private: bool enterInTransmitBuffer (const CANFDMessage & inMessage) ; private: void appendInControllerTxFIFO (const CANFDMessage & inMessage) ; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Polling - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// Polling +//······················································································································ public: void poll (void) ; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Interrupt service routine - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// Interrupt service routine +//······················································································································ public: void isr (void) ; public: void isr_poll_core (void) ; @@ -230,9 +220,9 @@ class ACAN2517FD { public: SemaphoreHandle_t mISRSemaphore ; #endif - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Optimized CS handling (thanks to Flole998) - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//---------------------------------------------------------------------------------------------------------------------- +// Optimized CS handling (thanks to Flole998) +//······················································································································ #if defined(__AVR__) private: volatile uint8_t *cs_pin_reg; @@ -303,8 +293,10 @@ class ACAN2517FD { *(cs_pin_reg+8+2) = cs_pin_mask; } #elif defined(ARDUINO_ARCH_ESP8266) + // private: volatile uint32_t *cs_pin_reg; private: uint32_t cs_pin_mask; private: inline void initCS () { + // cs_pin_reg = (volatile uint32_t*)GPO; cs_pin_mask = 1 << mCS; pinMode(mCS, OUTPUT); } @@ -341,9 +333,9 @@ class ACAN2517FD { } #endif - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // GPIO - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// GPIO +//······················································································································ public: void gpioSetMode (const uint8_t inPin, const uint8_t inMode) ; @@ -353,16 +345,16 @@ class ACAN2517FD { public: void configureGPIO0AsXSTBY (void) ; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // No copy - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// No copy +//······················································································································ private: ACAN2517FD (const ACAN2517FD &) = delete ; private: ACAN2517FD & operator = (const ACAN2517FD &) = delete ; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ } ; -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- diff --git a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDFilters.h b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDFilters.h index 68fb71fe..69895acb 100644 --- a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDFilters.h +++ b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDFilters.h @@ -1,27 +1,27 @@ -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- // An utility class for: // - ACAN2517FD CAN driver for MCP2517FD (CANFD mode) // by Pierre Molinaro // https://github.com/pierremolinaro/acan2517FD // -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- #ifndef ACAN2517FD_FILTERS_CLASS_DEFINED #define ACAN2517FD_FILTERS_CLASS_DEFINED -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- -#include "ACAN2517FD_CANFDMessage.h" +#include "CANFDMessage.h" -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- // ACAN2517FDFilters class -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- class ACAN2517FDFilters { -//------------------------------------------------------------------------------ +//······················································································································ // EMBEDDED CLASS -//------------------------------------------------------------------------------ +//······················································································································ private: class Filter { public: Filter * mNextFilter ; @@ -43,9 +43,9 @@ class ACAN2517FDFilters { private: Filter & operator = (const Filter &) ; } ; -//------------------------------------------------------------------------------ +//······················································································································ // ENUMERATED TYPE -//------------------------------------------------------------------------------ +//······················································································································ public: typedef enum { kFiltersOk, @@ -58,15 +58,15 @@ class ACAN2517FDFilters { kInconsistencyBetweenMaskAndAcceptance } FilterStatus ; -//------------------------------------------------------------------------------ +//······················································································································ // CONSTRUCTOR -//------------------------------------------------------------------------------ +//······················································································································ public: ACAN2517FDFilters (void) {} -//------------------------------------------------------------------------------ +//······················································································································ // DESTRUCTOR -//------------------------------------------------------------------------------ +//······················································································································ public: ~ ACAN2517FDFilters (void) { while (mFirstFilter != NULL) { @@ -76,9 +76,9 @@ class ACAN2517FDFilters { } } -//------------------------------------------------------------------------------ +//······················································································································ // RECEIVE FILTERS -//------------------------------------------------------------------------------ +//······················································································································ public: void appendPassAllFilter (const ACANFDCallBackRoutine inCallBackRoutine) { // Accept any frame Filter * f = new Filter (0, 0, inCallBackRoutine) ; @@ -91,7 +91,7 @@ class ACAN2517FDFilters { mFilterCount += 1 ; } -//------------------------------------------------------------------------------ +//······················································································································ public: void appendFormatFilter (const tFrameFormat inFormat, // Accept any identifier const ACANFDCallBackRoutine inCallBackRoutine) { @@ -107,7 +107,7 @@ class ACAN2517FDFilters { mFilterCount += 1 ; } -//------------------------------------------------------------------------------ +//······················································································································ public: void appendFrameFilter (const tFrameFormat inFormat, const uint32_t inIdentifier, @@ -141,7 +141,7 @@ class ACAN2517FDFilters { mFilterCount += 1 ; } -//------------------------------------------------------------------------------ +//······················································································································ public: void appendFilter (const tFrameFormat inFormat, const uint32_t inMask, @@ -196,9 +196,9 @@ class ACAN2517FDFilters { mFilterCount += 1 ; } -//------------------------------------------------------------------------------ +//······················································································································ // ACCESSORS -//------------------------------------------------------------------------------ +//······················································································································ public: FilterStatus filterStatus (void) const { return mFilterStatus ; } @@ -206,9 +206,9 @@ class ACAN2517FDFilters { public: uint8_t filterCount (void) const { return mFilterCount ; } -//------------------------------------------------------------------------------ +//······················································································································ // PRIVATE PROPERTIES -//------------------------------------------------------------------------------ +//······················································································································ private: uint8_t mFilterCount = 0 ; private: Filter * mFirstFilter = NULL ; @@ -216,23 +216,23 @@ class ACAN2517FDFilters { private: FilterStatus mFilterStatus = kFiltersOk ; private: uint8_t mFilterErrorIndex = 0 ; -//------------------------------------------------------------------------------ +//······················································································································ // NO COPY -//------------------------------------------------------------------------------ +//······················································································································ private: ACAN2517FDFilters (const ACAN2517FDFilters &) = delete ; private: ACAN2517FDFilters & operator = (const ACAN2517FDFilters &) = delete ; -//------------------------------------------------------------------------------ +//······················································································································ // Friend -//------------------------------------------------------------------------------ +//······················································································································ friend class ACAN2517FD ; -//------------------------------------------------------------------------------ +//······················································································································ } ; -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- #endif diff --git a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDSettings.cpp b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDSettings.cpp index 09184d30..0a97683a 100644 --- a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDSettings.cpp +++ b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDSettings.cpp @@ -1,19 +1,19 @@ -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- // A CAN driver for MCP2517FD (CANFD mode) // by Pierre Molinaro // https://github.com/pierremolinaro/acan2517FD // -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- #include "ACAN2517FDSettings.h" -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- #pragma GCC diagnostic error "-Wswitch-enum" -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- // sysClock -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- uint32_t ACAN2517FDSettings::sysClock (const Oscillator inOscillator) { uint32_t sysClock = 40UL * 1000 * 1000 ; @@ -39,9 +39,9 @@ uint32_t ACAN2517FDSettings::sysClock (const Oscillator inOscillator) { return sysClock ; } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- // CONSTRUCTOR -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- ACAN2517FDSettings::ACAN2517FDSettings (const Oscillator inOscillator, const uint32_t inDesiredArbitrationBitRate, @@ -51,93 +51,148 @@ mOscillator (inOscillator), mSysClock (sysClock (inOscillator)), mDesiredArbitrationBitRate (inDesiredArbitrationBitRate), mDataBitRateFactor (inDataBitRateFactor) { -// First compute data bit rate - const uint32_t maxDataTQCount = MAX_DATA_PHASE_SEGMENT_1 + MAX_DATA_PHASE_SEGMENT_2 ; // Setting for slowest bit rate - const uint32_t desiredDataBitRate = inDesiredArbitrationBitRate * uint8_t (inDataBitRateFactor) ; - uint32_t smallestError = UINT32_MAX ; - uint32_t bestBRP = MAX_BRP ; // Setting for lowest bit rate - uint32_t bestDataTQCount = maxDataTQCount ; // Setting for lowest bit rate - uint32_t dataTQCount = 4 ; - uint32_t brp = mSysClock / desiredDataBitRate / dataTQCount ; -//--- Loop for finding best BRP and best TQCount - while ((dataTQCount <= maxDataTQCount) && (brp > 0)) { - //--- Compute error using brp - if (brp <= MAX_BRP) { - const uint32_t error = mSysClock - desiredDataBitRate * dataTQCount * brp ; // error is always >= 0 - if (error <= smallestError) { - smallestError = error ; - bestBRP = brp ; - bestDataTQCount = dataTQCount ; + if (inDataBitRateFactor == DataBitRateFactor::x1) { // Single bit rate + const uint32_t maxTQCount = MAX_ARBITRATION_PHASE_SEGMENT_1 + MAX_ARBITRATION_PHASE_SEGMENT_2 + 1 ; // Setting for slowest bit rate + uint32_t BRP = MAX_BRP ; + uint32_t smallestError = UINT32_MAX ; + uint32_t bestBRP = 1 ; // Setting for highest bit rate + uint32_t bestTQCount = 4 ; // Setting for highest bit rate + uint32_t TQCount = mSysClock / inDesiredArbitrationBitRate / BRP ; + //--- Loop for finding best BRP and best TQCount + while ((TQCount <= (MAX_ARBITRATION_PHASE_SEGMENT_1 + MAX_ARBITRATION_PHASE_SEGMENT_2 + 1)) && (BRP > 0)) { + //--- Compute error using TQCount + if ((TQCount >= 4) && (TQCount <= maxTQCount)) { + const uint32_t error = mSysClock - inDesiredArbitrationBitRate * TQCount * BRP ; // error is always >= 0 + if (error <= smallestError) { + smallestError = error ; + bestBRP = BRP ; + bestTQCount = TQCount ; + } } - } - //--- Compute error using brp+1 - if (brp < MAX_BRP) { - const uint32_t error = desiredDataBitRate * dataTQCount * (brp + 1) - mSysClock ; // error is always >= 0 - if (error <= smallestError) { - smallestError = error ; - bestBRP = brp + 1 ; - bestDataTQCount = dataTQCount ; + //--- Compute error using TQCount+1 + if ((TQCount >= 3) && (TQCount < maxTQCount)) { + const uint32_t error = inDesiredArbitrationBitRate * (TQCount + 1) * BRP - mSysClock ; // error is always >= 0 + if (error <= smallestError) { + smallestError = error ; + bestBRP = BRP ; + bestTQCount = TQCount + 1 ; + } } + //--- Continue with next value of BRP + BRP -- ; + TQCount = (BRP == 0) ? (maxTQCount + 1) : (mSysClock / inDesiredArbitrationBitRate / BRP) ; } - //--- Continue with next value of BRP - dataTQCount += 1 ; - brp = mSysClock / desiredDataBitRate / dataTQCount ; + //--- Compute PS2 (1 <= PS2 <= 128) + uint32_t PS2 = bestTQCount / 5 ; // For sampling point at 80% + if (PS2 == 0) { + PS2 = 1 ; + }else if (PS2 > MAX_ARBITRATION_PHASE_SEGMENT_2) { + PS2 = MAX_ARBITRATION_PHASE_SEGMENT_2 ; + } + //--- Compute PS1 (1 <= PS1 <= 256) + uint32_t PS1 = bestTQCount - PS2 - 1 /* Sync Seg */ ; + if (PS1 > MAX_ARBITRATION_PHASE_SEGMENT_1) { + PS2 += PS1 - MAX_ARBITRATION_PHASE_SEGMENT_1 ; + PS1 = MAX_ARBITRATION_PHASE_SEGMENT_1 ; + } + //--- + mBitRatePrescaler = (uint16_t) bestBRP ; + mArbitrationPhaseSegment1 = (uint16_t) PS1 ; + mArbitrationPhaseSegment2 = (uint8_t) PS2 ; + mArbitrationSJW = mArbitrationPhaseSegment2 ; // Always 1 <= SJW <= 128, and SJW <= mArbitrationPhaseSegment2 + //--- Final check of the nominal configuration + const uint32_t W = bestTQCount * mDesiredArbitrationBitRate * bestBRP ; + const uint64_t diff = (mSysClock > W) ? (mSysClock - W) : (W - mSysClock) ; + const uint64_t ppm = (uint64_t) (1000UL * 1000UL) ; // UL suffix is required for Arduino Uno + mArbitrationBitRateClosedToDesiredRate = (diff * ppm) <= (((uint64_t) W) * inTolerancePPM) ; + }else{ // Dual bit rate, first compute data bit rate + const uint32_t maxDataTQCount = MAX_DATA_PHASE_SEGMENT_1 + MAX_DATA_PHASE_SEGMENT_2 ; // Setting for slowest bit rate + const uint32_t desiredDataBitRate = inDesiredArbitrationBitRate * uint8_t (inDataBitRateFactor) ; + uint32_t smallestError = UINT32_MAX ; + uint32_t bestBRP = MAX_BRP ; // Setting for lowest bit rate + uint32_t bestDataTQCount = maxDataTQCount ; // Setting for lowest bit rate + uint32_t dataTQCount = 4 ; + uint32_t brp = mSysClock / desiredDataBitRate / dataTQCount ; + //--- Loop for finding best BRP and best TQCount + while ((dataTQCount <= maxDataTQCount) && (brp > 0)) { + //--- Compute error using brp + if (brp <= MAX_BRP) { + const uint32_t error = mSysClock - desiredDataBitRate * dataTQCount * brp ; // error is always >= 0 + if (error <= smallestError) { + smallestError = error ; + bestBRP = brp ; + bestDataTQCount = dataTQCount ; + } + } + //--- Compute error using brp+1 + if (brp < MAX_BRP) { + const uint32_t error = desiredDataBitRate * dataTQCount * (brp + 1) - mSysClock ; // error is always >= 0 + if (error <= smallestError) { + smallestError = error ; + bestBRP = brp + 1 ; + bestDataTQCount = dataTQCount ; + } + } + //--- Continue with next value of BRP + dataTQCount += 1 ; + brp = mSysClock / desiredDataBitRate / dataTQCount ; + } + //--- Compute data PS2 (1 <= PS2 <= 16) + uint32_t dataPS2 = bestDataTQCount / 5 ; // For sampling point at 80% + if (dataPS2 == 0) { + dataPS2 = 1 ; + } + //--- Compute data PS1 (1 <= PS1 <= 32) + uint32_t dataPS1 = bestDataTQCount - dataPS2 - 1 /* Sync Seg */ ; + if (dataPS1 > MAX_DATA_PHASE_SEGMENT_1) { + dataPS2 += dataPS1 - MAX_DATA_PHASE_SEGMENT_1 ; + dataPS1 = MAX_DATA_PHASE_SEGMENT_1 ; + } + //--- + if ((mDesiredArbitrationBitRate * uint32_t (inDataBitRateFactor)) <= (1000UL * 1000)) { + mTDCO = 0 ; + }else{ + const int TDCO = bestBRP * dataPS1 ; // According to DS20005678D, §3.4.8 Page 20 + mTDCO = (TDCO > 63) ? 63 : (int8_t) TDCO ; + } + mDataPhaseSegment1 = (uint8_t) dataPS1 ; + mDataPhaseSegment2 = (uint8_t) dataPS2 ; + mDataSJW = mDataPhaseSegment2 ; + const uint32_t arbitrationTQCount = bestDataTQCount * uint8_t (mDataBitRateFactor) ; + //--- Compute arbiration PS2 (1 <= PS2 <= 128) + uint32_t arbitrationPS2 = arbitrationTQCount / 5 ; // For sampling point at 80% + if (arbitrationPS2 == 0) { + arbitrationPS2 = 1 ; + } + //--- Compute PS1 (1 <= PS1 <= 256) + uint32_t arbitrationPS1 = arbitrationTQCount - arbitrationPS2 - 1 /* Sync Seg */ ; + if (arbitrationPS1 > MAX_ARBITRATION_PHASE_SEGMENT_1) { + arbitrationPS2 += arbitrationPS1 - MAX_ARBITRATION_PHASE_SEGMENT_1 ; + arbitrationPS1 = MAX_ARBITRATION_PHASE_SEGMENT_1 ; + } + //--- + mBitRatePrescaler = (uint16_t) bestBRP ; + mArbitrationPhaseSegment1 = (uint16_t) arbitrationPS1 ; + mArbitrationPhaseSegment2 = (uint8_t) arbitrationPS2 ; + mArbitrationSJW = mArbitrationPhaseSegment2 ; // Always 1 <= SJW <= 128, and SJW <= mArbitrationPhaseSegment2 + //--- Final check of the nominal configuration + const uint32_t W = arbitrationTQCount * mDesiredArbitrationBitRate * bestBRP ; + const uint64_t diff = (mSysClock > W) ? (mSysClock - W) : (W - mSysClock) ; + const uint64_t ppm = (uint64_t) (1000UL * 1000UL) ; // UL suffix is required for Arduino Uno + mArbitrationBitRateClosedToDesiredRate = (diff * ppm) <= (((uint64_t) W) * inTolerancePPM) ; } -//--- Compute data PS2 (1 <= PS2 <= 16) - uint32_t dataPS2 = bestDataTQCount / 5 ; // For sampling point at 80% - if (dataPS2 == 0) { - dataPS2 = 1 ; - } -//--- Compute data PS1 (1 <= PS1 <= 32) - uint32_t dataPS1 = bestDataTQCount - dataPS2 - 1 /* Sync Seg */ ; - if (dataPS1 > MAX_DATA_PHASE_SEGMENT_1) { - dataPS2 += dataPS1 - MAX_DATA_PHASE_SEGMENT_1 ; - dataPS1 = MAX_DATA_PHASE_SEGMENT_1 ; - } -//--- - if ((mDesiredArbitrationBitRate * uint32_t (inDataBitRateFactor)) <= (1000UL * 1000)) { - mTDCO = 0 ; - }else{ - const int TDCO = bestBRP * dataPS1 ; // According to DS20005678D, §3.4.8 Page 20 - mTDCO = (TDCO > 63) ? 63 : (int8_t) TDCO ; - } - mDataPhaseSegment1 = (uint8_t) dataPS1 ; - mDataPhaseSegment2 = (uint8_t) dataPS2 ; - mDataSJW = mDataPhaseSegment2 ; - const uint32_t arbitrationTQCount = bestDataTQCount * uint8_t (mDataBitRateFactor) ; -//--- Compute arbitration PS2 (1 <= PS2 <= 128) - uint32_t arbitrationPS2 = arbitrationTQCount / 5 ; // For sampling point at 80% - if (arbitrationPS2 == 0) { - arbitrationPS2 = 1 ; - } -//--- Compute PS1 (1 <= PS1 <= 256) - uint32_t arbitrationPS1 = arbitrationTQCount - arbitrationPS2 - 1 /* Sync Seg */ ; - if (arbitrationPS1 > MAX_ARBITRATION_PHASE_SEGMENT_1) { - arbitrationPS2 += arbitrationPS1 - MAX_ARBITRATION_PHASE_SEGMENT_1 ; - arbitrationPS1 = MAX_ARBITRATION_PHASE_SEGMENT_1 ; - } -//--- - mBitRatePrescaler = (uint16_t) bestBRP ; - mArbitrationPhaseSegment1 = (uint16_t) arbitrationPS1 ; - mArbitrationPhaseSegment2 = (uint8_t) arbitrationPS2 ; - mArbitrationSJW = mArbitrationPhaseSegment2 ; // Always 1 <= SJW <= 128, and SJW <= mArbitrationPhaseSegment2 -//--- Final check of the nominal configuration - const uint32_t W = arbitrationTQCount * mDesiredArbitrationBitRate * bestBRP ; - const uint64_t diff = (mSysClock > W) ? (mSysClock - W) : (W - mSysClock) ; - const uint64_t ppm = (uint64_t) (1000UL * 1000UL) ; // UL suffix is required for Arduino Uno - mArbitrationBitRateClosedToDesiredRate = (diff * ppm) <= (((uint64_t) W) * inTolerancePPM) ; } ; -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- // ACCESSORS -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- uint32_t ACAN2517FDSettings::actualArbitrationBitRate (void) const { const uint32_t arbitrationTQCount = 1 /* Sync Seg */ + mArbitrationPhaseSegment1 + mArbitrationPhaseSegment2 ; return mSysClock / mBitRatePrescaler / arbitrationTQCount ; } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- uint32_t ACAN2517FDSettings::actualDataBitRate (void) const { if (mDataBitRateFactor == DataBitRateFactor::x1) { @@ -148,14 +203,14 @@ uint32_t ACAN2517FDSettings::actualDataBitRate (void) const { } } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- bool ACAN2517FDSettings::exactArbitrationBitRate (void) const { const uint32_t TQCount = 1 /* Sync Seg */ + mArbitrationPhaseSegment1 + mArbitrationPhaseSegment2 ; return mSysClock == (mBitRatePrescaler * mDesiredArbitrationBitRate * TQCount) ; } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- bool ACAN2517FDSettings::exactDataBitRate (void) const { if (mDataBitRateFactor == DataBitRateFactor::x1) { @@ -166,7 +221,7 @@ bool ACAN2517FDSettings::exactDataBitRate (void) const { } } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- bool ACAN2517FDSettings::dataBitRateIsAMultipleOfArbitrationBitRate (void) const { bool result = mDataBitRateFactor == DataBitRateFactor::x1 ; @@ -178,7 +233,7 @@ bool ACAN2517FDSettings::dataBitRateIsAMultipleOfArbitrationBitRate (void) const return result ; } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- uint32_t ACAN2517FDSettings::ppmFromDesiredArbitrationBitRate (void) const { const uint32_t TQCount = 1 /* Sync Seg */ + mArbitrationPhaseSegment1 + mArbitrationPhaseSegment2 ; @@ -188,7 +243,7 @@ uint32_t ACAN2517FDSettings::ppmFromDesiredArbitrationBitRate (void) const { return (uint32_t) ((diff * ppm) / W) ; } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- uint32_t ACAN2517FDSettings::arbitrationSamplePointFromBitStart (void) const { const uint32_t nominalTQCount = 1 /* Sync Seg */ + mArbitrationPhaseSegment1 + mArbitrationPhaseSegment2 ; @@ -197,7 +252,7 @@ uint32_t ACAN2517FDSettings::arbitrationSamplePointFromBitStart (void) const { return (samplePoint * partPerCent) / nominalTQCount ; } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- uint32_t ACAN2517FDSettings::dataSamplePointFromBitStart (void) const { const uint32_t nominalTQCount = 1 /* Sync Seg */ + mDataPhaseSegment1 + mDataPhaseSegment2 ; @@ -206,7 +261,7 @@ uint32_t ACAN2517FDSettings::dataSamplePointFromBitStart (void) const { return (samplePoint * partPerCent) / nominalTQCount ; } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- uint32_t ACAN2517FDSettings::CANBitSettingConsistency (void) const { uint32_t errorCode = 0 ; // Means no error @@ -274,9 +329,9 @@ uint32_t ACAN2517FDSettings::CANBitSettingConsistency (void) const { return errorCode ; } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- // RAM USAGE -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- uint32_t ACAN2517FDSettings::ramUsage (void) const { uint32_t result = 0 ; @@ -290,11 +345,11 @@ uint32_t ACAN2517FDSettings::ramUsage (void) const { return result ; } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- uint32_t ACAN2517FDSettings::objectSizeForPayload (const PayloadSize inPayload) { static const uint8_t kPayload [8] = {16, 20, 24, 28, 32, 40, 56, 72} ; return kPayload [inPayload] ; } -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- diff --git a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDSettings.h b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDSettings.h index 38eb0381..ec86560c 100644 --- a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDSettings.h +++ b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FDSettings.h @@ -1,25 +1,25 @@ -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- // A CAN driver for MCP2517FD (CANFD mode) // by Pierre Molinaro // https://github.com/pierremolinaro/acan2517FD // -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- #pragma once -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- -#include "ACAN2517FD_DataBitRateFactor.h" +#include "ACANFD_DataBitRateFactor.h" -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- // ACAN2517FDSettings class -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- class ACAN2517FDSettings { - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // ENUMERATED TYPES - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// ENUMERATED TYPES +//······················································································································ public: typedef enum : uint8_t { OSC_4MHz, @@ -64,9 +64,9 @@ class ACAN2517FDSettings { PAYLOAD_64 = 7 } PayloadSize ; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Deprecated enumeration (now use DataBitRateFactor declared in ACANFD_DataBitRateFactor.h) - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// Deprecated enumeration (now use DataBitRateFactor declared in ACANFD_DataBitRateFactor.h) +//······················································································································ public : typedef enum : uint8_t { DATA_BITRATE_x1 = 1, @@ -81,18 +81,18 @@ class ACAN2517FDSettings { DATA_BITRATE_x10 = 10 } DataBitRateFactor_Deprecated ; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // CONSTRUCTOR - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// CONSTRUCTOR +//······················································································································ public: ACAN2517FDSettings (const Oscillator inOscillator, const uint32_t inDesiredArbitrationBitRate, const DataBitRateFactor inDataBitRateFactor, const uint32_t inTolerancePPM = 1000) ; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // DEPRECATED CONSTRUCTOR (for compatibility with version < 2.1.0) - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// DEPRECATED CONSTRUCTOR (for compatibility with version < 2.1.0) +//······················································································································ public: ACAN2517FDSettings (const Oscillator inOscillator, const uint32_t inDesiredArbitrationBitRate, @@ -101,9 +101,9 @@ class ACAN2517FDSettings { ACAN2517FDSettings (inOscillator, inDesiredArbitrationBitRate, DataBitRateFactor (inDataBitRateFactor), inTolerancePPM) { } - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // CAN BIT TIMING - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// CAN BIT TIMING +//······················································································································ private: Oscillator mOscillator ; private: uint32_t mSysClock ; // In Hz @@ -123,44 +123,44 @@ class ACAN2517FDSettings { //--- Transmitter Delay Compensation Offset public: int8_t mTDCO = 0 ; // -64 ... +63 - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // MCP2517FD TXCAN pin is Open Drain ? - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// MCP2517FD TXCAN pin is Open Drain ? +//······················································································································ public: bool mTXCANIsOpenDrain = false ; // false --> Push/Pull Output, true --> Open Drain Output - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // MCP2517FD INT pin is Open Drain ? - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// MCP2517FD INT pin is Open Drain ? +//······················································································································ public: bool mINTIsOpenDrain = false ; // false --> Push/Pull Output, true --> Open Drain Output - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // ISO CRC Enable - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// ISO CRC Enable +//······················································································································ // false --> Do NOT include Stuff Bit Count in CRC Field and use CRC Initialization Vector with all zeros // true --> Include Stuff Bit Count in CRC Field and use Non-Zero CRC Initialization Vector according to ISO 11898-1:2015 public: bool mISOCRCEnabled = true ; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // CLKO pin function (default value is MCP2517FD power on setting) - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// CLKO pin function (default value is MCP2517FD power on setting) +//······················································································································ public: CLKOpin mCLKOPin = CLKO_DIVIDED_BY_10 ; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Requested mode - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// Requested mode +//······················································································································ public: OperationMode mRequestedMode = NormalFD ; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // TRANSMIT FIFO - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// TRANSMIT FIFO +//······················································································································ //--- Driver transmit buffer size - public: uint16_t mDriverTransmitFIFOSize = 16 ; // >= 0 + public: uint16_t mDriverTransmitFIFOSize = 22 ; // >= 0 //--- Controller transmit FIFO size public: uint8_t mControllerTransmitFIFOSize = 1 ; // 1 ... 32 @@ -174,9 +174,9 @@ class ACAN2517FDSettings { //--- Controller transmit FIFO retransmission attempts public: RetransmissionAttempts mControllerTransmitFIFORetransmissionAttempts = UnlimitedNumber ; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // TXQ BUFFER - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// TXQ BUFFER +//······················································································································ //--- TXQ buffer size (0 --> TXQ disabled) public: uint8_t mControllerTXQSize = 0 ; // 0 ... 32 @@ -191,9 +191,9 @@ class ACAN2517FDSettings { public: RetransmissionAttempts mControllerTXQBufferRetransmissionAttempts = UnlimitedNumber ; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // RECEIVE FIFO - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// RECEIVE FIFO +//······················································································································ //--- Driver receive buffer size public: uint16_t mDriverReceiveFIFOSize = 32 ; // > 0 @@ -204,15 +204,15 @@ class ACAN2517FDSettings { //--- Controller receive FIFO size public: uint8_t mControllerReceiveFIFOSize = 27 ; // 1 ... 32 - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // SYSCLOCK frequency computation - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// SYSCLOCK frequency computation +//······················································································································ public: static uint32_t sysClock (const Oscillator inOscillator) ; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Accessors - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// Accessors +//······················································································································ public: Oscillator oscillator (void) const { return mOscillator ; } public: uint32_t sysClock (void) const { return mSysClock ; } @@ -222,61 +222,61 @@ class ACAN2517FDSettings { public: bool exactDataBitRate (void) const ; public: bool dataBitRateIsAMultipleOfArbitrationBitRate (void) const ; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // RAM USAGE - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// RAM USAGE +//······················································································································ public: uint32_t ramUsage (void) const ; public: static uint32_t objectSizeForPayload (const PayloadSize inPayload) ; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Distance between actual bit rate and requested bit rate (in ppm, part-per-million) - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// Distance between actual bit rate and requested bit rate (in ppm, part-per-million) +//······················································································································ public: uint32_t ppmFromDesiredArbitrationBitRate (void) const ; public: uint32_t ppmFromDesiredDataBitRate (void) const ; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Distance of sample point from bit start (in ppc, part-per-cent, denoted by %) - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// Distance of sample point from bit start (in ppc, part-per-cent, denoted by %) +//······················································································································ public: uint32_t arbitrationSamplePointFromBitStart (void) const ; public: uint32_t dataSamplePointFromBitStart (void) const ; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Bit settings are consistent ? (returns 0 if ok) - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// Bit settings are consistent ? (returns 0 if ok) +//······················································································································ public: uint32_t CANBitSettingConsistency (void) const ; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Constants returned by CANBitSettingConsistency - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// Constants returned by CANBitSettingConsistency +//······················································································································ - public: static const uint32_t kBitRatePrescalerIsZero = uint32_t (1) << 0 ; - public: static const uint32_t kBitRatePrescalerIsGreaterThan256 = uint32_t (1) << 1 ; - public: static const uint32_t kArbitrationPhaseSegment1IsLowerThan2 = uint32_t (1) << 2 ; - public: static const uint32_t kArbitrationPhaseSegment1IsGreaterThan256 = uint32_t (1) << 3 ; - public: static const uint32_t kArbitrationPhaseSegment2IsZero = uint32_t (1) << 4 ; - public: static const uint32_t kArbitrationPhaseSegment2IsGreaterThan128 = uint32_t (1) << 5 ; - public: static const uint32_t kArbitrationSJWIsZero = uint32_t (1) << 6 ; - public: static const uint32_t kArbitrationSJWIsGreaterThan128 = uint32_t (1) << 7 ; - public: static const uint32_t kArbitrationSJWIsGreaterThanPhaseSegment1 = uint32_t (1) << 8 ; - public: static const uint32_t kArbitrationSJWIsGreaterThanPhaseSegment2 = uint32_t (1) << 9 ; - public: static const uint32_t kArbitrationTQCountNotDivisibleByDataBitRateFactor = uint32_t (1) << 10 ; - public: static const uint32_t kDataPhaseSegment1IsLowerThan2 = uint32_t (1) << 11 ; - public: static const uint32_t kDataPhaseSegment1IsGreaterThan32 = uint32_t (1) << 12 ; - public: static const uint32_t kDataPhaseSegment2IsZero = uint32_t (1) << 13 ; - public: static const uint32_t kDataPhaseSegment2IsGreaterThan16 = uint32_t (1) << 14 ; - public: static const uint32_t kDataSJWIsZero = uint32_t (1) << 15 ; - public: static const uint32_t kDataSJWIsGreaterThan16 = uint32_t (1) << 16 ; - public: static const uint32_t kDataSJWIsGreaterThanPhaseSegment1 = uint32_t (1) << 17 ; - public: static const uint32_t kDataSJWIsGreaterThanPhaseSegment2 = uint32_t (1) << 18 ; + public: static const uint32_t kBitRatePrescalerIsZero = ((uint32_t) 1) << 0 ; + public: static const uint32_t kBitRatePrescalerIsGreaterThan256 = ((uint32_t) 1) << 1 ; + public: static const uint32_t kArbitrationPhaseSegment1IsLowerThan2 = ((uint32_t) 1) << 2 ; + public: static const uint32_t kArbitrationPhaseSegment1IsGreaterThan256 = ((uint32_t) 1) << 3 ; + public: static const uint32_t kArbitrationPhaseSegment2IsZero = ((uint32_t) 1) << 4 ; + public: static const uint32_t kArbitrationPhaseSegment2IsGreaterThan128 = ((uint32_t) 1) << 5 ; + public: static const uint32_t kArbitrationSJWIsZero = ((uint32_t) 1) << 6 ; + public: static const uint32_t kArbitrationSJWIsGreaterThan128 = ((uint32_t) 1) << 7 ; + public: static const uint32_t kArbitrationSJWIsGreaterThanPhaseSegment1 = ((uint32_t) 1) << 8 ; + public: static const uint32_t kArbitrationSJWIsGreaterThanPhaseSegment2 = ((uint32_t) 1) << 9 ; + public: static const uint32_t kArbitrationTQCountNotDivisibleByDataBitRateFactor = ((uint32_t) 1) << 10 ; + public: static const uint32_t kDataPhaseSegment1IsLowerThan2 = ((uint32_t) 1) << 11 ; + public: static const uint32_t kDataPhaseSegment1IsGreaterThan32 = ((uint32_t) 1) << 12 ; + public: static const uint32_t kDataPhaseSegment2IsZero = ((uint32_t) 1) << 13 ; + public: static const uint32_t kDataPhaseSegment2IsGreaterThan16 = ((uint32_t) 1) << 14 ; + public: static const uint32_t kDataSJWIsZero = ((uint32_t) 1) << 15 ; + public: static const uint32_t kDataSJWIsGreaterThan16 = ((uint32_t) 1) << 16 ; + public: static const uint32_t kDataSJWIsGreaterThanPhaseSegment1 = ((uint32_t) 1) << 17 ; + public: static const uint32_t kDataSJWIsGreaterThanPhaseSegment2 = ((uint32_t) 1) << 18 ; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Max values - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ +// Max values +//······················································································································ public: static const uint16_t MAX_BRP = 256 ; @@ -288,9 +288,9 @@ class ACAN2517FDSettings { public: static const uint8_t MAX_DATA_PHASE_SEGMENT_2 = 16 ; public: static const uint8_t MAX_DATA_SJW = 16 ; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//······················································································································ } ; -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- diff --git a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD_ACANFDBuffer.h b/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD_ACANFDBuffer.h deleted file mode 100644 index e474f9f1..00000000 --- a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD_ACANFDBuffer.h +++ /dev/null @@ -1,122 +0,0 @@ -//------------------------------------------------------------------------------ -// A CAN driver for MCP2517FD CAN Controller in CANFD mode -// by Pierre Molinaro -// https://github.com/pierremolinaro/acan2517FD -// -//------------------------------------------------------------------------------ - -#ifndef ACANFD_BUFFER_CLASS_DEFINED -#define ACANFD_BUFFER_CLASS_DEFINED - -//------------------------------------------------------------------------------ - -#include "ACAN2517FD_CANFDMessage.h" - -//------------------------------------------------------------------------------ - -class ACANFDBuffer { - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Default constructor - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public: ACANFDBuffer (void) : - mBuffer (NULL), - mSize (0), - mReadIndex (0), - mCount (0), - mPeakCount (0) { - } - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Destructor - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public: ~ ACANFDBuffer (void) { - delete [] mBuffer ; - } - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Private properties - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - private: CANFDMessage * mBuffer ; - private: uint32_t mSize ; - private: uint32_t mReadIndex ; - private: uint32_t mCount ; - private: uint32_t mPeakCount ; // > mSize if overflow did occur - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Accessors - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public: inline uint32_t size (void) const { return mSize ; } - public: inline uint32_t count (void) const { return mCount ; } - public: inline bool isFull (void) const { return mCount == mSize ; } // Added in release 2.17 (thanks to Flole998) - public: inline uint32_t peakCount (void) const { return mPeakCount ; } - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // initWithSize - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public: void initWithSize (const uint32_t inSize) { - delete [] mBuffer ; mBuffer = new CANFDMessage [inSize] ; - mSize = inSize ; - mReadIndex = 0 ; - mCount = 0 ; - mPeakCount = 0 ; - } - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // append - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public: bool append (const CANFDMessage & inMessage) { - const bool ok = mCount < mSize ; - if (ok) { - uint32_t writeIndex = mReadIndex + mCount ; - if (writeIndex >= mSize) { - writeIndex -= mSize ; - } - mBuffer [writeIndex] = inMessage ; - mCount += 1 ; - if (mPeakCount < mCount) { - mPeakCount = mCount ; - } - }else{ - mPeakCount = mSize + 1 ; - } - return ok ; - } - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Remove - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public: bool remove (CANFDMessage & outMessage) { - const bool ok = mCount > 0 ; - if (ok) { - outMessage = mBuffer [mReadIndex] ; - mCount -= 1 ; - mReadIndex += 1 ; - if (mReadIndex == mSize) { - mReadIndex = 0 ; - } - } - return ok ; - } - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // No copy - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - private: ACANFDBuffer (const ACANFDBuffer &) = delete ; - private: ACANFDBuffer & operator = (const ACANFDBuffer &) = delete ; - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -} ; - -//------------------------------------------------------------------------------ - -#endif diff --git a/Software/src/lib/pierremolinaro-ACAN2517FD/ACANFDBuffer.h b/Software/src/lib/pierremolinaro-ACAN2517FD/ACANFDBuffer.h new file mode 100644 index 00000000..75de5ef8 --- /dev/null +++ b/Software/src/lib/pierremolinaro-ACAN2517FD/ACANFDBuffer.h @@ -0,0 +1,119 @@ +//---------------------------------------------------------------------------------------------------------------------- +// A CAN driver for MCP2517FD CAN Controller in CANFD mode +// by Pierre Molinaro +// https://github.com/pierremolinaro/acan2517FD +// +//---------------------------------------------------------------------------------------------------------------------- + +#ifndef ACANFD_BUFFER_CLASS_DEFINED +#define ACANFD_BUFFER_CLASS_DEFINED + +//---------------------------------------------------------------------------------------------------------------------- + +#include "CANFDMessage.h" + +//---------------------------------------------------------------------------------------------------------------------- + +class ACANFDBuffer { + +//······················································································································ +// Default constructor +//······················································································································ + + public: ACANFDBuffer (void) : + mBuffer (NULL), + mSize (0), + mReadIndex (0), + mCount (0), + mPeakCount (0) { + } + +//······················································································································ +// Destructor +//······················································································································ + + public: ~ ACANFDBuffer (void) { + delete [] mBuffer ; + } + +//······················································································································ +// Private properties +//······················································································································ + + private: CANFDMessage * mBuffer ; + private: uint32_t mSize ; + private: uint32_t mReadIndex ; + private: uint32_t mCount ; + private: uint32_t mPeakCount ; // > mSize if overflow did occur + +//······················································································································ +// Accessors +//······················································································································ + + public: inline uint32_t size (void) const { return mSize ; } + public: inline uint32_t count (void) const { return mCount ; } + public: inline bool isFull (void) const { return mCount == mSize ; } // Added in release 2.17 (thanks to Flole998) + public: inline uint32_t peakCount (void) const { return mPeakCount ; } + +//······················································································································ +// initWithSize +//······················································································································ + + public: void initWithSize (const uint32_t inSize) { + delete [] mBuffer ; mBuffer = new CANFDMessage [inSize] ; + mSize = inSize ; + mReadIndex = 0 ; + mCount = 0 ; + mPeakCount = 0 ; + } + +//······················································································································ +// append +//······················································································································ + + public: bool append (const CANFDMessage & inMessage) { + const bool ok = mCount < mSize ; + if (ok) { + uint32_t writeIndex = mReadIndex + mCount ; + if (writeIndex >= mSize) { + writeIndex -= mSize ; + } + mBuffer [writeIndex] = inMessage ; + mCount += 1 ; + if (mPeakCount < mCount) { + mPeakCount = mCount ; + } + }else{ + mPeakCount = mSize + 1 ; + } + return ok ; + } + +//······················································································································ +// Remove +//······················································································································ + + public: bool remove (CANFDMessage & outMessage) { + const bool ok = mCount > 0 ; + if (ok) { + outMessage = mBuffer [mReadIndex] ; + mCount -= 1 ; + mReadIndex += 1 ; + if (mReadIndex == mSize) { + mReadIndex = 0 ; + } + } + return ok ; + } + +//······················································································································ +// No copy +//······················································································································ + + private: ACANFDBuffer (const ACANFDBuffer &) = delete ; + private: ACANFDBuffer & operator = (const ACANFDBuffer &) = delete ; +} ; + +//---------------------------------------------------------------------------------------------------------------------- + +#endif diff --git a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD_DataBitRateFactor.h b/Software/src/lib/pierremolinaro-ACAN2517FD/ACANFD_DataBitRateFactor.h similarity index 73% rename from Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD_DataBitRateFactor.h rename to Software/src/lib/pierremolinaro-ACAN2517FD/ACANFD_DataBitRateFactor.h index b3868638..7177cae4 100644 --- a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD_DataBitRateFactor.h +++ b/Software/src/lib/pierremolinaro-ACAN2517FD/ACANFD_DataBitRateFactor.h @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- // A CANFD driver // by Pierre Molinaro @@ -6,16 +6,16 @@ // https://github.com/pierremolinaro/ACAN_T4 // https://github.com/pierremolinaro/ACAN2517FD // -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- #ifndef ACANFD_DATA_BIT_RATE_FACTOR_DEFINED #define ACANFD_DATA_BIT_RATE_FACTOR_DEFINED -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- #include -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- enum class DataBitRateFactor : uint8_t { x1 = 1, @@ -30,6 +30,6 @@ enum class DataBitRateFactor : uint8_t { x10 = 10 } ; -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- #endif diff --git a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD_CANFDMessage.h b/Software/src/lib/pierremolinaro-ACAN2517FD/CANFDMessage.h similarity index 65% rename from Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD_CANFDMessage.h rename to Software/src/lib/pierremolinaro-ACAN2517FD/CANFDMessage.h index 64f3838b..10cf7d3b 100644 --- a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD_CANFDMessage.h +++ b/Software/src/lib/pierremolinaro-ACAN2517FD/CANFDMessage.h @@ -1,21 +1,21 @@ -//------------------------------------------------------------------------------ +//----------------------------------------------------------------------------- // Generic CANFD Message // by Pierre Molinaro // // https://github.com/pierremolinaro/acan2517FD // -//------------------------------------------------------------------------------ +//----------------------------------------------------------------------------- #ifndef GENERIC_CANFD_MESSAGE_DEFINED #define GENERIC_CANFD_MESSAGE_DEFINED -//------------------------------------------------------------------------------ +//----------------------------------------------------------------------------- -#include "ACAN2517FD_CANMessage.h" +#include "CANMessage.h" -//------------------------------------------------------------------------------ +//----------------------------------------------------------------------------- // CANFDMessage class -//------------------------------------------------------------------------------ +//----------------------------------------------------------------------------- // Note that "len" field contains the actual length, not its encoding in CANFD frames // Valid values are: 0, 1, ..., 8, 12, 16, 20, 24, 32, 48, 64. // Having other values is an error that prevents frame to be sent by tryToSend @@ -23,9 +23,9 @@ class CANFDMessage { - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Constructors - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//············································································· +// Constructors +//············································································· public : CANFDMessage (void) : id (0), // Frame identifier @@ -36,7 +36,7 @@ class CANFDMessage { data () { } - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//············································································· public : CANFDMessage (const CANMessage & inMessage) : id (inMessage.id), // Frame identifier @@ -48,9 +48,9 @@ class CANFDMessage { data64 [0] = inMessage.data64 ; } - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Enumerated Type - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//············································································· +// Enumerated Type +//············································································· public: typedef enum : uint8_t { CAN_REMOTE, @@ -59,9 +59,9 @@ class CANFDMessage { CANFD_WITH_BIT_RATE_SWITCH } Type ; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Properties - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//············································································· +// Properties +//············································································· public : uint32_t id ; // Frame identifier public : bool ext ; // false -> base frame, true -> extended frame @@ -80,9 +80,9 @@ class CANFDMessage { uint8_t data [64] ; } ; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Methods - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//············································································· +// Methods +//············································································· public: void pad (void) { uint8_t paddedLength = len ; @@ -107,7 +107,7 @@ class CANFDMessage { } } - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//············································································· public: bool isValid (void) const { if ((type == CAN_REMOTE) || (type == CAN_DATA)) { // Remote frame @@ -121,14 +121,14 @@ class CANFDMessage { } } - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +//············································································· } ; -//------------------------------------------------------------------------------ +//----------------------------------------------------------------------------- typedef void (*ACANFDCallBackRoutine) (const CANFDMessage & inMessage) ; -//------------------------------------------------------------------------------ +//----------------------------------------------------------------------------- #endif diff --git a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD_CANMessage.h b/Software/src/lib/pierremolinaro-ACAN2517FD/CANMessage.h similarity index 84% rename from Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD_CANMessage.h rename to Software/src/lib/pierremolinaro-ACAN2517FD/CANMessage.h index 9e175a3a..6c687d7e 100644 --- a/Software/src/lib/pierremolinaro-ACAN2517FD/ACAN2517FD_CANMessage.h +++ b/Software/src/lib/pierremolinaro-ACAN2517FD/CANMessage.h @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- // Generic CAN Message // by Pierre Molinaro // @@ -8,16 +8,16 @@ // https://github.com/pierremolinaro/acan2517 // https://github.com/pierremolinaro/acan2517FD // -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- #ifndef GENERIC_CAN_MESSAGE_DEFINED #define GENERIC_CAN_MESSAGE_DEFINED -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- #include -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- class CANMessage { public : uint32_t id = 0 ; // Frame identifier @@ -38,12 +38,12 @@ class CANMessage { } ; } ; -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- typedef enum {kStandard, kExtended} tFrameFormat ; typedef enum {kData, kRemote} tFrameKind ; typedef void (*ACANCallBackRoutine) (const CANMessage & inMessage) ; -//------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------------------------------------------- #endif diff --git a/Software/src/lib/pierremolinaro-ACAN2517FD/README.md b/Software/src/lib/pierremolinaro-ACAN2517FD/README.md old mode 100755 new mode 100644 diff --git a/Software/src/lib/pierremolinaro-ACAN2517FD/library.properties b/Software/src/lib/pierremolinaro-ACAN2517FD/library.properties deleted file mode 100644 index b249255d..00000000 --- a/Software/src/lib/pierremolinaro-ACAN2517FD/library.properties +++ /dev/null @@ -1,9 +0,0 @@ -name=ACAN2517FD -version=2.1.16 -author=Pierre Molinaro -maintainer=Pierre Molinaro -sentence=Driver for MCP2517FD and MCP2518FD CAN Controller (CAN FD mode) -paragraph=This library is an Arduino CAN network driver for the MCP2517FD, the MCP2518FD and the MCP251863 CAN Controller, in CAN FD mode. Compatible with ACAN, ACAN2515, ACAN2517 libraries, with ACAN_T4 library from version 2.1.0. Default configuration sends and receives any frame – no default filter to provide. Reception filters (up to 32) can be easily defined. Compatible with ESP32 from version 1.1.0. -category=Communication -url=https://github.com/pierremolinaro/acan2517FD -architectures=* From 224c33ec1d6ed3233adef0c06df6acbc8272b5ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 2 Sep 2025 19:42:26 +0300 Subject: [PATCH 117/245] Make more settings configurable --- Software/USER_SETTINGS.h | 4 -- Software/src/communication/nvm/comm_nvm.cpp | 8 +++- .../precharge_control/precharge_control.cpp | 36 ++++++---------- .../precharge_control/precharge_control.h | 5 +++ Software/src/devboard/mqtt/mqtt.cpp | 25 ++++++----- Software/src/devboard/mqtt/mqtt.h | 7 +-- .../src/devboard/webserver/settings_html.cpp | 43 ++++++++++++++++++- Software/src/devboard/webserver/webserver.cpp | 12 ++++-- 8 files changed, 92 insertions(+), 48 deletions(-) diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index 30e2a80a..e2873c17 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -7,10 +7,6 @@ /* Shunt/Contactor settings (Optional) */ //#define BMW_SBOX // SBOX relay control & battery current/voltage measurement -/* Automatic Precharge settings (Optional) If you have a battery that expects an external voltage applied before opening contactors (within the battery), configure this section */ -//#define PRECHARGE_CONTROL //Enable this line to control a modified HIA4V1 via PWM on the HIA4V1_PIN (see Wiki and HAL for pin definition) -//#define INVERTER_DISCONNECT_CONTACTOR_IS_NORMALLY_OPEN //Enable this line if you use a normally open contactor instead of normally closed - /* Connectivity options */ //#define WIFICONFIG //Enable this line to set a static IP address / gateway /subnet mask for the device. see USER_SETTINGS.cpp for the settings diff --git a/Software/src/communication/nvm/comm_nvm.cpp b/Software/src/communication/nvm/comm_nvm.cpp index 8869fc15..2cb7a89e 100644 --- a/Software/src/communication/nvm/comm_nvm.cpp +++ b/Software/src/communication/nvm/comm_nvm.cpp @@ -8,6 +8,7 @@ #include "../../devboard/wifi/wifi.h" #include "../../inverter/INVERTERS.h" #include "../contactorcontrol/comm_contactorcontrol.h" +#include "../precharge_control/precharge_control.h" // Parameters Preferences settings; // Store user settings @@ -142,6 +143,10 @@ void init_stored_settings() { remote_bms_reset = settings.getBool("REMBMSRESET", false); use_canfd_as_can = settings.getBool("CANFDASCAN", false); + precharge_control_enabled = settings.getBool("EXTPRECHARGE", false); + precharge_inverter_normally_open_contactor = settings.getBool("NOINVDISC", false); + precharge_max_precharge_time_before_fault = settings.getUInt("MAXPRETIME", 15000); + datalayer.system.info.performance_measurement_active = settings.getBool("PERFPROFILE", false); datalayer.system.info.CAN_usb_logging_active = settings.getBool("CANLOGUSB", false); datalayer.system.info.usb_logging_active = settings.getBool("USBENABLED", false); @@ -154,8 +159,9 @@ void init_stored_settings() { wifiap_enabled = settings.getBool("WIFIAPENABLED", true); passwordAP = settings.getString("APPASSWORD", "123456789").c_str(); mqtt_enabled = settings.getBool("MQTTENABLED", false); + mqtt_timeout_ms = settings.getUInt("MQTTTIMEOUT", 2000); ha_autodiscovery_enabled = settings.getBool("HADISC", false); - + mqtt_transmit_all_cellvoltages = settings.getBool("MQTTCELLV", false); custom_hostname = settings.getString("HOSTNAME").c_str(); mqtt_server = settings.getString("MQTTSERVER").c_str(); diff --git a/Software/src/communication/precharge_control/precharge_control.cpp b/Software/src/communication/precharge_control/precharge_control.cpp index be7a01ce..31acf342 100644 --- a/Software/src/communication/precharge_control/precharge_control.cpp +++ b/Software/src/communication/precharge_control/precharge_control.cpp @@ -4,29 +4,21 @@ #include "../../datalayer/datalayer_extended.h" #include "../../devboard/hal/hal.h" -#ifdef PRECHARGE_CONTROL -const bool precharge_control_enabled_default = true; -#else -const bool precharge_control_enabled_default = false; -#endif +// Parameters adjustable by user in Settings page +bool precharge_control_enabled = false; +bool precharge_inverter_normally_open_contactor = false; +uint16_t precharge_max_precharge_time_before_fault = 15000; -bool precharge_control_enabled = precharge_control_enabled_default; - -// Parameters -#define MAX_PRECHARGE_TIME_MS 15000 // Maximum time precharge may be enabled +// Hardcoded parameters #define Precharge_default_PWM_Freq 11000 #define Precharge_min_PWM_Freq 5000 #define Precharge_max_PWM_Freq 34000 #define Precharge_PWM_Res 8 #define PWM_Freq 20000 // 20 kHz frequency, beyond audible range #define PWM_Precharge_Channel 0 -#ifndef INVERTER_DISCONNECT_CONTACTOR_IS_NORMALLY_OPEN -#define ON 0 //Normally closed contactors use inverted logic -#define OFF 1 //Normally closed contactors use inverted logic -#else -#define ON 1 -#define OFF 0 -#endif +#define CONTACTOR_ON (precharge_inverter_normally_open_contactor ? 1 : 0) +#define CONTACTOR_OFF (precharge_inverter_normally_open_contactor ? 0 : 1) + static unsigned long prechargeStartTime = 0; static uint32_t freq = Precharge_default_PWM_Freq; static uint16_t delta_freq = 1; @@ -66,7 +58,7 @@ void handle_precharge_control(unsigned long currentMillis) { if (datalayer.system.status.precharge_status == AUTO_PRECHARGE_FAILURE) { pinMode(hia4v1_pin, OUTPUT); digitalWrite(hia4v1_pin, LOW); - digitalWrite(inverter_disconnect_contactor_pin, ON); + digitalWrite(inverter_disconnect_contactor_pin, CONTACTOR_ON); return; // Exit immediately - no further processing allowed. Reboot required to recover } @@ -86,7 +78,7 @@ void handle_precharge_control(unsigned long currentMillis) { prechargeStartTime = currentMillis; datalayer.system.status.precharge_status = AUTO_PRECHARGE_PRECHARGING; logging.printf("Precharge: Starting sequence\n"); - digitalWrite(inverter_disconnect_contactor_pin, OFF); + digitalWrite(inverter_disconnect_contactor_pin, CONTACTOR_OFF); break; case AUTO_PRECHARGE_PRECHARGING: @@ -120,14 +112,14 @@ void handle_precharge_control(unsigned long currentMillis) { datalayer.battery.status.bms_status != ACTIVE || datalayer.system.settings.equipment_stop_active) { pinMode(hia4v1_pin, OUTPUT); digitalWrite(hia4v1_pin, LOW); - digitalWrite(inverter_disconnect_contactor_pin, ON); + digitalWrite(inverter_disconnect_contactor_pin, CONTACTOR_ON); datalayer.system.status.precharge_status = AUTO_PRECHARGE_IDLE; logging.printf("Precharge: Disabling Precharge bms not standby/active or equipment stop\n"); - } else if (currentMillis - prechargeStartTime >= MAX_PRECHARGE_TIME_MS || + } else if (currentMillis - prechargeStartTime >= precharge_max_precharge_time_before_fault || datalayer.battery.status.real_bms_status == BMS_FAULT) { pinMode(hia4v1_pin, OUTPUT); digitalWrite(hia4v1_pin, LOW); - digitalWrite(inverter_disconnect_contactor_pin, ON); + digitalWrite(inverter_disconnect_contactor_pin, CONTACTOR_ON); datalayer.system.status.precharge_status = AUTO_PRECHARGE_FAILURE; logging.printf("Precharge: CRITICAL FAILURE (timeout/BMS fault) -> REQUIRES REBOOT\n"); set_event(EVENT_AUTOMATIC_PRECHARGE_FAILURE, 0); @@ -137,7 +129,7 @@ void handle_precharge_control(unsigned long currentMillis) { } else if (datalayer.system.status.battery_allows_contactor_closing) { pinMode(hia4v1_pin, OUTPUT); digitalWrite(hia4v1_pin, LOW); - digitalWrite(inverter_disconnect_contactor_pin, ON); + digitalWrite(inverter_disconnect_contactor_pin, CONTACTOR_ON); datalayer.system.status.precharge_status = AUTO_PRECHARGE_COMPLETED; logging.printf("Precharge: Disabled (contacts closed) -> COMPLETED\n"); } diff --git a/Software/src/communication/precharge_control/precharge_control.h b/Software/src/communication/precharge_control/precharge_control.h index 0262ebd7..f02e082e 100644 --- a/Software/src/communication/precharge_control/precharge_control.h +++ b/Software/src/communication/precharge_control/precharge_control.h @@ -3,6 +3,11 @@ #include "../../devboard/utils/events.h" +// TODO: Ensure valid values at run-time +// User can update all these values via Settings page +extern bool precharge_control_enabled; +extern bool precharge_inverter_normally_open_contactor; +extern uint16_t precharge_max_precharge_time_before_fault; /** * @brief Contactor initialization * diff --git a/Software/src/devboard/mqtt/mqtt.cpp b/Software/src/devboard/mqtt/mqtt.cpp index 525df501..c5a5ee2f 100644 --- a/Software/src/devboard/mqtt/mqtt.cpp +++ b/Software/src/devboard/mqtt/mqtt.cpp @@ -16,6 +16,8 @@ bool mqtt_enabled = false; bool ha_autodiscovery_enabled = false; +bool mqtt_transmit_all_cellvoltages = false; +uint16_t mqtt_timeout_ms = 2000; const int mqtt_port_default = 0; const char* mqtt_server_default = ""; @@ -29,10 +31,7 @@ bool mqtt_manual_topic_object_name = // This naming convention was in place until version 7.5.0. Users should check the version from which they are updating, as this change // may break compatibility with previous versions of MQTT naming -#define MQTT_PUBLISH_CELL_VOLTAGES // Enable this line to publish cell voltages to MQTT -#define MQTT_QOS 0 // MQTT Quality of Service (0, 1, or 2) -#define MQTT_TIMEOUT 2000 // MQTT timeout in milliseconds - //TODO: Should all these defines be a configurable option? +#define MQTT_QOS 0 // MQTT Quality of Service (0, 1, or 2) //TODO: Should this be configurable? esp_mqtt_client_config_t mqtt_cfg; esp_mqtt_client_handle_t client; @@ -67,17 +66,17 @@ static void publish_values(void) { return; } -#ifdef MQTT_PUBLISH_CELL_VOLTAGES - if (publish_cell_voltages() == false) { - return; + if (mqtt_transmit_all_cellvoltages) { + if (publish_cell_voltages() == false) { + return; + } } -#endif -#ifdef MQTT_PUBLISH_CELL_VOLTAGES - if (publish_cell_balancing() == false) { - return; + if (mqtt_transmit_all_cellvoltages) { + if (publish_cell_balancing() == false) { + return; + } } -#endif } static bool ha_common_info_published = false; @@ -655,7 +654,7 @@ bool init_mqtt(void) { mqtt_cfg.session.last_will.retain = true; mqtt_cfg.session.last_will.msg = "offline"; mqtt_cfg.session.last_will.msg_len = strlen(mqtt_cfg.session.last_will.msg); - mqtt_cfg.network.timeout_ms = MQTT_TIMEOUT; + mqtt_cfg.network.timeout_ms = mqtt_timeout_ms; client = esp_mqtt_client_init(&mqtt_cfg); if (client == nullptr) { diff --git a/Software/src/devboard/mqtt/mqtt.h b/Software/src/devboard/mqtt/mqtt.h index 46261d5b..4189ca91 100644 --- a/Software/src/devboard/mqtt/mqtt.h +++ b/Software/src/devboard/mqtt/mqtt.h @@ -42,6 +42,10 @@ extern const char* version_number; // The current software version, used for mqtt +extern bool mqtt_enabled; +extern bool mqtt_transmit_all_cellvoltages; +extern uint16_t mqtt_timeout_ms; +extern bool ha_autodiscovery_enabled; extern std::string mqtt_server; extern std::string mqtt_user; extern std::string mqtt_password; @@ -57,7 +61,4 @@ bool init_mqtt(void); void mqtt_loop(void); bool mqtt_publish(const char* topic, const char* mqtt_msg, bool retain); -extern bool mqtt_enabled; -extern bool ha_autodiscovery_enabled; - #endif diff --git a/Software/src/devboard/webserver/settings_html.cpp b/Software/src/devboard/webserver/settings_html.cpp index 0517962b..74825e3d 100644 --- a/Software/src/devboard/webserver/settings_html.cpp +++ b/Software/src/devboard/webserver/settings_html.cpp @@ -270,6 +270,18 @@ String settings_processor(const String& var, BatteryEmulatorSettingsStore& setti return settings.getBool("REMBMSRESET") ? "checked" : ""; } + if (var == "EXTPRECHARGE") { + return settings.getBool("EXTPRECHARGE") ? "checked" : ""; + } + + if (var == "MAXPRETIME") { + return String(settings.getUInt("MAXPRETIME", 15000)); + } + + if (var == "NOINVDISC") { + return settings.getBool("NOINVDISC") ? "checked" : ""; + } + if (var == "CANFDASCAN") { return settings.getBool("CANFDASCAN") ? "checked" : ""; } @@ -330,6 +342,10 @@ String settings_processor(const String& var, BatteryEmulatorSettingsStore& setti return settings.getString("MQTTTOPIC"); } + if (var == "MQTTTIMEOUT") { + return String(settings.getUInt("MQTTTIMEOUT", 2000)); + } + if (var == "MQTTOBJIDPREFIX") { return settings.getString("MQTTOBJIDPREFIX"); } @@ -338,6 +354,10 @@ String settings_processor(const String& var, BatteryEmulatorSettingsStore& setti return settings.getString("MQTTDEVICENAME"); } + if (var == "MQTTCELLV") { + return settings.getBool("MQTTCELLV") ? "checked" : ""; + } + if (var == "HADEVICEID") { return settings.getString("HADEVICEID"); } @@ -807,6 +827,11 @@ const char* getCANInterfaceName(CAN_Interface interface) { display: contents; } + form .if-extprecharge { display: none; } + form[data-extprecharge="true"] .if-extprecharge { + display: contents; + } + form .if-sofar { display: none; } form[data-inverter="17"] .if-sofar { display: contents; @@ -1008,6 +1033,19 @@ const char* getCANInterfaceName(CAN_Interface interface) { + + + +
+ + + + + +
+ +
+ @@ -1047,7 +1085,8 @@ const char* getCANInterfaceName(CAN_Interface interface) { - + + @@ -1057,7 +1096,7 @@ const char* getCANInterfaceName(CAN_Interface interface) { - +

diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index fe71aa61..f1abe00c 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -396,9 +396,10 @@ void init_webserver() { }; const char* boolSettingNames[] = { - "DBLBTR", "CNTCTRL", "CNTCTRLDBL", "PWMCNTCTRL", "PERBMSRESET", "SDLOGENABLED", "REMBMSRESET", - "USBENABLED", "CANLOGUSB", "WEBENABLED", "CANFDASCAN", "CANLOGSD", "WIFIAPENABLED", "MQTTENABLED", - "HADISC", "MQTTTOPICS", "INVICNT", "GTWRHD", "DIGITALHVIL", "PERFPROFILE", "INTERLOCKREQ", + "DBLBTR", "CNTCTRL", "CNTCTRLDBL", "PWMCNTCTRL", "PERBMSRESET", "SDLOGENABLED", + "REMBMSRESET", "EXTPRECHARGE", "USBENABLED", "CANLOGUSB", "WEBENABLED", "CANFDASCAN", + "CANLOGSD", "WIFIAPENABLED", "MQTTENABLED", "NOINVDISC", "HADISC", "MQTTTOPICS", + "MQTTCELLV", "INVICNT", "GTWRHD", "DIGITALHVIL", "PERFPROFILE", "INTERLOCKREQ", }; // Handles the form POST from UI to save settings of the common image @@ -459,6 +460,9 @@ void init_webserver() { } else if (p->name() == "SHUNTCOMM") { auto type = static_cast(atoi(p->value().c_str())); settings.saveUInt("SHUNTCOMM", (int)type); + } else if (p->name() == "MAXPRETIME") { + auto type = atoi(p->value().c_str()); + settings.saveUInt("MAXPRETIME", type); } else if (p->name() == "HOSTNAME") { settings.saveString("HOSTNAME", p->value().c_str()); } else if (p->name() == "MQTTSERVER") { @@ -472,6 +476,8 @@ void init_webserver() { settings.saveString("MQTTPASSWORD", p->value().c_str()); } else if (p->name() == "MQTTTOPIC") { settings.saveString("MQTTTOPIC", p->value().c_str()); + } else if (p->name() == "MQTTTIMEOUT") { + settings.saveString("MQTTTIMEOUT", p->value().c_str()); } else if (p->name() == "MQTTOBJIDPREFIX") { settings.saveString("MQTTOBJIDPREFIX", p->value().c_str()); } else if (p->name() == "MQTTDEVICENAME") { From 58af5b658bb0e06d470562db3de69e1617dd8a86 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 2 Sep 2025 16:42:38 +0000 Subject: [PATCH 118/245] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- Software/Software.cpp | 8 ++++---- Software/USER_SETTINGS.cpp | 3 --- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Software/Software.cpp b/Software/Software.cpp index dde1c9f3..73cdea2c 100644 --- a/Software/Software.cpp +++ b/Software/Software.cpp @@ -52,10 +52,10 @@ TaskHandle_t mqtt_loop_task; Logging logging; -std::string mqtt_user; //TODO, move? -std::string mqtt_password; //TODO, move? -std::string http_username; //TODO, move? -std::string http_password; //TODO, move? +std::string mqtt_user; //TODO, move? +std::string mqtt_password; //TODO, move? +std::string http_username; //TODO, move? +std::string http_password; //TODO, move? static std::list transmitters; void register_transmitter(Transmitter* transmitter) { diff --git a/Software/USER_SETTINGS.cpp b/Software/USER_SETTINGS.cpp index d79a90ae..8e7b46f4 100644 --- a/Software/USER_SETTINGS.cpp +++ b/Software/USER_SETTINGS.cpp @@ -20,7 +20,6 @@ volatile CAN_Configuration can_config = { .shunt = CAN_NATIVE // (OPTIONAL) Which CAN is your shunt connected to? }; - // Set your Static IP address. Only used incase WIFICONFIG is set in USER_SETTINGS.h IPAddress local_IP(192, 168, 10, 150); IPAddress gateway(192, 168, 10, 1); @@ -32,10 +31,8 @@ std::string passwordAP; const uint8_t wifi_channel = 0; // Set to 0 for automatic channel selection - // MQTT - const char* mqtt_topic_name = "BE"; // Custom MQTT topic name. Previously, the name was automatically set to "battery-emulator_esp32-XXXXXX" const char* mqtt_object_id_prefix = From 21eda56c9e4ae8c7c9c4ea262a985ba701e2111f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 2 Sep 2025 20:12:42 +0300 Subject: [PATCH 119/245] Make Wifi channel configurable --- Software/USER_SETTINGS.cpp | 5 ----- Software/USER_SETTINGS.h | 1 - Software/src/communication/nvm/comm_nvm.cpp | 1 + Software/src/devboard/webserver/settings_html.cpp | 13 +++++++++---- Software/src/devboard/webserver/webserver.cpp | 3 +++ Software/src/devboard/wifi/wifi.cpp | 5 ++++- Software/src/devboard/wifi/wifi.h | 2 +- 7 files changed, 18 insertions(+), 12 deletions(-) diff --git a/Software/USER_SETTINGS.cpp b/Software/USER_SETTINGS.cpp index d79a90ae..ccfa3e83 100644 --- a/Software/USER_SETTINGS.cpp +++ b/Software/USER_SETTINGS.cpp @@ -20,7 +20,6 @@ volatile CAN_Configuration can_config = { .shunt = CAN_NATIVE // (OPTIONAL) Which CAN is your shunt connected to? }; - // Set your Static IP address. Only used incase WIFICONFIG is set in USER_SETTINGS.h IPAddress local_IP(192, 168, 10, 150); IPAddress gateway(192, 168, 10, 1); @@ -30,12 +29,8 @@ std::string ssid; std::string password; std::string passwordAP; -const uint8_t wifi_channel = 0; // Set to 0 for automatic channel selection - - // MQTT - const char* mqtt_topic_name = "BE"; // Custom MQTT topic name. Previously, the name was automatically set to "battery-emulator_esp32-XXXXXX" const char* mqtt_object_id_prefix = diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index e2873c17..9f89c7bb 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -21,7 +21,6 @@ typedef struct { } CAN_Configuration; extern volatile CAN_Configuration can_config; extern volatile uint8_t AccessPointEnabled; -extern const uint8_t wifi_channel; extern volatile float CHARGER_SET_HV; extern volatile float CHARGER_MAX_HV; extern volatile float CHARGER_MIN_HV; diff --git a/Software/src/communication/nvm/comm_nvm.cpp b/Software/src/communication/nvm/comm_nvm.cpp index 2cb7a89e..2351728b 100644 --- a/Software/src/communication/nvm/comm_nvm.cpp +++ b/Software/src/communication/nvm/comm_nvm.cpp @@ -157,6 +157,7 @@ void init_stored_settings() { // WIFI AP is enabled by default unless disabled in the settings wifiap_enabled = settings.getBool("WIFIAPENABLED", true); + wifi_channel = settings.getUInt("WIFICHANNEL", 2000); passwordAP = settings.getString("APPASSWORD", "123456789").c_str(); mqtt_enabled = settings.getBool("MQTTENABLED", false); mqtt_timeout_ms = settings.getUInt("MQTTTIMEOUT", 2000); diff --git a/Software/src/devboard/webserver/settings_html.cpp b/Software/src/devboard/webserver/settings_html.cpp index 74825e3d..521805c5 100644 --- a/Software/src/devboard/webserver/settings_html.cpp +++ b/Software/src/devboard/webserver/settings_html.cpp @@ -290,6 +290,10 @@ String settings_processor(const String& var, BatteryEmulatorSettingsStore& setti return settings.getBool("WIFIAPENABLED", wifiap_enabled) ? "checked" : ""; } + if (var == "WIFICHANNEL") { + return String(settings.getUInt("WIFICHANNEL", 0)); + } + if (var == "PERFPROFILE") { return settings.getBool("PERFPROFILE") ? "checked" : ""; } @@ -867,7 +871,7 @@ const char* getCANInterfaceName(CAN_Interface interface) {

SSID: %SSID%

Password: ########

- +
@@ -1042,16 +1046,17 @@ const char* getCANInterfaceName(CAN_Interface interface) { -
-
- + + + + diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index f1abe00c..8e3c3df4 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -463,6 +463,9 @@ void init_webserver() { } else if (p->name() == "MAXPRETIME") { auto type = atoi(p->value().c_str()); settings.saveUInt("MAXPRETIME", type); + } else if (p->name() == "WIFICHANNEL") { + auto type = atoi(p->value().c_str()); + settings.saveUInt("WIFICHANNEL", type); } else if (p->name() == "HOSTNAME") { settings.saveString("HOSTNAME", p->value().c_str()); } else if (p->name() == "MQTTSERVER") { diff --git a/Software/src/devboard/wifi/wifi.cpp b/Software/src/devboard/wifi/wifi.cpp index 869d863f..50ac3d2e 100644 --- a/Software/src/devboard/wifi/wifi.cpp +++ b/Software/src/devboard/wifi/wifi.cpp @@ -7,6 +7,7 @@ bool wifi_enabled = true; bool wifiap_enabled = true; bool mdns_enabled = true; //If true, allows battery monitor te be found by .local address +uint16_t wifi_channel = 0; std::string custom_hostname; //If not set, the default naming format 'esp32-XXXXXX' will be used std::string ssidAP; @@ -150,7 +151,9 @@ void connectToWiFi() { if (WiFi.status() != WL_CONNECTED) { lastReconnectAttempt = millis(); // Reset the reconnect attempt timer logging.println("Connecting to Wi-Fi..."); - + if (wifi_channel > 14) { + wifi_channel = 0; + } //prevent users going out of bounds DEBUG_PRINTF("Connecting to Wi-Fi SSID: %s, password: %s, Channel: %d\n", ssid.c_str(), password.c_str(), wifi_channel); WiFi.begin(ssid.c_str(), password.c_str(), wifi_channel); diff --git a/Software/src/devboard/wifi/wifi.h b/Software/src/devboard/wifi/wifi.h index 6e0b7b50..bf0ecb01 100644 --- a/Software/src/devboard/wifi/wifi.h +++ b/Software/src/devboard/wifi/wifi.h @@ -6,7 +6,7 @@ extern std::string ssid; extern std::string password; -extern const uint8_t wifi_channel; +extern uint16_t wifi_channel; extern std::string ssidAP; extern std::string passwordAP; extern std::string custom_hostname; From f03324585b596b08a497d5a0cf12f8cec3ba3836 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 2 Sep 2025 20:42:48 +0300 Subject: [PATCH 120/245] Move more USER_SETTINGS out --- Software/USER_SETTINGS.cpp | 31 ------------------- Software/USER_SETTINGS.h | 27 ---------------- Software/src/communication/can/comm_can.cpp | 6 ++++ Software/src/communication/can/comm_can.h | 9 ++++++ .../comm_equipmentstopbutton.cpp | 1 + Software/src/communication/nvm/comm_nvm.cpp | 1 + Software/src/devboard/mqtt/mqtt.cpp | 8 +++++ .../src/devboard/webserver/settings_html.cpp | 1 + Software/src/devboard/webserver/webserver.cpp | 1 + Software/src/devboard/wifi/wifi.cpp | 3 ++ 10 files changed, 30 insertions(+), 58 deletions(-) diff --git a/Software/USER_SETTINGS.cpp b/Software/USER_SETTINGS.cpp index 3581e093..46031b96 100644 --- a/Software/USER_SETTINGS.cpp +++ b/Software/USER_SETTINGS.cpp @@ -2,42 +2,11 @@ #include #include "src/devboard/hal/hal.h" -/* This file contains all the battery settings and limits */ -/* They can be defined here, or later on in the WebUI */ -/* Most important is to select which CAN interface each component is connected to */ -/* -CAN_NATIVE = Native CAN port on the LilyGo & Stark hardware -CANFD_NATIVE = Native CANFD port on the Stark CMR hardware -CAN_ADDON_MCP2515 = Add-on CAN MCP2515 connected to GPIO pins -CANFD_ADDON_MCP2518 = Add-on CAN-FD MCP2518 connected to GPIO pins -*/ - -volatile CAN_Configuration can_config = { - .battery = CAN_NATIVE, // Which CAN is your battery connected to? - .inverter = CAN_NATIVE, // Which CAN is your inverter connected to? (No need to configure incase you use RS485) - .battery_double = CAN_ADDON_MCP2515, // (OPTIONAL) Which CAN is your second battery connected to? - .charger = CAN_NATIVE, // (OPTIONAL) Which CAN is your charger connected to? - .shunt = CAN_NATIVE // (OPTIONAL) Which CAN is your shunt connected to? -}; - // Set your Static IP address. Only used incase WIFICONFIG is set in USER_SETTINGS.h IPAddress local_IP(192, 168, 10, 150); IPAddress gateway(192, 168, 10, 1); IPAddress subnet(255, 255, 255, 0); -std::string ssid; -std::string password; -std::string passwordAP; - -const char* mqtt_topic_name = - "BE"; // Custom MQTT topic name. Previously, the name was automatically set to "battery-emulator_esp32-XXXXXX" -const char* mqtt_object_id_prefix = - "be_"; // Custom prefix for MQTT object ID. Previously, the prefix was automatically set to "esp32-XXXXXX_" -const char* mqtt_device_name = - "Battery Emulator"; // Custom device name in Home Assistant. Previously, the name was automatically set to "BatteryEmulator_esp32-XXXXXX" -const char* ha_device_id = - "battery-emulator"; // Custom device ID in Home Assistant. Previously, the ID was always "battery-emulator" - /* Charger settings (Optional, when using generator charging) */ volatile float CHARGER_SET_HV = 384; // Reasonably appropriate 4.0v per cell charging of a 96s pack volatile float CHARGER_MAX_HV = 420; // Max permissible output (VDC) of charger diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index 9f89c7bb..db208f06 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -10,17 +10,6 @@ /* Connectivity options */ //#define WIFICONFIG //Enable this line to set a static IP address / gateway /subnet mask for the device. see USER_SETTINGS.cpp for the settings -/* Do not change any code below this line */ -/* Only change battery specific settings above and in "USER_SETTINGS.cpp" */ -typedef struct { - CAN_Interface battery; - CAN_Interface inverter; - CAN_Interface battery_double; - CAN_Interface charger; - CAN_Interface shunt; -} CAN_Configuration; -extern volatile CAN_Configuration can_config; -extern volatile uint8_t AccessPointEnabled; extern volatile float CHARGER_SET_HV; extern volatile float CHARGER_MAX_HV; extern volatile float CHARGER_MIN_HV; @@ -28,26 +17,10 @@ extern volatile float CHARGER_MAX_POWER; extern volatile float CHARGER_MAX_A; extern volatile float CHARGER_END_A; -#include "src/communication/equipmentstopbutton/comm_equipmentstopbutton.h" - -// Equipment stop button behavior. Use NC button for safety reasons. -//LATCHING_SWITCH - Normally closed (NC), latching switch. When pressed it activates e-stop -//MOMENTARY_SWITCH - Short press to activate e-stop, long 15s press to deactivate. E-stop is persistent between reboots - -#ifdef EQUIPMENT_STOP_BUTTON -const STOP_BUTTON_BEHAVIOR stop_button_default_behavior = STOP_BUTTON_BEHAVIOR::MOMENTARY_SWITCH; -#else -const STOP_BUTTON_BEHAVIOR stop_button_default_behavior = STOP_BUTTON_BEHAVIOR::NOT_CONNECTED; -#endif - #ifdef WIFICONFIG extern IPAddress local_IP; extern IPAddress gateway; extern IPAddress subnet; #endif -#if defined(MEB_BATTERY) -#define PRECHARGE_CONTROL -#endif - #endif // __USER_SETTINGS_H__ diff --git a/Software/src/communication/can/comm_can.cpp b/Software/src/communication/can/comm_can.cpp index eadb75d8..4dc2e47d 100644 --- a/Software/src/communication/can/comm_can.cpp +++ b/Software/src/communication/can/comm_can.cpp @@ -12,6 +12,12 @@ #include "src/devboard/sdcard/sdcard.h" #include "src/devboard/utils/logging.h" +volatile CAN_Configuration can_config = {.battery = CAN_NATIVE, + .inverter = CAN_NATIVE, + .battery_double = CAN_ADDON_MCP2515, + .charger = CAN_NATIVE, + .shunt = CAN_NATIVE}; + struct CanReceiverRegistration { CanReceiver* receiver; CAN_Speed speed; diff --git a/Software/src/communication/can/comm_can.h b/Software/src/communication/can/comm_can.h index 91e58017..b4624928 100644 --- a/Software/src/communication/can/comm_can.h +++ b/Software/src/communication/can/comm_can.h @@ -15,6 +15,15 @@ void transmit_can_frame_to_interface(const CAN_frame* tx_frame, int interface); class CanReceiver; +typedef struct { + CAN_Interface battery; + CAN_Interface inverter; + CAN_Interface battery_double; + CAN_Interface charger; + CAN_Interface shunt; +} CAN_Configuration; +extern volatile CAN_Configuration can_config; + enum class CAN_Speed { CAN_SPEED_100KBPS = 100, CAN_SPEED_125KBPS = 125, diff --git a/Software/src/communication/equipmentstopbutton/comm_equipmentstopbutton.cpp b/Software/src/communication/equipmentstopbutton/comm_equipmentstopbutton.cpp index de03820e..ca056b93 100644 --- a/Software/src/communication/equipmentstopbutton/comm_equipmentstopbutton.cpp +++ b/Software/src/communication/equipmentstopbutton/comm_equipmentstopbutton.cpp @@ -4,6 +4,7 @@ #include "../../devboard/utils/debounce_button.h" #include "USER_SETTINGS.h" +const STOP_BUTTON_BEHAVIOR stop_button_default_behavior = STOP_BUTTON_BEHAVIOR::NOT_CONNECTED; STOP_BUTTON_BEHAVIOR equipment_stop_behavior = stop_button_default_behavior; // Parameters diff --git a/Software/src/communication/nvm/comm_nvm.cpp b/Software/src/communication/nvm/comm_nvm.cpp index 2351728b..3ba5b478 100644 --- a/Software/src/communication/nvm/comm_nvm.cpp +++ b/Software/src/communication/nvm/comm_nvm.cpp @@ -8,6 +8,7 @@ #include "../../devboard/wifi/wifi.h" #include "../../inverter/INVERTERS.h" #include "../contactorcontrol/comm_contactorcontrol.h" +#include "../equipmentstopbutton/comm_equipmentstopbutton.h" #include "../precharge_control/precharge_control.h" // Parameters diff --git a/Software/src/devboard/mqtt/mqtt.cpp b/Software/src/devboard/mqtt/mqtt.cpp index c5a5ee2f..cf505a34 100644 --- a/Software/src/devboard/mqtt/mqtt.cpp +++ b/Software/src/devboard/mqtt/mqtt.cpp @@ -30,6 +30,14 @@ bool mqtt_manual_topic_object_name = // If this is not true, the previous default naming format 'battery-emulator_esp32-XXXXXX' (based on hardware ID) will be used. // This naming convention was in place until version 7.5.0. Users should check the version from which they are updating, as this change // may break compatibility with previous versions of MQTT naming +const char* mqtt_topic_name = + "BE"; // Custom MQTT topic name. Previously, the name was automatically set to "battery-emulator_esp32-XXXXXX" +const char* mqtt_object_id_prefix = + "be_"; // Custom prefix for MQTT object ID. Previously, the prefix was automatically set to "esp32-XXXXXX_" +const char* mqtt_device_name = + "Battery Emulator"; // Custom device name in Home Assistant. Previously, the name was automatically set to "BatteryEmulator_esp32-XXXXXX" +const char* ha_device_id = + "battery-emulator"; // Custom device ID in Home Assistant. Previously, the ID was always "battery-emulator" #define MQTT_QOS 0 // MQTT Quality of Service (0, 1, or 2) //TODO: Should this be configurable? diff --git a/Software/src/devboard/webserver/settings_html.cpp b/Software/src/devboard/webserver/settings_html.cpp index 521805c5..b2892d89 100644 --- a/Software/src/devboard/webserver/settings_html.cpp +++ b/Software/src/devboard/webserver/settings_html.cpp @@ -1,6 +1,7 @@ #include "settings_html.h" #include #include "../../../src/communication/contactorcontrol/comm_contactorcontrol.h" +#include "../../../src/communication/equipmentstopbutton/comm_equipmentstopbutton.h" #include "../../charger/CHARGERS.h" #include "../../communication/can/comm_can.h" #include "../../communication/nvm/comm_nvm.h" diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 8e3c3df4..2b5f0b9d 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -7,6 +7,7 @@ #include "../../charger/CHARGERS.h" #include "../../communication/can/comm_can.h" #include "../../communication/contactorcontrol/comm_contactorcontrol.h" +#include "../../communication/equipmentstopbutton/comm_equipmentstopbutton.h" #include "../../communication/nvm/comm_nvm.h" #include "../../datalayer/datalayer.h" #include "../../datalayer/datalayer_extended.h" diff --git a/Software/src/devboard/wifi/wifi.cpp b/Software/src/devboard/wifi/wifi.cpp index 50ac3d2e..1cf51b83 100644 --- a/Software/src/devboard/wifi/wifi.cpp +++ b/Software/src/devboard/wifi/wifi.cpp @@ -10,7 +10,10 @@ bool mdns_enabled = true; //If true, allows battery monitor te be found by .loc uint16_t wifi_channel = 0; std::string custom_hostname; //If not set, the default naming format 'esp32-XXXXXX' will be used +std::string ssid; +std::string password; std::string ssidAP; +std::string passwordAP; // Configuration Parameters static const uint16_t WIFI_CHECK_INTERVAL = 2000; // 1 seconds normal check interval when last connected From bb21b5d8e17ae06f2d06a735b3f9fa82d3b48d64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 2 Sep 2025 20:51:53 +0300 Subject: [PATCH 121/245] Remove unused ifdefs --- Software/Software.cpp | 9 +++++---- Software/src/inverter/AFORE-CAN.h | 4 ---- Software/src/inverter/BYD-CAN.h | 4 ---- Software/src/inverter/BYD-MODBUS.h | 4 ---- Software/src/inverter/FERROAMP-CAN.h | 4 ---- Software/src/inverter/FOXESS-CAN.h | 4 ---- Software/src/inverter/GROWATT-HV-CAN.h | 4 ---- Software/src/inverter/GROWATT-LV-CAN.h | 4 ---- Software/src/inverter/GROWATT-WIT-CAN.h | 4 ---- Software/src/inverter/KOSTAL-RS485.h | 5 ----- Software/src/inverter/PYLON-CAN.h | 4 ---- Software/src/inverter/PYLON-LV-CAN.h | 4 ---- Software/src/inverter/SCHNEIDER-CAN.h | 4 ---- Software/src/inverter/SMA-BYD-H-CAN.h | 4 ---- Software/src/inverter/SMA-BYD-HVS-CAN.h | 4 ---- Software/src/inverter/SMA-LV-CAN.h | 4 ---- Software/src/inverter/SMA-TRIPOWER-CAN.h | 4 ---- Software/src/inverter/SOFAR-CAN.h | 4 ---- Software/src/inverter/SOL-ARK-LV-CAN.h | 4 ---- Software/src/inverter/SOLAX-CAN.h | 4 ---- Software/src/inverter/SOLXPOW-CAN.h | 4 ---- Software/src/inverter/SUNGROW-CAN.h | 4 ---- 22 files changed, 5 insertions(+), 89 deletions(-) diff --git a/Software/Software.cpp b/Software/Software.cpp index 73cdea2c..0702406e 100644 --- a/Software/Software.cpp +++ b/Software/Software.cpp @@ -30,7 +30,7 @@ #include "src/inverter/INVERTERS.h" #if !defined(HW_LILYGO) && !defined(HW_LILYGO2CAN) && !defined(HW_STARK) && !defined(HW_3LB) && !defined(HW_DEVKIT) -#error You must select a target hardware in the USER_SETTINGS.h file! +#error You must select a target hardware! #endif // The current software version, shown on webserver @@ -396,9 +396,10 @@ void core_loop(void*) { } led_exe(); handle_contactors(); // Take care of startup precharge/contactor closing -#ifdef PRECHARGE_CONTROL - handle_precharge_control(currentMillis); -#endif // PRECHARGE_CONTROL + if (precharge_control_enabled) { + handle_precharge_control(currentMillis); //Drive the hia4v1 via PWM + } + if (datalayer.system.info.performance_measurement_active) { END_TIME_MEASUREMENT_MAX(10ms, datalayer.system.status.time_10ms_us); } diff --git a/Software/src/inverter/AFORE-CAN.h b/Software/src/inverter/AFORE-CAN.h index f98a582a..96e59ec6 100644 --- a/Software/src/inverter/AFORE-CAN.h +++ b/Software/src/inverter/AFORE-CAN.h @@ -1,10 +1,6 @@ #ifndef AFORE_CAN_H #define AFORE_CAN_H -#ifdef AFORE_CAN -#define SELECTED_INVERTER_CLASS AforeCanInverter -#endif - #include "CanInverterProtocol.h" class AforeCanInverter : public CanInverterProtocol { diff --git a/Software/src/inverter/BYD-CAN.h b/Software/src/inverter/BYD-CAN.h index b61d0a5c..07fd1189 100644 --- a/Software/src/inverter/BYD-CAN.h +++ b/Software/src/inverter/BYD-CAN.h @@ -1,10 +1,6 @@ #ifndef BYD_CAN_H #define BYD_CAN_H -#ifdef BYD_CAN -#define SELECTED_INVERTER_CLASS BydCanInverter -#endif - #include "../../USER_SETTINGS.h" #include "../datalayer/datalayer.h" #include "CanInverterProtocol.h" diff --git a/Software/src/inverter/BYD-MODBUS.h b/Software/src/inverter/BYD-MODBUS.h index a51010c5..83da47db 100644 --- a/Software/src/inverter/BYD-MODBUS.h +++ b/Software/src/inverter/BYD-MODBUS.h @@ -1,10 +1,6 @@ #ifndef BYD_MODBUS_H #define BYD_MODBUS_H -#ifdef BYD_MODBUS -#define SELECTED_INVERTER_CLASS BydModbusInverter -#endif - #include "../devboard/utils/types.h" #include "ModbusInverterProtocol.h" diff --git a/Software/src/inverter/FERROAMP-CAN.h b/Software/src/inverter/FERROAMP-CAN.h index 00ec78bd..aa4f064f 100644 --- a/Software/src/inverter/FERROAMP-CAN.h +++ b/Software/src/inverter/FERROAMP-CAN.h @@ -3,10 +3,6 @@ #include "CanInverterProtocol.h" -#ifdef FERROAMP_CAN -#define SELECTED_INVERTER_CLASS FerroampCanInverter -#endif - class FerroampCanInverter : public CanInverterProtocol { public: const char* name() override { return Name; } diff --git a/Software/src/inverter/FOXESS-CAN.h b/Software/src/inverter/FOXESS-CAN.h index 2bb82df4..cd756807 100644 --- a/Software/src/inverter/FOXESS-CAN.h +++ b/Software/src/inverter/FOXESS-CAN.h @@ -3,10 +3,6 @@ #include "CanInverterProtocol.h" -#ifdef FOXESS_CAN -#define SELECTED_INVERTER_CLASS FoxessCanInverter -#endif - class FoxessCanInverter : public CanInverterProtocol { public: const char* name() override { return Name; } diff --git a/Software/src/inverter/GROWATT-HV-CAN.h b/Software/src/inverter/GROWATT-HV-CAN.h index cf9d1b9a..44b86745 100644 --- a/Software/src/inverter/GROWATT-HV-CAN.h +++ b/Software/src/inverter/GROWATT-HV-CAN.h @@ -3,10 +3,6 @@ #include "CanInverterProtocol.h" -#ifdef GROWATT_HV_CAN -#define SELECTED_INVERTER_CLASS GrowattHvInverter -#endif - class GrowattHvInverter : public CanInverterProtocol { public: const char* name() override { return Name; } diff --git a/Software/src/inverter/GROWATT-LV-CAN.h b/Software/src/inverter/GROWATT-LV-CAN.h index ae6c71b2..a55a1ef9 100644 --- a/Software/src/inverter/GROWATT-LV-CAN.h +++ b/Software/src/inverter/GROWATT-LV-CAN.h @@ -3,10 +3,6 @@ #include "CanInverterProtocol.h" -#ifdef GROWATT_LV_CAN -#define SELECTED_INVERTER_CLASS GrowattLvInverter -#endif - class GrowattLvInverter : public CanInverterProtocol { public: const char* name() override { return Name; } diff --git a/Software/src/inverter/GROWATT-WIT-CAN.h b/Software/src/inverter/GROWATT-WIT-CAN.h index d94f6c18..fceef3d5 100644 --- a/Software/src/inverter/GROWATT-WIT-CAN.h +++ b/Software/src/inverter/GROWATT-WIT-CAN.h @@ -3,10 +3,6 @@ #include "CanInverterProtocol.h" -#ifdef GROWATT_WIT_CAN -#define SELECTED_INVERTER_CLASS GrowattWitInverter -#endif - class GrowattWitInverter : public CanInverterProtocol { public: const char* name() override { return Name; } diff --git a/Software/src/inverter/KOSTAL-RS485.h b/Software/src/inverter/KOSTAL-RS485.h index 0768461f..d67b7438 100644 --- a/Software/src/inverter/KOSTAL-RS485.h +++ b/Software/src/inverter/KOSTAL-RS485.h @@ -3,11 +3,6 @@ #include #include "Rs485InverterProtocol.h" -#ifdef BYD_KOSTAL_RS485 -#define RS485_INVERTER_SELECTED -#define SELECTED_INVERTER_CLASS KostalInverterProtocol -#endif - class KostalInverterProtocol : public Rs485InverterProtocol { public: const char* name() override { return Name; } diff --git a/Software/src/inverter/PYLON-CAN.h b/Software/src/inverter/PYLON-CAN.h index 33a8b1b6..7f03fa82 100644 --- a/Software/src/inverter/PYLON-CAN.h +++ b/Software/src/inverter/PYLON-CAN.h @@ -3,10 +3,6 @@ #include "CanInverterProtocol.h" -#ifdef PYLON_CAN -#define SELECTED_INVERTER_CLASS PylonInverter -#endif - class PylonInverter : public CanInverterProtocol { public: const char* name() override { return Name; } diff --git a/Software/src/inverter/PYLON-LV-CAN.h b/Software/src/inverter/PYLON-LV-CAN.h index 24800621..af2e7342 100644 --- a/Software/src/inverter/PYLON-LV-CAN.h +++ b/Software/src/inverter/PYLON-LV-CAN.h @@ -3,10 +3,6 @@ #include "CanInverterProtocol.h" -#ifdef PYLON_LV_CAN -#define SELECTED_INVERTER_CLASS PylonLvInverter -#endif - class PylonLvInverter : public CanInverterProtocol { public: const char* name() override { return Name; } diff --git a/Software/src/inverter/SCHNEIDER-CAN.h b/Software/src/inverter/SCHNEIDER-CAN.h index e895e939..dbaa2441 100644 --- a/Software/src/inverter/SCHNEIDER-CAN.h +++ b/Software/src/inverter/SCHNEIDER-CAN.h @@ -3,10 +3,6 @@ #include "CanInverterProtocol.h" -#ifdef SCHNEIDER_CAN -#define SELECTED_INVERTER_CLASS SchneiderInverter -#endif - class SchneiderInverter : public CanInverterProtocol { public: const char* name() override { return Name; } diff --git a/Software/src/inverter/SMA-BYD-H-CAN.h b/Software/src/inverter/SMA-BYD-H-CAN.h index 9d9eda6b..88a5b845 100644 --- a/Software/src/inverter/SMA-BYD-H-CAN.h +++ b/Software/src/inverter/SMA-BYD-H-CAN.h @@ -4,10 +4,6 @@ #include "../devboard/hal/hal.h" #include "SmaInverterBase.h" -#ifdef SMA_BYD_H_CAN -#define SELECTED_INVERTER_CLASS SmaBydHInverter -#endif - class SmaBydHInverter : public SmaInverterBase { public: const char* name() override { return Name; } diff --git a/Software/src/inverter/SMA-BYD-HVS-CAN.h b/Software/src/inverter/SMA-BYD-HVS-CAN.h index c3d78b07..2f9dac67 100644 --- a/Software/src/inverter/SMA-BYD-HVS-CAN.h +++ b/Software/src/inverter/SMA-BYD-HVS-CAN.h @@ -4,10 +4,6 @@ #include "../devboard/hal/hal.h" #include "SmaInverterBase.h" -#ifdef SMA_BYD_HVS_CAN -#define SELECTED_INVERTER_CLASS SmaBydHvsInverter -#endif - class SmaBydHvsInverter : public SmaInverterBase { public: const char* name() override { return Name; } diff --git a/Software/src/inverter/SMA-LV-CAN.h b/Software/src/inverter/SMA-LV-CAN.h index 4ceef71b..e414f305 100644 --- a/Software/src/inverter/SMA-LV-CAN.h +++ b/Software/src/inverter/SMA-LV-CAN.h @@ -3,10 +3,6 @@ #include "CanInverterProtocol.h" -#ifdef SMA_LV_CAN -#define SELECTED_INVERTER_CLASS SmaLvInverter -#endif - class SmaLvInverter : public CanInverterProtocol { public: const char* name() override { return Name; } diff --git a/Software/src/inverter/SMA-TRIPOWER-CAN.h b/Software/src/inverter/SMA-TRIPOWER-CAN.h index ace38bf9..7a18ea2a 100644 --- a/Software/src/inverter/SMA-TRIPOWER-CAN.h +++ b/Software/src/inverter/SMA-TRIPOWER-CAN.h @@ -4,10 +4,6 @@ #include "../devboard/hal/hal.h" #include "SmaInverterBase.h" -#ifdef SMA_TRIPOWER_CAN -#define SELECTED_INVERTER_CLASS SmaTripowerInverter -#endif - class SmaTripowerInverter : public SmaInverterBase { public: const char* name() override { return Name; } diff --git a/Software/src/inverter/SOFAR-CAN.h b/Software/src/inverter/SOFAR-CAN.h index 9a6a8642..59959688 100644 --- a/Software/src/inverter/SOFAR-CAN.h +++ b/Software/src/inverter/SOFAR-CAN.h @@ -2,10 +2,6 @@ #define SOFAR_CAN_H #include "CanInverterProtocol.h" -#ifdef SOFAR_CAN -#define SELECTED_INVERTER_CLASS SofarInverter -#endif - class SofarInverter : public CanInverterProtocol { public: bool setup() override; diff --git a/Software/src/inverter/SOL-ARK-LV-CAN.h b/Software/src/inverter/SOL-ARK-LV-CAN.h index 98b2cf9f..7e28f944 100644 --- a/Software/src/inverter/SOL-ARK-LV-CAN.h +++ b/Software/src/inverter/SOL-ARK-LV-CAN.h @@ -3,10 +3,6 @@ #include "CanInverterProtocol.h" -#ifdef SOL_ARK_LV_CAN -#define SELECTED_INVERTER_CLASS SolArkLvInverter -#endif - class SolArkLvInverter : public CanInverterProtocol { public: const char* name() override { return Name; } diff --git a/Software/src/inverter/SOLAX-CAN.h b/Software/src/inverter/SOLAX-CAN.h index 6a67e8bf..2599133c 100644 --- a/Software/src/inverter/SOLAX-CAN.h +++ b/Software/src/inverter/SOLAX-CAN.h @@ -3,10 +3,6 @@ #include "CanInverterProtocol.h" -#ifdef SOLAX_CAN -#define SELECTED_INVERTER_CLASS SolaxInverter -#endif - class SolaxInverter : public CanInverterProtocol { public: const char* name() override { return Name; } diff --git a/Software/src/inverter/SOLXPOW-CAN.h b/Software/src/inverter/SOLXPOW-CAN.h index f45970ee..8010c944 100644 --- a/Software/src/inverter/SOLXPOW-CAN.h +++ b/Software/src/inverter/SOLXPOW-CAN.h @@ -3,10 +3,6 @@ #include "CanInverterProtocol.h" -#ifdef SOLXPOW_CAN -#define SELECTED_INVERTER_CLASS SolxpowInverter -#endif - class SolxpowInverter : public CanInverterProtocol { public: const char* name() override { return Name; } diff --git a/Software/src/inverter/SUNGROW-CAN.h b/Software/src/inverter/SUNGROW-CAN.h index a28a2372..d4f5a7c9 100644 --- a/Software/src/inverter/SUNGROW-CAN.h +++ b/Software/src/inverter/SUNGROW-CAN.h @@ -3,10 +3,6 @@ #include "CanInverterProtocol.h" -#ifdef SUNGROW_CAN -#define SELECTED_INVERTER_CLASS SungrowInverter -#endif - class SungrowInverter : public CanInverterProtocol { public: const char* name() override { return Name; } From da560e162948400e452ba9266455aa6666316a22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 2 Sep 2025 21:06:18 +0300 Subject: [PATCH 122/245] Remove more USER_SETTINGS --- Software/Software.cpp | 1 + Software/USER_SETTINGS.cpp | 8 -------- Software/USER_SETTINGS.h | 7 ------- Software/src/battery/BATTERIES.h | 1 - Software/src/battery/CanBattery.h | 1 - Software/src/battery/MG-HS-PHEV-BATTERY.cpp | 2 -- Software/src/battery/Shunt.h | 1 - Software/src/charger/CHARGERS.cpp | 9 +++++++++ Software/src/charger/CHARGERS.h | 9 ++++++++- Software/src/charger/CHEVY-VOLT-CHARGER.cpp | 1 + Software/src/charger/NISSAN-LEAF-CHARGER.cpp | 1 + Software/src/communication/can/comm_can.cpp | 1 - .../contactorcontrol/comm_contactorcontrol.cpp | 2 +- .../equipmentstopbutton/comm_equipmentstopbutton.cpp | 1 - Software/src/datalayer/datalayer.h | 1 - Software/src/datalayer/datalayer_extended.h | 1 - 16 files changed, 21 insertions(+), 26 deletions(-) diff --git a/Software/Software.cpp b/Software/Software.cpp index 0702406e..da59ecbb 100644 --- a/Software/Software.cpp +++ b/Software/Software.cpp @@ -24,6 +24,7 @@ #include "src/devboard/utils/logging.h" #include "src/devboard/utils/time_meas.h" #include "src/devboard/utils/timer.h" +#include "src/devboard/utils/types.h" #include "src/devboard/utils/value_mapping.h" #include "src/devboard/webserver/webserver.h" #include "src/devboard/wifi/wifi.h" diff --git a/Software/USER_SETTINGS.cpp b/Software/USER_SETTINGS.cpp index 46031b96..78cb6eee 100644 --- a/Software/USER_SETTINGS.cpp +++ b/Software/USER_SETTINGS.cpp @@ -6,11 +6,3 @@ IPAddress local_IP(192, 168, 10, 150); IPAddress gateway(192, 168, 10, 1); IPAddress subnet(255, 255, 255, 0); - -/* Charger settings (Optional, when using generator charging) */ -volatile float CHARGER_SET_HV = 384; // Reasonably appropriate 4.0v per cell charging of a 96s pack -volatile float CHARGER_MAX_HV = 420; // Max permissible output (VDC) of charger -volatile float CHARGER_MIN_HV = 200; // Min permissible output (VDC) of charger -volatile float CHARGER_MAX_POWER = 3300; // Max power capable of charger, as a ceiling for validating config -volatile float CHARGER_MAX_A = 11.5; // Max current output (amps) of charger -volatile float CHARGER_END_A = 1.0; // Current at which charging is considered complete diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index db208f06..e8dd2c09 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -10,13 +10,6 @@ /* Connectivity options */ //#define WIFICONFIG //Enable this line to set a static IP address / gateway /subnet mask for the device. see USER_SETTINGS.cpp for the settings -extern volatile float CHARGER_SET_HV; -extern volatile float CHARGER_MAX_HV; -extern volatile float CHARGER_MIN_HV; -extern volatile float CHARGER_MAX_POWER; -extern volatile float CHARGER_MAX_A; -extern volatile float CHARGER_END_A; - #ifdef WIFICONFIG extern IPAddress local_IP; extern IPAddress gateway; diff --git a/Software/src/battery/BATTERIES.h b/Software/src/battery/BATTERIES.h index 22741186..cd4a8d56 100644 --- a/Software/src/battery/BATTERIES.h +++ b/Software/src/battery/BATTERIES.h @@ -1,6 +1,5 @@ #ifndef BATTERIES_H #define BATTERIES_H -#include "../../USER_SETTINGS.h" #include "Shunt.h" class Battery; diff --git a/Software/src/battery/CanBattery.h b/Software/src/battery/CanBattery.h index 53d720d7..cf08a7ae 100644 --- a/Software/src/battery/CanBattery.h +++ b/Software/src/battery/CanBattery.h @@ -3,7 +3,6 @@ #include "Battery.h" -#include "../../USER_SETTINGS.h" #include "../../src/communication/Transmitter.h" #include "../../src/communication/can/CanReceiver.h" #include "../../src/communication/can/comm_can.h" diff --git a/Software/src/battery/MG-HS-PHEV-BATTERY.cpp b/Software/src/battery/MG-HS-PHEV-BATTERY.cpp index daf7485f..4eede5ca 100644 --- a/Software/src/battery/MG-HS-PHEV-BATTERY.cpp +++ b/Software/src/battery/MG-HS-PHEV-BATTERY.cpp @@ -15,8 +15,6 @@ changing. OPTIONAL SETTINGS -Put these in your USER_SETTINGS.h: - // This will scale the SoC so the batteries top out at 4.2V/cell instead of 4.1V/cell. The car only seems to use up to 4.1V/cell in service. #define MG_HS_PHEV_USE_FULL_CAPACITY true diff --git a/Software/src/battery/Shunt.h b/Software/src/battery/Shunt.h index 7128e1e8..330988dd 100644 --- a/Software/src/battery/Shunt.h +++ b/Software/src/battery/Shunt.h @@ -1,7 +1,6 @@ #ifndef _SHUNT_H #define _SHUNT_H -#include "../../USER_SETTINGS.h" #include "../../src/communication/Transmitter.h" #include "../../src/communication/can/CanReceiver.h" #include "../../src/communication/can/comm_can.h" diff --git a/Software/src/charger/CHARGERS.cpp b/Software/src/charger/CHARGERS.cpp index 22e7d8c5..6e384fdb 100644 --- a/Software/src/charger/CHARGERS.cpp +++ b/Software/src/charger/CHARGERS.cpp @@ -6,6 +6,15 @@ CanCharger* charger = nullptr; ChargerType user_selected_charger_type = ChargerType::None; +/* Charger settings (Optional, when using generator charging) */ +//TODO: These should be user configurable via webserver +volatile float CHARGER_SET_HV = 384; // Reasonably appropriate 4.0v per cell charging of a 96s pack +volatile float CHARGER_MAX_HV = 420; // Max permissible output (VDC) of charger +volatile float CHARGER_MIN_HV = 200; // Min permissible output (VDC) of charger +volatile float CHARGER_MAX_POWER = 3300; // Max power capable of charger, as a ceiling for validating config +volatile float CHARGER_MAX_A = 11.5; // Max current output (amps) of charger +volatile float CHARGER_END_A = 1.0; // Current at which charging is considered complete + std::vector supported_charger_types() { std::vector types; diff --git a/Software/src/charger/CHARGERS.h b/Software/src/charger/CHARGERS.h index 5581d578..89d46bff 100644 --- a/Software/src/charger/CHARGERS.h +++ b/Software/src/charger/CHARGERS.h @@ -1,6 +1,5 @@ #ifndef CHARGERS_H #define CHARGERS_H -#include "../../USER_SETTINGS.h" #include "CHEVY-VOLT-CHARGER.h" #include "NISSAN-LEAF-CHARGER.h" @@ -12,4 +11,12 @@ void setup_charger(); // The selected charger or null if no charger in use. extern CanCharger* charger; +//TODO: These should be user configurable via webserver +extern volatile float CHARGER_SET_HV; +extern volatile float CHARGER_MAX_HV; +extern volatile float CHARGER_MIN_HV; +extern volatile float CHARGER_MAX_POWER; +extern volatile float CHARGER_MAX_A; +extern volatile float CHARGER_END_A; + #endif diff --git a/Software/src/charger/CHEVY-VOLT-CHARGER.cpp b/Software/src/charger/CHEVY-VOLT-CHARGER.cpp index f981e869..c7317e80 100644 --- a/Software/src/charger/CHEVY-VOLT-CHARGER.cpp +++ b/Software/src/charger/CHEVY-VOLT-CHARGER.cpp @@ -3,6 +3,7 @@ #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../devboard/utils/logging.h" +#include "CHARGERS.h" /* This implements Chevy Volt / Ampera charger support (2011-2015 model years). * diff --git a/Software/src/charger/NISSAN-LEAF-CHARGER.cpp b/Software/src/charger/NISSAN-LEAF-CHARGER.cpp index fa502c07..92f7a780 100644 --- a/Software/src/charger/NISSAN-LEAF-CHARGER.cpp +++ b/Software/src/charger/NISSAN-LEAF-CHARGER.cpp @@ -1,6 +1,7 @@ #include "NISSAN-LEAF-CHARGER.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" +#include "CHARGERS.h" /* This implements Nissan LEAF PDM charger support. 2013-2024 Gen2/3 PDMs are supported * diff --git a/Software/src/communication/can/comm_can.cpp b/Software/src/communication/can/comm_can.cpp index 4dc2e47d..04b67572 100644 --- a/Software/src/communication/can/comm_can.cpp +++ b/Software/src/communication/can/comm_can.cpp @@ -5,7 +5,6 @@ #include "../../lib/pierremolinaro-acan-esp32/ACAN_ESP32.h" #include "../../lib/pierremolinaro-acan2515/ACAN2515.h" #include "CanReceiver.h" -#include "USER_SETTINGS.h" #include "comm_can.h" #include "src/datalayer/datalayer.h" #include "src/devboard/safety/safety.h" diff --git a/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp b/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp index a88ff9d0..cc3269e3 100644 --- a/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp +++ b/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp @@ -257,7 +257,7 @@ REMOTE_BMS_RESET - Allows the user to remotely powercycle the BMS by sending a c This makes the BMS recalculate all SOC% and avoid memory leaks During that time we also set the emulator state to paused in order to not try and send CAN messages towards the battery -Feature is only used if user has enabled PERIODIC_BMS_RESET in the USER_SETTINGS */ +Feature is only used if user has enabled PERIODIC_BMS_RESET */ void handle_BMSpower() { if (periodic_bms_reset || remote_bms_reset) { diff --git a/Software/src/communication/equipmentstopbutton/comm_equipmentstopbutton.cpp b/Software/src/communication/equipmentstopbutton/comm_equipmentstopbutton.cpp index ca056b93..ca0b88c1 100644 --- a/Software/src/communication/equipmentstopbutton/comm_equipmentstopbutton.cpp +++ b/Software/src/communication/equipmentstopbutton/comm_equipmentstopbutton.cpp @@ -2,7 +2,6 @@ #include "../../devboard/hal/hal.h" #include "../../devboard/safety/safety.h" #include "../../devboard/utils/debounce_button.h" -#include "USER_SETTINGS.h" const STOP_BUTTON_BEHAVIOR stop_button_default_behavior = STOP_BUTTON_BEHAVIOR::NOT_CONNECTED; STOP_BUTTON_BEHAVIOR equipment_stop_behavior = stop_button_default_behavior; diff --git a/Software/src/datalayer/datalayer.h b/Software/src/datalayer/datalayer.h index fddc057c..e9ffc8af 100644 --- a/Software/src/datalayer/datalayer.h +++ b/Software/src/datalayer/datalayer.h @@ -1,7 +1,6 @@ #ifndef _DATALAYER_H_ #define _DATALAYER_H_ -#include "../../USER_SETTINGS.h" #include "../devboard/utils/types.h" #include "../system_settings.h" diff --git a/Software/src/datalayer/datalayer_extended.h b/Software/src/datalayer/datalayer_extended.h index 091316da..b982e10f 100644 --- a/Software/src/datalayer/datalayer_extended.h +++ b/Software/src/datalayer/datalayer_extended.h @@ -2,7 +2,6 @@ #define _DATALAYER_EXTENDED_H_ #include -#include "../../USER_SETTINGS.h" struct DATALAYER_INFO_BOLTAMPERA { /** uint16_t */ From 53d40cbe51815b6adfa7947bec1bd7e652c442a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 2 Sep 2025 21:18:15 +0300 Subject: [PATCH 123/245] Remove USER_SETTINGS for good --- .github/ISSUE_TEMPLATE.md | 16 +++++++--------- .github/ISSUE_TEMPLATE/bug_report.md | 10 +++++----- CONTRIBUTING.md | 2 +- Software/Software.cpp | 1 - Software/USER_SETTINGS.cpp | 8 -------- Software/USER_SETTINGS.h | 19 ------------------- Software/src/devboard/hal/hal.cpp | 2 -- Software/src/devboard/mqtt/mqtt.cpp | 1 - Software/src/devboard/utils/events.cpp | 1 - Software/src/devboard/utils/logging.cpp | 1 - Software/src/devboard/utils/logging.h | 2 +- Software/src/devboard/utils/time_meas.h | 1 - .../src/devboard/webserver/settings_html.h | 1 - Software/src/devboard/wifi/wifi.cpp | 7 ++++++- Software/src/inverter/BYD-CAN.h | 1 - Software/src/inverter/CanInverterProtocol.h | 4 +--- Software/src/inverter/INVERTERS.h | 2 -- test/CMakeLists.txt | 1 - 18 files changed, 21 insertions(+), 59 deletions(-) delete mode 100644 Software/USER_SETTINGS.cpp delete mode 100644 Software/USER_SETTINGS.h diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 71921d60..98f59888 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -16,13 +16,11 @@ ### Settings - + -- Software version: `` -- Battery used: `` -- Inverter communication protocol: `` -- Hardware used for Battery-Emulator: `HW_LILYGO, HW_STARK, Custom` -- CONTACTOR_CONTROL: `yes/no` -- CAN_ADDON: `yes/no` -- WEBSERVER: `yes/no` -- MQTT: `yes/no` +- Software version: X.Y.Z +- Battery used: [Nissan LEAF] +- Inverter communication protocol: [BYD Modbus] +- Hardware used for Battery-Emulator: [LilyGo T-CAN/Stark CMR/LilyGo T-2CAN] +- GPIO controlled contactors: [yes/no] +- MQTT: [yes/no] diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 2d7bc4b9..a9b633a6 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -20,10 +20,10 @@ A clear and concise description of what you expected to happen. If applicable, add screenshots to help explain your problem. **Version and settings (please complete the following information):** -- Please attach your USER_SETTINGS files for easier debugging +- Please attach screenshot of configuration for easier debugging - Software version: X.Y.Z -- Battery used: [NISSAN_LEAF] -- Inverter communication protocol: [BYD_MODBUS] -- Hardware used for Battery-Emulator: [HW_LILYGO/HW_STARK/HW_DEVKIT] -- CONTACTOR_CONTROL: [yes/no] +- Battery used: [Nissan LEAF] +- Inverter communication protocol: [BYD Modbus] +- Hardware used for Battery-Emulator: [LilyGo T-CAN/Stark CMR/LilyGo T-2CAN] +- GPIO controlled contactors: [yes/no] - MQTT: [yes/no] diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8136fb2e..b303cd06 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -16,7 +16,7 @@ You're in luck. There's various sources to contribute: - [Discord server](https://www.patreon.com/dala) ## Notes on embedded system -The Battery-Emulator is a real-time control system, which performs lots of time critical operations. Some operations, like contactor control, need to complete within 10 milliseconds periodically. The resources of the ESP32 microcontroller is limited, so keeping track of CPU and memory usage is essential. Keep this in mind when coding for the system! Performance profiling the system can be done by enabling the FUNCTION_TIME_MEASUREMENT option in the USER_SETTINGS.h file +The Battery-Emulator is a real-time control system, which performs lots of time critical operations. Some operations, like contactor control, need to complete within 10 milliseconds periodically. The resources of the ESP32 microcontroller is limited, so keeping track of CPU and memory usage is essential. Keep this in mind when coding for the system! Performance profiling the system can be done by enabling the "Enable performance profiling:" option in the webserver ## Code formatting The project enforces a specific code formatting in the workflows. To get your code formatted properly, it is easiest to use a pre-commit hook before pushing the code to a pull request. diff --git a/Software/Software.cpp b/Software/Software.cpp index da59ecbb..8ad87b7e 100644 --- a/Software/Software.cpp +++ b/Software/Software.cpp @@ -1,6 +1,5 @@ #include #include "HardwareSerial.h" -#include "USER_SETTINGS.h" #include "esp_system.h" #include "esp_task_wdt.h" #include "esp_timer.h" diff --git a/Software/USER_SETTINGS.cpp b/Software/USER_SETTINGS.cpp deleted file mode 100644 index 78cb6eee..00000000 --- a/Software/USER_SETTINGS.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include "USER_SETTINGS.h" -#include -#include "src/devboard/hal/hal.h" - -// Set your Static IP address. Only used incase WIFICONFIG is set in USER_SETTINGS.h -IPAddress local_IP(192, 168, 10, 150); -IPAddress gateway(192, 168, 10, 1); -IPAddress subnet(255, 255, 255, 0); diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h deleted file mode 100644 index e8dd2c09..00000000 --- a/Software/USER_SETTINGS.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef __USER_SETTINGS_H__ -#define __USER_SETTINGS_H__ -#include -#include -#include "src/devboard/utils/types.h" - -/* Shunt/Contactor settings (Optional) */ -//#define BMW_SBOX // SBOX relay control & battery current/voltage measurement - -/* Connectivity options */ -//#define WIFICONFIG //Enable this line to set a static IP address / gateway /subnet mask for the device. see USER_SETTINGS.cpp for the settings - -#ifdef WIFICONFIG -extern IPAddress local_IP; -extern IPAddress gateway; -extern IPAddress subnet; -#endif - -#endif // __USER_SETTINGS_H__ diff --git a/Software/src/devboard/hal/hal.cpp b/Software/src/devboard/hal/hal.cpp index ab10943f..e8b0a958 100644 --- a/Software/src/devboard/hal/hal.cpp +++ b/Software/src/devboard/hal/hal.cpp @@ -1,7 +1,5 @@ #include "hal.h" -#include "../../../USER_SETTINGS.h" - #include Esp32Hal* esp32hal = nullptr; diff --git a/Software/src/devboard/mqtt/mqtt.cpp b/Software/src/devboard/mqtt/mqtt.cpp index cf505a34..c6ed1640 100644 --- a/Software/src/devboard/mqtt/mqtt.cpp +++ b/Software/src/devboard/mqtt/mqtt.cpp @@ -4,7 +4,6 @@ #include #include #include -#include "../../../USER_SETTINGS.h" #include "../../battery/BATTERIES.h" #include "../../communication/contactorcontrol/comm_contactorcontrol.h" #include "../../datalayer/datalayer.h" diff --git a/Software/src/devboard/utils/events.cpp b/Software/src/devboard/utils/events.cpp index 19e877b3..d6875aab 100644 --- a/Software/src/devboard/utils/events.cpp +++ b/Software/src/devboard/utils/events.cpp @@ -1,6 +1,5 @@ #include "events.h" #include -#include "../../../USER_SETTINGS.h" #include "../../datalayer/datalayer.h" #include "../../devboard/hal/hal.h" #include "../../devboard/utils/logging.h" diff --git a/Software/src/devboard/utils/logging.cpp b/Software/src/devboard/utils/logging.cpp index e20607fe..2b4b1d30 100644 --- a/Software/src/devboard/utils/logging.cpp +++ b/Software/src/devboard/utils/logging.cpp @@ -1,5 +1,4 @@ #include "logging.h" -#include "../../../USER_SETTINGS.h" #include "../../datalayer/datalayer.h" #include "../sdcard/sdcard.h" diff --git a/Software/src/devboard/utils/logging.h b/Software/src/devboard/utils/logging.h index 69a1f170..21422385 100644 --- a/Software/src/devboard/utils/logging.h +++ b/Software/src/devboard/utils/logging.h @@ -2,8 +2,8 @@ #define __LOGGING_H__ #include -#include "../../../USER_SETTINGS.h" #include "../../datalayer/datalayer.h" +#include "Print.h" #include "types.h" #ifndef UNIT_TEST diff --git a/Software/src/devboard/utils/time_meas.h b/Software/src/devboard/utils/time_meas.h index df2c8b0c..aeac69da 100644 --- a/Software/src/devboard/utils/time_meas.h +++ b/Software/src/devboard/utils/time_meas.h @@ -1,7 +1,6 @@ #ifndef TIME_MEAS_H_ #define TIME_MEAS_H_ -#include "USER_SETTINGS.h" #include "esp_timer.h" /** Start time measurement in microseconds diff --git a/Software/src/devboard/webserver/settings_html.h b/Software/src/devboard/webserver/settings_html.h index 17043a37..227f2607 100644 --- a/Software/src/devboard/webserver/settings_html.h +++ b/Software/src/devboard/webserver/settings_html.h @@ -7,7 +7,6 @@ extern std::string ssid; extern std::string password; -#include "../../../USER_SETTINGS.h" // Needed for WiFi ssid and password #include "../../communication/nvm/comm_nvm.h" /** diff --git a/Software/src/devboard/wifi/wifi.cpp b/Software/src/devboard/wifi/wifi.cpp index 1cf51b83..b9ba7750 100644 --- a/Software/src/devboard/wifi/wifi.cpp +++ b/Software/src/devboard/wifi/wifi.cpp @@ -2,7 +2,6 @@ #include #include "../utils/events.h" #include "../utils/logging.h" -#include "USER_SETTINGS.h" bool wifi_enabled = true; bool wifiap_enabled = true; @@ -15,6 +14,12 @@ std::string password; std::string ssidAP; std::string passwordAP; +// Set your Static IP address. Only used incase Static address option is set +//TODO: Make configurable via webserver +IPAddress local_IP(192, 168, 10, 150); +IPAddress gateway(192, 168, 10, 1); +IPAddress subnet(255, 255, 255, 0); + // Configuration Parameters static const uint16_t WIFI_CHECK_INTERVAL = 2000; // 1 seconds normal check interval when last connected static const uint16_t STEP_WIFI_CHECK_INTERVAL = 2000; // 3 seconds wait step increase in checks for normal reconnects diff --git a/Software/src/inverter/BYD-CAN.h b/Software/src/inverter/BYD-CAN.h index 07fd1189..4eb63918 100644 --- a/Software/src/inverter/BYD-CAN.h +++ b/Software/src/inverter/BYD-CAN.h @@ -1,7 +1,6 @@ #ifndef BYD_CAN_H #define BYD_CAN_H -#include "../../USER_SETTINGS.h" #include "../datalayer/datalayer.h" #include "CanInverterProtocol.h" diff --git a/Software/src/inverter/CanInverterProtocol.h b/Software/src/inverter/CanInverterProtocol.h index 3c9585d5..777a4548 100644 --- a/Software/src/inverter/CanInverterProtocol.h +++ b/Software/src/inverter/CanInverterProtocol.h @@ -1,14 +1,12 @@ #ifndef CANINVERTER_PROTOCOL_H #define CANINVERTER_PROTOCOL_H -#include "../../USER_SETTINGS.h" -#include "InverterProtocol.h" - #include "../communication/Transmitter.h" #include "../communication/can/CanReceiver.h" #include "../communication/can/comm_can.h" #include "../devboard/safety/safety.h" #include "../devboard/utils/types.h" +#include "InverterProtocol.h" class CanInverterProtocol : public InverterProtocol, Transmitter, CanReceiver { public: diff --git a/Software/src/inverter/INVERTERS.h b/Software/src/inverter/INVERTERS.h index 08d411ef..231eb804 100644 --- a/Software/src/inverter/INVERTERS.h +++ b/Software/src/inverter/INVERTERS.h @@ -4,8 +4,6 @@ #include "InverterProtocol.h" extern InverterProtocol* inverter; -#include "../../USER_SETTINGS.h" - #include "AFORE-CAN.h" #include "BYD-CAN.h" #include "BYD-MODBUS.h" diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8d16116c..398cb69a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -82,7 +82,6 @@ add_executable(tests ../Software/src/lib/eModbus-eModbus/ModbusTypeDefs.cpp ../Software/src/lib/eModbus-eModbus/RTUutils.cpp # ../Software/src/lib/eModbus-eModbus/scripts/mbServerFCs.cpp - ../Software/USER_SETTINGS.cpp ../Software/src/battery/BATTERIES.cpp ../Software/src/battery/Battery.cpp ../Software/src/battery/BMW-I3-BATTERY.cpp From 081d96e055233b0e0648b4d830b0cd00f0c253b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 2 Sep 2025 21:56:30 +0300 Subject: [PATCH 124/245] Fix unit tests after removal of USER_SETTINGS --- test/emul/Arduino.cpp | 11 +++++++++++ test/emul/Arduino.h | 16 +++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/test/emul/Arduino.cpp b/test/emul/Arduino.cpp index aa0997e0..f14846c3 100644 --- a/test/emul/Arduino.cpp +++ b/test/emul/Arduino.cpp @@ -1,5 +1,16 @@ #include "Arduino.h" +#include "../../Software/src/communication/can/comm_can.h" + +// Provide the definition that was previously in USER_SETTINGS.cpp +volatile CAN_Configuration can_config = { + .battery = CAN_Interface::CAN_NATIVE, + .inverter = CAN_Interface::CAN_NATIVE, + .battery_double = CAN_Interface::CAN_NATIVE, + .charger = CAN_Interface::CAN_NATIVE, + .shunt = CAN_Interface::CAN_NATIVE +}; + void delay(unsigned long ms) {} void delayMicroseconds(unsigned long us) {} int digitalRead(uint8_t pin) { diff --git a/test/emul/Arduino.h b/test/emul/Arduino.h index 149191af..af908100 100644 --- a/test/emul/Arduino.h +++ b/test/emul/Arduino.h @@ -38,7 +38,6 @@ inline long random(long min, long max) { return min; // Return the minimum value for predictability } -// Also add randomSeed for completeness inline void randomSeed(unsigned long seed) { (void)seed; } @@ -98,6 +97,21 @@ inline int analogRead(uint8_t pin) { return 0; // Return 0 for predictable tests } +// Mock WiFi types +typedef int WiFiEvent_t; +typedef int WiFiEventInfo_t; + +// Mock WiFi functions +inline void onWifiConnect(WiFiEvent_t event, WiFiEventInfo_t info) { + (void)event; + (void)info; +} + +inline void onWifiDisconnect(WiFiEvent_t event, WiFiEventInfo_t info) { + (void)event; + (void)info; +} + unsigned long micros(); // Can be previously declared as a macro in stupid eModbus #undef millis From 630c67a482c75d21205912d581e84fff26df9478 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 2 Sep 2025 18:57:25 +0000 Subject: [PATCH 125/245] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- test/emul/Arduino.cpp | 12 +++++------- test/emul/Arduino.h | 8 ++++---- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/test/emul/Arduino.cpp b/test/emul/Arduino.cpp index f14846c3..563dd466 100644 --- a/test/emul/Arduino.cpp +++ b/test/emul/Arduino.cpp @@ -3,13 +3,11 @@ #include "../../Software/src/communication/can/comm_can.h" // Provide the definition that was previously in USER_SETTINGS.cpp -volatile CAN_Configuration can_config = { - .battery = CAN_Interface::CAN_NATIVE, - .inverter = CAN_Interface::CAN_NATIVE, - .battery_double = CAN_Interface::CAN_NATIVE, - .charger = CAN_Interface::CAN_NATIVE, - .shunt = CAN_Interface::CAN_NATIVE -}; +volatile CAN_Configuration can_config = {.battery = CAN_Interface::CAN_NATIVE, + .inverter = CAN_Interface::CAN_NATIVE, + .battery_double = CAN_Interface::CAN_NATIVE, + .charger = CAN_Interface::CAN_NATIVE, + .shunt = CAN_Interface::CAN_NATIVE}; void delay(unsigned long ms) {} void delayMicroseconds(unsigned long us) {} diff --git a/test/emul/Arduino.h b/test/emul/Arduino.h index af908100..0bcf55b5 100644 --- a/test/emul/Arduino.h +++ b/test/emul/Arduino.h @@ -103,13 +103,13 @@ typedef int WiFiEventInfo_t; // Mock WiFi functions inline void onWifiConnect(WiFiEvent_t event, WiFiEventInfo_t info) { - (void)event; - (void)info; + (void)event; + (void)info; } inline void onWifiDisconnect(WiFiEvent_t event, WiFiEventInfo_t info) { - (void)event; - (void)info; + (void)event; + (void)info; } unsigned long micros(); From 778d9b85853858e83e83b9f323c085b33d5316ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 2 Sep 2025 22:00:00 +0300 Subject: [PATCH 126/245] Wifi unit test wrap --- Software/src/communication/can/comm_can.h | 1 + Software/src/devboard/wifi/wifi.h | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/Software/src/communication/can/comm_can.h b/Software/src/communication/can/comm_can.h index b4624928..5af47131 100644 --- a/Software/src/communication/can/comm_can.h +++ b/Software/src/communication/can/comm_can.h @@ -22,6 +22,7 @@ typedef struct { CAN_Interface charger; CAN_Interface shunt; } CAN_Configuration; + extern volatile CAN_Configuration can_config; enum class CAN_Speed { diff --git a/Software/src/devboard/wifi/wifi.h b/Software/src/devboard/wifi/wifi.h index bf0ecb01..0a24d283 100644 --- a/Software/src/devboard/wifi/wifi.h +++ b/Software/src/devboard/wifi/wifi.h @@ -15,9 +15,19 @@ void init_WiFi(); void wifi_monitor(); void connectToWiFi(); void FullReconnectToWiFi(); + +// In the real wifi.h +#ifndef UNIT_TEST void onWifiConnect(WiFiEvent_t event, WiFiEventInfo_t info); void onWifiDisconnect(WiFiEvent_t event, WiFiEventInfo_t info); void onWifiGotIP(WiFiEvent_t event, WiFiEventInfo_t info); +#else +// Mock declarations for unit tests +typedef int WiFiEvent_t; +typedef int WiFiEventInfo_t; +void onWifiConnect(WiFiEvent_t event, WiFiEventInfo_t info); +void onWifiDisconnect(WiFiEvent_t event, WiFiEventInfo_t info); +#endif void init_WiFi_AP(); From 90d333214371e0f320bd476f7615cc0118061f29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 2 Sep 2025 23:34:58 +0300 Subject: [PATCH 127/245] Add configurable static IP --- Software/src/communication/nvm/comm_nvm.cpp | 14 ++ .../src/devboard/webserver/settings_html.cpp | 188 +++++++++++++----- Software/src/devboard/webserver/webserver.cpp | 44 +++- Software/src/devboard/wifi/wifi.cpp | 32 ++- Software/src/devboard/wifi/wifi.h | 13 ++ 5 files changed, 228 insertions(+), 63 deletions(-) diff --git a/Software/src/communication/nvm/comm_nvm.cpp b/Software/src/communication/nvm/comm_nvm.cpp index 3ba5b478..df9cae1b 100644 --- a/Software/src/communication/nvm/comm_nvm.cpp +++ b/Software/src/communication/nvm/comm_nvm.cpp @@ -166,6 +166,20 @@ void init_stored_settings() { mqtt_transmit_all_cellvoltages = settings.getBool("MQTTCELLV", false); custom_hostname = settings.getString("HOSTNAME").c_str(); + static_IP_enabled = settings.getBool("STATICIP", false); + static_local_IP1 = settings.getUInt("LOCALIP1", 192); + static_local_IP2 = settings.getUInt("LOCALIP2", 168); + static_local_IP3 = settings.getUInt("LOCALIP3", 10); + static_local_IP4 = settings.getUInt("LOCALIP4", 150); + static_gateway1 = settings.getUInt("GATEWAY1", 192); + static_gateway2 = settings.getUInt("GATEWAY2", 168); + static_gateway3 = settings.getUInt("GATEWAY3", 10); + static_gateway4 = settings.getUInt("GATEWAY4", 1); + static_subnet1 = settings.getUInt("SUBNET1", 255); + static_subnet2 = settings.getUInt("SUBNET2", 255); + static_subnet3 = settings.getUInt("SUBNET3", 255); + static_subnet4 = settings.getUInt("SUBNET4", 0); + mqtt_server = settings.getString("MQTTSERVER").c_str(); mqtt_port = settings.getUInt("MQTTPORT", 0); mqtt_user = settings.getString("MQTTUSER").c_str(); diff --git a/Software/src/devboard/webserver/settings_html.cpp b/Software/src/devboard/webserver/settings_html.cpp index b2892d89..63411805 100644 --- a/Software/src/devboard/webserver/settings_html.cpp +++ b/Software/src/devboard/webserver/settings_html.cpp @@ -291,10 +291,62 @@ String settings_processor(const String& var, BatteryEmulatorSettingsStore& setti return settings.getBool("WIFIAPENABLED", wifiap_enabled) ? "checked" : ""; } + if (var == "STATICIP") { + return settings.getBool("STATICIP") ? "checked" : ""; + } + if (var == "WIFICHANNEL") { return String(settings.getUInt("WIFICHANNEL", 0)); } + if (var == "LOCALIP1") { + return String(settings.getUInt("LOCALIP1", 0)); + } + + if (var == "LOCALIP2") { + return String(settings.getUInt("LOCALIP2", 0)); + } + + if (var == "LOCALIP3") { + return String(settings.getUInt("LOCALIP3", 0)); + } + + if (var == "LOCALIP4") { + return String(settings.getUInt("LOCALIP4", 0)); + } + + if (var == "GATEWAY1") { + return String(settings.getUInt("GATEWAY1", 0)); + } + + if (var == "GATEWAY2") { + return String(settings.getUInt("GATEWAY2", 0)); + } + + if (var == "GATEWAY3") { + return String(settings.getUInt("GATEWAY3", 0)); + } + + if (var == "GATEWAY4") { + return String(settings.getUInt("GATEWAY4", 0)); + } + + if (var == "SUBNET1") { + return String(settings.getUInt("SUBNET1", 0)); + } + + if (var == "SUBNET2") { + return String(settings.getUInt("SUBNET2", 0)); + } + + if (var == "SUBNET3") { + return String(settings.getUInt("SUBNET3", 0)); + } + + if (var == "SUBNET4") { + return String(settings.getUInt("SUBNET4", 0)); + } + if (var == "PERFPROFILE") { return settings.getBool("PERFPROFILE") ? "checked" : ""; } @@ -852,6 +904,11 @@ const char* getCANInterfaceName(CAN_Interface interface) { display: contents; } + form .if-staticip { display: none; } + form[data-staticip="true"] .if-staticip { + display: contents; + } + form .if-mqtt { display: none; } form[data-mqttenabled="true"] .if-mqtt { display: contents; @@ -869,40 +926,41 @@ const char* getCANInterfaceName(CAN_Interface interface) { R"rawliteral( -
+

SSID: %SSID%

Password: ########

+
-
-
+
- - + %BATTTYPE%
- - + +
- - - - - - - - + + + + + + + +
@@ -970,7 +1028,7 @@ const char* getCANInterfaceName(CAN_Interface interface) { - +
+ + + - +
@@ -1009,18 +1070,18 @@ const char* getCANInterfaceName(CAN_Interface interface) { %BATT2COMM% - +
- +
- +
@@ -1033,58 +1094,85 @@ const char* getCANInterfaceName(CAN_Interface interface) {
- + - + - +
- +
- - - - - + + - + + + + +
+
+
Local IP:
+ . + . + . + +
+ +
+
Gateway:
+ . + . + . + +
+ +
+
Subnet:
+ . + . + . + +
+
+
+ - + - + - + - + - + - + - +
@@ -1092,9 +1180,9 @@ const char* getCANInterfaceName(CAN_Interface interface) { - + - +
@@ -1106,14 +1194,14 @@ const char* getCANInterfaceName(CAN_Interface interface) {
- +
-

Settings saved. Reboot to take the settings into use.

+

Settings saved. Reboot to take the new settings into use.

diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 2b5f0b9d..4a173127 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -397,10 +397,10 @@ void init_webserver() { }; const char* boolSettingNames[] = { - "DBLBTR", "CNTCTRL", "CNTCTRLDBL", "PWMCNTCTRL", "PERBMSRESET", "SDLOGENABLED", - "REMBMSRESET", "EXTPRECHARGE", "USBENABLED", "CANLOGUSB", "WEBENABLED", "CANFDASCAN", - "CANLOGSD", "WIFIAPENABLED", "MQTTENABLED", "NOINVDISC", "HADISC", "MQTTTOPICS", - "MQTTCELLV", "INVICNT", "GTWRHD", "DIGITALHVIL", "PERFPROFILE", "INTERLOCKREQ", + "DBLBTR", "CNTCTRL", "CNTCTRLDBL", "PWMCNTCTRL", "PERBMSRESET", "SDLOGENABLED", "STATICIP", + "REMBMSRESET", "EXTPRECHARGE", "USBENABLED", "CANLOGUSB", "WEBENABLED", "CANFDASCAN", "CANLOGSD", + "WIFIAPENABLED", "MQTTENABLED", "NOINVDISC", "HADISC", "MQTTTOPICS", "MQTTCELLV", "INVICNT", + "GTWRHD", "DIGITALHVIL", "PERFPROFILE", "INTERLOCKREQ", }; // Handles the form POST from UI to save settings of the common image @@ -467,6 +467,42 @@ void init_webserver() { } else if (p->name() == "WIFICHANNEL") { auto type = atoi(p->value().c_str()); settings.saveUInt("WIFICHANNEL", type); + } else if (p->name() == "LOCALIP1") { + auto type = atoi(p->value().c_str()); + settings.saveUInt("LOCALIP1", type); + } else if (p->name() == "LOCALIP2") { + auto type = atoi(p->value().c_str()); + settings.saveUInt("LOCALIP2", type); + } else if (p->name() == "LOCALIP3") { + auto type = atoi(p->value().c_str()); + settings.saveUInt("LOCALIP3", type); + } else if (p->name() == "LOCALIP4") { + auto type = atoi(p->value().c_str()); + settings.saveUInt("LOCALIP4", type); + } else if (p->name() == "GATEWAY1") { + auto type = atoi(p->value().c_str()); + settings.saveUInt("GATEWAY1", type); + } else if (p->name() == "GATEWAY2") { + auto type = atoi(p->value().c_str()); + settings.saveUInt("GATEWAY2", type); + } else if (p->name() == "GATEWAY3") { + auto type = atoi(p->value().c_str()); + settings.saveUInt("GATEWAY3", type); + } else if (p->name() == "GATEWAY4") { + auto type = atoi(p->value().c_str()); + settings.saveUInt("GATEWAY4", type); + } else if (p->name() == "SUBNET1") { + auto type = atoi(p->value().c_str()); + settings.saveUInt("SUBNET1", type); + } else if (p->name() == "SUBNET2") { + auto type = atoi(p->value().c_str()); + settings.saveUInt("SUBNET2", type); + } else if (p->name() == "SUBNET3") { + auto type = atoi(p->value().c_str()); + settings.saveUInt("SUBNET3", type); + } else if (p->name() == "SUBNET4") { + auto type = atoi(p->value().c_str()); + settings.saveUInt("SUBNET4", type); } else if (p->name() == "HOSTNAME") { settings.saveString("HOSTNAME", p->value().c_str()); } else if (p->name() == "MQTTSERVER") { diff --git a/Software/src/devboard/wifi/wifi.cpp b/Software/src/devboard/wifi/wifi.cpp index b9ba7750..ab9831ee 100644 --- a/Software/src/devboard/wifi/wifi.cpp +++ b/Software/src/devboard/wifi/wifi.cpp @@ -15,10 +15,19 @@ std::string ssidAP; std::string passwordAP; // Set your Static IP address. Only used incase Static address option is set -//TODO: Make configurable via webserver -IPAddress local_IP(192, 168, 10, 150); -IPAddress gateway(192, 168, 10, 1); -IPAddress subnet(255, 255, 255, 0); +bool static_IP_enabled = false; +uint16_t static_local_IP1 = 0; +uint16_t static_local_IP2 = 0; +uint16_t static_local_IP3 = 0; +uint16_t static_local_IP4 = 0; +uint16_t static_gateway1 = 0; +uint16_t static_gateway2 = 0; +uint16_t static_gateway3 = 0; +uint16_t static_gateway4 = 0; +uint16_t static_subnet1 = 0; +uint16_t static_subnet2 = 0; +uint16_t static_subnet3 = 0; +uint16_t static_subnet4 = 0; // Configuration Parameters static const uint16_t WIFI_CHECK_INTERVAL = 2000; // 1 seconds normal check interval when last connected @@ -62,11 +71,16 @@ void init_WiFi() { // Set WiFi to auto reconnect WiFi.setAutoReconnect(true); -#ifdef WIFICONFIG - // Set static IP - WiFi.config(local_IP, gateway, subnet); -#endif - + if (static_IP_enabled) { + // Set static IP + IPAddress local_IP((uint8_t)static_local_IP1, (uint8_t)static_local_IP2, (uint8_t)static_local_IP3, + (uint8_t)static_local_IP4); + IPAddress gateway((uint8_t)static_gateway1, (uint8_t)static_gateway2, (uint8_t)static_gateway3, + (uint8_t)static_gateway4); + IPAddress subnet((uint8_t)static_subnet1, (uint8_t)static_subnet2, (uint8_t)static_subnet3, + (uint8_t)static_subnet4); + WiFi.config(local_IP, gateway, subnet); + } DEBUG_PRINTF("init_Wifi set event handlers\n"); // Initialize Wi-Fi event handlers diff --git a/Software/src/devboard/wifi/wifi.h b/Software/src/devboard/wifi/wifi.h index 0a24d283..ed6a9a89 100644 --- a/Software/src/devboard/wifi/wifi.h +++ b/Software/src/devboard/wifi/wifi.h @@ -37,5 +37,18 @@ void init_mDNS(); extern bool wifi_enabled; extern bool wifiap_enabled; extern bool mdns_enabled; +extern bool static_IP_enabled; +extern uint16_t static_local_IP1; +extern uint16_t static_local_IP2; +extern uint16_t static_local_IP3; +extern uint16_t static_local_IP4; +extern uint16_t static_gateway1; +extern uint16_t static_gateway2; +extern uint16_t static_gateway3; +extern uint16_t static_gateway4; +extern uint16_t static_subnet1; +extern uint16_t static_subnet2; +extern uint16_t static_subnet3; +extern uint16_t static_subnet4; #endif From 21870349d40f7d981a8c4a9d44a60aa1d124cb08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 2 Sep 2025 23:40:32 +0300 Subject: [PATCH 128/245] Update Software.cpp --- Software/Software.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/Software.cpp b/Software/Software.cpp index 8ad87b7e..81412e76 100644 --- a/Software/Software.cpp +++ b/Software/Software.cpp @@ -34,7 +34,7 @@ #endif // The current software version, shown on webserver -const char* version_number = "9.0.RC5experimental"; +const char* version_number = "9.0.RC5"; // Interval timers volatile unsigned long currentMillis = 0; From 0617bf4b8c95a3ef46546928816c161279c28b71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 2 Sep 2025 23:59:44 +0300 Subject: [PATCH 129/245] Update release-assets.yml --- .github/workflows/release-assets.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/release-assets.yml b/.github/workflows/release-assets.yml index fcbf4684..bb1c4dc9 100644 --- a/.github/workflows/release-assets.yml +++ b/.github/workflows/release-assets.yml @@ -47,10 +47,6 @@ jobs: - name: Install PlatformIO Core run: pip install --upgrade platformio - # Copy USER_SECRETS.TEMPLATE.h to USER_SECRETS.h - - name: Copy USER_SECRETS.TEMPLATE.h to USER_SECRETS.h - run: cp ./Software/USER_SECRETS.TEMPLATE.h ./Software/USER_SECRETS.h - - name: 🛠 Build ota image for Lilygo T-CAN run: | pio run -e lilygo_330 From 08b6c81e67c7ce2761d0940be7c0f55e403fed58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 3 Sep 2025 13:02:08 +0300 Subject: [PATCH 130/245] Increase CAN TX buffer --- Software/Software.cpp | 2 +- .../src/lib/pierremolinaro-acan-esp32/ACAN_ESP32_Settings.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Software/Software.cpp b/Software/Software.cpp index 81412e76..9dca621e 100644 --- a/Software/Software.cpp +++ b/Software/Software.cpp @@ -34,7 +34,7 @@ #endif // The current software version, shown on webserver -const char* version_number = "9.0.RC5"; +const char* version_number = "9.0.RC6experimental"; // Interval timers volatile unsigned long currentMillis = 0; diff --git a/Software/src/lib/pierremolinaro-acan-esp32/ACAN_ESP32_Settings.h b/Software/src/lib/pierremolinaro-acan-esp32/ACAN_ESP32_Settings.h index 9f1d937e..9ad3136f 100644 --- a/Software/src/lib/pierremolinaro-acan-esp32/ACAN_ESP32_Settings.h +++ b/Software/src/lib/pierremolinaro-acan-esp32/ACAN_ESP32_Settings.h @@ -101,7 +101,7 @@ class ACAN_ESP32_Settings { // Transmit buffer sizes // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - public: uint16_t mDriverTransmitBufferSize = 16 ; + public: uint16_t mDriverTransmitBufferSize = 32 ; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Compute actual bit rate From e38b6286c75f090362a2c0638aaa148ec6dd6c61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 3 Sep 2025 23:25:30 +0300 Subject: [PATCH 131/245] Add initial support for 50/75kWh vans --- Software/src/battery/ECMP-BATTERY.cpp | 155 +++++++++++++++++++++----- Software/src/battery/ECMP-BATTERY.h | 5 +- 2 files changed, 130 insertions(+), 30 deletions(-) diff --git a/Software/src/battery/ECMP-BATTERY.cpp b/Software/src/battery/ECMP-BATTERY.cpp index 536f4b90..456eb7b0 100644 --- a/Software/src/battery/ECMP-BATTERY.cpp +++ b/Software/src/battery/ECMP-BATTERY.cpp @@ -12,44 +12,86 @@ This integration is still ongoing. Here is what still needs to be done in order /* Do not change code below unless you are sure what you are doing */ void EcmpBattery::update_values() { - datalayer.battery.status.real_soc = battery_soc * 10; + if (!MysteryVan) { //Normal eCMP platform + datalayer.battery.status.real_soc = battery_soc * 10; - datalayer.battery.status.soh_pptt; + datalayer.battery.status.soh_pptt; - datalayer.battery.status.voltage_dV = battery_voltage * 10; + datalayer.battery.status.voltage_dV = battery_voltage * 10; - datalayer.battery.status.current_dA = -(battery_current * 10); + datalayer.battery.status.current_dA = -(battery_current * 10); - datalayer.battery.status.active_power_W = //Power in watts, Negative = charging batt - ((datalayer.battery.status.voltage_dV * datalayer.battery.status.current_dA) / 100); + datalayer.battery.status.active_power_W = //Power in watts, Negative = charging batt + ((datalayer.battery.status.voltage_dV * datalayer.battery.status.current_dA) / 100); - datalayer.battery.status.max_charge_power_W = battery_AllowedMaxChargeCurrent * battery_voltage; + datalayer.battery.status.max_charge_power_W = battery_AllowedMaxChargeCurrent * battery_voltage; - datalayer.battery.status.max_discharge_power_W = battery_AllowedMaxDischargeCurrent * battery_voltage; + datalayer.battery.status.max_discharge_power_W = battery_AllowedMaxDischargeCurrent * battery_voltage; - datalayer.battery.status.temperature_min_dC = battery_lowestTemperature * 10; + datalayer.battery.status.temperature_min_dC = battery_lowestTemperature * 10; - datalayer.battery.status.temperature_max_dC = battery_highestTemperature * 10; + datalayer.battery.status.temperature_max_dC = battery_highestTemperature * 10; - // Initialize min and max, lets find which cells are min and max! - uint16_t min_cell_mv_value = std::numeric_limits::max(); - uint16_t max_cell_mv_value = 0; - // Loop to find the min and max while ignoring zero values - for (uint8_t i = 0; i < datalayer.battery.info.number_of_cells; ++i) { - uint16_t voltage_mV = datalayer.battery.status.cell_voltages_mV[i]; - if (voltage_mV != 0) { // Skip unread values (0) - min_cell_mv_value = std::min(min_cell_mv_value, voltage_mV); - max_cell_mv_value = std::max(max_cell_mv_value, voltage_mV); + // Initialize min and max, lets find which cells are min and max! + uint16_t min_cell_mv_value = std::numeric_limits::max(); + uint16_t max_cell_mv_value = 0; + // Loop to find the min and max while ignoring zero values + for (uint8_t i = 0; i < datalayer.battery.info.number_of_cells; ++i) { + uint16_t voltage_mV = datalayer.battery.status.cell_voltages_mV[i]; + if (voltage_mV != 0) { // Skip unread values (0) + min_cell_mv_value = std::min(min_cell_mv_value, voltage_mV); + max_cell_mv_value = std::max(max_cell_mv_value, voltage_mV); + } + } + // If all array values are 0, reset min/max to 3700 + if (min_cell_mv_value == std::numeric_limits::max()) { + min_cell_mv_value = 3700; + max_cell_mv_value = 3700; + } + + datalayer.battery.status.cell_min_voltage_mV = min_cell_mv_value; + datalayer.battery.status.cell_max_voltage_mV = max_cell_mv_value; + } else { //Some variant of the 50/75kWh battery that is not using the eCMP CAN mappings. + // For these batteries we need to use the OBD2 PID polled values + datalayer.battery.status.real_soc = battery_soc * 10; //TTOD, calculate based on cap remaining? + + datalayer.battery.status.soh_pptt; + + if (pid_pack_voltage != NOT_SAMPLED_YET) { + datalayer.battery.status.voltage_dV = pid_pack_voltage; + } + + if (pid_current != NOT_SAMPLED_YET) { + datalayer.battery.status.current_dA = (int16_t)(pid_current / 100); + + datalayer.battery.status.active_power_W = + (uint16_t)((pid_current / 1000.0f) * (datalayer.battery.status.voltage_dV / 10.0f)); + } + + if (pid_max_charge_10s != NOT_SAMPLED_YET) { + datalayer.battery.status.max_charge_power_W = pid_max_charge_10s; + } + + if (pid_max_discharge_10s != NOT_SAMPLED_YET) { + datalayer.battery.status.max_discharge_power_W = pid_max_discharge_10s; + } + + if (pid_lowest_temperature != NOT_SAMPLED_YET) { + datalayer.battery.status.temperature_min_dC = pid_lowest_temperature * 10; + } + + if (pid_highest_temperature != NOT_SAMPLED_YET) { + datalayer.battery.status.temperature_max_dC = pid_highest_temperature * 10; + } + + if (pid_high_cell_voltage != NOT_SAMPLED_YET) { + datalayer.battery.status.cell_max_voltage_mV = pid_high_cell_voltage; + } + + if (pid_low_cell_voltage != NOT_SAMPLED_YET) { + datalayer.battery.status.cell_min_voltage_mV = pid_low_cell_voltage; } } - // If all array values are 0, reset min/max to 3700 - if (min_cell_mv_value == std::numeric_limits::max()) { - min_cell_mv_value = 3700; - max_cell_mv_value = 3700; - } - - datalayer.battery.status.cell_min_voltage_mV = min_cell_mv_value; - datalayer.battery.status.cell_max_voltage_mV = max_cell_mv_value; // Update extended datalayer (More Battery Info page) datalayer_extended.stellantisECMP.MainConnectorState = battery_MainConnectorState; @@ -145,9 +187,58 @@ void EcmpBattery::update_values() { } void EcmpBattery::handle_incoming_can_frame(CAN_frame rx_frame) { - datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; switch (rx_frame.ID) { + case 0x2D4: //MysteryVan 50/75kWh platform + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + MysteryVan = true; + break; + case 0x3B4: //MysteryVan 50/75kWh platform + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x2F4: //MysteryVan 50/75kWh platform + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x3F4: //MysteryVan 50/75kWh platform + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x554: //MysteryVan 50/75kWh platform + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x373: //MysteryVan 50/75kWh platform + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x4F4: //MysteryVan 50/75kWh platform + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x414: //MysteryVan 50/75kWh platform + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x353: //MysteryVan 50/75kWh platform + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x474: //MysteryVan 50/75kWh platform + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x574: //MysteryVan 50/75kWh platform + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x583: //MysteryVan 50/75kWh platform + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x314: //MysteryVan 50/75kWh platform + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x254: //MysteryVan 50/75kWh platform + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x2B4: //MysteryVan 50/75kWh platform + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x4D4: //MysteryVan 50/75kWh platform + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; case 0x125: //Common + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; battery_soc = (rx_frame.data.u8[0] << 2) | (rx_frame.data.u8[1] >> 6); // Byte1, bit 7 length 10 (0x3FE when abnormal) (0-1000 ppt) battery_MainConnectorState = ((rx_frame.data.u8[2] & 0x18) >> @@ -157,6 +248,7 @@ void EcmpBattery::handle_incoming_can_frame(CAN_frame rx_frame) { battery_current = (((rx_frame.data.u8[4] & 0x0F) << 8) | rx_frame.data.u8[5]) - 600; // TODO: Test break; case 0x127: //DFM specific + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; battery_AllowedMaxChargeCurrent = (rx_frame.data.u8[0] << 2) | ((rx_frame.data.u8[1] & 0xC0) >> 6); //Byte 1, bit 7, length 10 (0-600A) [0x3FF if invalid] @@ -165,12 +257,15 @@ void EcmpBattery::handle_incoming_can_frame(CAN_frame rx_frame) { (rx_frame.data.u8[3] >> 4); //Byte 2, bit 5, length 10 (0-600A) [0x3FF if invalid] break; case 0x129: //PSA specific + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; break; case 0x31B: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; battery_InterlockOpen = ((rx_frame.data.u8[1] & 0x10) >> 4); //Best guess, seems to work? //TODO: frame7 contains checksum, we can use this to check for CAN message corruption break; case 0x358: //Common + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; battery_highestTemperature = rx_frame.data.u8[6] - 40; battery_lowestTemperature = rx_frame.data.u8[7] - 40; break; @@ -185,11 +280,13 @@ void EcmpBattery::handle_incoming_can_frame(CAN_frame rx_frame) { case 0x494: break; case 0x594: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; battery_insulation_failure_diag = ((rx_frame.data.u8[6] & 0xE0) >> 5); //Unsure if this is right position //byte pos 6, bit pos 7, signal lenth 3 //0 = no failure, 1 = symmetric failure, 4 = invalid value , forbidden value 5-7 break; case 0x6D0: //Common + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; battery_insulationResistanceKOhm = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; //Byte 2, bit 7, length 16 (0-60000 kOhm) break; @@ -198,6 +295,7 @@ void EcmpBattery::handle_incoming_can_frame(CAN_frame rx_frame) { case 0x6D2: break; case 0x6D3: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; cellvoltages[0] = (rx_frame.data.u8[0] << 8) | rx_frame.data.u8[1]; cellvoltages[1] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; cellvoltages[2] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; @@ -374,6 +472,7 @@ void EcmpBattery::handle_incoming_can_frame(CAN_frame rx_frame) { memcpy(datalayer.battery.status.cell_voltages_mV, cellvoltages, 108 * sizeof(uint16_t)); break; case 0x694: // Poll reply + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; // Handle user requested functionality first if ongoing if (datalayer_extended.stellantisECMP.UserRequestDisableIsoMonitoring) { diff --git a/Software/src/battery/ECMP-BATTERY.h b/Software/src/battery/ECMP-BATTERY.h index fe4aa58a..201f59d8 100644 --- a/Software/src/battery/ECMP-BATTERY.h +++ b/Software/src/battery/ECMP-BATTERY.h @@ -38,6 +38,7 @@ class EcmpBattery : public CanBattery { static const int COMPLETED_STATE = 0; bool battery_RelayOpenRequest = false; bool battery_InterlockOpen = false; + bool MysteryVan = false; uint8_t ContactorResetStatemachine = 0; uint8_t CollisionResetStatemachine = 0; uint8_t IsolationResetStatemachine = 0; @@ -80,8 +81,8 @@ class EcmpBattery : public CanBattery { uint8_t pid_coldest_module = NOT_SAMPLED_YET; uint8_t pid_lowest_temperature = NOT_SAMPLED_YET; uint8_t pid_average_temperature = NOT_SAMPLED_YET; - uint8_t pid_highest_temperature = NOT_SAMPLED_YET; - uint8_t pid_hottest_module = NOT_SAMPLED_YET; + int8_t pid_highest_temperature = NOT_SAMPLED_YET; + int8_t pid_hottest_module = NOT_SAMPLED_YET; uint16_t pid_avg_cell_voltage = NOT_SAMPLED_YET; int32_t pid_current = NOT_SAMPLED_YET; uint32_t pid_insulation_res_neg = NOT_SAMPLED_YET; From 805d5060627bb38ff7919bb3702dbe5073840b89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 3 Sep 2025 23:39:50 +0300 Subject: [PATCH 132/245] Fix SOC and capacity for mystery van --- Software/src/battery/ECMP-BATTERY.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Software/src/battery/ECMP-BATTERY.cpp b/Software/src/battery/ECMP-BATTERY.cpp index 456eb7b0..9adf2661 100644 --- a/Software/src/battery/ECMP-BATTERY.cpp +++ b/Software/src/battery/ECMP-BATTERY.cpp @@ -24,6 +24,9 @@ void EcmpBattery::update_values() { datalayer.battery.status.active_power_W = //Power in watts, Negative = charging batt ((datalayer.battery.status.voltage_dV * datalayer.battery.status.current_dA) / 100); + datalayer.battery.status.remaining_capacity_Wh = static_cast( + (static_cast(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh); + datalayer.battery.status.max_charge_power_W = battery_AllowedMaxChargeCurrent * battery_voltage; datalayer.battery.status.max_discharge_power_W = battery_AllowedMaxDischargeCurrent * battery_voltage; @@ -53,7 +56,13 @@ void EcmpBattery::update_values() { datalayer.battery.status.cell_max_voltage_mV = max_cell_mv_value; } else { //Some variant of the 50/75kWh battery that is not using the eCMP CAN mappings. // For these batteries we need to use the OBD2 PID polled values - datalayer.battery.status.real_soc = battery_soc * 10; //TTOD, calculate based on cap remaining? + + if (pid_energy_capacity != NOT_SAMPLED_YET) { + datalayer.battery.status.remaining_capacity_Wh = pid_energy_capacity; + // calculate SOC based on datalayer.battery.info.total_capacity_Wh and remaining_capacity_Wh + datalayer.battery.status.real_soc = + ((datalayer.battery.status.remaining_capacity_Wh / datalayer.battery.info.total_capacity_Wh) * 10000); + } datalayer.battery.status.soh_pptt; From 3597a99a089b64f11d155ae7aa3bbcbf4009ffc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 3 Sep 2025 23:52:32 +0300 Subject: [PATCH 133/245] Fix float calculation for SOC --- Software/src/battery/ECMP-BATTERY.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Software/src/battery/ECMP-BATTERY.cpp b/Software/src/battery/ECMP-BATTERY.cpp index 9adf2661..0e77e5d9 100644 --- a/Software/src/battery/ECMP-BATTERY.cpp +++ b/Software/src/battery/ECMP-BATTERY.cpp @@ -60,8 +60,9 @@ void EcmpBattery::update_values() { if (pid_energy_capacity != NOT_SAMPLED_YET) { datalayer.battery.status.remaining_capacity_Wh = pid_energy_capacity; // calculate SOC based on datalayer.battery.info.total_capacity_Wh and remaining_capacity_Wh - datalayer.battery.status.real_soc = - ((datalayer.battery.status.remaining_capacity_Wh / datalayer.battery.info.total_capacity_Wh) * 10000); + datalayer.battery.status.real_soc = (uint16_t)(((float)datalayer.battery.status.remaining_capacity_Wh / + datalayer.battery.info.total_capacity_Wh) * + 10000); } datalayer.battery.status.soh_pptt; From bd0923cab3b66dcfed81e3406bd1857c4e0dffee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 3 Sep 2025 23:55:35 +0300 Subject: [PATCH 134/245] Only update temp and cellv when both min/max are available --- Software/src/battery/ECMP-BATTERY.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/Software/src/battery/ECMP-BATTERY.cpp b/Software/src/battery/ECMP-BATTERY.cpp index 0e77e5d9..1fe61714 100644 --- a/Software/src/battery/ECMP-BATTERY.cpp +++ b/Software/src/battery/ECMP-BATTERY.cpp @@ -86,19 +86,13 @@ void EcmpBattery::update_values() { datalayer.battery.status.max_discharge_power_W = pid_max_discharge_10s; } - if (pid_lowest_temperature != NOT_SAMPLED_YET) { + if ((pid_highest_temperature != NOT_SAMPLED_YET) && (pid_lowest_temperature != NOT_SAMPLED_YET)) { + datalayer.battery.status.temperature_max_dC = pid_highest_temperature * 10; datalayer.battery.status.temperature_min_dC = pid_lowest_temperature * 10; } - if (pid_highest_temperature != NOT_SAMPLED_YET) { - datalayer.battery.status.temperature_max_dC = pid_highest_temperature * 10; - } - - if (pid_high_cell_voltage != NOT_SAMPLED_YET) { + if ((pid_high_cell_voltage != NOT_SAMPLED_YET) && (pid_low_cell_voltage != NOT_SAMPLED_YET)) { datalayer.battery.status.cell_max_voltage_mV = pid_high_cell_voltage; - } - - if (pid_low_cell_voltage != NOT_SAMPLED_YET) { datalayer.battery.status.cell_min_voltage_mV = pid_low_cell_voltage; } } From 5770df46e4fc6d94d2d5db96fe2628b7d2c1e350 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 4 Sep 2025 09:28:07 +0300 Subject: [PATCH 135/245] Add offset to voltage value to make it more realistic --- Software/src/battery/ECMP-BATTERY.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/src/battery/ECMP-BATTERY.cpp b/Software/src/battery/ECMP-BATTERY.cpp index 1fe61714..2e5547e7 100644 --- a/Software/src/battery/ECMP-BATTERY.cpp +++ b/Software/src/battery/ECMP-BATTERY.cpp @@ -68,7 +68,7 @@ void EcmpBattery::update_values() { datalayer.battery.status.soh_pptt; if (pid_pack_voltage != NOT_SAMPLED_YET) { - datalayer.battery.status.voltage_dV = pid_pack_voltage; + datalayer.battery.status.voltage_dV = pid_pack_voltage + 800; } if (pid_current != NOT_SAMPLED_YET) { From 8fe21fddb7090712462383325464b961fd4fe563 Mon Sep 17 00:00:00 2001 From: Matt Holmes Date: Thu, 4 Sep 2025 10:35:32 +0100 Subject: [PATCH 136/245] Fixing mqtt_loop setup regression bug --- Software/Software.cpp | 24 ++++++++++++------------ Software/src/devboard/mqtt/mqtt.cpp | 2 +- Software/src/devboard/mqtt/mqtt.h | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Software/Software.cpp b/Software/Software.cpp index 9dca621e..c0ef4e99 100644 --- a/Software/Software.cpp +++ b/Software/Software.cpp @@ -474,6 +474,18 @@ void core_loop(void*) { } } +void mqtt_loop(void*) { + esp_task_wdt_add(NULL); // Register this task with WDT + + while (true) { + START_TIME_MEASUREMENT(mqtt); + mqtt_client_loop(); + END_TIME_MEASUREMENT_MAX(mqtt, datalayer.system.status.mqtt_task_10s_max_us); + esp_task_wdt_reset(); // Reset watchdog + delay(1); + } +} + // Initialization void setup() { init_hal(); @@ -573,15 +585,3 @@ void setup() { // Loop empty, all functionality runs in tasks void loop() {} - -void mqtt_loop(void*) { - esp_task_wdt_add(NULL); // Register this task with WDT - - while (true) { - START_TIME_MEASUREMENT(mqtt); - mqtt_loop(); - END_TIME_MEASUREMENT_MAX(mqtt, datalayer.system.status.mqtt_task_10s_max_us); - esp_task_wdt_reset(); // Reset watchdog - delay(1); - } -} diff --git a/Software/src/devboard/mqtt/mqtt.cpp b/Software/src/devboard/mqtt/mqtt.cpp index c6ed1640..d50e25b1 100644 --- a/Software/src/devboard/mqtt/mqtt.cpp +++ b/Software/src/devboard/mqtt/mqtt.cpp @@ -675,7 +675,7 @@ bool init_mqtt(void) { return true; } -void mqtt_loop(void) { +void mqtt_client_loop(void) { // Only attempt to publish/reconnect MQTT if Wi-Fi is connectedand checkTimmer is elapsed if (check_global_timer.elapsed() && WiFi.status() == WL_CONNECTED) { diff --git a/Software/src/devboard/mqtt/mqtt.h b/Software/src/devboard/mqtt/mqtt.h index 4189ca91..9ea4e50e 100644 --- a/Software/src/devboard/mqtt/mqtt.h +++ b/Software/src/devboard/mqtt/mqtt.h @@ -58,7 +58,7 @@ extern const char* ha_device_id; extern char mqtt_msg[MQTT_MSG_BUFFER_SIZE]; bool init_mqtt(void); -void mqtt_loop(void); +void mqtt_client_loop(void); bool mqtt_publish(const char* topic, const char* mqtt_msg, bool retain); #endif From a9185be603c0dc8a25f1e296c3450436d181b5f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 4 Sep 2025 20:26:17 +0300 Subject: [PATCH 137/245] Add manual override charge(discharge limits --- Software/src/battery/BMW-IX-BATTERY.cpp | 6 ++--- Software/src/battery/BMW-IX-BATTERY.h | 2 -- Software/src/battery/BOLT-AMPERA-BATTERY.cpp | 6 ++--- Software/src/battery/BOLT-AMPERA-BATTERY.h | 2 -- Software/src/battery/CELLPOWER-BMS.cpp | 6 +++-- .../src/battery/IMIEV-CZERO-ION-BATTERY.cpp | 6 +++-- Software/src/battery/RJXZS-BMS.cpp | 10 +++---- Software/src/battery/RJXZS-BMS.h | 3 --- Software/src/battery/TESLA-BATTERY.cpp | 6 ++--- Software/src/battery/TESLA-BATTERY.h | 9 ------- Software/src/communication/nvm/comm_nvm.cpp | 4 +++ Software/src/datalayer/datalayer.h | 7 +++++ .../src/devboard/webserver/settings_html.cpp | 27 +++++++++++++++++++ Software/src/devboard/webserver/webserver.cpp | 6 +++++ 14 files changed, 66 insertions(+), 34 deletions(-) diff --git a/Software/src/battery/BMW-IX-BATTERY.cpp b/Software/src/battery/BMW-IX-BATTERY.cpp index 93596ca8..cf8dc1de 100644 --- a/Software/src/battery/BMW-IX-BATTERY.cpp +++ b/Software/src/battery/BMW-IX-BATTERY.cpp @@ -60,7 +60,7 @@ void BmwIXBattery::update_values() { //This function maps all the values fetche datalayer.battery.status.soh_pptt = min_soh_state; - datalayer.battery.status.max_discharge_power_W = MAX_DISCHARGE_POWER_ALLOWED_W; + datalayer.battery.status.max_discharge_power_W = datalayer.battery.status.override_discharge_power_W; //datalayer.battery.status.max_charge_power_W = 3200; //10000; //Aux HV Port has 100A Fuse Moved to Ramping @@ -70,10 +70,10 @@ void BmwIXBattery::update_values() { //This function maps all the values fetche } else if (datalayer.battery.status.real_soc > RAMPDOWN_SOC) { // When real SOC is between RAMPDOWN_SOC-99%, ramp the value between Max<->0 datalayer.battery.status.max_charge_power_W = - MAX_CHARGE_POWER_ALLOWED_W * + datalayer.battery.status.override_charge_power_W * (1 - (datalayer.battery.status.real_soc - RAMPDOWN_SOC) / (10000.0 - RAMPDOWN_SOC)); } else { // No limits, max charging power allowed - datalayer.battery.status.max_charge_power_W = MAX_CHARGE_POWER_ALLOWED_W; + datalayer.battery.status.max_charge_power_W = datalayer.battery.status.override_charge_power_W; } datalayer.battery.status.temperature_min_dC = min_battery_temperature; diff --git a/Software/src/battery/BMW-IX-BATTERY.h b/Software/src/battery/BMW-IX-BATTERY.h index 7bc54095..d892c100 100644 --- a/Software/src/battery/BMW-IX-BATTERY.h +++ b/Software/src/battery/BMW-IX-BATTERY.h @@ -48,8 +48,6 @@ class BmwIXBattery : public CanBattery { static const int MAX_CELL_DEVIATION_MV = 250; static const int MAX_CELL_VOLTAGE_MV = 4300; //Battery is put into emergency stop if one cell goes over this value static const int MIN_CELL_VOLTAGE_MV = 2800; //Battery is put into emergency stop if one cell goes below this value - static const int MAX_DISCHARGE_POWER_ALLOWED_W = 10000; - static const int MAX_CHARGE_POWER_ALLOWED_W = 10000; static const int MAX_CHARGE_POWER_WHEN_TOPBALANCING_W = 500; static const int RAMPDOWN_SOC = 9000; // (90.00) SOC% to start ramping down from max charge power towards 0 at 100.00% diff --git a/Software/src/battery/BOLT-AMPERA-BATTERY.cpp b/Software/src/battery/BOLT-AMPERA-BATTERY.cpp index b740c11b..fce8e1b8 100644 --- a/Software/src/battery/BOLT-AMPERA-BATTERY.cpp +++ b/Software/src/battery/BOLT-AMPERA-BATTERY.cpp @@ -96,14 +96,14 @@ void BoltAmperaBattery::update_values() { //This function maps all the values f } else if (datalayer.battery.status.real_soc > RAMPDOWN_SOC) { // When real SOC is between RAMPDOWN_SOC-99%, ramp the value between Max<->0 datalayer.battery.status.max_charge_power_W = - MAX_CHARGE_POWER_ALLOWED_W * + datalayer.battery.status.override_charge_power_W * (1 - (datalayer.battery.status.real_soc - RAMPDOWN_SOC) / (10000.0 - RAMPDOWN_SOC)); } else { // No limits, max charging power allowed - datalayer.battery.status.max_charge_power_W = MAX_CHARGE_POWER_ALLOWED_W; + datalayer.battery.status.max_charge_power_W = datalayer.battery.status.override_charge_power_W; } // Discharge power is also set in .h file (TODO: Remove this estimation when real value has been found) - datalayer.battery.status.max_discharge_power_W = MAX_DISCHARGE_POWER_ALLOWED_W; + datalayer.battery.status.max_discharge_power_W = datalayer.battery.status.override_discharge_power_W; datalayer.battery.status.temperature_min_dC = temperature_lowest_C * 10; diff --git a/Software/src/battery/BOLT-AMPERA-BATTERY.h b/Software/src/battery/BOLT-AMPERA-BATTERY.h index 05b28f80..02d8bbda 100644 --- a/Software/src/battery/BOLT-AMPERA-BATTERY.h +++ b/Software/src/battery/BOLT-AMPERA-BATTERY.h @@ -16,8 +16,6 @@ class BoltAmperaBattery : public CanBattery { private: BoltAmperaHtmlRenderer renderer; - static const int MAX_DISCHARGE_POWER_ALLOWED_W = 10000; - static const int MAX_CHARGE_POWER_ALLOWED_W = 10000; static const int MAX_CHARGE_POWER_WHEN_TOPBALANCING_W = 500; static const int RAMPDOWN_SOC = 9000; // (90.00) SOC% to start ramping down from max charge power towards 0 at 100.00% diff --git a/Software/src/battery/CELLPOWER-BMS.cpp b/Software/src/battery/CELLPOWER-BMS.cpp index baa375d6..8d5d089d 100644 --- a/Software/src/battery/CELLPOWER-BMS.cpp +++ b/Software/src/battery/CELLPOWER-BMS.cpp @@ -20,9 +20,11 @@ void CellPowerBms::update_values() { datalayer.battery.status.current_dA = battery_pack_current_dA; - datalayer.battery.status.max_charge_power_W = 5000; //TODO, is this available via CAN? + datalayer.battery.status.max_charge_power_W = + datalayer.battery.status.override_charge_power_W; //TODO, is this available via CAN? - datalayer.battery.status.max_discharge_power_W = 5000; //TODO, is this available via CAN? + datalayer.battery.status.max_discharge_power_W = + datalayer.battery.status.override_discharge_power_W; //TODO, is this available via CAN? datalayer.battery.status.temperature_min_dC = (int16_t)(pack_temperature_low_C * 10); diff --git a/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp b/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp index d77820a9..d6825caa 100644 --- a/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp +++ b/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp @@ -20,9 +20,11 @@ void ImievCZeroIonBattery:: (static_cast(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh); //We do not know the max charge/discharge power is sent by the battery. We hardcode value for now. - datalayer.battery.status.max_charge_power_W = 10000; // 10kW //TODO: Fix when CAN is decoded + datalayer.battery.status.max_charge_power_W = + datalayer.battery.status.override_charge_power_W; //TODO: Fix when CAN is decoded - datalayer.battery.status.max_discharge_power_W = 10000; // 10kW //TODO: Fix when CAN is decoded + datalayer.battery.status.max_discharge_power_W = + datalayer.battery.status.override_discharge_power_W; //TODO: Fix when CAN is decoded static int n = sizeof(cell_voltages) / sizeof(cell_voltages[0]); max_volt_cel = cell_voltages[0]; // Initialize max with the first element of the array diff --git a/Software/src/battery/RJXZS-BMS.cpp b/Software/src/battery/RJXZS-BMS.cpp index 6bbb4fe8..b6bb91cf 100644 --- a/Software/src/battery/RJXZS-BMS.cpp +++ b/Software/src/battery/RJXZS-BMS.cpp @@ -33,20 +33,20 @@ void RjxzsBms::update_values() { datalayer.battery.status.current_dA = total_current; } - // Charge power is set in .h file + // Charge power is manually set if (datalayer.battery.status.real_soc > 9900) { datalayer.battery.status.max_charge_power_W = MAX_CHARGE_POWER_WHEN_TOPBALANCING_W; } else if (datalayer.battery.status.real_soc > RAMPDOWN_SOC) { // When real SOC is between RAMPDOWN_SOC-99%, ramp the value between Max<->0 datalayer.battery.status.max_charge_power_W = - MAX_CHARGE_POWER_ALLOWED_W * + datalayer.battery.status.override_charge_power_W * (1 - (datalayer.battery.status.real_soc - RAMPDOWN_SOC) / (10000.0 - RAMPDOWN_SOC)); } else { // No limits, max charging power allowed - datalayer.battery.status.max_charge_power_W = MAX_CHARGE_POWER_ALLOWED_W; + datalayer.battery.status.max_charge_power_W = datalayer.battery.status.override_charge_power_W; } - // Discharge power is also set in .h file - datalayer.battery.status.max_discharge_power_W = MAX_DISCHARGE_POWER_ALLOWED_W; + // Discharge power is manually set + datalayer.battery.status.max_discharge_power_W = datalayer.battery.status.override_discharge_power_W; uint16_t temperatures[] = { module_1_temperature, module_2_temperature, module_3_temperature, module_4_temperature, diff --git a/Software/src/battery/RJXZS-BMS.h b/Software/src/battery/RJXZS-BMS.h index 6d549752..aadc4f8d 100644 --- a/Software/src/battery/RJXZS-BMS.h +++ b/Software/src/battery/RJXZS-BMS.h @@ -15,9 +15,6 @@ class RjxzsBms : public CanBattery { static constexpr const char* Name = "RJXZS BMS, DIY battery"; private: - /* Tweak these according to your battery build */ - static const int MAX_DISCHARGE_POWER_ALLOWED_W = 5000; - static const int MAX_CHARGE_POWER_ALLOWED_W = 5000; static const int MAX_CHARGE_POWER_WHEN_TOPBALANCING_W = 500; static const int RAMPDOWN_SOC = 9000; // (90.00) SOC% to start ramping down from max charge power towards 0 at 100.00% diff --git a/Software/src/battery/TESLA-BATTERY.cpp b/Software/src/battery/TESLA-BATTERY.cpp index 3f338ee4..42226c9f 100644 --- a/Software/src/battery/TESLA-BATTERY.cpp +++ b/Software/src/battery/TESLA-BATTERY.cpp @@ -627,8 +627,8 @@ void TeslaBattery:: // Define the allowed discharge power datalayer.battery.status.max_discharge_power_W = (battery_max_discharge_current * (battery_volts / 10)); // Cap the allowed discharge power if higher than the maximum discharge power allowed - if (datalayer.battery.status.max_discharge_power_W > MAXDISCHARGEPOWERALLOWED) { - datalayer.battery.status.max_discharge_power_W = MAXDISCHARGEPOWERALLOWED; + if (datalayer.battery.status.max_discharge_power_W > datalayer.battery.status.override_discharge_power_W) { + datalayer.battery.status.max_discharge_power_W = datalayer.battery.status.override_discharge_power_W; } //The allowed charge power behaves strangely. We instead estimate this value @@ -649,7 +649,7 @@ void TeslaBattery:: } } } else { // No limits, max charging power allowed - datalayer.battery.status.max_charge_power_W = MAXCHARGEPOWERALLOWED; + datalayer.battery.status.max_charge_power_W = datalayer.battery.status.override_charge_power_W; } datalayer.battery.status.temperature_min_dC = battery_min_temp; diff --git a/Software/src/battery/TESLA-BATTERY.h b/Software/src/battery/TESLA-BATTERY.h index 6276cf3c..0bf4037f 100644 --- a/Software/src/battery/TESLA-BATTERY.h +++ b/Software/src/battery/TESLA-BATTERY.h @@ -39,15 +39,6 @@ class TeslaBattery : public CanBattery { TeslaHtmlRenderer renderer; protected: - /* Modify these if needed */ - static const int MAXCHARGEPOWERALLOWED = - 15000; // 15000W we use a define since the value supplied by Tesla is always 0 - static const int MAXDISCHARGEPOWERALLOWED = - 60000; // 60000W we use a define since the value supplied by Tesla is always 0 - - // Set this to true to try to close contactors/full startup even with no inverter defined/connected - bool batteryTestOverride = false; - /* Do not change anything below this line! */ static const int RAMPDOWN_SOC = 900; // 90.0 SOC% to start ramping down from max charge power towards 0 at 100.00% static const int RAMPDOWNPOWERALLOWED = 10000; // What power we ramp down from towards top balancing diff --git a/Software/src/communication/nvm/comm_nvm.cpp b/Software/src/communication/nvm/comm_nvm.cpp index df9cae1b..e433a064 100644 --- a/Software/src/communication/nvm/comm_nvm.cpp +++ b/Software/src/communication/nvm/comm_nvm.cpp @@ -156,6 +156,10 @@ void init_stored_settings() { datalayer.system.info.SD_logging_active = settings.getBool("SDLOGENABLED", false); datalayer.battery.status.led_mode = (led_mode_enum)settings.getUInt("LEDMODE", false); + //Some early integrations need manually set allowed charge/discharge power + datalayer.battery.status.override_charge_power_W = settings.getUInt("CHGPOWER", 1000); + datalayer.battery.status.override_discharge_power_W = settings.getUInt("DCHGPOWER", 1000); + // WIFI AP is enabled by default unless disabled in the settings wifiap_enabled = settings.getBool("WIFIAPENABLED", true); wifi_channel = settings.getUInt("WIFICHANNEL", 2000); diff --git a/Software/src/datalayer/datalayer.h b/Software/src/datalayer/datalayer.h index e9ffc8af..8c152325 100644 --- a/Software/src/datalayer/datalayer.h +++ b/Software/src/datalayer/datalayer.h @@ -69,6 +69,13 @@ struct DATALAYER_BATTERY_STATUS_TYPE { /** Instantaneous battery current in deciAmpere. 95 = 9.5 A */ int16_t current_dA; + /* Some early integrations do not support reading allowed charge power from battery + On these integrations we need to have the user specify what limits the battery can take */ + /** Overriden allowed battery discharge power in Watts. Set by user */ + uint32_t override_discharge_power_W = 0; + /** Overriden allowed battery charge power in Watts. Set by user */ + uint32_t override_charge_power_W = 0; + /** uint16_t */ /** State of health in integer-percent x 100. 9900 = 99.00% */ uint16_t soh_pptt = 9900; diff --git a/Software/src/devboard/webserver/settings_html.cpp b/Software/src/devboard/webserver/settings_html.cpp index 63411805..64ee9cda 100644 --- a/Software/src/devboard/webserver/settings_html.cpp +++ b/Software/src/devboard/webserver/settings_html.cpp @@ -299,6 +299,14 @@ String settings_processor(const String& var, BatteryEmulatorSettingsStore& setti return String(settings.getUInt("WIFICHANNEL", 0)); } + if (var == "CHGPOWER") { + return String(settings.getUInt("CHGPOWER", 0)); + } + + if (var == "DCHGPOWER") { + return String(settings.getUInt("DCHGPOWER", 0)); + } + if (var == "LOCALIP1") { return String(settings.getUInt("LOCALIP1", 0)); } @@ -869,6 +877,17 @@ const char* getCANInterfaceName(CAN_Interface interface) { display: contents; } + form .if-estimated { display: none; } /* Integrations with manually set charge/discharge power */ + form[data-battery="3"] .if-estimated, + form[data-battery="4"] .if-estimated, + form[data-battery="6"] .if-estimated, + form[data-battery="14"] .if-estimated, + form[data-battery="24"] .if-estimated, + form[data-battery="32"] .if-estimated, + form[data-battery="33"] .if-estimated { + display: contents; + } + form .if-dblbtr { display: none; } form[data-dblbtr="true"] .if-dblbtr { display: contents; @@ -963,6 +982,14 @@ const char* getCANInterfaceName(CAN_Interface interface) {
+
+ + + + + +
+
- + + + + + + + diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 1236199d..74599e62 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -509,6 +509,8 @@ void init_webserver() { } else if (p->name() == "SUBNET4") { auto type = atoi(p->value().c_str()); settings.saveUInt("SUBNET4", type); + } else if (p->name() == "APPASSWORD") { + settings.saveString("APPASSWORD", p->value().c_str()); } else if (p->name() == "HOSTNAME") { settings.saveString("HOSTNAME", p->value().c_str()); } else if (p->name() == "MQTTSERVER") { From f609427e00f43ff23ec95a659c322a2b6887d07a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 9 Sep 2025 20:45:44 +0300 Subject: [PATCH 168/245] Make Zoe2 double-battery possible --- Software/src/battery/BATTERIES.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Software/src/battery/BATTERIES.cpp b/Software/src/battery/BATTERIES.cpp index f4639e26..ab380b56 100644 --- a/Software/src/battery/BATTERIES.cpp +++ b/Software/src/battery/BATTERIES.cpp @@ -268,9 +268,15 @@ void setup_battery() { case BatteryType::RenaultZoe1: battery2 = new RenaultZoeGen1Battery(&datalayer.battery2, nullptr, can_config.battery_double); break; + case BatteryType::RenaultZoe2: + battery2 = new RenaultZoeGen2Battery(&datalayer.battery2, nullptr, can_config.battery_double); + break; case BatteryType::TestFake: battery2 = new TestFakeBattery(&datalayer.battery2, can_config.battery_double); break; + default: + DEBUG_PRINTF("User tried enabling double battery on non-supported integration!\n"); + break; } if (battery2) { From 5f73a4c32bbd43412ec5d08331a011063426aab9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 9 Sep 2025 20:46:31 +0300 Subject: [PATCH 169/245] Update version number --- Software/Software.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/Software.cpp b/Software/Software.cpp index a2d045e9..681f222c 100644 --- a/Software/Software.cpp +++ b/Software/Software.cpp @@ -34,7 +34,7 @@ #endif // The current software version, shown on webserver -const char* version_number = "9.0.RC8"; +const char* version_number = "9.0.RC9experimental"; // Interval timers volatile unsigned long currentMillis = 0; From fc109cd954545a86659605a2d2c68b3863e9be3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 9 Sep 2025 21:08:07 +0300 Subject: [PATCH 170/245] Add Rivian battery support --- Software/src/battery/BATTERIES.cpp | 8 +- Software/src/battery/BATTERIES.h | 1 + Software/src/battery/Battery.h | 1 + Software/src/battery/RIVIAN-BATTERY.cpp | 177 ++++++++++++++++++++++++ Software/src/battery/RIVIAN-BATTERY.h | 60 ++++++++ 5 files changed, 243 insertions(+), 4 deletions(-) create mode 100644 Software/src/battery/RIVIAN-BATTERY.cpp create mode 100644 Software/src/battery/RIVIAN-BATTERY.h diff --git a/Software/src/battery/BATTERIES.cpp b/Software/src/battery/BATTERIES.cpp index f4639e26..a58c1062 100644 --- a/Software/src/battery/BATTERIES.cpp +++ b/Software/src/battery/BATTERIES.cpp @@ -116,6 +116,8 @@ const char* name_for_battery_type(BatteryType type) { return RenaultZoeGen1Battery::Name; case BatteryType::RenaultZoe2: return RenaultZoeGen2Battery::Name; + case BatteryType::RivianBattery: + return RivianBattery::Name; case BatteryType::SamsungSdiLv: return SamsungSdiLVBattery::Name; case BatteryType::SantaFePhev: @@ -137,11 +139,7 @@ const char* name_for_battery_type(BatteryType type) { } } -#ifdef LFP_CHEMISTRY -const battery_chemistry_enum battery_chemistry_default = battery_chemistry_enum::LFP; -#else const battery_chemistry_enum battery_chemistry_default = battery_chemistry_enum::NMC; -#endif battery_chemistry_enum user_selected_battery_chemistry = battery_chemistry_default; @@ -216,6 +214,8 @@ Battery* create_battery(BatteryType type) { return new RenaultZoeGen1Battery(); case BatteryType::RenaultZoe2: return new RenaultZoeGen2Battery(); + case BatteryType::RivianBattery: + return new RivianBattery(); case BatteryType::SamsungSdiLv: return new SamsungSdiLVBattery(); case BatteryType::SantaFePhev: diff --git a/Software/src/battery/BATTERIES.h b/Software/src/battery/BATTERIES.h index 84ebbe87..0324c189 100644 --- a/Software/src/battery/BATTERIES.h +++ b/Software/src/battery/BATTERIES.h @@ -44,6 +44,7 @@ void setup_can_shunt(); #include "RENAULT-TWIZY.h" #include "RENAULT-ZOE-GEN1-BATTERY.h" #include "RENAULT-ZOE-GEN2-BATTERY.h" +#include "RIVIAN-BATTERY.h" #include "RJXZS-BMS.h" #include "SAMSUNG-SDI-LV-BATTERY.h" #include "SANTA-FE-PHEV-BATTERY.h" diff --git a/Software/src/battery/Battery.h b/Software/src/battery/Battery.h index 0b99f588..c929519f 100644 --- a/Software/src/battery/Battery.h +++ b/Software/src/battery/Battery.h @@ -47,6 +47,7 @@ enum class BatteryType { HyundaiIoniq28 = 39, Kia64FD = 40, RelionBattery = 41, + RivianBattery = 42, Highest }; diff --git a/Software/src/battery/RIVIAN-BATTERY.cpp b/Software/src/battery/RIVIAN-BATTERY.cpp new file mode 100644 index 00000000..4492710b --- /dev/null +++ b/Software/src/battery/RIVIAN-BATTERY.cpp @@ -0,0 +1,177 @@ +#include "RIVIAN-BATTERY.h" + +#include "../battery/BATTERIES.h" +#include "../communication/can/comm_can.h" +#include "../datalayer/datalayer.h" +#include "../devboard/utils/events.h" + +/* +Initial support for Rivian BIG battery (135kWh) +The battery has 3x CAN channels +- Failover CAN (CAN-FD) lots of content, not required for operation +- Platform CAN (500kbps) with all the control messages needed to control the battery <- This is the one we want +- Battery CAN (500kbps) lots of content, not required for operation +*/ + +void RivianBattery::update_values() { + + datalayer.battery.status.real_soc = battery_SOC; + + datalayer.battery.status.soh_pptt; + + datalayer.battery.status.voltage_dV = battery_voltage; + datalayer.battery.status.current_dA = ((int16_t)battery_current / 10.0 - 3200) * 10; + + datalayer.battery.info.total_capacity_Wh = kWh_available_total * 5; + datalayer.battery.status.remaining_capacity_Wh = kWh_available_max * 5; + + //static lower limits for testing + // datalayer.battery.info.total_capacity_Wh = 10000; + // datalayer.battery.status.remaining_capacity_Wh = 9800; + + datalayer.battery.status.max_charge_power_W = ((battery_voltage / 10) * ((battery_charge_limit_amp / 20))); + datalayer.battery.status.max_discharge_power_W = + (abs((battery_voltage / 10) * ((battery_discharge_limit_amp / 20) - 3276.75))); + + //static lower limits for testing + // datalayer.battery.status.max_charge_power_W = 2000; + // datalayer.battery.status.max_discharge_power_W = 3000; + + datalayer.battery.status.cell_min_voltage_mV = 3700 - BMS_state; + datalayer.battery.status.cell_max_voltage_mV = 3700; + + datalayer.battery.status.temperature_min_dC = battery_min_temperature * 10; + datalayer.battery.status.temperature_max_dC = battery_max_temperature * 10; +} + +void RivianBattery::handle_incoming_can_frame(CAN_frame rx_frame) { + switch (rx_frame.ID) { + case 0x160: //Current [Platform CAN]+ + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + battery_current = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]); + break; + case 0x151: //Celltemps (requires other CAN channel) + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x120: //Voltages [Platform CAN]+ + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + battery_voltage = (((rx_frame.data.u8[7] & 0x1F) << 8) | rx_frame.data.u8[6]); + break; + case 0x25A: //SOC and kWh [Platform CAN]+ + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + //battery_SOC = (((rx_frame.data.u8[1] & 0x03) << 8) | rx_frame.data.u8[0]); + kWh_available_max = ((uint32_t)((rx_frame.data.u8[3] << 24) | (rx_frame.data.u8[2] << 16) | + (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]) & + 0b11111111111111110000000000) >> + 10; + kWh_available_total = ((uint32_t)((rx_frame.data.u8[6] << 24) | (rx_frame.data.u8[5] << 16) | + (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[3]) & + 0b111111111111111100) >> + 2; + ; + break; + case 0x405: //State [Platform CAN]+ + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + BMS_state = (rx_frame.data.u8[0] & 0x03); + break; + case 0x100: //Discharge/Charge speed (Not visible on Platform-CAN?) + battery_charge_limit_amp = ((uint32_t)((rx_frame.data.u8[3] << 24) | (rx_frame.data.u8[2] << 16) | + (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]) & + 0b1111111111111111000000000000) >> + 12; + battery_discharge_limit_amp = + ((uint32_t)((rx_frame.data.u8[5] << 16) | (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[3]) & + 0b11111111111111110000) >> + 4; + break; + case 0x153: //Temperatures (Not visible on Platform-CAN?)+ + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + battery_max_temperature = (rx_frame.data.u8[5] / 2) - 40; + battery_min_temperature = (rx_frame.data.u8[6] / 2) - 40; + break; + case 0x55B: //Temperatures (Not visible on Platform-CAN?)+ + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + battery_SOC = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + break; + default: + break; + } +} + +void RivianBattery::transmit_can(unsigned long currentMillis) { + // Send 500ms CAN Message, too fast and the pack can't change states (pre-charge is built in, seems we can't change state during pre-charge) + // 100ms seems to draw too much current for a 5A supply during contactor pull in + if (currentMillis - previousMillis10 >= (INTERVAL_200_MS)) { + previousMillis10 = currentMillis; + + //If we want to close contactors, and preconditions are met + if ((datalayer.system.status.inverter_allows_contactor_closing) && (datalayer.battery.status.bms_status != FAULT)) { + //Standby -> Ready Mode + if (BMS_state == STANDBY || BMS_state == SLEEP) { + RIVIAN_150.data.u8[0] = 0x03; + RIVIAN_150.data.u8[2] = 0x01; + RIVIAN_420.data.u8[0] = 0x02; + RIVIAN_200.data.u8[0] = 0x08; + + transmit_can_frame(&RIVIAN_150); + transmit_can_frame(&RIVIAN_420); + transmit_can_frame(&RIVIAN_41F); + transmit_can_frame(&RIVIAN_207); + transmit_can_frame(&RIVIAN_200); + } + //Ready mode -> Go Mode + + if (BMS_state == READY) { + RIVIAN_150.data.u8[0] = 0x3E; + RIVIAN_150.data.u8[2] = 0x03; + RIVIAN_420.data.u8[0] = 0x03; + + transmit_can_frame(&RIVIAN_150); + transmit_can_frame(&RIVIAN_420); + } + + } else { //If we want to open contactors, transition the other way + //Go mode -> Ready Mode + + if (BMS_state == GO) { + RIVIAN_150.data.u8[0] = 0x03; + RIVIAN_150.data.u8[2] = 0x01; + + transmit_can_frame(&RIVIAN_150); + } + + if (BMS_state == READY) { + RIVIAN_150.data.u8[0] = 0x03; + RIVIAN_150.data.u8[2] = 0x01; + RIVIAN_420.data.u8[0] = 0x01; + RIVIAN_200.data.u8[0] = 0x10; + + transmit_can_frame(&RIVIAN_245); + transmit_can_frame(&RIVIAN_150); + transmit_can_frame(&RIVIAN_420); + transmit_can_frame(&RIVIAN_41F); + transmit_can_frame(&RIVIAN_200); + } + } + //disabled this because the battery didn't like it so fast (slowed to 100ms) and because the battery couldn't change states fast enough + //as much as I don't like the "free-running" aspect of it, checking the BMS_state and acting on it should fix issues caused by that. + //transmit_can_frame(&RIVIAN_150); + //transmit_can_frame(&RIVIAN_420); + //transmit_can_frame(&RIVIAN_41F); + //transmit_can_frame(&RIVIAN_207); + //transmit_can_frame(&RIVIAN_200); + } +} + +void RivianBattery::setup(void) { // Performs one time setup at startup + strncpy(datalayer.system.info.battery_protocol, Name, 63); + datalayer.system.info.battery_protocol[63] = '\0'; + datalayer.battery.info.number_of_cells = 108; + datalayer.battery.info.total_capacity_Wh = 135000; + datalayer.battery.info.chemistry = NMC; + datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; + datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; + datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; + datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; + datalayer.system.status.battery_allows_contactor_closing = true; +} diff --git a/Software/src/battery/RIVIAN-BATTERY.h b/Software/src/battery/RIVIAN-BATTERY.h new file mode 100644 index 00000000..d74acb65 --- /dev/null +++ b/Software/src/battery/RIVIAN-BATTERY.h @@ -0,0 +1,60 @@ +#ifndef RIVIAN_BATTERY_H +#define RIVIAN_BATTERY_H +#include "CanBattery.h" + +class RivianBattery : public CanBattery { + public: + virtual void setup(void); + virtual void handle_incoming_can_frame(CAN_frame rx_frame); + virtual void update_values(); + virtual void transmit_can(unsigned long currentMillis); + static constexpr const char* Name = "Rivian R1T large 135kWh battery"; + + private: + static const int MAX_PACK_VOLTAGE_DV = 4480; + static const int MIN_PACK_VOLTAGE_DV = 2920; + static const int MAX_CELL_DEVIATION_MV = 150; + static const int MAX_CELL_VOLTAGE_MV = 4200; //Battery is put into emergency stop if one cell goes over this value + static const int MIN_CELL_VOLTAGE_MV = 3300; //Battery is put into emergency stop if one cell goes below this value + + uint8_t BMS_state = 0; + uint16_t battery_voltage = 3700; + uint16_t battery_SOC = 5000; + int32_t battery_current = 0; + uint16_t kWh_available_total = 135; + uint16_t kWh_available_max = 135; + int16_t battery_min_temperature = 0; + int16_t battery_max_temperature = 0; + uint16_t battery_discharge_limit_amp = 0; + uint16_t battery_charge_limit_amp = 0; + static const uint8_t SLEEP = 0; + static const uint8_t STANDBY = 1; + static const uint8_t READY = 2; + static const uint8_t GO = 3; + unsigned long previousMillis10 = 0; // will store last time a 10ms CAN Message was sent + + CAN_frame RIVIAN_150 = {.FD = false, + .ext_ID = false, + .DLC = 6, + .ID = 0x150, + .data = {0x03, 0x00, 0x01, 0x00, 0x01, 0x00}}; + CAN_frame RIVIAN_420 = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x420, + .data = {0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame RIVIAN_41F = {.FD = false, .ext_ID = false, .DLC = 3, .ID = 0x41F, .data = {0x62, 0x10, 0x00}}; + CAN_frame RIVIAN_245 = {.FD = false, + .ext_ID = false, + .DLC = 6, + .ID = 0x245, + .data = {0x10, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame RIVIAN_200 = {.FD = false, .ext_ID = false, .DLC = 1, .ID = 0x200, .data = {0x08}}; + CAN_frame RIVIAN_207 = {.FD = false, + .ext_ID = false, + .DLC = 1, + .ID = 0x207, + .data = {0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00}}; +}; + +#endif From ac153ec901ab9cb644bad02a3764c4141e87d6c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 9 Sep 2025 21:15:35 +0300 Subject: [PATCH 171/245] Simplify capacity calculation and initial values --- Software/src/battery/RIVIAN-BATTERY.cpp | 4 ++-- Software/src/battery/RIVIAN-BATTERY.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Software/src/battery/RIVIAN-BATTERY.cpp b/Software/src/battery/RIVIAN-BATTERY.cpp index 4492710b..d0f5ce75 100644 --- a/Software/src/battery/RIVIAN-BATTERY.cpp +++ b/Software/src/battery/RIVIAN-BATTERY.cpp @@ -22,8 +22,8 @@ void RivianBattery::update_values() { datalayer.battery.status.voltage_dV = battery_voltage; datalayer.battery.status.current_dA = ((int16_t)battery_current / 10.0 - 3200) * 10; - datalayer.battery.info.total_capacity_Wh = kWh_available_total * 5; - datalayer.battery.status.remaining_capacity_Wh = kWh_available_max * 5; + datalayer.battery.status.remaining_capacity_Wh = static_cast( + (static_cast(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh); //static lower limits for testing // datalayer.battery.info.total_capacity_Wh = 10000; diff --git a/Software/src/battery/RIVIAN-BATTERY.h b/Software/src/battery/RIVIAN-BATTERY.h index d74acb65..156ad84a 100644 --- a/Software/src/battery/RIVIAN-BATTERY.h +++ b/Software/src/battery/RIVIAN-BATTERY.h @@ -20,7 +20,7 @@ class RivianBattery : public CanBattery { uint8_t BMS_state = 0; uint16_t battery_voltage = 3700; uint16_t battery_SOC = 5000; - int32_t battery_current = 0; + int32_t battery_current = 32000; uint16_t kWh_available_total = 135; uint16_t kWh_available_max = 135; int16_t battery_min_temperature = 0; From 521ab481d7b4443a36638e0c5ab634fc7b5eb1d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 9 Sep 2025 21:25:20 +0300 Subject: [PATCH 172/245] Add Rivian to tests --- test/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 91bcbd9d..e00fd2b9 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -128,6 +128,7 @@ add_executable(tests ../Software/src/battery/RENAULT-TWIZY.cpp ../Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp ../Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp + ../Software/src/battery/RIVIAN-BATTERY.cpp ../Software/src/battery/RJXZS-BMS.cpp ../Software/src/battery/SAMSUNG-SDI-LV-BATTERY.cpp ../Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp From 8ba6a04d610c887c5c4ad383abe3665b822b574c Mon Sep 17 00:00:00 2001 From: Jonny Date: Tue, 9 Sep 2025 21:16:52 +0100 Subject: [PATCH 173/245] Use ths correct chip ID for the T-2CAN factory image --- .github/workflows/release-assets.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-assets.yml b/.github/workflows/release-assets.yml index d707ae14..3d9b84d3 100644 --- a/.github/workflows/release-assets.yml +++ b/.github/workflows/release-assets.yml @@ -64,7 +64,7 @@ jobs: - name: 🛠 Build factory image for Lilygo 2-CAN run: | - esptool --chip esp32 merge-bin -o .pio/build/lilygo_2CAN_330/factory.bin --flash-mode dio --flash-freq 40m --flash-size 4MB 0x1000 .pio/build/lilygo_2CAN_330/bootloader.bin 0x8000 .pio/build/lilygo_2CAN_330/partitions.bin 0xe000 ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin 0x10000 .pio/build/lilygo_2CAN_330/firmware.bin + esptool --chip esp32s3 merge-bin -o .pio/build/lilygo_2CAN_330/factory.bin --flash-mode dio --flash-freq 40m --flash-size 4MB 0x1000 .pio/build/lilygo_2CAN_330/bootloader.bin 0x8000 .pio/build/lilygo_2CAN_330/partitions.bin 0xe000 ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin 0x10000 .pio/build/lilygo_2CAN_330/firmware.bin mv .pio/build/lilygo_2CAN_330/factory.bin output/BE_${{ steps.vars.outputs.tag }}_LilygoT-2CAN.factory.bin - name: 🛠 Build ota image for Stark From 2e859a1ee69e0415bf7bd711af9ead192a7e83be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 10 Sep 2025 21:01:13 +0300 Subject: [PATCH 174/245] Correct shifting of amp limits --- Software/src/battery/RIVIAN-BATTERY.cpp | 31 +++++++------------------ 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/Software/src/battery/RIVIAN-BATTERY.cpp b/Software/src/battery/RIVIAN-BATTERY.cpp index d0f5ce75..b333523b 100644 --- a/Software/src/battery/RIVIAN-BATTERY.cpp +++ b/Software/src/battery/RIVIAN-BATTERY.cpp @@ -8,7 +8,7 @@ /* Initial support for Rivian BIG battery (135kWh) The battery has 3x CAN channels -- Failover CAN (CAN-FD) lots of content, not required for operation +- Failover CAN (CAN-FD) lots of content, not required for operation. Has all cellvoltages! - Platform CAN (500kbps) with all the control messages needed to control the battery <- This is the one we want - Battery CAN (500kbps) lots of content, not required for operation */ @@ -25,17 +25,8 @@ void RivianBattery::update_values() { datalayer.battery.status.remaining_capacity_Wh = static_cast( (static_cast(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh); - //static lower limits for testing - // datalayer.battery.info.total_capacity_Wh = 10000; - // datalayer.battery.status.remaining_capacity_Wh = 9800; - - datalayer.battery.status.max_charge_power_W = ((battery_voltage / 10) * ((battery_charge_limit_amp / 20))); - datalayer.battery.status.max_discharge_power_W = - (abs((battery_voltage / 10) * ((battery_discharge_limit_amp / 20) - 3276.75))); - - //static lower limits for testing - // datalayer.battery.status.max_charge_power_W = 2000; - // datalayer.battery.status.max_discharge_power_W = 3000; + datalayer.battery.status.max_charge_power_W = ((battery_voltage / 10) * battery_charge_limit_amp); + datalayer.battery.status.max_discharge_power_W = ((battery_voltage / 10) * battery_discharge_limit_amp); datalayer.battery.status.cell_min_voltage_mV = 3700 - BMS_state; datalayer.battery.status.cell_max_voltage_mV = 3700; @@ -74,22 +65,18 @@ void RivianBattery::handle_incoming_can_frame(CAN_frame rx_frame) { datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; BMS_state = (rx_frame.data.u8[0] & 0x03); break; - case 0x100: //Discharge/Charge speed (Not visible on Platform-CAN?) - battery_charge_limit_amp = ((uint32_t)((rx_frame.data.u8[3] << 24) | (rx_frame.data.u8[2] << 16) | - (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]) & - 0b1111111111111111000000000000) >> - 12; + case 0x100: //Discharge/Charge speed + battery_charge_limit_amp = + (((rx_frame.data.u8[3] & 0x0F) << 8) | (rx_frame.data.u8[2] << 4) | (rx_frame.data.u8[1] >> 4)) / 20; battery_discharge_limit_amp = - ((uint32_t)((rx_frame.data.u8[5] << 16) | (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[3]) & - 0b11111111111111110000) >> - 4; + (((rx_frame.data.u8[5] & 0x0F) << 8) | (rx_frame.data.u8[4] << 4) | (rx_frame.data.u8[3] >> 4)) / 20; break; - case 0x153: //Temperatures (Not visible on Platform-CAN?)+ + case 0x153: //Temperatures datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; battery_max_temperature = (rx_frame.data.u8[5] / 2) - 40; battery_min_temperature = (rx_frame.data.u8[6] / 2) - 40; break; - case 0x55B: //Temperatures (Not visible on Platform-CAN?)+ + case 0x55B: //Temperatures datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; battery_SOC = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; From 62d0d59e74ebfe42bd537ace41db4ad0ba5be371 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 10 Sep 2025 21:13:38 +0300 Subject: [PATCH 175/245] Simplify CAN writing --- Software/src/battery/RIVIAN-BATTERY.cpp | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/Software/src/battery/RIVIAN-BATTERY.cpp b/Software/src/battery/RIVIAN-BATTERY.cpp index b333523b..8b4c429b 100644 --- a/Software/src/battery/RIVIAN-BATTERY.cpp +++ b/Software/src/battery/RIVIAN-BATTERY.cpp @@ -28,8 +28,8 @@ void RivianBattery::update_values() { datalayer.battery.status.max_charge_power_W = ((battery_voltage / 10) * battery_charge_limit_amp); datalayer.battery.status.max_discharge_power_W = ((battery_voltage / 10) * battery_discharge_limit_amp); - datalayer.battery.status.cell_min_voltage_mV = 3700 - BMS_state; - datalayer.battery.status.cell_max_voltage_mV = 3700; + //datalayer.battery.status.cell_min_voltage_mV = 3700; //TODO: Take from failover CAN? + //datalayer.battery.status.cell_max_voltage_mV = 3700; //TODO: Take from failover CAN? datalayer.battery.status.temperature_min_dC = battery_min_temperature * 10; datalayer.battery.status.temperature_max_dC = battery_max_temperature * 10; @@ -51,15 +51,10 @@ void RivianBattery::handle_incoming_can_frame(CAN_frame rx_frame) { case 0x25A: //SOC and kWh [Platform CAN]+ datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; //battery_SOC = (((rx_frame.data.u8[1] & 0x03) << 8) | rx_frame.data.u8[0]); - kWh_available_max = ((uint32_t)((rx_frame.data.u8[3] << 24) | (rx_frame.data.u8[2] << 16) | - (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]) & - 0b11111111111111110000000000) >> - 10; - kWh_available_total = ((uint32_t)((rx_frame.data.u8[6] << 24) | (rx_frame.data.u8[5] << 16) | - (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[3]) & - 0b111111111111111100) >> - 2; - ; + kWh_available_max = + (((rx_frame.data.u8[3] & 0x03) << 14) | (rx_frame.data.u8[2] << 6) | (rx_frame.data.u8[1] >> 2)) / 200; + kWh_available_total = + (((rx_frame.data.u8[5] & 0x03) << 14) | (rx_frame.data.u8[4] << 6) | (rx_frame.data.u8[3] >> 2)) / 200; break; case 0x405: //State [Platform CAN]+ datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; From c7b6d0adee3fc13a56d4a3a128d8b2471b18200b Mon Sep 17 00:00:00 2001 From: Jonny Date: Thu, 11 Sep 2025 16:34:24 +0100 Subject: [PATCH 176/245] Change bootloader offset (and flash config) for T-2CAN --- .github/workflows/release-assets.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-assets.yml b/.github/workflows/release-assets.yml index 3d9b84d3..1e433b97 100644 --- a/.github/workflows/release-assets.yml +++ b/.github/workflows/release-assets.yml @@ -64,7 +64,7 @@ jobs: - name: 🛠 Build factory image for Lilygo 2-CAN run: | - esptool --chip esp32s3 merge-bin -o .pio/build/lilygo_2CAN_330/factory.bin --flash-mode dio --flash-freq 40m --flash-size 4MB 0x1000 .pio/build/lilygo_2CAN_330/bootloader.bin 0x8000 .pio/build/lilygo_2CAN_330/partitions.bin 0xe000 ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin 0x10000 .pio/build/lilygo_2CAN_330/firmware.bin + esptool --chip esp32s3 merge-bin -o .pio/build/lilygo_2CAN_330/factory.bin --flash-mode qio --flash-freq 80m --flash-size 4MB 0x0000 .pio/build/lilygo_2CAN_330/bootloader.bin 0x8000 .pio/build/lilygo_2CAN_330/partitions.bin 0xe000 ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin 0x10000 .pio/build/lilygo_2CAN_330/firmware.bin mv .pio/build/lilygo_2CAN_330/factory.bin output/BE_${{ steps.vars.outputs.tag }}_LilygoT-2CAN.factory.bin - name: 🛠 Build ota image for Stark From a2a00a488fd4d954c03d2acabe0539a0d7e6471a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 11 Sep 2025 18:38:29 +0300 Subject: [PATCH 177/245] Update CONTRIBUTING.md --- CONTRIBUTING.md | 64 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b303cd06..14e85c18 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ ### Contributing to the Battery-Emulator project -What can I do? +What can I do? 🦸 -------------- **"Help - I want to contribute something, but I don't know what?"** @@ -15,10 +15,66 @@ You're in luck. There's various sources to contribute: - [Discussion page](https://github.com/dalathegreat/Battery-Emulator/discussions) - [Discord server](https://www.patreon.com/dala) -## Notes on embedded system +## Notes on embedded system 🕙 The Battery-Emulator is a real-time control system, which performs lots of time critical operations. Some operations, like contactor control, need to complete within 10 milliseconds periodically. The resources of the ESP32 microcontroller is limited, so keeping track of CPU and memory usage is essential. Keep this in mind when coding for the system! Performance profiling the system can be done by enabling the "Enable performance profiling:" option in the webserver -## Code formatting +## Setting up the compilation environment (VScode + PlatformIO) 💻 + +This project uses the PlatformIO extension within Visual Studio Code for development and uploading. It handles all the complex toolchains and library management for you. +### 1. Installing VSCode +- Download the stable build of Visual Studio Code for your operating system (Windows, macOS, or Linux) from the official website: https://code.visualstudio.com/ +- Run the installer and follow the setup instructions. +- (Recommended) Launch VSCode after installation. + +### 2. Installing the PlatformIO IDE Plugin + +PlatformIO is an extension that adds all the necessary functionality to VSCode. + - Inside VSCode, open the Extensions view by: + - Clicking the Extensions icon in the Activity Bar on the left side. + - Or using the keyboard shortcut: Ctrl+Shift+X (Windows/Linux) or Cmd+Shift+X (macOS). + - In the extensions search bar, type: PlatformIO IDE. + - Find the extension published by PlatformIO and click the Install button. +- Wait for the installation to complete. This may take a few minutes as PlatformIO downloads and installs its core tools in the background. VSCode might require a reload once finished. + +### 3. Opening the Project and Building +- Clone the repository to your local machine using Git. +- In VSCode: + - Go to File > Open Folder... + - Navigate to and select the root folder of the cloned project (the folder containing the platformio.ini file). + - Click Open. +- PlatformIO will automatically recognize the project structure and begin indexing the code. You'll see the PlatformIO icon (a grey alien) appear in the Activity Bar on the left. + +- To verify everything is set up correctly, build/compile the project: + - Click the PlatformIO icon in the Activity Bar to open the PIO Home screen. + - Go to Quick Access > PIO > Build. + - Alternatively, you can use the checkmark icon in the blue status bar at the bottom of the VSCode window, or the keyboard shortcut Ctrl+Alt+B (Windows/Linux) / Cmd+Alt+B (macOS). + - The build process will start. You can monitor the output in the integrated terminal. A successful build will end with ===== [SUCCESS] Took X.XX seconds =====. + +### 4. Uploading Code to Board via USB + +- Connect your Battery-Emulator hardware to your computer using a USB cable. +- Select the right board type (Stark, LilyGo) +- At the bottom left of VScode, click the Env to bring up a menu of boards. Select the board you are using +image +image + +- Ensure the correct upload port is set in the platformio.ini file (it's often auto-detected, but you may need to set it manually. See Troubleshooting below). +- Upload the code: + - Click the PlatformIO icon in the Activity Bar. + - Go to Quick Access > PIO > Upload. + - Alternatively, use the right-arrow icon (→) in the blue status bar at the bottom of the VSCode window, or the keyboard shortcut Ctrl+Alt+U (Windows/Linux) / Cmd+Alt+U (macOS). +- The upload process will begin. The board may reset automatically. A successful upload will end with ===== [SUCCESS] Took X.XX seconds =====. + +### ⚠️ Troubleshooting & Tips + +#### "Upload port not found" or wrong port errors: + +- Find the correct port: + - Windows: Check Device Manager under "Ports (COM & LPT)". It's usually COM3, COM4, etc. + - macOS/Linux: Run ls /dev/tty.* or ls /dev/ttyUSB* in a terminal. It's often /dev/tty.usbserial-XXX or /dev/ttyUSB0. +- Add the line upload_port = COM4 (replace COM4 with your port) to your platformio.ini file in the [env:...] section. + +## Code formatting 📜 The project enforces a specific code formatting in the workflows. To get your code formatted properly, it is easiest to use a pre-commit hook before pushing the code to a pull request. Before you begin, make sure you have installed Python on the system! @@ -39,7 +95,7 @@ Or force it to check all files with pre-commit run --all-files ``` -## Local Unit test run +## Local Unit test run 🧪 The Unit tests run gtest. Here is how to install this on Debian/Ubuntu and run it locally ``` sudo apt-get install libgtest-dev From 5246cd34c93ec1af004666137002e8d597038356 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 11 Sep 2025 20:32:11 +0300 Subject: [PATCH 178/245] Update Software.cpp --- Software/Software.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/Software.cpp b/Software/Software.cpp index 681f222c..299b4eb2 100644 --- a/Software/Software.cpp +++ b/Software/Software.cpp @@ -34,7 +34,7 @@ #endif // The current software version, shown on webserver -const char* version_number = "9.0.RC9experimental"; +const char* version_number = "9.0.RC9"; // Interval timers volatile unsigned long currentMillis = 0; From b33f42c9c5d2f8186f90f98e4127eb8bff49de3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 11 Sep 2025 20:36:48 +0300 Subject: [PATCH 179/245] Update Software.cpp --- Software/Software.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/Software.cpp b/Software/Software.cpp index 299b4eb2..95f25ed8 100644 --- a/Software/Software.cpp +++ b/Software/Software.cpp @@ -34,7 +34,7 @@ #endif // The current software version, shown on webserver -const char* version_number = "9.0.RC9"; +const char* version_number = "9.0.RC10experimental"; // Interval timers volatile unsigned long currentMillis = 0; From fd2e5f2e524c2183bc5f9d58f7b5a54ef1eb1396 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 11 Sep 2025 21:30:56 +0300 Subject: [PATCH 180/245] Add start for MysteryVan More Info page --- Software/src/battery/ECMP-BATTERY.cpp | 9 ++++ Software/src/battery/ECMP-HTML.h | 52 +++++++++++++++++++++ Software/src/datalayer/datalayer_extended.h | 8 ++++ 3 files changed, 69 insertions(+) diff --git a/Software/src/battery/ECMP-BATTERY.cpp b/Software/src/battery/ECMP-BATTERY.cpp index 16f276ac..b2532f49 100644 --- a/Software/src/battery/ECMP-BATTERY.cpp +++ b/Software/src/battery/ECMP-BATTERY.cpp @@ -191,6 +191,15 @@ void EcmpBattery::update_values() { datalayer_extended.stellantisECMP.pid_contactor_closing_counter = pid_contactor_closing_counter; datalayer_extended.stellantisECMP.pid_date_of_manufacture = pid_date_of_manufacture; datalayer_extended.stellantisECMP.pid_SOH_cell_1 = pid_SOH_cell_1; + // Update extended datalayer for MysteryVan + datalayer_extended.stellantisECMP.MysteryVan = MysteryVan; + datalayer_extended.stellantisECMP.CrashMemorized = HV_BATT_CRASH_MEMORIZED; + datalayer_extended.stellantisECMP.CONTACTOR_OPENING_REASON = CONTACTOR_OPENING_REASON; + datalayer_extended.stellantisECMP.TBMU_FAULT_TYPE = TBMU_FAULT_TYPE; + datalayer_extended.stellantisECMP.HV_BATT_FC_INSU_MINUS_RES = HV_BATT_FC_INSU_MINUS_RES; + datalayer_extended.stellantisECMP.HV_BATT_FC_INSU_PLUS_RES = HV_BATT_FC_INSU_PLUS_RES; + datalayer_extended.stellantisECMP.HV_BATT_FC_VHL_INSU_PLUS_RES = HV_BATT_FC_VHL_INSU_PLUS_RES; + datalayer_extended.stellantisECMP.HV_BATT_ONLY_INSU_MINUS_RES = HV_BATT_ONLY_INSU_MINUS_RES; if (battery_InterlockOpen) { set_event(EVENT_HVIL_FAILURE, 0); diff --git a/Software/src/battery/ECMP-HTML.h b/Software/src/battery/ECMP-HTML.h index 96bd56c6..2c2b6cd8 100644 --- a/Software/src/battery/ECMP-HTML.h +++ b/Software/src/battery/ECMP-HTML.h @@ -383,6 +383,58 @@ class EcmpHtmlRenderer : public BatteryHtmlRenderer { : String(datalayer_extended.stellantisECMP.pid_SOH_cell_1)) + ""; + if (datalayer_extended.stellantisECMP.MysteryVan) { + content += "

MysteryVan platform detected:

"; + content += "

Crash Memorized: "; + if (datalayer_extended.stellantisECMP.CrashMemorized == 255) { + content += "Yes

"; + } else { + content += "No"; + } + content += "

Contactor Opening Reason: "; + if (datalayer_extended.stellantisECMP.CONTACTOR_OPENING_REASON == 0) { + content += "No error"; + } else if (datalayer_extended.stellantisECMP.CONTACTOR_OPENING_REASON == 1) { + content += "Crash!"; + } else if (datalayer_extended.stellantisECMP.CONTACTOR_OPENING_REASON == 2) { + content += "12V supply source undervoltage"; + } else if (datalayer_extended.stellantisECMP.CONTACTOR_OPENING_REASON == 3) { + content += "12V supply source overvoltage"; + } else if (datalayer_extended.stellantisECMP.CONTACTOR_OPENING_REASON == 4) { + content += "Battery temperature"; + } else if (datalayer_extended.stellantisECMP.CONTACTOR_OPENING_REASON == 5) { + content += "Interlock line open"; + } else if (datalayer_extended.stellantisECMP.CONTACTOR_OPENING_REASON == 6) { + content += "e-Service plug disconnected"; + } + content += "

"; + content += "

Battery fault type: "; + if (datalayer_extended.stellantisECMP.TBMU_FAULT_TYPE == 0) { + content += "No fault"; + } else if (datalayer_extended.stellantisECMP.TBMU_FAULT_TYPE == 1) { + content += "FirstLevelFault: Warning Lamp"; + } else if (datalayer_extended.stellantisECMP.TBMU_FAULT_TYPE == 2) { + content += "SecondLevelFault: Stop Lamp"; + } else if (datalayer_extended.stellantisECMP.TBMU_FAULT_TYPE == 3) { + content += "ThirdLevelFault: Stop Lamp + contactor opening (EPS shutdown)"; + } else if (datalayer_extended.stellantisECMP.TBMU_FAULT_TYPE == 4) { + content += "FourthLevelFault: Stop Lamp + Active Discharge"; + } else if (datalayer_extended.stellantisECMP.TBMU_FAULT_TYPE == 5) { + content += "Inhibition of powertrain activation"; + } else if (datalayer_extended.stellantisECMP.TBMU_FAULT_TYPE == 6) { + content += "Reserved"; + } + content += "

"; + content += "

FC insulation minus resistance " + + String(datalayer_extended.stellantisECMP.HV_BATT_FC_INSU_MINUS_RES) + " kOhm

"; + content += "

FC insulation plus resistance " + + String(datalayer_extended.stellantisECMP.HV_BATT_FC_INSU_PLUS_RES) + " kOhm

"; + content += "

FC vehicle insulation plus resistance " + + String(datalayer_extended.stellantisECMP.HV_BATT_FC_VHL_INSU_PLUS_RES) + " kOhm

"; + content += "

FC vehicle insulation plus resistance " + + String(datalayer_extended.stellantisECMP.HV_BATT_ONLY_INSU_MINUS_RES) + " kOhm

"; + } + return content; } }; diff --git a/Software/src/datalayer/datalayer_extended.h b/Software/src/datalayer/datalayer_extended.h index b982e10f..5756a05a 100644 --- a/Software/src/datalayer/datalayer_extended.h +++ b/Software/src/datalayer/datalayer_extended.h @@ -235,6 +235,14 @@ struct DATALAYER_INFO_CMFAEV { }; struct DATALAYER_INFO_ECMP { + //mysteryvan parameters + bool MysteryVan = false; + bool CrashMemorized = false; + uint8_t CONTACTOR_OPENING_REASON = 0; + uint8_t TBMU_FAULT_TYPE = 0; + uint16_t HV_BATT_FC_INSU_MINUS_RES, HV_BATT_FC_INSU_PLUS_RES, HV_BATT_FC_VHL_INSU_PLUS_RES, + HV_BATT_ONLY_INSU_MINUS_RES = 0; + //ecmp below uint8_t MainConnectorState = 0; uint16_t InsulationResistance = 0; uint8_t InsulationDiag = 0; From 29e6f52c4cac1f386f43433390e65bad21d08a17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 11 Sep 2025 23:05:06 +0300 Subject: [PATCH 181/245] Add alerts to more battery info page --- Software/src/battery/ECMP-BATTERY.cpp | 12 ++++ Software/src/battery/ECMP-HTML.h | 71 ++++++++++++++++++++- Software/src/datalayer/datalayer_extended.h | 2 + 3 files changed, 82 insertions(+), 3 deletions(-) diff --git a/Software/src/battery/ECMP-BATTERY.cpp b/Software/src/battery/ECMP-BATTERY.cpp index b2532f49..eb47aff9 100644 --- a/Software/src/battery/ECMP-BATTERY.cpp +++ b/Software/src/battery/ECMP-BATTERY.cpp @@ -200,6 +200,18 @@ void EcmpBattery::update_values() { datalayer_extended.stellantisECMP.HV_BATT_FC_INSU_PLUS_RES = HV_BATT_FC_INSU_PLUS_RES; datalayer_extended.stellantisECMP.HV_BATT_FC_VHL_INSU_PLUS_RES = HV_BATT_FC_VHL_INSU_PLUS_RES; datalayer_extended.stellantisECMP.HV_BATT_ONLY_INSU_MINUS_RES = HV_BATT_ONLY_INSU_MINUS_RES; + datalayer_extended.stellantisECMP.HV_BATT_ONLY_INSU_MINUS_RES = HV_BATT_ONLY_INSU_MINUS_RES; + datalayer_extended.stellantisECMP.ALERT_CELL_POOR_CONSIST = ALERT_CELL_POOR_CONSIST; + datalayer_extended.stellantisECMP.ALERT_OVERCHARGE = ALERT_OVERCHARGE; + datalayer_extended.stellantisECMP.ALERT_BATT = ALERT_BATT; + datalayer_extended.stellantisECMP.ALERT_LOW_SOC = ALERT_LOW_SOC; + datalayer_extended.stellantisECMP.ALERT_HIGH_SOC = ALERT_HIGH_SOC; + datalayer_extended.stellantisECMP.ALERT_SOC_JUMP = ALERT_SOC_JUMP; + datalayer_extended.stellantisECMP.ALERT_TEMP_DIFF = ALERT_TEMP_DIFF; + datalayer_extended.stellantisECMP.ALERT_HIGH_TEMP = ALERT_HIGH_TEMP; + datalayer_extended.stellantisECMP.ALERT_OVERVOLTAGE = ALERT_OVERVOLTAGE; + datalayer_extended.stellantisECMP.ALERT_CELL_OVERVOLTAGE = ALERT_CELL_OVERVOLTAGE; + datalayer_extended.stellantisECMP.ALERT_CELL_UNDERVOLTAGE = ALERT_CELL_UNDERVOLTAGE; if (battery_InterlockOpen) { set_event(EVENT_HVIL_FAILURE, 0); diff --git a/Software/src/battery/ECMP-HTML.h b/Software/src/battery/ECMP-HTML.h index 2c2b6cd8..4b8da671 100644 --- a/Software/src/battery/ECMP-HTML.h +++ b/Software/src/battery/ECMP-HTML.h @@ -384,9 +384,9 @@ class EcmpHtmlRenderer : public BatteryHtmlRenderer { ""; if (datalayer_extended.stellantisECMP.MysteryVan) { - content += "

MysteryVan platform detected:

"; + content += "

MysteryVan platform detected!

"; content += "

Crash Memorized: "; - if (datalayer_extended.stellantisECMP.CrashMemorized == 255) { + if (datalayer_extended.stellantisECMP.CrashMemorized) { content += "Yes

"; } else { content += "No"; @@ -434,7 +434,72 @@ class EcmpHtmlRenderer : public BatteryHtmlRenderer { content += "

FC vehicle insulation plus resistance " + String(datalayer_extended.stellantisECMP.HV_BATT_ONLY_INSU_MINUS_RES) + " kOhm

"; } - + content += "

Alert Battery: "; + if (datalayer_extended.stellantisECMP.ALERT_BATT) { + content += "Yes

"; + } else { + content += "No"; + } + content += "

Alert Low SOC: "; + if (datalayer_extended.stellantisECMP.ALERT_LOW_SOC) { + content += "Yes

"; + } else { + content += "No"; + } + content += "

Alert High SOC: "; + if (datalayer_extended.stellantisECMP.ALERT_HIGH_SOC) { + content += "Yes

"; + } else { + content += "No"; + } + content += "

Alert SOC Jump: "; + if (datalayer_extended.stellantisECMP.ALERT_SOC_JUMP) { + content += "Yes

"; + } else { + content += "No"; + } + content += "

Alert Overcharge: "; + if (datalayer_extended.stellantisECMP.ALERT_OVERCHARGE) { + content += "Yes

"; + } else { + content += "No"; + } + content += "

Alert Temp Diff: "; + if (datalayer_extended.stellantisECMP.ALERT_TEMP_DIFF) { + content += "Yes

"; + } else { + content += "No"; + } + content += "

Alert Temp High: "; + if (datalayer_extended.stellantisECMP.ALERT_HIGH_TEMP) { + content += "Yes

"; + } else { + content += "No"; + } + content += "

Alert Overvoltage: "; + if (datalayer_extended.stellantisECMP.ALERT_OVERVOLTAGE) { + content += "Yes

"; + } else { + content += "No"; + } + content += "

Alert Cell Overvoltage: "; + if (datalayer_extended.stellantisECMP.ALERT_CELL_OVERVOLTAGE) { + content += "Yes

"; + } else { + content += "No"; + } + content += "

Alert Cell Undervoltage: "; + if (datalayer_extended.stellantisECMP.ALERT_CELL_UNDERVOLTAGE) { + content += "Yes

"; + } else { + content += "No"; + } + content += "

Alert Cell Poor Consistency: "; + if (datalayer_extended.stellantisECMP.ALERT_CELL_POOR_CONSIST) { + content += "Yes

"; + } else { + content += "No"; + } return content; } }; diff --git a/Software/src/datalayer/datalayer_extended.h b/Software/src/datalayer/datalayer_extended.h index 5756a05a..ebd6fbc3 100644 --- a/Software/src/datalayer/datalayer_extended.h +++ b/Software/src/datalayer/datalayer_extended.h @@ -242,6 +242,8 @@ struct DATALAYER_INFO_ECMP { uint8_t TBMU_FAULT_TYPE = 0; uint16_t HV_BATT_FC_INSU_MINUS_RES, HV_BATT_FC_INSU_PLUS_RES, HV_BATT_FC_VHL_INSU_PLUS_RES, HV_BATT_ONLY_INSU_MINUS_RES = 0; + bool ALERT_CELL_POOR_CONSIST, ALERT_OVERCHARGE, ALERT_BATT, ALERT_LOW_SOC, ALERT_HIGH_SOC, ALERT_SOC_JUMP, + ALERT_TEMP_DIFF, ALERT_HIGH_TEMP, ALERT_OVERVOLTAGE, ALERT_CELL_OVERVOLTAGE, ALERT_CELL_UNDERVOLTAGE = false; //ecmp below uint8_t MainConnectorState = 0; uint16_t InsulationResistance = 0; From 0aad11d9bc7c47a96fd0eca977856e419542dd91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 11 Sep 2025 23:27:15 +0300 Subject: [PATCH 182/245] Add contactor state to more battery info --- Software/src/battery/ECMP-BATTERY.cpp | 3 ++- Software/src/battery/ECMP-HTML.h | 11 ++++++++++- Software/src/datalayer/datalayer_extended.h | 1 + 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Software/src/battery/ECMP-BATTERY.cpp b/Software/src/battery/ECMP-BATTERY.cpp index eb47aff9..ebcd0c27 100644 --- a/Software/src/battery/ECMP-BATTERY.cpp +++ b/Software/src/battery/ECMP-BATTERY.cpp @@ -193,6 +193,7 @@ void EcmpBattery::update_values() { datalayer_extended.stellantisECMP.pid_SOH_cell_1 = pid_SOH_cell_1; // Update extended datalayer for MysteryVan datalayer_extended.stellantisECMP.MysteryVan = MysteryVan; + datalayer_extended.stellantisECMP.CONTACTORS_STATE = CONTACTORS_STATE; datalayer_extended.stellantisECMP.CrashMemorized = HV_BATT_CRASH_MEMORIZED; datalayer_extended.stellantisECMP.CONTACTOR_OPENING_REASON = CONTACTOR_OPENING_REASON; datalayer_extended.stellantisECMP.TBMU_FAULT_TYPE = TBMU_FAULT_TYPE; @@ -268,7 +269,7 @@ void EcmpBattery::handle_incoming_can_frame(CAN_frame rx_frame) { //TBMU_EVSE_DC_MES_CURRENT = (rx_frame.data.u8[3] << 4) | ((rx_frame.data.u8[4] & 0xF0) >> 4); //A -2000 - 2000 //Fastcharger info, not needed for BE //TBMU_EVSE_CHRG_REQ = (rx_frame.data.u8[4] & 0x0C) >> 2; //00 No request, 01 Stop request //Fastcharger info, not needed for BE //HV_STORAGE_MAX_I = ((rx_frame.data.u8[4] & 0x03) << 12) | (rx_frame.data.u8[5] << 2) | //Fastcharger info, not needed for BE - ((rx_frame.data.u8[6] & 0xC0) >> 6); //A -2000 - 2000 + //((rx_frame.data.u8[6] & 0xC0) >> 6); //A -2000 - 2000 //TBMU_EVSE_DC_MAX_POWER = ((rx_frame.data.u8[6] & 0x3F) << 8) | rx_frame.data.u8[7]; //W -1000000 - 0 //Fastcharger info, not needed for BE break; case 0x3F4: //MysteryVan 50/75kWh platform (Temperature sensors) diff --git a/Software/src/battery/ECMP-HTML.h b/Software/src/battery/ECMP-HTML.h index 4b8da671..0b177feb 100644 --- a/Software/src/battery/ECMP-HTML.h +++ b/Software/src/battery/ECMP-HTML.h @@ -384,7 +384,16 @@ class EcmpHtmlRenderer : public BatteryHtmlRenderer { ""; if (datalayer_extended.stellantisECMP.MysteryVan) { - content += "

MysteryVan platform detected!

"; + content += "

MysteryVan platform detected!

"; + content += "

Contactor State: "; + if (datalayer_extended.stellantisECMP.CONTACTORS_STATE == 0) { + content += "Open"; + } else if (datalayer_extended.stellantisECMP.CONTACTORS_STATE == 1) { + content += "Precharge"; + } else if (datalayer_extended.stellantisECMP.CONTACTORS_STATE == 2) { + content += "Closed"; + } + content += "

"; content += "

Crash Memorized: "; if (datalayer_extended.stellantisECMP.CrashMemorized) { content += "Yes

"; diff --git a/Software/src/datalayer/datalayer_extended.h b/Software/src/datalayer/datalayer_extended.h index ebd6fbc3..07406f6f 100644 --- a/Software/src/datalayer/datalayer_extended.h +++ b/Software/src/datalayer/datalayer_extended.h @@ -240,6 +240,7 @@ struct DATALAYER_INFO_ECMP { bool CrashMemorized = false; uint8_t CONTACTOR_OPENING_REASON = 0; uint8_t TBMU_FAULT_TYPE = 0; + uint8_t CONTACTORS_STATE = 0; uint16_t HV_BATT_FC_INSU_MINUS_RES, HV_BATT_FC_INSU_PLUS_RES, HV_BATT_FC_VHL_INSU_PLUS_RES, HV_BATT_ONLY_INSU_MINUS_RES = 0; bool ALERT_CELL_POOR_CONSIST, ALERT_OVERCHARGE, ALERT_BATT, ALERT_LOW_SOC, ALERT_HIGH_SOC, ALERT_SOC_JUMP, From 018cd4ed52cedf977449659ca1f4698baa65962b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 11 Sep 2025 23:48:55 +0300 Subject: [PATCH 183/245] Allocate less memory for ELEGANT_HTLM array --- Software/src/lib/ayushsharma82-ElegantOTA/src/elop.cpp | 2 +- Software/src/lib/ayushsharma82-ElegantOTA/src/elop.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Software/src/lib/ayushsharma82-ElegantOTA/src/elop.cpp b/Software/src/lib/ayushsharma82-ElegantOTA/src/elop.cpp index f7e4e712..4a00db1f 100644 --- a/Software/src/lib/ayushsharma82-ElegantOTA/src/elop.cpp +++ b/Software/src/lib/ayushsharma82-ElegantOTA/src/elop.cpp @@ -1,3 +1,3 @@ #include "elop.h" -const uint8_t ELEGANT_HTML[41354] PROGMEM = {31,139,8,0,128,47,232,102,0,255,237,125,249,127,219,70,146,239,207,201,231,147,255,1,102,54,50,25,1,16,78,94,18,157,181,157,120,226,93,59,206,139,237,204,225,104,178,16,9,146,136,65,130,3,144,58,44,115,254,246,253,86,117,55,46,66,18,229,57,118,223,123,195,19,232,163,186,186,174,174,238,46,0,39,15,38,201,120,125,181,10,181,249,122,17,63,250,226,243,19,250,215,226,96,57,27,181,194,101,11,41,154,118,50,15,131,9,31,225,120,17,174,3,109,60,15,210,44,92,143,90,111,223,60,51,250,45,237,168,146,187,12,22,225,168,117,30,133,23,171,36,93,183,180,113,178,92,135,75,148,190,136,38,235,249,104,18,158,71,227,208,224,19,93,139,150,209,58,10,98,35,27,7,113,56,178,77,171,4,109,29,173,227,240,209,147,96,189,14,211,43,237,187,197,38,14,214,73,170,189,122,243,88,123,187,154,4,235,240,228,72,20,1,222,217,56,141,86,107,28,197,225,90,139,147,96,18,45,103,223,70,231,111,194,203,181,54,210,90,47,68,138,105,154,173,227,47,62,15,178,171,229,88,155,110,150,227,117,148,44,209,157,112,252,94,129,127,189,14,214,155,236,167,240,44,73,214,237,142,118,253,197,231,159,161,3,217,90,67,131,1,64,5,23,65,180,214,102,225,250,91,156,183,31,30,253,46,92,63,139,210,197,69,144,134,207,151,211,228,97,7,240,63,139,166,90,155,202,139,250,159,129,200,155,5,72,96,162,218,119,113,72,135,79,174,158,79,218,15,167,178,230,207,97,154,1,147,135,29,115,13,132,159,10,130,161,49,130,97,170,66,4,248,102,80,96,201,132,145,248,182,25,138,202,103,40,217,58,89,73,146,124,55,13,199,232,40,39,167,33,184,250,51,24,135,243,207,240,66,218,86,11,227,44,20,221,56,58,42,152,176,76,214,90,178,140,163,101,168,107,239,195,112,165,17,89,0,142,161,135,235,55,209,34,76,54,235,246,141,164,213,53,199,178,172,206,177,6,160,79,169,144,22,204,130,104,9,121,208,28,45,11,65,242,73,70,205,127,241,249,22,168,229,172,202,214,65,186,174,98,126,43,133,91,41,183,134,210,198,34,204,178,96,22,182,106,212,169,202,202,237,52,46,128,145,76,3,221,48,5,177,199,113,144,101,47,162,108,109,166,225,34,57,15,193,139,104,50,9,193,205,219,161,73,189,168,64,8,38,229,102,94,9,24,32,66,137,0,117,206,221,46,97,119,161,204,13,126,58,190,170,199,183,163,12,158,9,174,255,30,66,34,48,222,85,81,81,162,80,82,165,118,209,185,77,50,124,19,86,217,102,60,6,103,159,38,241,102,33,187,32,21,240,1,85,45,225,42,9,144,21,253,149,234,89,146,215,29,249,210,53,155,229,116,127,185,246,85,249,29,217,133,209,249,158,91,126,74,56,85,141,203,29,189,92,165,201,12,218,89,233,102,94,211,185,173,102,192,141,103,79,54,235,53,254,184,166,50,80,251,145,71,227,38,204,108,125,21,135,230,36,202,86,113,112,53,122,120,22,39,227,247,15,171,6,162,185,228,50,89,134,92,80,144,67,96,157,156,101,97,122,30,166,192,124,25,94,104,47,55,32,33,208,124,37,147,219,32,206,232,145,228,78,157,108,36,94,244,83,133,244,12,35,73,54,227,113,1,188,219,7,110,93,44,21,220,218,232,160,76,125,26,130,217,209,121,248,99,176,158,51,101,52,109,141,97,137,15,52,26,228,128,12,88,180,194,65,152,143,19,211,112,61,158,87,171,162,5,170,64,28,80,197,205,228,125,7,40,49,90,13,131,77,94,236,183,44,89,2,79,178,153,63,210,8,92,180,23,100,218,127,188,126,245,131,48,226,235,77,186,100,8,82,10,53,109,171,141,3,32,162,181,195,52,77,82,137,189,6,56,212,90,2,110,113,122,251,225,51,66,87,227,147,225,67,93,28,40,132,37,220,229,38,142,57,69,242,243,34,90,78,146,11,19,99,1,180,6,40,23,20,166,145,88,141,58,218,168,245,13,43,53,37,170,1,77,165,225,131,30,189,70,78,160,173,133,130,105,201,20,138,167,6,2,173,77,74,104,105,139,40,142,35,153,6,130,21,116,87,181,4,215,127,76,147,5,138,181,219,191,234,192,250,55,104,49,227,36,42,208,171,164,200,2,93,81,170,77,149,191,19,148,248,41,252,203,38,148,144,39,26,74,66,29,132,45,176,100,203,66,147,152,138,218,79,193,56,212,206,194,245,69,24,230,2,163,5,203,137,182,158,135,10,57,81,75,226,102,166,168,209,126,119,139,27,161,171,122,167,178,61,122,153,128,183,100,207,66,18,89,101,40,137,202,157,142,114,6,189,254,110,46,200,222,128,239,225,144,148,225,109,201,156,144,28,11,117,248,116,223,169,37,133,157,69,236,211,28,167,26,8,86,37,137,101,153,41,172,91,162,104,110,93,254,22,188,37,227,255,54,204,203,64,10,172,25,34,190,205,14,224,167,143,69,202,10,155,242,128,7,23,93,187,214,224,188,167,209,217,102,29,102,67,152,203,13,188,197,60,229,89,20,195,177,31,106,239,30,242,32,244,240,84,226,183,223,184,214,48,238,55,15,5,101,148,156,79,67,137,236,220,209,17,77,85,32,186,233,123,109,19,125,241,121,142,152,58,144,216,213,124,171,22,85,104,17,16,12,152,65,252,26,238,2,156,80,210,99,154,36,113,38,242,78,142,242,249,11,207,124,196,153,70,51,179,81,107,145,76,54,113,136,121,84,154,100,89,146,70,179,104,137,114,109,53,64,193,159,18,20,155,142,114,156,198,105,136,233,145,196,168,221,130,155,14,28,224,172,197,132,215,113,52,109,79,15,14,166,102,182,89,209,12,45,43,31,183,101,115,43,20,134,124,180,58,29,97,247,143,167,176,138,162,157,128,140,115,222,20,204,100,122,245,58,140,33,68,73,250,56,142,219,15,169,181,119,168,174,48,87,160,78,97,66,39,237,160,115,220,56,50,7,163,71,215,69,27,99,106,35,232,0,211,177,201,84,24,141,90,227,121,20,79,168,3,173,78,81,48,162,130,99,34,117,56,249,33,153,132,89,39,50,215,193,236,7,154,131,162,206,139,231,63,252,103,235,224,32,162,190,211,121,21,163,131,131,73,59,234,108,59,133,136,200,94,233,215,121,99,195,7,150,158,109,206,214,105,24,226,16,226,80,248,115,232,139,36,253,120,116,189,61,150,3,100,96,70,208,194,89,26,173,175,14,14,128,126,126,54,42,229,116,244,0,40,77,97,51,194,244,199,36,142,198,162,108,53,9,21,170,9,84,139,165,224,21,75,1,117,104,147,133,6,184,13,239,136,166,209,89,235,155,49,49,95,157,142,90,209,114,28,111,38,97,107,184,83,51,128,99,118,181,72,54,187,117,48,66,173,91,195,90,98,6,138,26,66,250,90,250,120,155,83,129,56,122,13,62,5,102,184,82,178,66,199,163,7,214,177,34,14,81,234,88,184,66,176,251,232,147,62,238,108,183,29,24,157,6,165,0,106,172,22,31,63,62,104,183,56,9,157,192,148,190,40,216,57,56,144,94,199,130,44,239,203,112,18,5,237,86,27,92,5,177,50,76,116,226,36,197,130,2,85,29,178,194,118,32,252,92,52,204,190,201,121,188,159,226,234,55,206,44,41,255,37,36,142,103,175,103,201,37,218,224,89,65,56,65,215,59,195,125,218,145,179,167,191,169,41,187,115,124,159,122,232,218,119,231,40,64,237,135,152,12,182,91,99,72,214,251,150,94,50,39,159,128,199,189,201,122,163,53,252,36,202,53,65,139,163,217,28,166,162,172,177,143,219,113,231,90,170,105,124,112,16,155,191,254,26,102,232,16,76,194,193,193,171,51,242,1,77,140,110,235,132,76,14,28,148,236,213,197,18,14,219,42,76,215,87,24,229,97,220,98,189,53,9,167,193,38,6,228,111,98,83,30,15,227,237,121,144,106,47,70,215,225,37,91,209,225,245,118,171,95,86,78,143,203,6,155,74,195,62,62,126,242,244,219,239,158,253,238,251,231,255,241,159,47,94,254,240,234,199,255,243,211,235,55,111,127,254,253,31,254,248,167,224,108,12,216,179,121,244,219,251,120,177,76,86,127,73,179,245,230,252,226,242,234,131,101,59,174,231,119,123,253,193,225,17,184,54,186,6,190,241,48,135,157,233,147,188,139,217,201,201,228,99,246,232,209,35,215,49,38,91,29,5,211,27,11,82,17,46,139,130,225,18,10,181,44,21,101,5,207,104,134,152,97,192,36,67,63,26,253,176,89,156,133,169,212,120,109,106,18,26,0,218,239,28,216,221,158,109,15,92,255,99,158,232,120,157,3,207,233,245,29,223,119,187,22,15,37,68,129,201,200,58,158,156,100,102,28,46,103,235,249,241,228,240,176,147,189,155,156,142,166,166,192,160,77,103,29,101,87,51,244,0,46,117,178,120,114,69,67,119,25,187,2,224,187,211,227,236,145,117,156,25,70,103,98,174,54,217,188,253,18,115,46,115,26,39,40,194,135,2,70,187,243,181,227,119,59,57,112,244,250,140,192,190,73,126,159,164,147,155,161,235,193,200,210,199,192,59,40,240,14,14,15,245,241,225,168,223,153,188,27,131,128,254,233,199,81,246,46,56,61,57,113,60,99,252,149,235,148,218,184,32,224,111,146,219,123,64,109,148,224,127,13,8,1,131,23,29,2,108,110,5,63,104,32,64,3,7,160,107,67,79,190,15,47,247,109,131,250,160,224,83,3,4,220,131,79,153,188,134,99,180,156,181,109,144,74,47,231,31,216,126,45,59,111,223,252,45,137,150,237,22,52,79,159,135,151,247,236,44,245,212,81,152,172,104,142,251,28,14,76,6,247,228,12,162,215,14,116,167,163,87,90,203,123,251,36,200,194,174,119,159,118,92,118,35,168,0,70,40,102,152,221,253,136,131,67,27,135,125,62,114,78,245,8,245,162,19,239,56,2,133,130,175,251,135,209,215,221,147,81,206,155,254,55,18,215,24,150,48,72,31,99,101,8,164,235,126,221,118,141,168,115,208,117,59,157,161,44,208,26,193,11,108,160,209,25,227,221,68,166,12,205,164,33,22,81,48,83,60,122,247,231,199,198,159,44,99,112,248,203,209,233,81,52,211,81,183,164,71,205,130,57,30,29,30,6,95,121,157,241,131,145,5,87,71,178,47,134,23,50,9,47,95,177,70,11,156,3,195,238,116,14,88,61,86,201,69,219,209,13,231,235,241,97,191,131,228,147,147,241,215,206,199,166,58,157,14,245,212,64,118,137,27,219,227,75,83,26,189,209,148,71,120,66,240,251,81,158,170,63,27,93,111,214,211,254,240,26,252,132,236,236,116,188,176,208,207,204,179,104,137,213,164,82,177,246,102,25,98,167,96,21,182,195,229,24,3,209,219,159,158,63,77,22,88,2,33,55,55,238,116,136,158,66,28,132,100,54,129,157,96,253,160,86,83,194,20,13,86,0,8,160,128,26,45,111,195,88,113,98,74,156,200,192,133,236,36,86,92,200,32,56,211,178,144,60,69,243,32,96,214,169,232,237,244,54,212,247,132,47,106,154,83,172,49,60,149,13,181,227,119,217,105,161,47,211,66,242,208,171,231,163,103,199,71,95,63,192,164,227,107,237,219,16,51,159,5,214,135,105,29,33,88,98,93,141,134,67,45,202,224,239,63,217,76,225,89,81,49,46,250,239,193,102,61,199,92,87,211,158,133,228,83,106,143,207,146,205,251,57,102,147,191,133,115,108,23,173,215,171,108,120,116,132,42,200,52,147,116,70,211,26,84,131,163,17,210,98,149,246,242,249,27,74,57,162,46,189,26,53,240,40,126,48,162,117,38,248,196,47,145,250,241,227,143,252,251,224,1,70,236,8,75,153,132,77,103,91,140,235,84,72,86,165,50,165,113,234,224,128,134,114,76,17,42,169,166,2,2,95,83,1,193,100,160,185,12,96,23,222,46,33,162,144,204,33,99,178,53,121,134,9,197,250,197,119,85,128,121,137,140,186,94,205,3,206,34,185,109,233,22,100,108,215,61,248,30,163,251,115,147,148,5,44,127,165,79,112,2,49,132,162,231,37,199,122,132,25,72,117,92,22,50,240,77,68,147,30,86,18,156,145,79,141,170,65,122,5,95,127,52,169,233,212,184,51,28,99,204,221,73,164,31,20,127,156,166,193,85,201,45,98,164,133,55,52,6,230,195,7,162,64,148,241,63,234,28,28,84,80,122,48,26,189,197,188,167,207,217,52,203,25,97,74,167,212,11,146,169,100,59,29,197,74,247,120,12,6,36,125,133,178,202,210,234,203,145,221,115,29,191,239,193,185,208,195,145,225,244,112,238,246,123,3,61,25,25,121,150,167,175,71,42,167,175,111,160,43,155,147,84,233,202,6,186,146,190,219,156,142,218,244,75,86,158,254,121,36,45,249,45,50,211,241,84,46,156,154,146,251,146,190,91,201,97,222,118,250,39,39,43,140,192,122,250,174,189,58,236,122,72,31,156,156,120,157,67,219,59,29,173,242,174,45,48,147,251,117,58,213,103,244,63,155,233,115,250,159,207,117,154,225,253,26,69,187,88,142,48,198,177,20,60,29,45,245,215,163,80,127,63,74,244,95,71,235,227,229,104,209,94,234,161,158,232,107,180,185,57,180,78,245,158,110,116,251,86,191,215,29,184,221,14,58,191,104,175,117,81,132,10,216,167,186,13,123,238,246,7,126,215,243,251,40,145,160,4,85,167,50,84,2,99,156,221,211,187,86,215,182,252,190,61,232,128,180,139,182,104,97,201,5,220,83,221,1,8,219,242,60,223,241,93,215,234,128,21,85,52,60,70,195,238,117,61,187,223,31,244,118,209,240,25,13,155,246,252,250,150,231,52,160,209,101,52,12,219,3,35,93,219,245,236,93,68,122,2,17,207,239,89,254,160,239,238,162,209,39,52,236,94,207,178,92,223,3,9,119,208,24,8,106,216,3,18,21,207,179,129,105,29,15,27,36,37,68,60,199,194,240,189,131,131,13,130,50,53,6,3,203,179,208,8,28,146,58,26,54,72,10,60,208,209,174,229,118,251,40,177,195,21,208,148,16,241,44,116,212,182,208,215,29,52,64,82,166,135,111,129,104,142,51,0,213,119,112,1,85,129,11,38,4,93,223,245,93,7,188,91,142,102,85,84,78,117,31,64,186,126,111,208,245,109,192,88,163,64,25,19,144,125,64,188,237,14,124,203,238,186,192,53,65,137,10,38,36,66,158,222,245,92,104,85,207,102,154,204,42,120,128,98,142,5,25,235,65,202,122,174,197,20,169,162,1,68,129,70,207,178,125,136,224,0,221,173,163,65,84,31,232,110,31,56,88,196,217,29,36,72,128,60,72,122,215,242,122,125,215,245,119,177,0,193,8,11,207,130,150,246,250,94,127,23,11,112,223,215,253,110,223,243,208,25,228,239,32,1,16,76,12,123,208,7,227,136,228,117,60,136,111,64,195,238,247,220,174,59,232,178,144,86,209,128,12,2,13,27,180,244,93,48,15,37,118,120,2,32,196,20,15,120,244,109,175,203,234,82,197,4,2,4,68,124,27,242,129,174,236,162,1,77,0,26,48,114,224,122,31,26,179,139,6,201,32,145,195,30,56,160,89,175,231,2,202,114,52,175,115,5,125,65,11,224,10,225,48,175,224,128,142,216,54,60,65,8,159,223,3,170,204,149,249,174,104,116,33,233,238,192,114,45,159,116,33,68,145,10,30,196,22,23,173,248,174,53,240,49,229,218,193,2,48,136,162,148,63,128,206,177,140,86,49,33,85,176,33,230,61,167,63,0,235,27,16,33,122,116,9,136,239,13,122,44,197,59,120,144,144,2,15,219,26,128,94,86,215,99,43,86,195,4,140,129,160,247,209,210,192,238,129,96,117,68,200,52,128,36,174,223,135,136,57,14,43,75,21,17,146,15,32,130,76,223,177,7,61,182,166,85,68,160,112,192,163,215,181,156,129,221,103,141,173,34,1,33,37,57,7,231,187,158,215,103,217,168,34,65,156,37,44,60,199,238,219,126,159,84,97,135,47,164,45,93,29,36,237,121,64,132,77,71,21,11,146,14,80,99,48,128,61,239,195,50,16,30,87,245,145,133,40,58,232,119,93,11,26,67,120,92,85,240,32,154,147,160,59,221,254,192,246,48,31,4,26,87,187,54,140,37,157,180,193,27,88,172,181,87,21,68,200,132,161,51,144,48,151,52,119,23,15,234,46,68,12,54,221,131,156,246,216,118,84,17,33,154,147,164,247,7,30,176,197,64,214,128,9,113,142,48,177,124,219,119,88,93,170,104,144,85,103,97,7,154,14,216,207,140,169,34,2,125,32,81,135,99,97,67,8,81,160,142,7,83,157,236,32,134,82,136,15,171,109,21,13,26,227,8,11,191,75,38,6,214,174,1,17,26,109,33,236,208,6,219,183,187,94,3,34,160,42,113,198,243,209,19,171,199,250,82,67,132,212,146,40,130,241,214,177,97,66,0,164,142,10,9,145,175,247,208,159,126,207,161,222,212,17,129,28,18,69,92,240,5,116,23,18,178,60,124,10,15,199,66,217,240,240,53,31,37,163,228,240,61,31,173,71,235,195,95,233,104,171,124,120,181,138,243,78,34,127,10,151,157,253,160,138,3,171,167,250,74,224,46,60,158,245,104,124,216,142,14,210,143,127,141,14,86,157,195,246,146,96,118,14,19,57,131,105,175,79,78,194,143,107,177,168,21,118,14,163,45,214,196,225,83,237,3,115,245,49,61,248,235,222,48,231,243,61,96,254,57,253,243,222,0,163,232,110,128,233,159,219,209,71,224,184,47,76,14,195,200,162,15,8,157,235,210,249,36,154,97,215,90,37,188,40,230,226,149,41,3,109,171,140,120,134,213,89,207,211,228,130,119,205,197,198,119,235,121,28,135,179,32,214,130,116,198,107,175,90,235,112,44,102,241,228,160,151,23,176,218,1,67,203,231,150,60,229,8,50,206,251,38,29,202,83,57,31,153,212,230,213,105,103,152,187,251,88,168,194,185,216,16,160,134,222,142,114,196,229,22,194,79,163,199,237,183,29,253,124,20,99,151,232,166,229,233,184,33,48,169,37,98,89,104,161,229,201,254,149,121,161,186,168,121,113,107,205,150,218,15,125,67,177,136,88,18,143,150,88,87,255,254,205,203,23,163,120,171,127,119,123,93,222,52,110,172,248,102,143,138,63,133,1,34,66,234,53,191,29,137,8,22,212,175,68,67,76,245,172,3,144,20,129,49,25,181,90,152,72,82,54,182,60,67,192,153,132,41,148,83,68,113,148,164,5,155,1,163,159,104,15,14,226,16,18,85,51,44,119,119,244,105,123,2,186,208,246,84,48,121,44,38,126,197,100,185,163,255,80,96,112,253,164,221,218,172,8,170,216,169,197,98,61,82,16,128,65,91,179,114,247,22,105,231,5,25,85,218,49,135,138,20,123,154,117,10,36,235,128,246,34,208,251,243,32,222,132,199,8,198,225,190,101,35,17,53,243,45,112,57,190,104,183,16,157,149,114,72,16,197,141,130,24,180,107,12,232,114,151,89,22,22,155,83,255,117,4,160,71,28,24,244,13,118,10,195,209,191,93,79,183,7,216,7,152,227,40,219,254,87,135,182,78,31,76,40,92,103,71,111,184,25,110,3,29,161,54,180,105,0,210,98,7,85,54,21,200,166,38,188,85,15,81,87,177,55,113,50,43,87,87,49,61,67,112,72,213,29,11,86,37,233,130,194,69,152,50,17,39,253,225,229,139,239,177,220,34,163,85,142,35,19,155,21,88,222,249,241,213,235,55,45,189,197,221,17,212,7,141,35,83,28,54,236,1,41,218,151,182,129,210,14,83,115,53,18,171,230,201,102,57,105,99,134,138,250,225,228,40,197,12,126,29,196,95,35,30,230,150,237,39,5,245,73,144,130,75,34,44,76,196,0,175,14,91,95,181,110,222,239,82,21,127,38,198,86,196,155,43,110,117,236,122,21,221,73,150,170,124,33,184,192,30,172,82,51,106,90,233,195,126,243,89,28,254,47,238,212,22,61,74,150,164,81,87,144,192,117,136,165,194,229,44,44,186,196,61,162,141,108,20,160,136,67,172,39,121,180,65,30,1,9,138,63,28,141,48,81,236,124,2,222,45,116,120,31,196,69,205,2,109,89,15,234,92,87,93,82,231,74,124,6,148,128,163,4,43,232,122,64,247,110,171,250,207,194,151,13,106,158,240,93,187,245,150,165,171,208,98,18,156,116,68,12,16,250,201,225,194,111,104,224,226,174,253,95,211,17,253,13,172,141,8,190,20,3,55,226,219,4,75,16,76,55,9,49,222,43,22,193,196,35,178,98,5,139,2,100,166,168,221,210,99,61,54,41,190,159,212,47,163,116,44,210,193,198,138,38,200,200,182,14,101,129,173,136,138,194,62,198,167,227,153,153,50,104,155,150,224,127,166,1,81,173,122,63,178,15,14,30,32,252,118,29,173,226,240,155,54,174,29,72,17,231,242,199,100,131,64,71,172,93,47,227,43,77,88,7,28,135,90,155,22,78,59,136,59,140,17,41,169,162,12,77,52,1,59,50,140,223,89,167,140,178,137,128,85,196,129,182,144,129,109,144,85,187,243,128,87,77,91,183,130,47,129,206,20,68,68,62,144,27,243,151,155,199,46,81,89,196,228,162,210,191,221,92,146,0,63,95,194,122,65,2,255,178,199,238,61,150,167,99,44,214,134,84,234,91,177,59,221,70,3,112,110,80,174,93,217,13,255,64,133,105,64,251,153,118,57,228,242,185,125,252,3,182,12,44,248,234,121,185,223,193,244,200,120,11,218,103,167,52,10,167,65,7,0,47,15,255,36,63,130,17,29,125,56,150,137,249,229,4,163,223,113,172,228,78,148,19,233,2,78,190,214,135,103,33,214,74,67,125,24,76,177,11,113,141,208,2,3,62,44,237,130,156,193,225,12,83,3,41,199,242,144,117,103,104,169,83,6,50,196,64,26,77,84,18,7,128,12,191,12,253,176,23,158,109,107,176,13,99,125,193,177,240,32,208,80,107,181,182,116,197,203,53,93,200,96,204,67,10,25,24,218,166,127,108,92,132,103,239,163,181,65,67,53,161,18,26,193,228,183,13,162,129,72,245,142,141,69,242,193,192,120,194,57,67,239,216,72,202,103,165,195,41,218,49,166,1,226,86,175,134,155,200,200,130,101,102,32,210,40,130,43,6,35,31,46,140,77,164,27,208,176,56,52,68,130,254,132,162,167,94,6,227,215,124,250,12,245,245,215,225,44,9,181,183,207,245,159,146,51,44,194,235,223,135,241,121,184,142,198,129,246,67,136,232,181,199,41,2,116,244,31,144,163,189,6,124,189,212,72,235,49,129,214,160,100,216,173,249,110,145,252,22,193,41,80,224,84,66,126,254,250,106,113,150,32,192,129,65,149,235,200,110,32,164,108,147,2,81,233,187,13,151,112,71,130,88,100,66,222,35,150,140,122,246,246,44,153,92,93,47,224,62,98,7,205,58,46,211,57,90,206,129,229,122,59,79,175,101,10,69,12,17,235,100,142,226,39,98,21,37,215,237,213,229,54,56,59,75,135,23,40,128,248,89,190,252,231,180,115,93,97,23,109,240,165,140,204,16,227,123,152,82,155,48,207,184,138,104,114,124,87,129,237,220,214,231,142,62,119,245,185,167,207,125,125,222,197,222,27,250,199,252,84,104,113,202,69,181,19,1,34,193,202,168,215,27,82,229,206,116,236,139,36,203,153,0,43,129,128,236,64,99,75,230,87,127,127,54,1,11,23,43,29,74,44,10,21,242,179,72,150,73,182,194,126,176,254,250,217,75,28,27,63,133,51,92,130,144,234,47,67,56,238,58,146,130,113,162,35,10,20,234,16,100,250,139,8,17,26,220,188,70,165,145,177,73,35,152,253,31,194,11,61,7,37,122,195,253,179,195,197,54,3,207,226,82,159,251,214,87,91,108,190,35,4,110,85,74,237,249,95,85,88,105,29,175,146,12,215,255,160,163,42,214,253,24,3,12,9,105,108,4,136,196,89,14,105,159,155,170,16,52,40,56,44,223,98,104,152,142,79,109,2,54,120,140,83,58,99,63,237,154,233,71,155,206,208,210,92,215,27,165,3,137,113,176,130,191,172,14,182,136,228,132,93,213,35,178,70,122,178,90,207,224,232,173,116,52,79,215,115,16,96,196,204,6,21,218,86,56,187,35,232,149,220,6,73,175,228,11,74,146,149,104,16,147,38,249,175,201,124,174,42,43,24,123,178,128,150,234,143,232,128,160,204,26,97,44,25,204,218,2,106,6,154,202,18,239,56,84,82,156,156,202,51,54,194,234,4,180,71,108,223,105,174,47,52,184,7,0,53,14,135,162,214,241,89,48,126,79,244,90,78,36,181,185,37,68,98,128,17,229,204,104,129,97,89,52,62,100,115,56,197,8,150,209,212,254,26,81,199,212,205,33,118,137,19,153,9,217,141,150,152,166,69,19,97,219,177,87,156,92,136,218,202,53,184,190,73,94,134,67,133,45,123,64,70,182,138,150,134,236,112,145,135,70,171,121,202,166,48,22,178,247,232,235,120,222,216,123,34,234,52,10,227,201,177,196,222,72,166,83,16,110,104,56,48,57,69,51,2,68,73,177,155,128,9,170,228,117,104,8,55,196,136,175,112,187,153,254,36,52,185,177,200,54,11,136,195,213,181,188,150,102,136,139,31,64,5,140,11,91,94,247,249,203,38,89,135,250,36,214,39,19,125,199,110,233,243,84,159,70,51,8,50,150,153,200,150,40,193,218,114,63,209,183,194,42,23,162,134,181,31,120,117,215,69,2,6,132,77,172,195,31,217,96,140,68,235,98,184,165,30,54,73,42,214,251,48,155,45,85,207,181,13,44,38,197,80,76,222,178,114,130,70,36,29,28,226,50,103,27,152,171,231,110,214,117,2,115,133,208,217,161,45,53,230,203,193,56,112,131,169,2,213,12,101,31,0,74,123,82,204,198,149,246,92,143,55,41,130,173,135,43,132,75,64,178,182,67,240,128,12,211,68,101,200,208,191,109,180,152,233,217,249,76,63,143,38,97,162,195,61,60,135,229,13,54,147,40,209,163,105,10,183,82,15,17,40,55,209,69,60,69,206,74,102,96,221,70,46,176,228,20,135,12,146,193,129,67,151,106,228,35,139,82,17,105,177,62,117,154,67,100,169,219,113,165,216,221,81,174,18,245,31,87,2,94,14,53,235,184,41,227,42,207,96,173,135,33,15,75,133,139,180,162,28,162,2,144,144,159,102,239,195,139,82,13,62,45,10,243,53,190,148,109,151,207,145,47,207,87,193,146,114,139,19,100,201,19,132,49,207,141,15,9,198,12,153,2,79,18,230,222,200,150,193,10,50,153,70,227,53,34,131,16,208,15,115,114,25,193,198,93,137,98,179,20,211,18,152,46,131,98,98,140,124,144,146,64,242,220,243,40,184,57,115,157,236,228,129,108,8,167,136,115,100,48,218,206,195,137,241,1,17,47,42,109,9,127,30,104,65,251,73,7,235,169,146,226,59,133,113,89,80,185,29,178,167,48,123,100,136,202,41,194,54,73,201,208,172,213,229,110,158,144,112,237,203,233,116,90,202,148,169,233,236,172,237,15,52,108,50,104,142,215,213,142,52,19,113,72,59,32,164,149,214,44,188,191,164,11,159,74,69,110,200,187,53,89,180,30,78,118,178,207,226,13,144,146,199,136,48,159,75,94,138,20,242,213,211,0,174,119,193,151,43,150,28,149,48,223,132,185,28,138,20,140,53,80,172,156,59,52,156,151,114,179,112,21,5,234,100,130,240,222,28,109,137,2,70,57,78,174,224,149,39,238,32,152,103,213,49,205,51,118,80,206,115,118,113,207,179,170,157,200,147,149,17,171,167,215,123,89,100,136,238,98,64,82,73,255,50,10,255,50,10,255,50,10,255,50,10,48,10,102,150,26,180,156,117,157,11,115,112,134,201,35,156,105,44,226,200,25,183,114,59,232,48,247,236,148,247,103,80,42,118,218,82,132,216,95,12,133,79,114,140,197,166,21,38,130,184,172,16,177,141,244,198,70,204,28,126,43,75,56,121,143,23,105,176,170,173,231,108,77,213,242,46,46,91,90,111,226,89,101,145,167,82,182,166,152,76,26,150,154,85,2,84,28,78,215,72,160,63,58,101,238,224,156,255,139,4,71,38,152,62,118,11,183,88,253,95,161,12,77,68,81,130,78,28,62,145,185,31,12,223,186,254,192,83,210,203,161,143,18,139,51,132,19,72,31,26,54,147,155,118,100,97,202,235,214,242,60,149,227,212,50,138,58,94,45,199,22,25,113,81,133,123,164,42,164,69,122,165,35,139,117,9,51,234,66,142,22,50,242,42,148,225,230,201,121,211,156,92,148,175,20,47,146,43,197,37,154,107,35,239,50,167,202,210,236,233,86,253,222,45,22,179,105,146,149,167,138,211,45,174,211,8,47,243,68,58,81,37,141,74,78,41,109,107,10,153,171,186,193,230,28,136,75,177,149,104,32,229,23,211,207,19,187,142,74,206,211,108,172,70,112,218,133,49,192,130,39,73,165,83,36,253,242,206,233,195,168,254,114,170,178,232,76,100,184,86,57,131,207,100,134,83,201,160,51,202,152,98,227,93,166,145,91,191,253,247,247,225,21,79,21,50,141,38,176,144,186,235,98,126,47,12,65,27,129,167,147,112,134,117,112,51,88,98,238,205,186,132,162,226,132,244,129,78,53,59,211,136,50,88,124,142,150,83,186,94,21,164,16,243,21,140,86,60,145,169,207,107,176,138,79,171,10,6,145,45,159,150,226,162,66,140,255,156,33,166,122,98,18,95,79,173,39,0,183,124,62,91,5,88,155,28,11,120,245,196,250,12,154,165,1,134,240,226,154,15,38,17,89,20,86,253,228,66,102,98,240,168,103,34,9,59,10,144,27,76,147,113,45,32,198,106,116,154,167,87,60,115,206,134,34,105,107,210,114,110,52,189,82,69,242,83,185,52,92,47,38,175,238,223,41,199,38,77,229,110,205,25,92,15,251,26,191,67,37,58,148,226,136,148,34,193,21,9,189,34,197,227,20,161,74,188,196,2,215,129,5,69,154,73,114,60,54,217,112,128,23,73,145,42,18,207,106,5,100,35,42,255,178,14,64,181,41,82,85,102,177,188,42,51,140,9,123,47,42,95,76,251,69,90,94,228,151,119,191,124,137,24,180,129,29,64,198,203,174,99,62,28,217,213,149,59,114,49,28,79,115,124,205,33,7,3,75,105,237,134,106,8,134,47,183,112,102,157,185,103,253,123,180,128,48,100,205,238,193,147,233,123,123,53,66,3,49,34,191,172,253,187,224,12,52,196,40,227,235,239,213,128,76,65,27,205,77,152,206,78,81,247,166,162,238,78,81,239,166,162,30,138,206,200,95,9,141,110,222,59,56,114,165,158,213,23,251,168,119,110,79,27,80,255,170,125,203,235,113,191,102,117,162,221,13,246,38,162,53,2,198,80,187,55,92,15,112,125,248,172,190,125,43,92,118,64,246,135,234,67,70,229,247,70,168,164,198,202,33,82,106,181,186,164,177,87,166,138,225,90,140,190,202,117,146,67,181,26,146,81,222,169,22,111,42,93,20,246,170,133,201,94,212,202,10,19,130,162,126,173,168,48,72,245,210,202,76,161,66,183,94,161,177,188,44,126,5,51,167,178,216,55,168,66,87,190,141,130,142,226,5,89,10,159,164,94,62,39,11,132,171,10,190,17,122,94,56,103,132,40,204,204,216,41,173,88,148,150,72,83,163,2,47,174,203,17,129,143,197,194,156,26,12,196,118,224,162,180,19,98,246,69,91,213,13,196,50,180,203,172,92,188,161,52,23,229,34,184,213,76,180,145,208,229,198,1,238,38,181,69,160,11,46,65,30,99,65,188,190,248,159,103,200,182,206,176,218,249,94,8,57,159,151,196,188,144,109,154,104,89,101,169,46,23,37,185,230,115,214,67,68,221,222,5,13,10,210,245,181,126,69,249,110,6,56,184,27,160,221,195,252,83,115,7,123,0,196,136,11,83,113,39,68,215,211,16,139,173,13,42,195,65,35,72,76,68,247,1,232,0,187,110,159,62,119,1,188,10,99,204,139,96,163,239,134,73,54,204,194,224,120,55,37,37,208,189,16,69,215,49,20,222,141,104,125,52,169,194,164,177,68,149,232,90,249,66,186,217,69,109,18,70,158,146,9,243,153,9,223,85,164,96,225,133,47,158,39,175,44,73,113,173,107,213,212,234,229,161,149,23,237,75,59,43,50,21,187,39,49,239,153,190,167,16,191,28,50,34,56,72,125,85,144,194,112,188,57,195,82,201,89,248,1,27,156,109,211,195,164,211,196,149,49,157,114,149,9,77,133,169,172,105,251,25,124,85,90,31,255,37,159,99,202,245,114,229,214,229,212,146,9,157,98,135,179,152,149,74,16,60,219,196,252,128,124,253,59,193,136,33,129,28,45,89,155,102,154,251,86,102,195,89,170,59,55,188,59,235,84,204,140,172,7,137,188,179,158,116,9,203,213,202,126,233,157,245,155,253,86,9,73,100,238,11,163,228,157,86,234,11,155,130,9,215,157,128,246,118,237,44,120,19,182,139,111,197,254,236,186,118,10,15,233,88,236,137,192,223,201,241,80,146,83,200,54,54,237,239,22,158,6,189,68,189,127,136,86,201,70,201,129,127,136,183,146,236,122,240,13,133,183,54,99,139,153,57,173,42,229,156,86,214,167,111,13,57,227,6,159,183,95,212,43,124,212,74,141,191,147,167,202,219,221,57,118,236,100,195,30,15,57,121,239,137,68,101,185,245,22,97,147,141,169,173,105,154,29,203,150,212,94,59,140,130,198,49,80,184,163,85,177,95,95,219,203,102,203,33,97,241,130,45,110,151,80,224,219,180,202,155,227,84,172,57,119,216,123,176,106,89,229,197,231,78,115,30,119,187,188,162,188,71,35,88,30,29,183,61,244,237,240,182,246,234,13,202,150,74,145,6,141,149,69,30,98,17,43,153,59,169,34,65,47,214,167,11,134,112,133,38,222,139,102,10,206,223,189,216,94,195,112,135,247,156,156,11,244,125,155,186,65,164,239,108,139,76,235,189,219,106,54,161,245,182,86,33,236,191,188,123,208,95,249,204,144,103,172,187,255,136,105,235,29,77,10,127,242,62,109,58,142,134,43,219,180,94,117,157,97,239,54,203,102,92,108,171,237,55,178,238,110,198,113,116,64,49,45,200,243,232,234,115,233,244,21,197,75,178,93,218,189,131,22,201,165,198,130,101,124,142,12,218,178,43,146,197,126,158,76,254,99,45,157,224,240,174,198,31,74,25,98,139,79,229,148,171,136,205,190,61,9,165,188,129,123,12,184,123,140,248,55,12,184,59,70,152,17,100,101,144,232,53,88,229,79,176,199,55,129,21,6,250,95,166,249,190,166,249,86,122,178,85,129,81,187,135,57,195,13,9,120,246,136,59,31,220,105,206,110,106,86,88,150,123,182,139,169,27,205,51,237,158,115,75,187,195,40,107,155,124,79,74,254,205,117,132,22,78,29,186,246,183,251,11,133,199,238,169,11,110,87,131,25,117,43,179,197,29,69,184,173,73,55,112,207,220,241,125,154,244,251,26,198,193,110,101,61,226,94,77,134,94,216,13,207,238,211,164,227,244,65,89,12,188,119,44,223,222,212,42,143,140,24,38,246,111,177,135,5,18,159,134,167,79,105,174,88,61,190,161,65,44,31,239,214,157,85,86,231,85,205,125,150,83,27,150,231,111,151,56,209,84,93,218,246,24,177,155,164,237,174,166,212,178,212,254,205,52,45,80,221,217,76,201,106,119,174,111,139,137,221,173,205,203,24,229,141,11,137,234,109,43,94,141,27,22,181,117,154,155,27,170,169,192,45,203,65,205,146,191,87,67,60,234,222,221,66,243,104,186,87,11,197,2,83,99,51,88,108,218,169,87,138,238,20,146,65,215,148,53,132,142,138,120,161,82,225,102,62,248,224,3,252,57,27,250,90,66,190,161,94,83,31,110,192,229,159,141,134,162,34,110,246,82,172,211,217,187,229,110,159,94,223,100,106,250,55,3,18,102,64,141,0,85,48,123,104,105,211,56,112,171,150,222,54,3,191,199,248,179,223,20,252,166,214,121,56,174,244,187,220,254,126,211,192,134,158,223,53,202,223,60,255,188,87,203,251,76,64,75,109,151,125,28,41,243,141,14,86,63,55,207,123,161,1,20,186,180,76,125,219,212,244,126,88,8,127,235,158,104,208,100,206,178,53,255,54,119,235,139,207,249,249,72,149,107,71,52,190,0,235,24,55,44,23,1,40,8,189,96,39,95,68,120,107,165,93,36,77,108,35,29,19,24,71,2,225,93,33,141,226,112,142,53,25,82,165,89,102,223,13,23,228,213,154,182,69,7,199,90,121,75,72,163,203,133,100,210,223,132,131,91,193,193,169,225,96,113,211,249,193,63,6,135,47,27,158,79,131,91,180,23,161,149,121,56,26,45,125,227,217,42,184,238,136,22,208,249,168,152,240,138,89,22,207,120,161,10,95,233,26,253,118,142,85,184,22,24,43,46,200,186,173,125,121,169,105,137,36,216,235,4,73,106,17,94,20,195,243,197,231,184,86,148,175,70,145,241,17,67,187,155,79,248,190,156,186,244,46,95,188,38,47,55,112,113,127,154,201,89,109,97,156,58,34,215,183,9,118,190,92,207,39,181,240,30,103,39,188,71,133,225,241,37,1,95,124,126,175,72,34,244,162,244,216,30,200,130,188,249,61,69,56,182,49,55,237,28,231,42,99,153,3,236,127,112,196,144,193,151,150,34,206,145,99,118,0,4,87,117,202,235,56,113,73,231,81,254,172,176,19,186,240,79,227,123,106,136,59,59,211,8,241,78,122,164,167,154,90,56,231,251,115,139,241,255,157,116,100,78,53,218,139,22,207,30,195,51,101,0,10,119,238,215,34,220,179,162,65,88,112,91,124,209,132,40,47,94,13,21,212,211,159,30,21,79,2,59,57,66,177,122,45,9,77,178,183,245,168,92,72,29,127,118,50,119,118,30,75,134,174,59,156,183,122,116,50,119,31,209,157,28,54,242,33,101,56,61,57,90,137,76,213,128,220,73,214,40,184,15,241,201,134,223,122,68,87,60,209,245,202,184,48,246,124,166,97,244,164,199,67,224,98,111,211,105,105,151,139,120,137,90,116,107,77,220,89,243,226,226,194,188,112,233,206,154,71,116,71,189,35,148,111,105,244,192,181,39,201,229,168,69,179,225,190,103,209,172,181,165,169,43,205,109,60,88,77,136,214,168,229,226,184,204,152,42,245,91,184,26,28,252,68,94,85,145,143,43,20,94,225,6,10,116,213,115,108,164,184,123,54,30,22,7,169,72,38,147,22,167,225,94,253,27,220,181,126,137,7,82,64,240,91,116,187,145,133,109,153,174,230,219,230,32,134,107,98,186,134,111,14,206,123,221,49,208,52,123,36,95,154,107,98,234,129,175,103,58,113,15,231,158,217,253,176,176,7,166,175,217,54,234,32,20,207,232,82,157,65,76,199,90,47,207,52,171,217,56,21,5,76,123,183,72,143,10,120,57,132,174,105,27,125,207,244,198,6,116,221,0,22,134,11,212,232,223,51,251,154,21,27,93,154,160,0,65,174,0,248,90,119,64,133,6,38,42,27,62,37,122,72,136,209,35,100,81,145,129,217,167,236,46,117,114,0,140,92,224,208,71,175,112,138,102,75,197,186,84,204,167,98,253,106,49,63,198,226,105,15,199,40,228,83,33,207,176,93,163,103,250,40,210,5,86,182,107,246,69,47,186,93,228,1,61,119,44,177,39,250,25,8,161,224,127,208,241,220,232,117,63,144,12,19,183,26,185,215,196,41,220,238,205,180,201,51,56,199,29,29,77,123,110,184,32,10,142,125,211,159,187,24,146,248,112,128,100,117,236,206,81,2,141,217,61,68,83,66,92,207,251,232,212,135,133,107,131,227,0,210,7,70,115,2,229,158,211,225,135,133,131,158,226,38,106,166,61,38,65,0,158,154,103,128,32,196,39,151,15,250,72,130,96,128,109,26,160,211,129,167,33,29,53,93,116,210,246,204,30,49,172,11,17,241,32,71,54,81,11,55,187,66,33,58,232,146,60,17,183,125,128,6,31,28,240,129,8,63,39,122,163,69,27,148,242,32,113,174,1,236,88,16,113,134,187,135,65,58,152,25,14,36,200,96,20,249,136,90,67,62,18,224,31,64,86,197,17,178,6,132,34,121,80,36,187,150,72,243,197,17,234,0,54,166,89,0,194,165,73,206,108,144,196,113,0,12,201,93,112,12,253,5,50,4,19,124,227,250,92,148,143,152,114,4,11,253,34,250,240,17,126,252,115,3,242,214,5,53,187,102,247,28,80,153,14,134,139,78,251,16,17,52,226,3,67,121,224,19,218,16,45,18,94,116,139,142,156,30,117,70,67,38,228,136,26,65,66,151,208,147,71,146,240,46,0,128,231,84,142,180,202,195,67,189,88,83,21,185,128,19,36,215,35,236,233,8,37,136,130,182,1,108,29,84,133,108,162,171,36,189,232,13,88,37,249,225,144,82,137,3,226,14,18,123,64,131,16,230,211,34,3,157,33,44,0,10,28,102,221,67,67,92,3,12,238,49,199,153,189,121,6,100,141,229,199,28,0,203,1,10,129,5,252,3,61,21,71,80,31,238,24,186,133,100,136,175,1,245,134,201,1,19,145,131,115,252,119,209,176,55,71,102,119,12,17,33,49,233,226,219,3,214,30,255,247,161,119,184,233,44,122,68,157,69,38,36,205,0,171,6,134,3,148,196,17,241,133,200,206,180,161,74,56,2,169,81,53,182,33,28,44,242,172,154,56,38,136,46,228,90,252,51,191,192,0,112,2,53,232,31,236,37,33,50,108,50,141,168,67,255,100,61,137,51,232,249,24,116,6,139,192,18,54,11,160,34,91,7,54,123,172,39,144,254,177,176,99,2,83,54,52,68,62,74,33,91,75,199,61,52,67,226,227,146,113,2,12,179,7,102,67,77,128,65,31,217,44,243,104,129,140,55,29,125,88,248,172,30,48,56,160,2,85,167,126,144,50,144,157,162,127,146,37,48,129,148,26,95,250,135,73,60,119,33,65,115,224,230,157,147,226,67,152,136,121,164,171,208,50,100,177,160,136,35,129,30,137,44,99,200,162,10,186,67,38,88,234,251,231,62,58,65,160,128,75,143,112,243,80,21,89,160,223,57,18,129,57,9,45,148,158,218,38,158,209,63,130,191,81,147,196,121,110,91,231,224,137,67,226,76,10,192,98,66,212,198,15,173,192,148,57,1,106,128,63,164,97,176,109,98,148,96,139,65,74,76,194,1,217,231,127,24,100,166,49,87,224,127,26,220,136,72,104,78,152,72,96,96,131,47,88,236,2,37,88,128,152,89,64,148,128,128,93,128,74,71,192,137,52,144,18,72,214,197,1,231,0,95,140,63,66,230,145,0,186,201,44,54,125,160,57,39,208,56,33,14,40,7,99,20,122,68,200,66,238,176,38,67,58,194,156,97,213,146,255,108,147,44,28,19,78,62,253,115,50,224,161,60,110,0,73,36,38,173,150,25,196,37,22,32,218,251,34,27,43,192,96,112,2,99,208,172,28,9,168,199,93,12,227,231,124,8,100,6,231,93,216,189,185,77,188,196,1,4,137,250,39,210,160,188,49,15,39,152,179,153,125,48,11,230,154,19,60,141,19,136,55,49,4,129,48,244,230,52,174,160,103,68,94,172,21,65,179,72,247,161,151,24,146,8,18,101,145,45,195,141,41,73,97,193,71,22,28,48,4,0,33,41,32,200,185,59,0,190,184,202,129,134,39,20,70,9,216,251,115,58,66,109,18,112,8,41,184,68,18,136,47,137,50,169,147,248,74,114,145,218,139,28,200,1,204,7,73,5,165,208,89,254,229,178,156,47,115,168,60,212,147,45,38,186,66,236,58,7,30,220,20,12,26,248,72,6,149,135,68,22,120,96,69,232,17,249,104,240,97,233,70,112,28,17,134,37,219,230,127,232,13,161,68,22,139,199,68,16,133,52,102,64,118,140,68,20,195,129,28,242,8,101,250,167,65,199,130,224,10,43,71,255,208,24,240,13,163,207,28,45,192,113,32,162,224,8,116,194,33,43,6,76,23,228,149,172,44,13,172,132,8,255,3,48,35,69,72,144,234,8,191,141,198,55,152,34,194,128,240,35,141,36,251,71,250,45,254,73,122,73,105,168,101,26,100,28,228,184,16,5,57,66,147,166,65,34,25,109,170,76,86,85,152,36,32,79,30,17,217,6,143,134,58,178,22,56,67,219,144,86,58,32,85,149,38,136,124,1,156,66,24,104,68,230,35,114,226,132,238,195,6,66,155,186,36,16,187,78,18,102,53,231,116,223,250,207,148,231,174,166,20,114,131,113,103,222,81,158,61,208,37,9,154,186,64,65,43,95,137,160,241,37,56,26,95,31,84,248,211,39,15,12,3,143,52,230,27,10,137,59,34,105,134,81,228,170,166,43,247,209,107,110,171,122,81,67,209,154,87,113,222,169,185,167,252,136,48,220,11,73,92,240,94,110,16,37,104,194,88,58,167,233,156,40,86,224,33,239,94,180,59,147,16,243,186,34,148,27,107,5,52,225,149,127,152,104,136,133,113,204,1,235,129,236,26,47,26,150,67,119,196,178,129,90,244,199,122,3,22,121,42,59,174,50,73,236,150,150,79,114,8,229,160,53,154,89,250,26,197,11,107,116,245,20,211,77,16,143,238,230,199,23,102,84,217,85,76,92,197,254,198,169,72,201,241,124,39,23,254,100,122,169,253,90,78,222,121,153,90,230,135,184,25,18,230,124,123,204,242,228,124,206,41,77,231,232,184,50,249,163,61,28,79,205,196,136,72,52,165,163,0,202,186,183,47,82,197,86,45,77,52,253,60,137,232,139,103,98,96,6,77,212,171,36,211,3,36,242,116,201,252,120,51,198,253,10,52,241,167,110,120,1,157,226,121,6,36,230,37,249,137,154,243,125,55,128,115,43,54,144,65,125,231,220,46,37,224,31,195,165,83,78,48,156,159,97,32,95,136,202,31,90,71,0,152,196,87,124,187,30,94,141,64,203,54,250,10,197,70,132,171,131,49,155,139,228,109,34,195,57,239,150,211,48,72,193,251,131,191,76,206,191,75,57,82,205,171,172,192,101,198,181,52,13,143,39,165,235,156,248,118,153,181,226,71,59,229,79,142,132,182,84,19,249,62,21,242,153,126,124,55,53,86,166,226,6,95,138,152,242,198,163,90,128,27,232,173,192,95,126,234,3,63,154,100,246,161,133,245,1,121,99,192,86,233,158,91,109,190,255,88,167,162,228,71,21,29,174,46,118,176,5,192,189,48,249,206,43,183,153,156,218,141,219,246,54,58,30,155,56,40,180,232,75,101,238,89,1,44,238,125,90,89,21,81,96,112,165,164,80,127,44,147,148,215,41,164,249,96,147,125,195,242,77,85,175,43,26,205,215,90,105,23,134,184,156,175,166,135,101,24,226,58,189,220,142,237,152,18,242,41,253,93,3,177,163,216,117,82,210,221,252,84,27,165,64,39,9,175,220,68,190,18,163,174,233,254,170,186,30,213,196,217,6,18,139,27,78,214,22,158,234,104,90,95,85,97,86,40,91,149,157,93,73,122,45,238,245,120,155,32,85,111,7,185,199,64,41,248,36,6,176,6,41,186,159,185,244,176,132,166,204,37,29,255,61,204,37,192,124,154,177,172,94,188,80,182,146,152,72,96,167,26,215,166,6,88,6,99,19,200,51,224,62,252,172,94,15,41,42,13,110,13,29,86,11,201,159,60,13,187,42,84,44,79,144,64,42,133,104,66,72,83,139,74,161,157,230,120,254,216,253,83,197,144,98,174,68,134,154,150,110,188,6,59,138,85,208,178,60,241,179,88,67,37,39,144,237,178,112,21,69,133,104,149,37,139,111,174,123,155,92,149,239,34,185,167,84,193,33,250,127,85,170,228,245,43,37,153,90,96,154,212,35,255,221,192,236,201,43,13,191,152,67,240,10,39,102,161,222,227,98,204,165,11,80,230,149,97,153,234,27,110,133,251,52,180,14,206,153,239,149,209,182,55,55,45,187,81,26,10,94,85,13,62,93,97,94,53,69,111,151,184,245,55,70,90,44,133,51,243,155,37,165,10,83,222,7,187,66,138,203,172,121,208,40,183,245,99,140,138,120,64,55,150,241,131,25,182,19,170,109,229,15,12,223,21,75,60,171,156,111,253,118,171,197,171,222,227,122,239,161,19,220,219,103,80,171,213,220,145,114,27,219,15,242,226,48,49,138,130,32,165,107,189,107,227,94,190,47,209,64,51,177,185,86,190,196,167,62,120,40,114,236,59,132,52,82,169,58,173,170,146,41,31,12,186,52,114,243,21,246,183,140,220,205,132,82,143,103,223,29,101,118,134,236,170,241,162,23,109,231,208,221,198,235,174,95,189,228,137,32,49,119,77,221,160,60,31,236,105,35,84,171,93,34,79,147,17,155,166,37,133,175,83,185,90,174,54,121,218,113,69,226,89,121,166,85,154,127,168,176,0,153,86,11,147,184,105,98,147,79,83,42,169,77,161,45,37,237,42,109,228,53,33,80,202,168,97,81,39,60,232,135,219,53,210,253,42,249,150,238,224,36,217,71,166,104,56,121,164,158,143,127,114,36,10,221,89,25,114,250,34,90,195,220,60,123,141,61,253,215,63,62,127,246,236,245,13,149,97,177,184,153,187,60,171,127,172,148,125,75,1,14,111,159,223,41,100,113,112,22,198,10,15,117,35,20,181,223,206,120,85,154,175,222,1,162,129,232,229,153,9,7,93,211,179,125,89,134,119,158,248,171,136,155,75,181,188,129,140,70,65,24,72,20,33,219,187,77,84,28,235,1,156,93,191,226,89,23,33,28,213,217,125,41,93,78,241,235,41,42,164,87,74,106,83,46,2,65,106,171,0,40,149,107,64,190,184,80,142,57,23,225,229,59,161,249,77,101,202,81,233,154,72,82,87,63,189,123,136,39,200,139,36,21,199,32,79,41,42,128,174,249,83,217,124,9,97,57,33,223,31,175,52,162,46,202,168,164,202,147,74,15,69,18,46,15,148,71,184,224,79,181,92,185,104,172,162,245,42,176,181,218,203,210,28,165,105,250,65,2,202,242,120,151,234,204,33,136,165,49,166,136,83,211,138,192,63,248,14,255,68,117,251,62,72,39,100,80,180,231,223,238,169,114,164,18,120,176,38,215,122,254,109,29,219,127,182,185,80,6,81,251,89,196,10,52,117,226,179,207,62,43,225,62,149,53,100,133,150,118,123,15,154,39,126,136,201,200,135,111,113,103,50,60,41,147,150,59,178,27,67,7,228,4,214,245,41,168,229,31,179,116,137,126,202,211,102,28,100,28,146,186,95,3,118,231,16,177,195,65,54,42,228,201,103,220,176,190,66,183,74,167,73,119,245,150,231,244,220,122,109,164,61,60,122,136,82,194,86,138,6,255,185,75,160,202,151,187,239,82,40,89,141,210,77,6,118,150,69,97,89,209,156,88,22,37,175,188,97,109,212,249,31,90,27,213,180,39,8,25,213,112,87,242,122,208,205,13,139,110,36,161,127,95,97,200,111,101,223,238,252,127,192,124,0,248,31,101,62,248,135,123,148,203,85,3,222,131,145,172,109,88,130,42,130,178,242,68,20,71,236,25,14,17,122,133,112,205,71,255,13,10,108,23,134,230,146,0,0}; +const uint8_t ELEGANT_HTML[11640] PROGMEM = {31,139,8,0,128,47,232,102,0,255,237,125,249,127,219,70,146,239,207,201,231,147,255,1,102,54,50,25,1,16,78,94,18,157,181,157,120,226,93,59,206,139,237,204,225,104,178,16,9,146,136,65,130,3,144,58,44,115,254,246,253,86,117,55,46,66,18,229,57,118,223,123,195,19,232,163,186,186,174,174,238,46,0,39,15,38,201,120,125,181,10,181,249,122,17,63,250,226,243,19,250,215,226,96,57,27,181,194,101,11,41,154,118,50,15,131,9,31,225,120,17,174,3,109,60,15,210,44,92,143,90,111,223,60,51,250,45,237,168,146,187,12,22,225,168,117,30,133,23,171,36,93,183,180,113,178,92,135,75,148,190,136,38,235,249,104,18,158,71,227,208,224,19,93,139,150,209,58,10,98,35,27,7,113,56,178,77,171,4,109,29,173,227,240,209,147,96,189,14,211,43,237,187,197,38,14,214,73,170,189,122,243,88,123,187,154,4,235,240,228,72,20,1,222,217,56,141,86,107,28,197,225,90,139,147,96,18,45,103,223,70,231,111,194,203,181,54,210,90,47,68,138,105,154,173,227,47,62,15,178,171,229,88,155,110,150,227,117,148,44,209,157,112,252,94,129,127,189,14,214,155,236,167,240,44,73,214,237,142,118,253,197,231,159,161,3,217,90,67,131,1,64,5,23,65,180,214,102,225,250,91,156,183,31,30,253,46,92,63,139,210,197,69,144,134,207,151,211,228,97,7,240,63,139,166,90,155,202,139,250,159,129,200,155,5,72,96,162,218,119,113,72,135,79,174,158,79,218,15,167,178,230,207,97,154,1,147,135,29,115,13,132,159,10,130,161,49,130,97,170,66,4,248,102,80,96,201,132,145,248,182,25,138,202,103,40,217,58,89,73,146,124,55,13,199,232,40,39,167,33,184,250,51,24,135,243,207,240,66,218,86,11,227,44,20,221,56,58,42,152,176,76,214,90,178,140,163,101,168,107,239,195,112,165,17,89,0,142,161,135,235,55,209,34,76,54,235,246,141,164,213,53,199,178,172,206,177,6,160,79,169,144,22,204,130,104,9,121,208,28,45,11,65,242,73,70,205,127,241,249,22,168,229,172,202,214,65,186,174,98,126,43,133,91,41,183,134,210,198,34,204,178,96,22,182,106,212,169,202,202,237,52,46,128,145,76,3,221,48,5,177,199,113,144,101,47,162,108,109,166,225,34,57,15,193,139,104,50,9,193,205,219,161,73,189,168,64,8,38,229,102,94,9,24,32,66,137,0,117,206,221,46,97,119,161,204,13,126,58,190,170,199,183,163,12,158,9,174,255,30,66,34,48,222,85,81,81,162,80,82,165,118,209,185,77,50,124,19,86,217,102,60,6,103,159,38,241,102,33,187,32,21,240,1,85,45,225,42,9,144,21,253,149,234,89,146,215,29,249,210,53,155,229,116,127,185,246,85,249,29,217,133,209,249,158,91,126,74,56,85,141,203,29,189,92,165,201,12,218,89,233,102,94,211,185,173,102,192,141,103,79,54,235,53,254,184,166,50,80,251,145,71,227,38,204,108,125,21,135,230,36,202,86,113,112,53,122,120,22,39,227,247,15,171,6,162,185,228,50,89,134,92,80,144,67,96,157,156,101,97,122,30,166,192,124,25,94,104,47,55,32,33,208,124,37,147,219,32,206,232,145,228,78,157,108,36,94,244,83,133,244,12,35,73,54,227,113,1,188,219,7,110,93,44,21,220,218,232,160,76,125,26,130,217,209,121,248,99,176,158,51,101,52,109,141,97,137,15,52,26,228,128,12,88,180,194,65,152,143,19,211,112,61,158,87,171,162,5,170,64,28,80,197,205,228,125,7,40,49,90,13,131,77,94,236,183,44,89,2,79,178,153,63,210,8,92,180,23,100,218,127,188,126,245,131,48,226,235,77,186,100,8,82,10,53,109,171,141,3,32,162,181,195,52,77,82,137,189,6,56,212,90,2,110,113,122,251,225,51,66,87,227,147,225,67,93,28,40,132,37,220,229,38,142,57,69,242,243,34,90,78,146,11,19,99,1,180,6,40,23,20,166,145,88,141,58,218,168,245,13,43,53,37,170,1,77,165,225,131,30,189,70,78,160,173,133,130,105,201,20,138,167,6,2,173,77,74,104,105,139,40,142,35,153,6,130,21,116,87,181,4,215,127,76,147,5,138,181,219,191,234,192,250,55,104,49,227,36,42,208,171,164,200,2,93,81,170,77,149,191,19,148,248,41,252,203,38,148,144,39,26,74,66,29,132,45,176,100,203,66,147,152,138,218,79,193,56,212,206,194,245,69,24,230,2,163,5,203,137,182,158,135,10,57,81,75,226,102,166,168,209,126,119,139,27,161,171,122,167,178,61,122,153,128,183,100,207,66,18,89,101,40,137,202,157,142,114,6,189,254,110,46,200,222,128,239,225,144,148,225,109,201,156,144,28,11,117,248,116,223,169,37,133,157,69,236,211,28,167,26,8,86,37,137,101,153,41,172,91,162,104,110,93,254,22,188,37,227,255,54,204,203,64,10,172,25,34,190,205,14,224,167,143,69,202,10,155,242,128,7,23,93,187,214,224,188,167,209,217,102,29,102,67,152,203,13,188,197,60,229,89,20,195,177,31,106,239,30,242,32,244,240,84,226,183,223,184,214,48,238,55,15,5,101,148,156,79,67,137,236,220,209,17,77,85,32,186,233,123,109,19,125,241,121,142,152,58,144,216,213,124,171,22,85,104,17,16,12,152,65,252,26,238,2,156,80,210,99,154,36,113,38,242,78,142,242,249,11,207,124,196,153,70,51,179,81,107,145,76,54,113,136,121,84,154,100,89,146,70,179,104,137,114,109,53,64,193,159,18,20,155,142,114,156,198,105,136,233,145,196,168,221,130,155,14,28,224,172,197,132,215,113,52,109,79,15,14,166,102,182,89,209,12,45,43,31,183,101,115,43,20,134,124,180,58,29,97,247,143,167,176,138,162,157,128,140,115,222,20,204,100,122,245,58,140,33,68,73,250,56,142,219,15,169,181,119,168,174,48,87,160,78,97,66,39,237,160,115,220,56,50,7,163,71,215,69,27,99,106,35,232,0,211,177,201,84,24,141,90,227,121,20,79,168,3,173,78,81,48,162,130,99,34,117,56,249,33,153,132,89,39,50,215,193,236,7,154,131,162,206,139,231,63,252,103,235,224,32,162,190,211,121,21,163,131,131,73,59,234,108,59,133,136,200,94,233,215,121,99,195,7,150,158,109,206,214,105,24,226,16,226,80,248,115,232,139,36,253,120,116,189,61,150,3,100,96,70,208,194,89,26,173,175,14,14,128,126,126,54,42,229,116,244,0,40,77,97,51,194,244,199,36,142,198,162,108,53,9,21,170,9,84,139,165,224,21,75,1,117,104,147,133,6,184,13,239,136,166,209,89,235,155,49,49,95,157,142,90,209,114,28,111,38,97,107,184,83,51,128,99,118,181,72,54,187,117,48,66,173,91,195,90,98,6,138,26,66,250,90,250,120,155,83,129,56,122,13,62,5,102,184,82,178,66,199,163,7,214,177,34,14,81,234,88,184,66,176,251,232,147,62,238,108,183,29,24,157,6,165,0,106,172,22,31,63,62,104,183,56,9,157,192,148,190,40,216,57,56,144,94,199,130,44,239,203,112,18,5,237,86,27,92,5,177,50,76,116,226,36,197,130,2,85,29,178,194,118,32,252,92,52,204,190,201,121,188,159,226,234,55,206,44,41,255,37,36,142,103,175,103,201,37,218,224,89,65,56,65,215,59,195,125,218,145,179,167,191,169,41,187,115,124,159,122,232,218,119,231,40,64,237,135,152,12,182,91,99,72,214,251,150,94,50,39,159,128,199,189,201,122,163,53,252,36,202,53,65,139,163,217,28,166,162,172,177,143,219,113,231,90,170,105,124,112,16,155,191,254,26,102,232,16,76,194,193,193,171,51,242,1,77,140,110,235,132,76,14,28,148,236,213,197,18,14,219,42,76,215,87,24,229,97,220,98,189,53,9,167,193,38,6,228,111,98,83,30,15,227,237,121,144,106,47,70,215,225,37,91,209,225,245,118,171,95,86,78,143,203,6,155,74,195,62,62,126,242,244,219,239,158,253,238,251,231,255,241,159,47,94,254,240,234,199,255,243,211,235,55,111,127,254,253,31,254,248,167,224,108,12,216,179,121,244,219,251,120,177,76,86,127,73,179,245,230,252,226,242,234,131,101,59,174,231,119,123,253,193,225,17,184,54,186,6,190,241,48,135,157,233,147,188,139,217,201,201,228,99,246,232,209,35,215,49,38,91,29,5,211,27,11,82,17,46,139,130,225,18,10,181,44,21,101,5,207,104,134,152,97,192,36,67,63,26,253,176,89,156,133,169,212,120,109,106,18,26,0,218,239,28,216,221,158,109,15,92,255,99,158,232,120,157,3,207,233,245,29,223,119,187,22,15,37,68,129,201,200,58,158,156,100,102,28,46,103,235,249,241,228,240,176,147,189,155,156,142,166,166,192,160,77,103,29,101,87,51,244,0,46,117,178,120,114,69,67,119,25,187,2,224,187,211,227,236,145,117,156,25,70,103,98,174,54,217,188,253,18,115,46,115,26,39,40,194,135,2,70,187,243,181,227,119,59,57,112,244,250,140,192,190,73,126,159,164,147,155,161,235,193,200,210,199,192,59,40,240,14,14,15,245,241,225,168,223,153,188,27,131,128,254,233,199,81,246,46,56,61,57,113,60,99,252,149,235,148,218,184,32,224,111,146,219,123,64,109,148,224,127,13,8,1,131,23,29,2,108,110,5,63,104,32,64,3,7,160,107,67,79,190,15,47,247,109,131,250,160,224,83,3,4,220,131,79,153,188,134,99,180,156,181,109,144,74,47,231,31,216,126,45,59,111,223,252,45,137,150,237,22,52,79,159,135,151,247,236,44,245,212,81,152,172,104,142,251,28,14,76,6,247,228,12,162,215,14,116,167,163,87,90,203,123,251,36,200,194,174,119,159,118,92,118,35,168,0,70,40,102,152,221,253,136,131,67,27,135,125,62,114,78,245,8,245,162,19,239,56,2,133,130,175,251,135,209,215,221,147,81,206,155,254,55,18,215,24,150,48,72,31,99,101,8,164,235,126,221,118,141,168,115,208,117,59,157,161,44,208,26,193,11,108,160,209,25,227,221,68,166,12,205,164,33,22,81,48,83,60,122,247,231,199,198,159,44,99,112,248,203,209,233,81,52,211,81,183,164,71,205,130,57,30,29,30,6,95,121,157,241,131,145,5,87,71,178,47,134,23,50,9,47,95,177,70,11,156,3,195,238,116,14,88,61,86,201,69,219,209,13,231,235,241,97,191,131,228,147,147,241,215,206,199,166,58,157,14,245,212,64,118,137,27,219,227,75,83,26,189,209,148,71,120,66,240,251,81,158,170,63,27,93,111,214,211,254,240,26,252,132,236,236,116,188,176,208,207,204,179,104,137,213,164,82,177,246,102,25,98,167,96,21,182,195,229,24,3,209,219,159,158,63,77,22,88,2,33,55,55,238,116,136,158,66,28,132,100,54,129,157,96,253,160,86,83,194,20,13,86,0,8,160,128,26,45,111,195,88,113,98,74,156,200,192,133,236,36,86,92,200,32,56,211,178,144,60,69,243,32,96,214,169,232,237,244,54,212,247,132,47,106,154,83,172,49,60,149,13,181,227,119,217,105,161,47,211,66,242,208,171,231,163,103,199,71,95,63,192,164,227,107,237,219,16,51,159,5,214,135,105,29,33,88,98,93,141,134,67,45,202,224,239,63,217,76,225,89,81,49,46,250,239,193,102,61,199,92,87,211,158,133,228,83,106,143,207,146,205,251,57,102,147,191,133,115,108,23,173,215,171,108,120,116,132,42,200,52,147,116,70,211,26,84,131,163,17,210,98,149,246,242,249,27,74,57,162,46,189,26,53,240,40,126,48,162,117,38,248,196,47,145,250,241,227,143,252,251,224,1,70,236,8,75,153,132,77,103,91,140,235,84,72,86,165,50,165,113,234,224,128,134,114,76,17,42,169,166,2,2,95,83,1,193,100,160,185,12,96,23,222,46,33,162,144,204,33,99,178,53,121,134,9,197,250,197,119,85,128,121,137,140,186,94,205,3,206,34,185,109,233,22,100,108,215,61,248,30,163,251,115,147,148,5,44,127,165,79,112,2,49,132,162,231,37,199,122,132,25,72,117,92,22,50,240,77,68,147,30,86,18,156,145,79,141,170,65,122,5,95,127,52,169,233,212,184,51,28,99,204,221,73,164,31,20,127,156,166,193,85,201,45,98,164,133,55,52,6,230,195,7,162,64,148,241,63,234,28,28,84,80,122,48,26,189,197,188,167,207,217,52,203,25,97,74,167,212,11,146,169,100,59,29,197,74,247,120,12,6,36,125,133,178,202,210,234,203,145,221,115,29,191,239,193,185,208,195,145,225,244,112,238,246,123,3,61,25,25,121,150,167,175,71,42,167,175,111,160,43,155,147,84,233,202,6,186,146,190,219,156,142,218,244,75,86,158,254,121,36,45,249,45,50,211,241,84,46,156,154,146,251,146,190,91,201,97,222,118,250,39,39,43,140,192,122,250,174,189,58,236,122,72,31,156,156,120,157,67,219,59,29,173,242,174,45,48,147,251,117,58,213,103,244,63,155,233,115,250,159,207,117,154,225,253,26,69,187,88,142,48,198,177,20,60,29,45,245,215,163,80,127,63,74,244,95,71,235,227,229,104,209,94,234,161,158,232,107,180,185,57,180,78,245,158,110,116,251,86,191,215,29,184,221,14,58,191,104,175,117,81,132,10,216,167,186,13,123,238,246,7,126,215,243,251,40,145,160,4,85,167,50,84,2,99,156,221,211,187,86,215,182,252,190,61,232,128,180,139,182,104,97,201,5,220,83,221,1,8,219,242,60,223,241,93,215,234,128,21,85,52,60,70,195,238,117,61,187,223,31,244,118,209,240,25,13,155,246,252,250,150,231,52,160,209,101,52,12,219,3,35,93,219,245,236,93,68,122,2,17,207,239,89,254,160,239,238,162,209,39,52,236,94,207,178,92,223,3,9,119,208,24,8,106,216,3,18,21,207,179,129,105,29,15,27,36,37,68,60,199,194,240,189,131,131,13,130,50,53,6,3,203,179,208,8,28,146,58,26,54,72,10,60,208,209,174,229,118,251,40,177,195,21,208,148,16,241,44,116,212,182,208,215,29,52,64,82,166,135,111,129,104,142,51,0,213,119,112,1,85,129,11,38,4,93,223,245,93,7,188,91,142,102,85,84,78,117,31,64,186,126,111,208,245,109,192,88,163,64,25,19,144,125,64,188,237,14,124,203,238,186,192,53,65,137,10,38,36,66,158,222,245,92,104,85,207,102,154,204,42,120,128,98,142,5,25,235,65,202,122,174,197,20,169,162,1,68,129,70,207,178,125,136,224,0,221,173,163,65,84,31,232,110,31,56,88,196,217,29,36,72,128,60,72,122,215,242,122,125,215,245,119,177,0,193,8,11,207,130,150,246,250,94,127,23,11,112,223,215,253,110,223,243,208,25,228,239,32,1,16,76,12,123,208,7,227,136,228,117,60,136,111,64,195,238,247,220,174,59,232,178,144,86,209,128,12,2,13,27,180,244,93,48,15,37,118,120,2,32,196,20,15,120,244,109,175,203,234,82,197,4,2,4,68,124,27,242,129,174,236,162,1,77,0,26,48,114,224,122,31,26,179,139,6,201,32,145,195,30,56,160,89,175,231,2,202,114,52,175,115,5,125,65,11,224,10,225,48,175,224,128,142,216,54,60,65,8,159,223,3,170,204,149,249,174,104,116,33,233,238,192,114,45,159,116,33,68,145,10,30,196,22,23,173,248,174,53,240,49,229,218,193,2,48,136,162,148,63,128,206,177,140,86,49,33,85,176,33,230,61,167,63,0,235,27,16,33,122,116,9,136,239,13,122,44,197,59,120,144,144,2,15,219,26,128,94,86,215,99,43,86,195,4,140,129,160,247,209,210,192,238,129,96,117,68,200,52,128,36,174,223,135,136,57,14,43,75,21,17,146,15,32,130,76,223,177,7,61,182,166,85,68,160,112,192,163,215,181,156,129,221,103,141,173,34,1,33,37,57,7,231,187,158,215,103,217,168,34,65,156,37,44,60,199,238,219,126,159,84,97,135,47,164,45,93,29,36,237,121,64,132,77,71,21,11,146,14,80,99,48,128,61,239,195,50,16,30,87,245,145,133,40,58,232,119,93,11,26,67,120,92,85,240,32,154,147,160,59,221,254,192,246,48,31,4,26,87,187,54,140,37,157,180,193,27,88,172,181,87,21,68,200,132,161,51,144,48,151,52,119,23,15,234,46,68,12,54,221,131,156,246,216,118,84,17,33,154,147,164,247,7,30,176,197,64,214,128,9,113,142,48,177,124,219,119,88,93,170,104,144,85,103,97,7,154,14,216,207,140,169,34,2,125,32,81,135,99,97,67,8,81,160,142,7,83,157,236,32,134,82,136,15,171,109,21,13,26,227,8,11,191,75,38,6,214,174,1,17,26,109,33,236,208,6,219,183,187,94,3,34,160,42,113,198,243,209,19,171,199,250,82,67,132,212,146,40,130,241,214,177,97,66,0,164,142,10,9,145,175,247,208,159,126,207,161,222,212,17,129,28,18,69,92,240,5,116,23,18,178,60,124,10,15,199,66,217,240,240,53,31,37,163,228,240,61,31,173,71,235,195,95,233,104,171,124,120,181,138,243,78,34,127,10,151,157,253,160,138,3,171,167,250,74,224,46,60,158,245,104,124,216,142,14,210,143,127,141,14,86,157,195,246,146,96,118,14,19,57,131,105,175,79,78,194,143,107,177,168,21,118,14,163,45,214,196,225,83,237,3,115,245,49,61,248,235,222,48,231,243,61,96,254,57,253,243,222,0,163,232,110,128,233,159,219,209,71,224,184,47,76,14,195,200,162,15,8,157,235,210,249,36,154,97,215,90,37,188,40,230,226,149,41,3,109,171,140,120,134,213,89,207,211,228,130,119,205,197,198,119,235,121,28,135,179,32,214,130,116,198,107,175,90,235,112,44,102,241,228,160,151,23,176,218,1,67,203,231,150,60,229,8,50,206,251,38,29,202,83,57,31,153,212,230,213,105,103,152,187,251,88,168,194,185,216,16,160,134,222,142,114,196,229,22,194,79,163,199,237,183,29,253,124,20,99,151,232,166,229,233,184,33,48,169,37,98,89,104,161,229,201,254,149,121,161,186,168,121,113,107,205,150,218,15,125,67,177,136,88,18,143,150,88,87,255,254,205,203,23,163,120,171,127,119,123,93,222,52,110,172,248,102,143,138,63,133,1,34,66,234,53,191,29,137,8,22,212,175,68,67,76,245,172,3,144,20,129,49,25,181,90,152,72,82,54,182,60,67,192,153,132,41,148,83,68,113,148,164,5,155,1,163,159,104,15,14,226,16,18,85,51,44,119,119,244,105,123,2,186,208,246,84,48,121,44,38,126,197,100,185,163,255,80,96,112,253,164,221,218,172,8,170,216,169,197,98,61,82,16,128,65,91,179,114,247,22,105,231,5,25,85,218,49,135,138,20,123,154,117,10,36,235,128,246,34,208,251,243,32,222,132,199,8,198,225,190,101,35,17,53,243,45,112,57,190,104,183,16,157,149,114,72,16,197,141,130,24,180,107,12,232,114,151,89,22,22,155,83,255,117,4,160,71,28,24,244,13,118,10,195,209,191,93,79,183,7,216,7,152,227,40,219,254,87,135,182,78,31,76,40,92,103,71,111,184,25,110,3,29,161,54,180,105,0,210,98,7,85,54,21,200,166,38,188,85,15,81,87,177,55,113,50,43,87,87,49,61,67,112,72,213,29,11,86,37,233,130,194,69,152,50,17,39,253,225,229,139,239,177,220,34,163,85,142,35,19,155,21,88,222,249,241,213,235,55,45,189,197,221,17,212,7,141,35,83,28,54,236,1,41,218,151,182,129,210,14,83,115,53,18,171,230,201,102,57,105,99,134,138,250,225,228,40,197,12,126,29,196,95,35,30,230,150,237,39,5,245,73,144,130,75,34,44,76,196,0,175,14,91,95,181,110,222,239,82,21,127,38,198,86,196,155,43,110,117,236,122,21,221,73,150,170,124,33,184,192,30,172,82,51,106,90,233,195,126,243,89,28,254,47,238,212,22,61,74,150,164,81,87,144,192,117,136,165,194,229,44,44,186,196,61,162,141,108,20,160,136,67,172,39,121,180,65,30,1,9,138,63,28,141,48,81,236,124,2,222,45,116,120,31,196,69,205,2,109,89,15,234,92,87,93,82,231,74,124,6,148,128,163,4,43,232,122,64,247,110,171,250,207,194,151,13,106,158,240,93,187,245,150,165,171,208,98,18,156,116,68,12,16,250,201,225,194,111,104,224,226,174,253,95,211,17,253,13,172,141,8,190,20,3,55,226,219,4,75,16,76,55,9,49,222,43,22,193,196,35,178,98,5,139,2,100,166,168,221,210,99,61,54,41,190,159,212,47,163,116,44,210,193,198,138,38,200,200,182,14,101,129,173,136,138,194,62,198,167,227,153,153,50,104,155,150,224,127,166,1,81,173,122,63,178,15,14,30,32,252,118,29,173,226,240,155,54,174,29,72,17,231,242,199,100,131,64,71,172,93,47,227,43,77,88,7,28,135,90,155,22,78,59,136,59,140,17,41,169,162,12,77,52,1,59,50,140,223,89,167,140,178,137,128,85,196,129,182,144,129,109,144,85,187,243,128,87,77,91,183,130,47,129,206,20,68,68,62,144,27,243,151,155,199,46,81,89,196,228,162,210,191,221,92,146,0,63,95,194,122,65,2,255,178,199,238,61,150,167,99,44,214,134,84,234,91,177,59,221,70,3,112,110,80,174,93,217,13,255,64,133,105,64,251,153,118,57,228,242,185,125,252,3,182,12,44,248,234,121,185,223,193,244,200,120,11,218,103,167,52,10,167,65,7,0,47,15,255,36,63,130,17,29,125,56,150,137,249,229,4,163,223,113,172,228,78,148,19,233,2,78,190,214,135,103,33,214,74,67,125,24,76,177,11,113,141,208,2,3,62,44,237,130,156,193,225,12,83,3,41,199,242,144,117,103,104,169,83,6,50,196,64,26,77,84,18,7,128,12,191,12,253,176,23,158,109,107,176,13,99,125,193,177,240,32,208,80,107,181,182,116,197,203,53,93,200,96,204,67,10,25,24,218,166,127,108,92,132,103,239,163,181,65,67,53,161,18,26,193,228,183,13,162,129,72,245,142,141,69,242,193,192,120,194,57,67,239,216,72,202,103,165,195,41,218,49,166,1,226,86,175,134,155,200,200,130,101,102,32,210,40,130,43,6,35,31,46,140,77,164,27,208,176,56,52,68,130,254,132,162,167,94,6,227,215,124,250,12,245,245,215,225,44,9,181,183,207,245,159,146,51,44,194,235,223,135,241,121,184,142,198,129,246,67,136,232,181,199,41,2,116,244,31,144,163,189,6,124,189,212,72,235,49,129,214,160,100,216,173,249,110,145,252,22,193,41,80,224,84,66,126,254,250,106,113,150,32,192,129,65,149,235,200,110,32,164,108,147,2,81,233,187,13,151,112,71,130,88,100,66,222,35,150,140,122,246,246,44,153,92,93,47,224,62,98,7,205,58,46,211,57,90,206,129,229,122,59,79,175,101,10,69,12,17,235,100,142,226,39,98,21,37,215,237,213,229,54,56,59,75,135,23,40,128,248,89,190,252,231,180,115,93,97,23,109,240,165,140,204,16,227,123,152,82,155,48,207,184,138,104,114,124,87,129,237,220,214,231,142,62,119,245,185,167,207,125,125,222,197,222,27,250,199,252,84,104,113,202,69,181,19,1,34,193,202,168,215,27,82,229,206,116,236,139,36,203,153,0,43,129,128,236,64,99,75,230,87,127,127,54,1,11,23,43,29,74,44,10,21,242,179,72,150,73,182,194,126,176,254,250,217,75,28,27,63,133,51,92,130,144,234,47,67,56,238,58,146,130,113,162,35,10,20,234,16,100,250,139,8,17,26,220,188,70,165,145,177,73,35,152,253,31,194,11,61,7,37,122,195,253,179,195,197,54,3,207,226,82,159,251,214,87,91,108,190,35,4,110,85,74,237,249,95,85,88,105,29,175,146,12,215,255,160,163,42,214,253,24,3,12,9,105,108,4,136,196,89,14,105,159,155,170,16,52,40,56,44,223,98,104,152,142,79,109,2,54,120,140,83,58,99,63,237,154,233,71,155,206,208,210,92,215,27,165,3,137,113,176,130,191,172,14,182,136,228,132,93,213,35,178,70,122,178,90,207,224,232,173,116,52,79,215,115,16,96,196,204,6,21,218,86,56,187,35,232,149,220,6,73,175,228,11,74,146,149,104,16,147,38,249,175,201,124,174,42,43,24,123,178,128,150,234,143,232,128,160,204,26,97,44,25,204,218,2,106,6,154,202,18,239,56,84,82,156,156,202,51,54,194,234,4,180,71,108,223,105,174,47,52,184,7,0,53,14,135,162,214,241,89,48,126,79,244,90,78,36,181,185,37,68,98,128,17,229,204,104,129,97,89,52,62,100,115,56,197,8,150,209,212,254,26,81,199,212,205,33,118,137,19,153,9,217,141,150,152,166,69,19,97,219,177,87,156,92,136,218,202,53,184,190,73,94,134,67,133,45,123,64,70,182,138,150,134,236,112,145,135,70,171,121,202,166,48,22,178,247,232,235,120,222,216,123,34,234,52,10,227,201,177,196,222,72,166,83,16,110,104,56,48,57,69,51,2,68,73,177,155,128,9,170,228,117,104,8,55,196,136,175,112,187,153,254,36,52,185,177,200,54,11,136,195,213,181,188,150,102,136,139,31,64,5,140,11,91,94,247,249,203,38,89,135,250,36,214,39,19,125,199,110,233,243,84,159,70,51,8,50,150,153,200,150,40,193,218,114,63,209,183,194,42,23,162,134,181,31,120,117,215,69,2,6,132,77,172,195,31,217,96,140,68,235,98,184,165,30,54,73,42,214,251,48,155,45,85,207,181,13,44,38,197,80,76,222,178,114,130,70,36,29,28,226,50,103,27,152,171,231,110,214,117,2,115,133,208,217,161,45,53,230,203,193,56,112,131,169,2,213,12,101,31,0,74,123,82,204,198,149,246,92,143,55,41,130,173,135,43,132,75,64,178,182,67,240,128,12,211,68,101,200,208,191,109,180,152,233,217,249,76,63,143,38,97,162,195,61,60,135,229,13,54,147,40,209,163,105,10,183,82,15,17,40,55,209,69,60,69,206,74,102,96,221,70,46,176,228,20,135,12,146,193,129,67,151,106,228,35,139,82,17,105,177,62,117,154,67,100,169,219,113,165,216,221,81,174,18,245,31,87,2,94,14,53,235,184,41,227,42,207,96,173,135,33,15,75,133,139,180,162,28,162,2,144,144,159,102,239,195,139,82,13,62,45,10,243,53,190,148,109,151,207,145,47,207,87,193,146,114,139,19,100,201,19,132,49,207,141,15,9,198,12,153,2,79,18,230,222,200,150,193,10,50,153,70,227,53,34,131,16,208,15,115,114,25,193,198,93,137,98,179,20,211,18,152,46,131,98,98,140,124,144,146,64,242,220,243,40,184,57,115,157,236,228,129,108,8,167,136,115,100,48,218,206,195,137,241,1,17,47,42,109,9,127,30,104,65,251,73,7,235,169,146,226,59,133,113,89,80,185,29,178,167,48,123,100,136,202,41,194,54,73,201,208,172,213,229,110,158,144,112,237,203,233,116,90,202,148,169,233,236,172,237,15,52,108,50,104,142,215,213,142,52,19,113,72,59,32,164,149,214,44,188,191,164,11,159,74,69,110,200,187,53,89,180,30,78,118,178,207,226,13,144,146,199,136,48,159,75,94,138,20,242,213,211,0,174,119,193,151,43,150,28,149,48,223,132,185,28,138,20,140,53,80,172,156,59,52,156,151,114,179,112,21,5,234,100,130,240,222,28,109,137,2,70,57,78,174,224,149,39,238,32,152,103,213,49,205,51,118,80,206,115,118,113,207,179,170,157,200,147,149,17,171,167,215,123,89,100,136,238,98,64,82,73,255,50,10,255,50,10,255,50,10,255,50,10,48,10,102,150,26,180,156,117,157,11,115,112,134,201,35,156,105,44,226,200,25,183,114,59,232,48,247,236,148,247,103,80,42,118,218,82,132,216,95,12,133,79,114,140,197,166,21,38,130,184,172,16,177,141,244,198,70,204,28,126,43,75,56,121,143,23,105,176,170,173,231,108,77,213,242,46,46,91,90,111,226,89,101,145,167,82,182,166,152,76,26,150,154,85,2,84,28,78,215,72,160,63,58,101,238,224,156,255,139,4,71,38,152,62,118,11,183,88,253,95,161,12,77,68,81,130,78,28,62,145,185,31,12,223,186,254,192,83,210,203,161,143,18,139,51,132,19,72,31,26,54,147,155,118,100,97,202,235,214,242,60,149,227,212,50,138,58,94,45,199,22,25,113,81,133,123,164,42,164,69,122,165,35,139,117,9,51,234,66,142,22,50,242,42,148,225,230,201,121,211,156,92,148,175,20,47,146,43,197,37,154,107,35,239,50,167,202,210,236,233,86,253,222,45,22,179,105,146,149,167,138,211,45,174,211,8,47,243,68,58,81,37,141,74,78,41,109,107,10,153,171,186,193,230,28,136,75,177,149,104,32,229,23,211,207,19,187,142,74,206,211,108,172,70,112,218,133,49,192,130,39,73,165,83,36,253,242,206,233,195,168,254,114,170,178,232,76,100,184,86,57,131,207,100,134,83,201,160,51,202,152,98,227,93,166,145,91,191,253,247,247,225,21,79,21,50,141,38,176,144,186,235,98,126,47,12,65,27,129,167,147,112,134,117,112,51,88,98,238,205,186,132,162,226,132,244,129,78,53,59,211,136,50,88,124,142,150,83,186,94,21,164,16,243,21,140,86,60,145,169,207,107,176,138,79,171,10,6,145,45,159,150,226,162,66,140,255,156,33,166,122,98,18,95,79,173,39,0,183,124,62,91,5,88,155,28,11,120,245,196,250,12,154,165,1,134,240,226,154,15,38,17,89,20,86,253,228,66,102,98,240,168,103,34,9,59,10,144,27,76,147,113,45,32,198,106,116,154,167,87,60,115,206,134,34,105,107,210,114,110,52,189,82,69,242,83,185,52,92,47,38,175,238,223,41,199,38,77,229,110,205,25,92,15,251,26,191,67,37,58,148,226,136,148,34,193,21,9,189,34,197,227,20,161,74,188,196,2,215,129,5,69,154,73,114,60,54,217,112,128,23,73,145,42,18,207,106,5,100,35,42,255,178,14,64,181,41,82,85,102,177,188,42,51,140,9,123,47,42,95,76,251,69,90,94,228,151,119,191,124,137,24,180,129,29,64,198,203,174,99,62,28,217,213,149,59,114,49,28,79,115,124,205,33,7,3,75,105,237,134,106,8,134,47,183,112,102,157,185,103,253,123,180,128,48,100,205,238,193,147,233,123,123,53,66,3,49,34,191,172,253,187,224,12,52,196,40,227,235,239,213,128,76,65,27,205,77,152,206,78,81,247,166,162,238,78,81,239,166,162,30,138,206,200,95,9,141,110,222,59,56,114,165,158,213,23,251,168,119,110,79,27,80,255,170,125,203,235,113,191,102,117,162,221,13,246,38,162,53,2,198,80,187,55,92,15,112,125,248,172,190,125,43,92,118,64,246,135,234,67,70,229,247,70,168,164,198,202,33,82,106,181,186,164,177,87,166,138,225,90,140,190,202,117,146,67,181,26,146,81,222,169,22,111,42,93,20,246,170,133,201,94,212,202,10,19,130,162,126,173,168,48,72,245,210,202,76,161,66,183,94,161,177,188,44,126,5,51,167,178,216,55,168,66,87,190,141,130,142,226,5,89,10,159,164,94,62,39,11,132,171,10,190,17,122,94,56,103,132,40,204,204,216,41,173,88,148,150,72,83,163,2,47,174,203,17,129,143,197,194,156,26,12,196,118,224,162,180,19,98,246,69,91,213,13,196,50,180,203,172,92,188,161,52,23,229,34,184,213,76,180,145,208,229,198,1,238,38,181,69,160,11,46,65,30,99,65,188,190,248,159,103,200,182,206,176,218,249,94,8,57,159,151,196,188,144,109,154,104,89,101,169,46,23,37,185,230,115,214,67,68,221,222,5,13,10,210,245,181,126,69,249,110,6,56,184,27,160,221,195,252,83,115,7,123,0,196,136,11,83,113,39,68,215,211,16,139,173,13,42,195,65,35,72,76,68,247,1,232,0,187,110,159,62,119,1,188,10,99,204,139,96,163,239,134,73,54,204,194,224,120,55,37,37,208,189,16,69,215,49,20,222,141,104,125,52,169,194,164,177,68,149,232,90,249,66,186,217,69,109,18,70,158,146,9,243,153,9,223,85,164,96,225,133,47,158,39,175,44,73,113,173,107,213,212,234,229,161,149,23,237,75,59,43,50,21,187,39,49,239,153,190,167,16,191,28,50,34,56,72,125,85,144,194,112,188,57,195,82,201,89,248,1,27,156,109,211,195,164,211,196,149,49,157,114,149,9,77,133,169,172,105,251,25,124,85,90,31,255,37,159,99,202,245,114,229,214,229,212,146,9,157,98,135,179,152,149,74,16,60,219,196,252,128,124,253,59,193,136,33,129,28,45,89,155,102,154,251,86,102,195,89,170,59,55,188,59,235,84,204,140,172,7,137,188,179,158,116,9,203,213,202,126,233,157,245,155,253,86,9,73,100,238,11,163,228,157,86,234,11,155,130,9,215,157,128,246,118,237,44,120,19,182,139,111,197,254,236,186,118,10,15,233,88,236,137,192,223,201,241,80,146,83,200,54,54,237,239,22,158,6,189,68,189,127,136,86,201,70,201,129,127,136,183,146,236,122,240,13,133,183,54,99,139,153,57,173,42,229,156,86,214,167,111,13,57,227,6,159,183,95,212,43,124,212,74,141,191,147,167,202,219,221,57,118,236,100,195,30,15,57,121,239,137,68,101,185,245,22,97,147,141,169,173,105,154,29,203,150,212,94,59,140,130,198,49,80,184,163,85,177,95,95,219,203,102,203,33,97,241,130,45,110,151,80,224,219,180,202,155,227,84,172,57,119,216,123,176,106,89,229,197,231,78,115,30,119,187,188,162,188,71,35,88,30,29,183,61,244,237,240,182,246,234,13,202,150,74,145,6,141,149,69,30,98,17,43,153,59,169,34,65,47,214,167,11,134,112,133,38,222,139,102,10,206,223,189,216,94,195,112,135,247,156,156,11,244,125,155,186,65,164,239,108,139,76,235,189,219,106,54,161,245,182,86,33,236,191,188,123,208,95,249,204,144,103,172,187,255,136,105,235,29,77,10,127,242,62,109,58,142,134,43,219,180,94,117,157,97,239,54,203,102,92,108,171,237,55,178,238,110,198,113,116,64,49,45,200,243,232,234,115,233,244,21,197,75,178,93,218,189,131,22,201,165,198,130,101,124,142,12,218,178,43,146,197,126,158,76,254,99,45,157,224,240,174,198,31,74,25,98,139,79,229,148,171,136,205,190,61,9,165,188,129,123,12,184,123,140,248,55,12,184,59,70,152,17,100,101,144,232,53,88,229,79,176,199,55,129,21,6,250,95,166,249,190,166,249,86,122,178,85,129,81,187,135,57,195,13,9,120,246,136,59,31,220,105,206,110,106,86,88,150,123,182,139,169,27,205,51,237,158,115,75,187,195,40,107,155,124,79,74,254,205,117,132,22,78,29,186,246,183,251,11,133,199,238,169,11,110,87,131,25,117,43,179,197,29,69,184,173,73,55,112,207,220,241,125,154,244,251,26,198,193,110,101,61,226,94,77,134,94,216,13,207,238,211,164,227,244,65,89,12,188,119,44,223,222,212,42,143,140,24,38,246,111,177,135,5,18,159,134,167,79,105,174,88,61,190,161,65,44,31,239,214,157,85,86,231,85,205,125,150,83,27,150,231,111,151,56,209,84,93,218,246,24,177,155,164,237,174,166,212,178,212,254,205,52,45,80,221,217,76,201,106,119,174,111,139,137,221,173,205,203,24,229,141,11,137,234,109,43,94,141,27,22,181,117,154,155,27,170,169,192,45,203,65,205,146,191,87,67,60,234,222,221,66,243,104,186,87,11,197,2,83,99,51,88,108,218,169,87,138,238,20,146,65,215,148,53,132,142,138,120,161,82,225,102,62,248,224,3,252,57,27,250,90,66,190,161,94,83,31,110,192,229,159,141,134,162,34,110,246,82,172,211,217,187,229,110,159,94,223,100,106,250,55,3,18,102,64,141,0,85,48,123,104,105,211,56,112,171,150,222,54,3,191,199,248,179,223,20,252,166,214,121,56,174,244,187,220,254,126,211,192,134,158,223,53,202,223,60,255,188,87,203,251,76,64,75,109,151,125,28,41,243,141,14,86,63,55,207,123,161,1,20,186,180,76,125,219,212,244,126,88,8,127,235,158,104,208,100,206,178,53,255,54,119,235,139,207,249,249,72,149,107,71,52,190,0,235,24,55,44,23,1,40,8,189,96,39,95,68,120,107,165,93,36,77,108,35,29,19,24,71,2,225,93,33,141,226,112,142,53,25,82,165,89,102,223,13,23,228,213,154,182,69,7,199,90,121,75,72,163,203,133,100,210,223,132,131,91,193,193,169,225,96,113,211,249,193,63,6,135,47,27,158,79,131,91,180,23,161,149,121,56,26,45,125,227,217,42,184,238,136,22,208,249,168,152,240,138,89,22,207,120,161,10,95,233,26,253,118,142,85,184,22,24,43,46,200,186,173,125,121,169,105,137,36,216,235,4,73,106,17,94,20,195,243,197,231,184,86,148,175,70,145,241,17,67,187,155,79,248,190,156,186,244,46,95,188,38,47,55,112,113,127,154,201,89,109,97,156,58,34,215,183,9,118,190,92,207,39,181,240,30,103,39,188,71,133,225,241,37,1,95,124,126,175,72,34,244,162,244,216,30,200,130,188,249,61,69,56,182,49,55,237,28,231,42,99,153,3,236,127,112,196,144,193,151,150,34,206,145,99,118,0,4,87,117,202,235,56,113,73,231,81,254,172,176,19,186,240,79,227,123,106,136,59,59,211,8,241,78,122,164,167,154,90,56,231,251,115,139,241,255,157,116,100,78,53,218,139,22,207,30,195,51,101,0,10,119,238,215,34,220,179,162,65,88,112,91,124,209,132,40,47,94,13,21,212,211,159,30,21,79,2,59,57,66,177,122,45,9,77,178,183,245,168,92,72,29,127,118,50,119,118,30,75,134,174,59,156,183,122,116,50,119,31,209,157,28,54,242,33,101,56,61,57,90,137,76,213,128,220,73,214,40,184,15,241,201,134,223,122,68,87,60,209,245,202,184,48,246,124,166,97,244,164,199,67,224,98,111,211,105,105,151,139,120,137,90,116,107,77,220,89,243,226,226,194,188,112,233,206,154,71,116,71,189,35,148,111,105,244,192,181,39,201,229,168,69,179,225,190,103,209,172,181,165,169,43,205,109,60,88,77,136,214,168,229,226,184,204,152,42,245,91,184,26,28,252,68,94,85,145,143,43,20,94,225,6,10,116,213,115,108,164,184,123,54,30,22,7,169,72,38,147,22,167,225,94,253,27,220,181,126,137,7,82,64,240,91,116,187,145,133,109,153,174,230,219,230,32,134,107,98,186,134,111,14,206,123,221,49,208,52,123,36,95,154,107,98,234,129,175,103,58,113,15,231,158,217,253,176,176,7,166,175,217,54,234,32,20,207,232,82,157,65,76,199,90,47,207,52,171,217,56,21,5,76,123,183,72,143,10,120,57,132,174,105,27,125,207,244,198,6,116,221,0,22,134,11,212,232,223,51,251,154,21,27,93,154,160,0,65,174,0,248,90,119,64,133,6,38,42,27,62,37,122,72,136,209,35,100,81,145,129,217,167,236,46,117,114,0,140,92,224,208,71,175,112,138,102,75,197,186,84,204,167,98,253,106,49,63,198,226,105,15,199,40,228,83,33,207,176,93,163,103,250,40,210,5,86,182,107,246,69,47,186,93,228,1,61,119,44,177,39,250,25,8,161,224,127,208,241,220,232,117,63,144,12,19,183,26,185,215,196,41,220,238,205,180,201,51,56,199,29,29,77,123,110,184,32,10,142,125,211,159,187,24,146,248,112,128,100,117,236,206,81,2,141,217,61,68,83,66,92,207,251,232,212,135,133,107,131,227,0,210,7,70,115,2,229,158,211,225,135,133,131,158,226,38,106,166,61,38,65,0,158,154,103,128,32,196,39,151,15,250,72,130,96,128,109,26,160,211,129,167,33,29,53,93,116,210,246,204,30,49,172,11,17,241,32,71,54,81,11,55,187,66,33,58,232,146,60,17,183,125,128,6,31,28,240,129,8,63,39,122,163,69,27,148,242,32,113,174,1,236,88,16,113,134,187,135,65,58,152,25,14,36,200,96,20,249,136,90,67,62,18,224,31,64,86,197,17,178,6,132,34,121,80,36,187,150,72,243,197,17,234,0,54,166,89,0,194,165,73,206,108,144,196,113,0,12,201,93,112,12,253,5,50,4,19,124,227,250,92,148,143,152,114,4,11,253,34,250,240,17,126,252,115,3,242,214,5,53,187,102,247,28,80,153,14,134,139,78,251,16,17,52,226,3,67,121,224,19,218,16,45,18,94,116,139,142,156,30,117,70,67,38,228,136,26,65,66,151,208,147,71,146,240,46,0,128,231,84,142,180,202,195,67,189,88,83,21,185,128,19,36,215,35,236,233,8,37,136,130,182,1,108,29,84,133,108,162,171,36,189,232,13,88,37,249,225,144,82,137,3,226,14,18,123,64,131,16,230,211,34,3,157,33,44,0,10,28,102,221,67,67,92,3,12,238,49,199,153,189,121,6,100,141,229,199,28,0,203,1,10,129,5,252,3,61,21,71,80,31,238,24,186,133,100,136,175,1,245,134,201,1,19,145,131,115,252,119,209,176,55,71,102,119,12,17,33,49,233,226,219,3,214,30,255,247,161,119,184,233,44,122,68,157,69,38,36,205,0,171,6,134,3,148,196,17,241,133,200,206,180,161,74,56,2,169,81,53,182,33,28,44,242,172,154,56,38,136,46,228,90,252,51,191,192,0,112,2,53,232,31,236,37,33,50,108,50,141,168,67,255,100,61,137,51,232,249,24,116,6,139,192,18,54,11,160,34,91,7,54,123,172,39,144,254,177,176,99,2,83,54,52,68,62,74,33,91,75,199,61,52,67,226,227,146,113,2,12,179,7,102,67,77,128,65,31,217,44,243,104,129,140,55,29,125,88,248,172,30,48,56,160,2,85,167,126,144,50,144,157,162,127,146,37,48,129,148,26,95,250,135,73,60,119,33,65,115,224,230,157,147,226,67,152,136,121,164,171,208,50,100,177,160,136,35,129,30,137,44,99,200,162,10,186,67,38,88,234,251,231,62,58,65,160,128,75,143,112,243,80,21,89,160,223,57,18,129,57,9,45,148,158,218,38,158,209,63,130,191,81,147,196,121,110,91,231,224,137,67,226,76,10,192,98,66,212,198,15,173,192,148,57,1,106,128,63,164,97,176,109,98,148,96,139,65,74,76,194,1,217,231,127,24,100,166,49,87,224,127,26,220,136,72,104,78,152,72,96,96,131,47,88,236,2,37,88,128,152,89,64,148,128,128,93,128,74,71,192,137,52,144,18,72,214,197,1,231,0,95,140,63,66,230,145,0,186,201,44,54,125,160,57,39,208,56,33,14,40,7,99,20,122,68,200,66,238,176,38,67,58,194,156,97,213,146,255,108,147,44,28,19,78,62,253,115,50,224,161,60,110,0,73,36,38,173,150,25,196,37,22,32,218,251,34,27,43,192,96,112,2,99,208,172,28,9,168,199,93,12,227,231,124,8,100,6,231,93,216,189,185,77,188,196,1,4,137,250,39,210,160,188,49,15,39,152,179,153,125,48,11,230,154,19,60,141,19,136,55,49,4,129,48,244,230,52,174,160,103,68,94,172,21,65,179,72,247,161,151,24,146,8,18,101,145,45,195,141,41,73,97,193,71,22,28,48,4,0,33,41,32,200,185,59,0,190,184,202,129,134,39,20,70,9,216,251,115,58,66,109,18,112,8,41,184,68,18,136,47,137,50,169,147,248,74,114,145,218,139,28,200,1,204,7,73,5,165,208,89,254,229,178,156,47,115,168,60,212,147,45,38,186,66,236,58,7,30,220,20,12,26,248,72,6,149,135,68,22,120,96,69,232,17,249,104,240,97,233,70,112,28,17,134,37,219,230,127,232,13,161,68,22,139,199,68,16,133,52,102,64,118,140,68,20,195,129,28,242,8,101,250,167,65,199,130,224,10,43,71,255,208,24,240,13,163,207,28,45,192,113,32,162,224,8,116,194,33,43,6,76,23,228,149,172,44,13,172,132,8,255,3,48,35,69,72,144,234,8,191,141,198,55,152,34,194,128,240,35,141,36,251,71,250,45,254,73,122,73,105,168,101,26,100,28,228,184,16,5,57,66,147,166,65,34,25,109,170,76,86,85,152,36,32,79,30,17,217,6,143,134,58,178,22,56,67,219,144,86,58,32,85,149,38,136,124,1,156,66,24,104,68,230,35,114,226,132,238,195,6,66,155,186,36,16,187,78,18,102,53,231,116,223,250,207,148,231,174,166,20,114,131,113,103,222,81,158,61,208,37,9,154,186,64,65,43,95,137,160,241,37,56,26,95,31,84,248,211,39,15,12,3,143,52,230,27,10,137,59,34,105,134,81,228,170,166,43,247,209,107,110,171,122,81,67,209,154,87,113,222,169,185,167,252,136,48,220,11,73,92,240,94,110,16,37,104,194,88,58,167,233,156,40,86,224,33,239,94,180,59,147,16,243,186,34,148,27,107,5,52,225,149,127,152,104,136,133,113,204,1,235,129,236,26,47,26,150,67,119,196,178,129,90,244,199,122,3,22,121,42,59,174,50,73,236,150,150,79,114,8,229,160,53,154,89,250,26,197,11,107,116,245,20,211,77,16,143,238,230,199,23,102,84,217,85,76,92,197,254,198,169,72,201,241,124,39,23,254,100,122,169,253,90,78,222,121,153,90,230,135,184,25,18,230,124,123,204,242,228,124,206,41,77,231,232,184,50,249,163,61,28,79,205,196,136,72,52,165,163,0,202,186,183,47,82,197,86,45,77,52,253,60,137,232,139,103,98,96,6,77,212,171,36,211,3,36,242,116,201,252,120,51,198,253,10,52,241,167,110,120,1,157,226,121,6,36,230,37,249,137,154,243,125,55,128,115,43,54,144,65,125,231,220,46,37,224,31,195,165,83,78,48,156,159,97,32,95,136,202,31,90,71,0,152,196,87,124,187,30,94,141,64,203,54,250,10,197,70,132,171,131,49,155,139,228,109,34,195,57,239,150,211,48,72,193,251,131,191,76,206,191,75,57,82,205,171,172,192,101,198,181,52,13,143,39,165,235,156,248,118,153,181,226,71,59,229,79,142,132,182,84,19,249,62,21,242,153,126,124,55,53,86,166,226,6,95,138,152,242,198,163,90,128,27,232,173,192,95,126,234,3,63,154,100,246,161,133,245,1,121,99,192,86,233,158,91,109,190,255,88,167,162,228,71,21,29,174,46,118,176,5,192,189,48,249,206,43,183,153,156,218,141,219,246,54,58,30,155,56,40,180,232,75,101,238,89,1,44,238,125,90,89,21,81,96,112,165,164,80,127,44,147,148,215,41,164,249,96,147,125,195,242,77,85,175,43,26,205,215,90,105,23,134,184,156,175,166,135,101,24,226,58,189,220,142,237,152,18,242,41,253,93,3,177,163,216,117,82,210,221,252,84,27,165,64,39,9,175,220,68,190,18,163,174,233,254,170,186,30,213,196,217,6,18,139,27,78,214,22,158,234,104,90,95,85,97,86,40,91,149,157,93,73,122,45,238,245,120,155,32,85,111,7,185,199,64,41,248,36,6,176,6,41,186,159,185,244,176,132,166,204,37,29,255,61,204,37,192,124,154,177,172,94,188,80,182,146,152,72,96,167,26,215,166,6,88,6,99,19,200,51,224,62,252,172,94,15,41,42,13,110,13,29,86,11,201,159,60,13,187,42,84,44,79,144,64,42,133,104,66,72,83,139,74,161,157,230,120,254,216,253,83,197,144,98,174,68,134,154,150,110,188,6,59,138,85,208,178,60,241,179,88,67,37,39,144,237,178,112,21,69,133,104,149,37,139,111,174,123,155,92,149,239,34,185,167,84,193,33,250,127,85,170,228,245,43,37,153,90,96,154,212,35,255,221,192,236,201,43,13,191,152,67,240,10,39,102,161,222,227,98,204,165,11,80,230,149,97,153,234,27,110,133,251,52,180,14,206,153,239,149,209,182,55,55,45,187,81,26,10,94,85,13,62,93,97,94,53,69,111,151,184,245,55,70,90,44,133,51,243,155,37,165,10,83,222,7,187,66,138,203,172,121,208,40,183,245,99,140,138,120,64,55,150,241,131,25,182,19,170,109,229,15,12,223,21,75,60,171,156,111,253,118,171,197,171,222,227,122,239,161,19,220,219,103,80,171,213,220,145,114,27,219,15,242,226,48,49,138,130,32,165,107,189,107,227,94,190,47,209,64,51,177,185,86,190,196,167,62,120,40,114,236,59,132,52,82,169,58,173,170,146,41,31,12,186,52,114,243,21,246,183,140,220,205,132,82,143,103,223,29,101,118,134,236,170,241,162,23,109,231,208,221,198,235,174,95,189,228,137,32,49,119,77,221,160,60,31,236,105,35,84,171,93,34,79,147,17,155,166,37,133,175,83,185,90,174,54,121,218,113,69,226,89,121,166,85,154,127,168,176,0,153,86,11,147,184,105,98,147,79,83,42,169,77,161,45,37,237,42,109,228,53,33,80,202,168,97,81,39,60,232,135,219,53,210,253,42,249,150,238,224,36,217,71,166,104,56,121,164,158,143,127,114,36,10,221,89,25,114,250,34,90,195,220,60,123,141,61,253,215,63,62,127,246,236,245,13,149,97,177,184,153,187,60,171,127,172,148,125,75,1,14,111,159,223,41,100,113,112,22,198,10,15,117,35,20,181,223,206,120,85,154,175,222,1,162,129,232,229,153,9,7,93,211,179,125,89,134,119,158,248,171,136,155,75,181,188,129,140,70,65,24,72,20,33,219,187,77,84,28,235,1,156,93,191,226,89,23,33,28,213,217,125,41,93,78,241,235,41,42,164,87,74,106,83,46,2,65,106,171,0,40,149,107,64,190,184,80,142,57,23,225,229,59,161,249,77,101,202,81,233,154,72,82,87,63,189,123,136,39,200,139,36,21,199,32,79,41,42,128,174,249,83,217,124,9,97,57,33,223,31,175,52,162,46,202,168,164,202,147,74,15,69,18,46,15,148,71,184,224,79,181,92,185,104,172,162,245,42,176,181,218,203,210,28,165,105,250,65,2,202,242,120,151,234,204,33,136,165,49,166,136,83,211,138,192,63,248,14,255,68,117,251,62,72,39,100,80,180,231,223,238,169,114,164,18,120,176,38,215,122,254,109,29,219,127,182,185,80,6,81,251,89,196,10,52,117,226,179,207,62,43,225,62,149,53,100,133,150,118,123,15,154,39,126,136,201,200,135,111,113,103,50,60,41,147,150,59,178,27,67,7,228,4,214,245,41,168,229,31,179,116,137,126,202,211,102,28,100,28,146,186,95,3,118,231,16,177,195,65,54,42,228,201,103,220,176,190,66,183,74,167,73,119,245,150,231,244,220,122,109,164,61,60,122,136,82,194,86,138,6,255,185,75,160,202,151,187,239,82,40,89,141,210,77,6,118,150,69,97,89,209,156,88,22,37,175,188,97,109,212,249,31,90,27,213,180,39,8,25,213,112,87,242,122,208,205,13,139,110,36,161,127,95,97,200,111,101,223,238,252,127,192,124,0,248,31,101,62,248,135,123,148,203,85,3,222,131,145,172,109,88,130,42,130,178,242,68,20,71,236,25,14,17,122,133,112,205,71,255,13,10,108,23,134,230,146,0,0}; diff --git a/Software/src/lib/ayushsharma82-ElegantOTA/src/elop.h b/Software/src/lib/ayushsharma82-ElegantOTA/src/elop.h index 6df00faf..525c16aa 100644 --- a/Software/src/lib/ayushsharma82-ElegantOTA/src/elop.h +++ b/Software/src/lib/ayushsharma82-ElegantOTA/src/elop.h @@ -3,6 +3,6 @@ #include -extern const uint8_t ELEGANT_HTML[41354]; +extern const uint8_t ELEGANT_HTML[11640]; #endif From 5b7491c7a71fc762b23de387ddd87074f391a745 Mon Sep 17 00:00:00 2001 From: Jonny Date: Fri, 12 Sep 2025 09:01:28 +0100 Subject: [PATCH 184/245] Switch T-2CAN back to dio, and try 16mb --- .github/workflows/release-assets.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-assets.yml b/.github/workflows/release-assets.yml index 1e433b97..74772519 100644 --- a/.github/workflows/release-assets.yml +++ b/.github/workflows/release-assets.yml @@ -64,7 +64,7 @@ jobs: - name: 🛠 Build factory image for Lilygo 2-CAN run: | - esptool --chip esp32s3 merge-bin -o .pio/build/lilygo_2CAN_330/factory.bin --flash-mode qio --flash-freq 80m --flash-size 4MB 0x0000 .pio/build/lilygo_2CAN_330/bootloader.bin 0x8000 .pio/build/lilygo_2CAN_330/partitions.bin 0xe000 ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin 0x10000 .pio/build/lilygo_2CAN_330/firmware.bin + esptool --chip esp32s3 merge-bin -o .pio/build/lilygo_2CAN_330/factory.bin --flash-mode dio --flash-freq 40m --flash-size 16MB 0x0000 .pio/build/lilygo_2CAN_330/bootloader.bin 0x8000 .pio/build/lilygo_2CAN_330/partitions.bin 0xe000 ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin 0x10000 .pio/build/lilygo_2CAN_330/firmware.bin mv .pio/build/lilygo_2CAN_330/factory.bin output/BE_${{ steps.vars.outputs.tag }}_LilygoT-2CAN.factory.bin - name: 🛠 Build ota image for Stark From 9554cbf808a2c31b9dcdeb957a922d2a3fe6efa6 Mon Sep 17 00:00:00 2001 From: Jonny Date: Fri, 12 Sep 2025 15:19:32 +0100 Subject: [PATCH 185/245] Include CAN interface number in logs (with distinct TX/RX) --- Software/src/battery/MEB-BATTERY.cpp | 2 +- Software/src/communication/can/comm_can.cpp | 26 ++++++++++++------- Software/src/communication/can/comm_can.h | 6 ++--- Software/src/communication/can/obd.cpp | 6 ++--- Software/src/communication/can/obd.h | 4 +-- Software/src/devboard/webserver/webserver.cpp | 2 +- test/emul/can.cpp | 4 +-- 7 files changed, 29 insertions(+), 21 deletions(-) diff --git a/Software/src/battery/MEB-BATTERY.cpp b/Software/src/battery/MEB-BATTERY.cpp index a20ccb2e..bda14b14 100644 --- a/Software/src/battery/MEB-BATTERY.cpp +++ b/Software/src/battery/MEB-BATTERY.cpp @@ -1271,7 +1271,7 @@ void MebBattery::handle_incoming_can_frame(CAN_frame rx_frame) { } break; case 0x18DAF105: - handle_obd_frame(rx_frame); + handle_obd_frame(rx_frame, can_interface); break; default: break; diff --git a/Software/src/communication/can/comm_can.cpp b/Software/src/communication/can/comm_can.cpp index 4696d8df..a9e3e615 100644 --- a/Software/src/communication/can/comm_can.cpp +++ b/Software/src/communication/can/comm_can.cpp @@ -228,11 +228,11 @@ bool init_CAN() { return true; } -void transmit_can_frame_to_interface(const CAN_frame* tx_frame, int interface) { +void transmit_can_frame_to_interface(const CAN_frame* tx_frame, CAN_Interface interface) { if (!allowed_to_send_CAN) { return; } - print_can_frame(*tx_frame, frameDirection(MSG_TX)); + print_can_frame(*tx_frame, interface, frameDirection(MSG_TX)); if (datalayer.system.info.CAN_SD_logging_active) { add_can_frame_to_buffer(*tx_frame, frameDirection(MSG_TX)); @@ -367,13 +367,20 @@ void receive_frame_canfd_addon() { // This section checks if we have a complete } // Support functions -void print_can_frame(CAN_frame frame, frameDirection msgDir) { +void print_can_frame(CAN_frame frame, CAN_Interface interface, frameDirection msgDir) { if (datalayer.system.info.CAN_usb_logging_active) { uint8_t i = 0; Serial.print("("); Serial.print(millis() / 1000.0); - (msgDir == MSG_RX) ? Serial.print(") RX0 ") : Serial.print(") TX1 "); + if (msgDir == MSG_RX) { + Serial.print(") RX"); + Serial.print((int)(interface * 2)); + } else { + Serial.print(") TX"); + Serial.print((int)(interface * 2) + 1); + } + Serial.print(" "); Serial.print(frame.ID, HEX); Serial.print(" ["); Serial.print(frame.DLC); @@ -388,7 +395,7 @@ void print_can_frame(CAN_frame frame, frameDirection msgDir) { } if (datalayer.system.info.can_logging_active) { // If user clicked on CAN Logging page in webserver, start recording - dump_can_frame(frame, msgDir); + dump_can_frame(frame, interface, msgDir); } } @@ -396,7 +403,7 @@ void map_can_frame_to_variable(CAN_frame* rx_frame, CAN_Interface interface) { if (interface != CANFD_NATIVE) { //Avoid printing twice due to receive_frame_canfd_addon sending to both FD interfaces //TODO: This check can be removed later when refactored to use inline functions for logging - print_can_frame(*rx_frame, frameDirection(MSG_RX)); + print_can_frame(*rx_frame, interface, frameDirection(MSG_RX)); } if (datalayer.system.info.CAN_SD_logging_active) { @@ -416,7 +423,7 @@ void map_can_frame_to_variable(CAN_frame* rx_frame, CAN_Interface interface) { } } -void dump_can_frame(CAN_frame& frame, frameDirection msgDir) { +void dump_can_frame(CAN_frame& frame, CAN_Interface interface, frameDirection msgDir) { char* message_string = datalayer.system.info.logged_can_messages; int offset = datalayer.system.info.logged_can_messages_offset; // Keeps track of the current position in the buffer size_t message_string_size = sizeof(datalayer.system.info.logged_can_messages); @@ -430,8 +437,9 @@ void dump_can_frame(CAN_frame& frame, frameDirection msgDir) { offset += snprintf(message_string + offset, message_string_size - offset, "(%lu.%03lu) ", currentTime / 1000, currentTime % 1000); - // Add direction. The 0 and 1 after RX and TX ensures that SavvyCAN puts TX and RX in a different bus. - offset += snprintf(message_string + offset, message_string_size - offset, "%s ", (msgDir == MSG_RX) ? "RX0" : "TX1"); + // Add direction. Multiplying the interface by two ensures that SavvyCAN puts TX and RX in a different bus. + offset += snprintf(message_string + offset, message_string_size - offset, "%s%d ", (msgDir == MSG_RX) ? "RX" : "TX", + (int)(interface * 2) + (msgDir == MSG_RX ? 0 : 1)); // Add ID and DLC offset += snprintf(message_string + offset, message_string_size - offset, "%X [%u] ", frame.ID, frame.DLC); diff --git a/Software/src/communication/can/comm_can.h b/Software/src/communication/can/comm_can.h index 2efad224..e77e86c2 100644 --- a/Software/src/communication/can/comm_can.h +++ b/Software/src/communication/can/comm_can.h @@ -7,8 +7,8 @@ extern bool use_canfd_as_can; extern uint8_t user_selected_can_addon_crystal_frequency_mhz; extern uint8_t user_selected_canfd_addon_crystal_frequency_mhz; -void dump_can_frame(CAN_frame& frame, frameDirection msgDir); -void transmit_can_frame_to_interface(const CAN_frame* tx_frame, int interface); +void dump_can_frame(CAN_frame& frame, CAN_Interface interface, frameDirection msgDir); +void transmit_can_frame_to_interface(const CAN_frame* tx_frame, CAN_Interface interface); //These defines are not used if user updates values via Settings page #define CRYSTAL_FREQUENCY_MHZ 8 @@ -94,7 +94,7 @@ void receive_frame_canfd_addon(); * * @return void */ -void print_can_frame(CAN_frame frame, frameDirection msgDir); +void print_can_frame(CAN_frame frame, CAN_Interface interface, frameDirection msgDir); // Stop/pause CAN communication for all interfaces void stop_can(); diff --git a/Software/src/communication/can/obd.cpp b/Software/src/communication/can/obd.cpp index ec2bc49b..26aab251 100644 --- a/Software/src/communication/can/obd.cpp +++ b/Software/src/communication/can/obd.cpp @@ -23,7 +23,7 @@ void show_dtc(uint8_t byte0, uint8_t byte1) { logging.printf("%c%d\n", letter, ((byte0 & 0x3F) << 8) | byte1); } -void handle_obd_frame(CAN_frame& rx_frame) { +void handle_obd_frame(CAN_frame& rx_frame, CAN_Interface interface) { if (rx_frame.data.u8[1] == 0x7F) { const char* error_str = "?"; switch (rx_frame.data.u8[3]) { // See https://automotive.wiki/index.php/ISO_14229 @@ -105,10 +105,10 @@ void handle_obd_frame(CAN_frame& rx_frame) { logging.printf("ODBx reply frame received:\n"); } } - dump_can_frame(rx_frame, MSG_RX); + dump_can_frame(rx_frame, interface, MSG_RX); } -void transmit_obd_can_frame(unsigned int address, int interface, bool canFD) { +void transmit_obd_can_frame(unsigned int address, CAN_Interface interface, bool canFD) { static CAN_frame OBD_frame; OBD_frame.FD = canFD; OBD_frame.ID = address; diff --git a/Software/src/communication/can/obd.h b/Software/src/communication/can/obd.h index 2a495820..38671d4a 100644 --- a/Software/src/communication/can/obd.h +++ b/Software/src/communication/can/obd.h @@ -3,8 +3,8 @@ #include "comm_can.h" -void handle_obd_frame(CAN_frame& rx_frame); +void handle_obd_frame(CAN_frame& rx_frame, CAN_Interface interface); -void transmit_obd_can_frame(unsigned int address, int interface, bool canFD); +void transmit_obd_can_frame(unsigned int address, CAN_Interface interface, bool canFD); #endif // _OBD_H_ diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 74599e62..5a58b66a 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -157,7 +157,7 @@ void canReplayTask(void* param) { (datalayer.system.info.can_replay_interface == CANFD_ADDON_MCP2518); currentFrame.ext_ID = (currentFrame.ID > 0x7F0); - transmit_can_frame_to_interface(¤tFrame, datalayer.system.info.can_replay_interface); + transmit_can_frame_to_interface(¤tFrame, (CAN_Interface)datalayer.system.info.can_replay_interface); } } while (datalayer.system.info.loop_playback); diff --git a/test/emul/can.cpp b/test/emul/can.cpp index 8beb1574..2903a1c6 100644 --- a/test/emul/can.cpp +++ b/test/emul/can.cpp @@ -1,7 +1,7 @@ #include "../../Software/src/communication/Transmitter.h" #include "../../Software/src/communication/can/comm_can.h" -void transmit_can_frame_to_interface(const CAN_frame* tx_frame, int interface) {} +void transmit_can_frame_to_interface(const CAN_frame* tx_frame, CAN_Interface interface) {} void register_can_receiver(CanReceiver* receiver, CAN_Interface interface, CAN_Speed speed) {} @@ -19,4 +19,4 @@ char const* getCANInterfaceName(CAN_Interface) { void register_transmitter(Transmitter* transmitter) {} -void dump_can_frame(CAN_frame& frame, frameDirection msgDir) {} +void dump_can_frame(CAN_frame& frame, CAN_Interface interface, frameDirection msgDir) {} From 5510d3aeb5e83dd3071c8a3b9651c52306fc89f2 Mon Sep 17 00:00:00 2001 From: Jonny Date: Fri, 12 Sep 2025 20:07:25 +0100 Subject: [PATCH 186/245] Wait at most 100ms for Serial in init_serial on 2CAN so it will boot without USB --- Software/Software.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Software/Software.cpp b/Software/Software.cpp index 95f25ed8..19cb5382 100644 --- a/Software/Software.cpp +++ b/Software/Software.cpp @@ -67,7 +67,18 @@ void register_transmitter(Transmitter* transmitter) { void init_serial() { // Init Serial monitor Serial.begin(115200); +#if HW_LILYGO2CAN + // Wait up to 100ms for Serial to be available. On the ESP32S3 Serial is + // provided by the USB controller, so will only work if the board is connected + // to a computer. + for (int i = 0; i < 10; i++) { + if (Serial) + break; + delay(10); + } +#else while (!Serial) {} +#endif } void connectivity_loop(void*) { From 7178e0376ef0a74a1c2c34254719bdb4182f3e7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Fri, 12 Sep 2025 22:19:44 +0300 Subject: [PATCH 187/245] Reduce CAN messages used by Kia PHEV --- .../battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp | 16 ++++++++---- .../src/battery/KIA-HYUNDAI-HYBRID-BATTERY.h | 25 ++++--------------- 2 files changed, 16 insertions(+), 25 deletions(-) diff --git a/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp b/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp index 38c32872..d17b4aca 100644 --- a/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp +++ b/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp @@ -201,15 +201,21 @@ void KiaHyundaiHybridBattery::transmit_can(unsigned long currentMillis) { } poll_data_pid++; if (poll_data_pid == 1) { - transmit_can_frame(&KIA_7E4_id1); + KIA_7E4.data.u8[2] = 0x01; + KIA_7E4.data.u8[3] = 0x00; + transmit_can_frame(&KIA_7E4); } else if (poll_data_pid == 2) { - transmit_can_frame(&KIA_7E4_id2); + KIA_7E4.data.u8[2] = 0x02; + transmit_can_frame(&KIA_7E4); } else if (poll_data_pid == 3) { - transmit_can_frame(&KIA_7E4_id3); + KIA_7E4.data.u8[2] = 0x03; + transmit_can_frame(&KIA_7E4); } else if (poll_data_pid == 4) { - + //Group 4 not polled } else if (poll_data_pid == 5) { - transmit_can_frame(&KIA_7E4_id5); + KIA_7E4.data.u8[2] = 0x05; + KIA_7E4.data.u8[3] = 0x04; + transmit_can_frame(&KIA_7E4); } } } diff --git a/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.h b/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.h index 3bd9f499..e1b5366a 100644 --- a/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.h +++ b/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.h @@ -34,26 +34,11 @@ class KiaHyundaiHybridBattery : public CanBattery { uint16_t min_cell_voltage_mv = 3700; uint16_t max_cell_voltage_mv = 3700; - CAN_frame KIA_7E4_id1 = {.FD = false, - .ext_ID = false, - .DLC = 8, - .ID = 0x7E4, - .data = {0x02, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}}; - CAN_frame KIA_7E4_id2 = {.FD = false, - .ext_ID = false, - .DLC = 8, - .ID = 0x7E4, - .data = {0x02, 0x21, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00}}; - CAN_frame KIA_7E4_id3 = {.FD = false, - .ext_ID = false, - .DLC = 8, - .ID = 0x7E4, - .data = {0x02, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}}; - CAN_frame KIA_7E4_id5 = {.FD = false, - .ext_ID = false, - .DLC = 8, - .ID = 0x7E4, - .data = {0x02, 0x21, 0x05, 0x04, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame KIA_7E4 = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x7E4, + .data = {0x02, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}}; CAN_frame KIA_7E4_ack = {.FD = false, .ext_ID = false, .DLC = 8, From 2546b6da21ac11709d7627d1a6de94e7cff847b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Fri, 12 Sep 2025 22:32:03 +0300 Subject: [PATCH 188/245] Reduce CAN templates in BYD-CAN --- Software/src/inverter/BYD-CAN.cpp | 12 ++++++++---- Software/src/inverter/BYD-CAN.h | 25 +++++-------------------- 2 files changed, 13 insertions(+), 24 deletions(-) diff --git a/Software/src/inverter/BYD-CAN.cpp b/Software/src/inverter/BYD-CAN.cpp index a779a270..10c4a3a0 100644 --- a/Software/src/inverter/BYD-CAN.cpp +++ b/Software/src/inverter/BYD-CAN.cpp @@ -166,8 +166,12 @@ void BydCanInverter::send_initial_data() { transmit_can_frame(&BYD_250); transmit_can_frame(&BYD_290); transmit_can_frame(&BYD_2D0); - transmit_can_frame(&BYD_3D0_0); - transmit_can_frame(&BYD_3D0_1); - transmit_can_frame(&BYD_3D0_2); - transmit_can_frame(&BYD_3D0_3); + BYD_3D0.data = {0x00, 0x42, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79}; //Battery + transmit_can_frame(&BYD_3D0); + BYD_3D0.data = {0x01, 0x2D, 0x42, 0x6F, 0x78, 0x20, 0x50, 0x72}; //-Box Pr + transmit_can_frame(&BYD_3D0); + BYD_3D0.data = {0x02, 0x65, 0x6D, 0x69, 0x75, 0x6D, 0x20, 0x48}; //emium H + transmit_can_frame(&BYD_3D0); + BYD_3D0.data = {0x03, 0x56, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00}; //VS + transmit_can_frame(&BYD_3D0); } diff --git a/Software/src/inverter/BYD-CAN.h b/Software/src/inverter/BYD-CAN.h index 4eb63918..e7af38c5 100644 --- a/Software/src/inverter/BYD-CAN.h +++ b/Software/src/inverter/BYD-CAN.h @@ -40,26 +40,11 @@ class BydCanInverter : public CanInverterProtocol { .DLC = 8, .ID = 0x2D0, .data = {0x00, 0x42, 0x59, 0x44, 0x00, 0x00, 0x00, 0x00}}; //BYD - CAN_frame BYD_3D0_0 = {.FD = false, - .ext_ID = false, - .DLC = 8, - .ID = 0x3D0, - .data = {0x00, 0x42, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79}}; //Battery - CAN_frame BYD_3D0_1 = {.FD = false, - .ext_ID = false, - .DLC = 8, - .ID = 0x3D0, - .data = {0x01, 0x2D, 0x42, 0x6F, 0x78, 0x20, 0x50, 0x72}}; //-Box Pr - CAN_frame BYD_3D0_2 = {.FD = false, - .ext_ID = false, - .DLC = 8, - .ID = 0x3D0, - .data = {0x02, 0x65, 0x6D, 0x69, 0x75, 0x6D, 0x20, 0x48}}; //emium H - CAN_frame BYD_3D0_3 = {.FD = false, - .ext_ID = false, - .DLC = 8, - .ID = 0x3D0, - .data = {0x03, 0x56, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00}}; //VS + CAN_frame BYD_3D0 = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x3D0, + .data = {0x00, 0x42, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79}}; //Battery //Actual content messages CAN_frame BYD_110 = {.FD = false, .ext_ID = false, From fdc1fb61ba162b53d5b72537ceca5c7e12454f3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Fri, 12 Sep 2025 22:39:01 +0300 Subject: [PATCH 189/245] Improve Afore writing of name --- Software/src/inverter/AFORE-CAN.cpp | 9 ++++----- Software/src/inverter/AFORE-CAN.h | 5 ----- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/Software/src/inverter/AFORE-CAN.cpp b/Software/src/inverter/AFORE-CAN.cpp index a195e4ab..ade4756b 100644 --- a/Software/src/inverter/AFORE-CAN.cpp +++ b/Software/src/inverter/AFORE-CAN.cpp @@ -139,11 +139,10 @@ void AforeCanInverter::map_can_frame_to_variable(CAN_frame rx_frame) { switch (rx_frame.ID) { case 0x305: // Every 1s from inverter datalayer.system.status.CAN_inverter_still_alive = CAN_STILL_ALIVE; - char0 = rx_frame.data.u8[0]; // A - char1 = rx_frame.data.u8[0]; // F - char2 = rx_frame.data.u8[0]; // O - char3 = rx_frame.data.u8[0]; // R - char4 = rx_frame.data.u8[0]; // E + for (uint8_t i = 0; i < 5; i++) { + datalayer.system.info.inverter_brand[i] = rx_frame.data.u8[i]; + } + datalayer.system.info.inverter_brand[7] = '\0'; inverter_status = rx_frame.data.u8[7]; time_to_send_info = true; break; diff --git a/Software/src/inverter/AFORE-CAN.h b/Software/src/inverter/AFORE-CAN.h index 96e59ec6..09f9eb09 100644 --- a/Software/src/inverter/AFORE-CAN.h +++ b/Software/src/inverter/AFORE-CAN.h @@ -16,11 +16,6 @@ class AforeCanInverter : public CanInverterProtocol { uint8_t inverter_status = 0; //0 = init, 1 = standby, 2 = starting, 3 = grid connected, 4 off-grid, 5 diesel generator, 6 grid connected, but disconnected, 7off grid and disconnected, 8 = power failure processing, 9 = power off, 10 = Failure bool time_to_send_info = false; - uint8_t char0 = 0; - uint8_t char1 = 0; - uint8_t char2 = 0; - uint8_t char3 = 0; - uint8_t char4 = 0; //Actual content messages CAN_frame AFORE_350 = {.FD = false, // Operation information .ext_ID = false, From bf14553d777621d1f20fde0746181681600be463 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sat, 13 Sep 2025 10:53:11 +0300 Subject: [PATCH 190/245] Add notes on BYD messages --- Software/src/inverter/BYD-CAN.cpp | 14 +++++++++----- Software/src/inverter/BYD-CAN.h | 10 +++++----- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/Software/src/inverter/BYD-CAN.cpp b/Software/src/inverter/BYD-CAN.cpp index 10c4a3a0..e57b2c6c 100644 --- a/Software/src/inverter/BYD-CAN.cpp +++ b/Software/src/inverter/BYD-CAN.cpp @@ -69,6 +69,10 @@ void BydCanInverter:: BYD_150.data.u8[6] = (fully_charged_capacity_ah >> 8); BYD_150.data.u8[7] = (fully_charged_capacity_ah & 0x00FF); + //Alarms + //TODO: BYD Alarms are not implemented yet. Investigation needed on the bits in this message + //BYD_190.data.u8[0] = + //Voltage (ex 370.0) BYD_1D0.data.u8[0] = (datalayer.battery.status.voltage_dV >> 8); BYD_1D0.data.u8[1] = (datalayer.battery.status.voltage_dV & 0x00FF); @@ -144,21 +148,21 @@ void BydCanInverter::transmit_can(unsigned long currentMillis) { if (currentMillis - previousMillis2s >= INTERVAL_2_S) { previousMillis2s = currentMillis; - transmit_can_frame(&BYD_110); + transmit_can_frame(&BYD_110); //Send Limits } // Send 10s CAN Message if (currentMillis - previousMillis10s >= INTERVAL_10_S) { previousMillis10s = currentMillis; - transmit_can_frame(&BYD_150); - transmit_can_frame(&BYD_1D0); - transmit_can_frame(&BYD_210); + transmit_can_frame(&BYD_150); //Send States + transmit_can_frame(&BYD_1D0); //Send Battery Info + transmit_can_frame(&BYD_210); //Send Cell Info } //Send 60s message if (currentMillis - previousMillis60s >= INTERVAL_60_S) { previousMillis60s = currentMillis; - transmit_can_frame(&BYD_190); + transmit_can_frame(&BYD_190); //Send Alarm } } diff --git a/Software/src/inverter/BYD-CAN.h b/Software/src/inverter/BYD-CAN.h index e7af38c5..09538ad3 100644 --- a/Software/src/inverter/BYD-CAN.h +++ b/Software/src/inverter/BYD-CAN.h @@ -46,27 +46,27 @@ class BydCanInverter : public CanInverterProtocol { .ID = 0x3D0, .data = {0x00, 0x42, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79}}; //Battery //Actual content messages - CAN_frame BYD_110 = {.FD = false, + CAN_frame BYD_110 = {.FD = false, //Limits .ext_ID = false, .DLC = 8, .ID = 0x110, .data = {0x01, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - CAN_frame BYD_150 = {.FD = false, + CAN_frame BYD_150 = {.FD = false, //States .ext_ID = false, .DLC = 8, .ID = 0x150, .data = {0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00}}; - CAN_frame BYD_190 = {.FD = false, + CAN_frame BYD_190 = {.FD = false, //Alarm .ext_ID = false, .DLC = 8, .ID = 0x190, .data = {0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}}; - CAN_frame BYD_1D0 = {.FD = false, + CAN_frame BYD_1D0 = {.FD = false, //Battery Info .ext_ID = false, .DLC = 8, .ID = 0x1D0, .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x08}}; - CAN_frame BYD_210 = {.FD = false, + CAN_frame BYD_210 = {.FD = false, //Cell info .ext_ID = false, .DLC = 8, .ID = 0x210, From b5d14edb9407a8f5f7f7bb2062073e4ea9e8574c Mon Sep 17 00:00:00 2001 From: korhojoa Date: Sat, 13 Sep 2025 17:28:47 +0300 Subject: [PATCH 191/245] Repackage OTA gzip with zopfli --- Software/src/lib/ayushsharma82-ElegantOTA/src/elop.cpp | 2 +- Software/src/lib/ayushsharma82-ElegantOTA/src/elop.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Software/src/lib/ayushsharma82-ElegantOTA/src/elop.cpp b/Software/src/lib/ayushsharma82-ElegantOTA/src/elop.cpp index 4a00db1f..6bab7982 100644 --- a/Software/src/lib/ayushsharma82-ElegantOTA/src/elop.cpp +++ b/Software/src/lib/ayushsharma82-ElegantOTA/src/elop.cpp @@ -1,3 +1,3 @@ #include "elop.h" -const uint8_t ELEGANT_HTML[11640] PROGMEM = {31,139,8,0,128,47,232,102,0,255,237,125,249,127,219,70,146,239,207,201,231,147,255,1,102,54,50,25,1,16,78,94,18,157,181,157,120,226,93,59,206,139,237,204,225,104,178,16,9,146,136,65,130,3,144,58,44,115,254,246,253,86,117,55,46,66,18,229,57,118,223,123,195,19,232,163,186,186,174,174,238,46,0,39,15,38,201,120,125,181,10,181,249,122,17,63,250,226,243,19,250,215,226,96,57,27,181,194,101,11,41,154,118,50,15,131,9,31,225,120,17,174,3,109,60,15,210,44,92,143,90,111,223,60,51,250,45,237,168,146,187,12,22,225,168,117,30,133,23,171,36,93,183,180,113,178,92,135,75,148,190,136,38,235,249,104,18,158,71,227,208,224,19,93,139,150,209,58,10,98,35,27,7,113,56,178,77,171,4,109,29,173,227,240,209,147,96,189,14,211,43,237,187,197,38,14,214,73,170,189,122,243,88,123,187,154,4,235,240,228,72,20,1,222,217,56,141,86,107,28,197,225,90,139,147,96,18,45,103,223,70,231,111,194,203,181,54,210,90,47,68,138,105,154,173,227,47,62,15,178,171,229,88,155,110,150,227,117,148,44,209,157,112,252,94,129,127,189,14,214,155,236,167,240,44,73,214,237,142,118,253,197,231,159,161,3,217,90,67,131,1,64,5,23,65,180,214,102,225,250,91,156,183,31,30,253,46,92,63,139,210,197,69,144,134,207,151,211,228,97,7,240,63,139,166,90,155,202,139,250,159,129,200,155,5,72,96,162,218,119,113,72,135,79,174,158,79,218,15,167,178,230,207,97,154,1,147,135,29,115,13,132,159,10,130,161,49,130,97,170,66,4,248,102,80,96,201,132,145,248,182,25,138,202,103,40,217,58,89,73,146,124,55,13,199,232,40,39,167,33,184,250,51,24,135,243,207,240,66,218,86,11,227,44,20,221,56,58,42,152,176,76,214,90,178,140,163,101,168,107,239,195,112,165,17,89,0,142,161,135,235,55,209,34,76,54,235,246,141,164,213,53,199,178,172,206,177,6,160,79,169,144,22,204,130,104,9,121,208,28,45,11,65,242,73,70,205,127,241,249,22,168,229,172,202,214,65,186,174,98,126,43,133,91,41,183,134,210,198,34,204,178,96,22,182,106,212,169,202,202,237,52,46,128,145,76,3,221,48,5,177,199,113,144,101,47,162,108,109,166,225,34,57,15,193,139,104,50,9,193,205,219,161,73,189,168,64,8,38,229,102,94,9,24,32,66,137,0,117,206,221,46,97,119,161,204,13,126,58,190,170,199,183,163,12,158,9,174,255,30,66,34,48,222,85,81,81,162,80,82,165,118,209,185,77,50,124,19,86,217,102,60,6,103,159,38,241,102,33,187,32,21,240,1,85,45,225,42,9,144,21,253,149,234,89,146,215,29,249,210,53,155,229,116,127,185,246,85,249,29,217,133,209,249,158,91,126,74,56,85,141,203,29,189,92,165,201,12,218,89,233,102,94,211,185,173,102,192,141,103,79,54,235,53,254,184,166,50,80,251,145,71,227,38,204,108,125,21,135,230,36,202,86,113,112,53,122,120,22,39,227,247,15,171,6,162,185,228,50,89,134,92,80,144,67,96,157,156,101,97,122,30,166,192,124,25,94,104,47,55,32,33,208,124,37,147,219,32,206,232,145,228,78,157,108,36,94,244,83,133,244,12,35,73,54,227,113,1,188,219,7,110,93,44,21,220,218,232,160,76,125,26,130,217,209,121,248,99,176,158,51,101,52,109,141,97,137,15,52,26,228,128,12,88,180,194,65,152,143,19,211,112,61,158,87,171,162,5,170,64,28,80,197,205,228,125,7,40,49,90,13,131,77,94,236,183,44,89,2,79,178,153,63,210,8,92,180,23,100,218,127,188,126,245,131,48,226,235,77,186,100,8,82,10,53,109,171,141,3,32,162,181,195,52,77,82,137,189,6,56,212,90,2,110,113,122,251,225,51,66,87,227,147,225,67,93,28,40,132,37,220,229,38,142,57,69,242,243,34,90,78,146,11,19,99,1,180,6,40,23,20,166,145,88,141,58,218,168,245,13,43,53,37,170,1,77,165,225,131,30,189,70,78,160,173,133,130,105,201,20,138,167,6,2,173,77,74,104,105,139,40,142,35,153,6,130,21,116,87,181,4,215,127,76,147,5,138,181,219,191,234,192,250,55,104,49,227,36,42,208,171,164,200,2,93,81,170,77,149,191,19,148,248,41,252,203,38,148,144,39,26,74,66,29,132,45,176,100,203,66,147,152,138,218,79,193,56,212,206,194,245,69,24,230,2,163,5,203,137,182,158,135,10,57,81,75,226,102,166,168,209,126,119,139,27,161,171,122,167,178,61,122,153,128,183,100,207,66,18,89,101,40,137,202,157,142,114,6,189,254,110,46,200,222,128,239,225,144,148,225,109,201,156,144,28,11,117,248,116,223,169,37,133,157,69,236,211,28,167,26,8,86,37,137,101,153,41,172,91,162,104,110,93,254,22,188,37,227,255,54,204,203,64,10,172,25,34,190,205,14,224,167,143,69,202,10,155,242,128,7,23,93,187,214,224,188,167,209,217,102,29,102,67,152,203,13,188,197,60,229,89,20,195,177,31,106,239,30,242,32,244,240,84,226,183,223,184,214,48,238,55,15,5,101,148,156,79,67,137,236,220,209,17,77,85,32,186,233,123,109,19,125,241,121,142,152,58,144,216,213,124,171,22,85,104,17,16,12,152,65,252,26,238,2,156,80,210,99,154,36,113,38,242,78,142,242,249,11,207,124,196,153,70,51,179,81,107,145,76,54,113,136,121,84,154,100,89,146,70,179,104,137,114,109,53,64,193,159,18,20,155,142,114,156,198,105,136,233,145,196,168,221,130,155,14,28,224,172,197,132,215,113,52,109,79,15,14,166,102,182,89,209,12,45,43,31,183,101,115,43,20,134,124,180,58,29,97,247,143,167,176,138,162,157,128,140,115,222,20,204,100,122,245,58,140,33,68,73,250,56,142,219,15,169,181,119,168,174,48,87,160,78,97,66,39,237,160,115,220,56,50,7,163,71,215,69,27,99,106,35,232,0,211,177,201,84,24,141,90,227,121,20,79,168,3,173,78,81,48,162,130,99,34,117,56,249,33,153,132,89,39,50,215,193,236,7,154,131,162,206,139,231,63,252,103,235,224,32,162,190,211,121,21,163,131,131,73,59,234,108,59,133,136,200,94,233,215,121,99,195,7,150,158,109,206,214,105,24,226,16,226,80,248,115,232,139,36,253,120,116,189,61,150,3,100,96,70,208,194,89,26,173,175,14,14,128,126,126,54,42,229,116,244,0,40,77,97,51,194,244,199,36,142,198,162,108,53,9,21,170,9,84,139,165,224,21,75,1,117,104,147,133,6,184,13,239,136,166,209,89,235,155,49,49,95,157,142,90,209,114,28,111,38,97,107,184,83,51,128,99,118,181,72,54,187,117,48,66,173,91,195,90,98,6,138,26,66,250,90,250,120,155,83,129,56,122,13,62,5,102,184,82,178,66,199,163,7,214,177,34,14,81,234,88,184,66,176,251,232,147,62,238,108,183,29,24,157,6,165,0,106,172,22,31,63,62,104,183,56,9,157,192,148,190,40,216,57,56,144,94,199,130,44,239,203,112,18,5,237,86,27,92,5,177,50,76,116,226,36,197,130,2,85,29,178,194,118,32,252,92,52,204,190,201,121,188,159,226,234,55,206,44,41,255,37,36,142,103,175,103,201,37,218,224,89,65,56,65,215,59,195,125,218,145,179,167,191,169,41,187,115,124,159,122,232,218,119,231,40,64,237,135,152,12,182,91,99,72,214,251,150,94,50,39,159,128,199,189,201,122,163,53,252,36,202,53,65,139,163,217,28,166,162,172,177,143,219,113,231,90,170,105,124,112,16,155,191,254,26,102,232,16,76,194,193,193,171,51,242,1,77,140,110,235,132,76,14,28,148,236,213,197,18,14,219,42,76,215,87,24,229,97,220,98,189,53,9,167,193,38,6,228,111,98,83,30,15,227,237,121,144,106,47,70,215,225,37,91,209,225,245,118,171,95,86,78,143,203,6,155,74,195,62,62,126,242,244,219,239,158,253,238,251,231,255,241,159,47,94,254,240,234,199,255,243,211,235,55,111,127,254,253,31,254,248,167,224,108,12,216,179,121,244,219,251,120,177,76,86,127,73,179,245,230,252,226,242,234,131,101,59,174,231,119,123,253,193,225,17,184,54,186,6,190,241,48,135,157,233,147,188,139,217,201,201,228,99,246,232,209,35,215,49,38,91,29,5,211,27,11,82,17,46,139,130,225,18,10,181,44,21,101,5,207,104,134,152,97,192,36,67,63,26,253,176,89,156,133,169,212,120,109,106,18,26,0,218,239,28,216,221,158,109,15,92,255,99,158,232,120,157,3,207,233,245,29,223,119,187,22,15,37,68,129,201,200,58,158,156,100,102,28,46,103,235,249,241,228,240,176,147,189,155,156,142,166,166,192,160,77,103,29,101,87,51,244,0,46,117,178,120,114,69,67,119,25,187,2,224,187,211,227,236,145,117,156,25,70,103,98,174,54,217,188,253,18,115,46,115,26,39,40,194,135,2,70,187,243,181,227,119,59,57,112,244,250,140,192,190,73,126,159,164,147,155,161,235,193,200,210,199,192,59,40,240,14,14,15,245,241,225,168,223,153,188,27,131,128,254,233,199,81,246,46,56,61,57,113,60,99,252,149,235,148,218,184,32,224,111,146,219,123,64,109,148,224,127,13,8,1,131,23,29,2,108,110,5,63,104,32,64,3,7,160,107,67,79,190,15,47,247,109,131,250,160,224,83,3,4,220,131,79,153,188,134,99,180,156,181,109,144,74,47,231,31,216,126,45,59,111,223,252,45,137,150,237,22,52,79,159,135,151,247,236,44,245,212,81,152,172,104,142,251,28,14,76,6,247,228,12,162,215,14,116,167,163,87,90,203,123,251,36,200,194,174,119,159,118,92,118,35,168,0,70,40,102,152,221,253,136,131,67,27,135,125,62,114,78,245,8,245,162,19,239,56,2,133,130,175,251,135,209,215,221,147,81,206,155,254,55,18,215,24,150,48,72,31,99,101,8,164,235,126,221,118,141,168,115,208,117,59,157,161,44,208,26,193,11,108,160,209,25,227,221,68,166,12,205,164,33,22,81,48,83,60,122,247,231,199,198,159,44,99,112,248,203,209,233,81,52,211,81,183,164,71,205,130,57,30,29,30,6,95,121,157,241,131,145,5,87,71,178,47,134,23,50,9,47,95,177,70,11,156,3,195,238,116,14,88,61,86,201,69,219,209,13,231,235,241,97,191,131,228,147,147,241,215,206,199,166,58,157,14,245,212,64,118,137,27,219,227,75,83,26,189,209,148,71,120,66,240,251,81,158,170,63,27,93,111,214,211,254,240,26,252,132,236,236,116,188,176,208,207,204,179,104,137,213,164,82,177,246,102,25,98,167,96,21,182,195,229,24,3,209,219,159,158,63,77,22,88,2,33,55,55,238,116,136,158,66,28,132,100,54,129,157,96,253,160,86,83,194,20,13,86,0,8,160,128,26,45,111,195,88,113,98,74,156,200,192,133,236,36,86,92,200,32,56,211,178,144,60,69,243,32,96,214,169,232,237,244,54,212,247,132,47,106,154,83,172,49,60,149,13,181,227,119,217,105,161,47,211,66,242,208,171,231,163,103,199,71,95,63,192,164,227,107,237,219,16,51,159,5,214,135,105,29,33,88,98,93,141,134,67,45,202,224,239,63,217,76,225,89,81,49,46,250,239,193,102,61,199,92,87,211,158,133,228,83,106,143,207,146,205,251,57,102,147,191,133,115,108,23,173,215,171,108,120,116,132,42,200,52,147,116,70,211,26,84,131,163,17,210,98,149,246,242,249,27,74,57,162,46,189,26,53,240,40,126,48,162,117,38,248,196,47,145,250,241,227,143,252,251,224,1,70,236,8,75,153,132,77,103,91,140,235,84,72,86,165,50,165,113,234,224,128,134,114,76,17,42,169,166,2,2,95,83,1,193,100,160,185,12,96,23,222,46,33,162,144,204,33,99,178,53,121,134,9,197,250,197,119,85,128,121,137,140,186,94,205,3,206,34,185,109,233,22,100,108,215,61,248,30,163,251,115,147,148,5,44,127,165,79,112,2,49,132,162,231,37,199,122,132,25,72,117,92,22,50,240,77,68,147,30,86,18,156,145,79,141,170,65,122,5,95,127,52,169,233,212,184,51,28,99,204,221,73,164,31,20,127,156,166,193,85,201,45,98,164,133,55,52,6,230,195,7,162,64,148,241,63,234,28,28,84,80,122,48,26,189,197,188,167,207,217,52,203,25,97,74,167,212,11,146,169,100,59,29,197,74,247,120,12,6,36,125,133,178,202,210,234,203,145,221,115,29,191,239,193,185,208,195,145,225,244,112,238,246,123,3,61,25,25,121,150,167,175,71,42,167,175,111,160,43,155,147,84,233,202,6,186,146,190,219,156,142,218,244,75,86,158,254,121,36,45,249,45,50,211,241,84,46,156,154,146,251,146,190,91,201,97,222,118,250,39,39,43,140,192,122,250,174,189,58,236,122,72,31,156,156,120,157,67,219,59,29,173,242,174,45,48,147,251,117,58,213,103,244,63,155,233,115,250,159,207,117,154,225,253,26,69,187,88,142,48,198,177,20,60,29,45,245,215,163,80,127,63,74,244,95,71,235,227,229,104,209,94,234,161,158,232,107,180,185,57,180,78,245,158,110,116,251,86,191,215,29,184,221,14,58,191,104,175,117,81,132,10,216,167,186,13,123,238,246,7,126,215,243,251,40,145,160,4,85,167,50,84,2,99,156,221,211,187,86,215,182,252,190,61,232,128,180,139,182,104,97,201,5,220,83,221,1,8,219,242,60,223,241,93,215,234,128,21,85,52,60,70,195,238,117,61,187,223,31,244,118,209,240,25,13,155,246,252,250,150,231,52,160,209,101,52,12,219,3,35,93,219,245,236,93,68,122,2,17,207,239,89,254,160,239,238,162,209,39,52,236,94,207,178,92,223,3,9,119,208,24,8,106,216,3,18,21,207,179,129,105,29,15,27,36,37,68,60,199,194,240,189,131,131,13,130,50,53,6,3,203,179,208,8,28,146,58,26,54,72,10,60,208,209,174,229,118,251,40,177,195,21,208,148,16,241,44,116,212,182,208,215,29,52,64,82,166,135,111,129,104,142,51,0,213,119,112,1,85,129,11,38,4,93,223,245,93,7,188,91,142,102,85,84,78,117,31,64,186,126,111,208,245,109,192,88,163,64,25,19,144,125,64,188,237,14,124,203,238,186,192,53,65,137,10,38,36,66,158,222,245,92,104,85,207,102,154,204,42,120,128,98,142,5,25,235,65,202,122,174,197,20,169,162,1,68,129,70,207,178,125,136,224,0,221,173,163,65,84,31,232,110,31,56,88,196,217,29,36,72,128,60,72,122,215,242,122,125,215,245,119,177,0,193,8,11,207,130,150,246,250,94,127,23,11,112,223,215,253,110,223,243,208,25,228,239,32,1,16,76,12,123,208,7,227,136,228,117,60,136,111,64,195,238,247,220,174,59,232,178,144,86,209,128,12,2,13,27,180,244,93,48,15,37,118,120,2,32,196,20,15,120,244,109,175,203,234,82,197,4,2,4,68,124,27,242,129,174,236,162,1,77,0,26,48,114,224,122,31,26,179,139,6,201,32,145,195,30,56,160,89,175,231,2,202,114,52,175,115,5,125,65,11,224,10,225,48,175,224,128,142,216,54,60,65,8,159,223,3,170,204,149,249,174,104,116,33,233,238,192,114,45,159,116,33,68,145,10,30,196,22,23,173,248,174,53,240,49,229,218,193,2,48,136,162,148,63,128,206,177,140,86,49,33,85,176,33,230,61,167,63,0,235,27,16,33,122,116,9,136,239,13,122,44,197,59,120,144,144,2,15,219,26,128,94,86,215,99,43,86,195,4,140,129,160,247,209,210,192,238,129,96,117,68,200,52,128,36,174,223,135,136,57,14,43,75,21,17,146,15,32,130,76,223,177,7,61,182,166,85,68,160,112,192,163,215,181,156,129,221,103,141,173,34,1,33,37,57,7,231,187,158,215,103,217,168,34,65,156,37,44,60,199,238,219,126,159,84,97,135,47,164,45,93,29,36,237,121,64,132,77,71,21,11,146,14,80,99,48,128,61,239,195,50,16,30,87,245,145,133,40,58,232,119,93,11,26,67,120,92,85,240,32,154,147,160,59,221,254,192,246,48,31,4,26,87,187,54,140,37,157,180,193,27,88,172,181,87,21,68,200,132,161,51,144,48,151,52,119,23,15,234,46,68,12,54,221,131,156,246,216,118,84,17,33,154,147,164,247,7,30,176,197,64,214,128,9,113,142,48,177,124,219,119,88,93,170,104,144,85,103,97,7,154,14,216,207,140,169,34,2,125,32,81,135,99,97,67,8,81,160,142,7,83,157,236,32,134,82,136,15,171,109,21,13,26,227,8,11,191,75,38,6,214,174,1,17,26,109,33,236,208,6,219,183,187,94,3,34,160,42,113,198,243,209,19,171,199,250,82,67,132,212,146,40,130,241,214,177,97,66,0,164,142,10,9,145,175,247,208,159,126,207,161,222,212,17,129,28,18,69,92,240,5,116,23,18,178,60,124,10,15,199,66,217,240,240,53,31,37,163,228,240,61,31,173,71,235,195,95,233,104,171,124,120,181,138,243,78,34,127,10,151,157,253,160,138,3,171,167,250,74,224,46,60,158,245,104,124,216,142,14,210,143,127,141,14,86,157,195,246,146,96,118,14,19,57,131,105,175,79,78,194,143,107,177,168,21,118,14,163,45,214,196,225,83,237,3,115,245,49,61,248,235,222,48,231,243,61,96,254,57,253,243,222,0,163,232,110,128,233,159,219,209,71,224,184,47,76,14,195,200,162,15,8,157,235,210,249,36,154,97,215,90,37,188,40,230,226,149,41,3,109,171,140,120,134,213,89,207,211,228,130,119,205,197,198,119,235,121,28,135,179,32,214,130,116,198,107,175,90,235,112,44,102,241,228,160,151,23,176,218,1,67,203,231,150,60,229,8,50,206,251,38,29,202,83,57,31,153,212,230,213,105,103,152,187,251,88,168,194,185,216,16,160,134,222,142,114,196,229,22,194,79,163,199,237,183,29,253,124,20,99,151,232,166,229,233,184,33,48,169,37,98,89,104,161,229,201,254,149,121,161,186,168,121,113,107,205,150,218,15,125,67,177,136,88,18,143,150,88,87,255,254,205,203,23,163,120,171,127,119,123,93,222,52,110,172,248,102,143,138,63,133,1,34,66,234,53,191,29,137,8,22,212,175,68,67,76,245,172,3,144,20,129,49,25,181,90,152,72,82,54,182,60,67,192,153,132,41,148,83,68,113,148,164,5,155,1,163,159,104,15,14,226,16,18,85,51,44,119,119,244,105,123,2,186,208,246,84,48,121,44,38,126,197,100,185,163,255,80,96,112,253,164,221,218,172,8,170,216,169,197,98,61,82,16,128,65,91,179,114,247,22,105,231,5,25,85,218,49,135,138,20,123,154,117,10,36,235,128,246,34,208,251,243,32,222,132,199,8,198,225,190,101,35,17,53,243,45,112,57,190,104,183,16,157,149,114,72,16,197,141,130,24,180,107,12,232,114,151,89,22,22,155,83,255,117,4,160,71,28,24,244,13,118,10,195,209,191,93,79,183,7,216,7,152,227,40,219,254,87,135,182,78,31,76,40,92,103,71,111,184,25,110,3,29,161,54,180,105,0,210,98,7,85,54,21,200,166,38,188,85,15,81,87,177,55,113,50,43,87,87,49,61,67,112,72,213,29,11,86,37,233,130,194,69,152,50,17,39,253,225,229,139,239,177,220,34,163,85,142,35,19,155,21,88,222,249,241,213,235,55,45,189,197,221,17,212,7,141,35,83,28,54,236,1,41,218,151,182,129,210,14,83,115,53,18,171,230,201,102,57,105,99,134,138,250,225,228,40,197,12,126,29,196,95,35,30,230,150,237,39,5,245,73,144,130,75,34,44,76,196,0,175,14,91,95,181,110,222,239,82,21,127,38,198,86,196,155,43,110,117,236,122,21,221,73,150,170,124,33,184,192,30,172,82,51,106,90,233,195,126,243,89,28,254,47,238,212,22,61,74,150,164,81,87,144,192,117,136,165,194,229,44,44,186,196,61,162,141,108,20,160,136,67,172,39,121,180,65,30,1,9,138,63,28,141,48,81,236,124,2,222,45,116,120,31,196,69,205,2,109,89,15,234,92,87,93,82,231,74,124,6,148,128,163,4,43,232,122,64,247,110,171,250,207,194,151,13,106,158,240,93,187,245,150,165,171,208,98,18,156,116,68,12,16,250,201,225,194,111,104,224,226,174,253,95,211,17,253,13,172,141,8,190,20,3,55,226,219,4,75,16,76,55,9,49,222,43,22,193,196,35,178,98,5,139,2,100,166,168,221,210,99,61,54,41,190,159,212,47,163,116,44,210,193,198,138,38,200,200,182,14,101,129,173,136,138,194,62,198,167,227,153,153,50,104,155,150,224,127,166,1,81,173,122,63,178,15,14,30,32,252,118,29,173,226,240,155,54,174,29,72,17,231,242,199,100,131,64,71,172,93,47,227,43,77,88,7,28,135,90,155,22,78,59,136,59,140,17,41,169,162,12,77,52,1,59,50,140,223,89,167,140,178,137,128,85,196,129,182,144,129,109,144,85,187,243,128,87,77,91,183,130,47,129,206,20,68,68,62,144,27,243,151,155,199,46,81,89,196,228,162,210,191,221,92,146,0,63,95,194,122,65,2,255,178,199,238,61,150,167,99,44,214,134,84,234,91,177,59,221,70,3,112,110,80,174,93,217,13,255,64,133,105,64,251,153,118,57,228,242,185,125,252,3,182,12,44,248,234,121,185,223,193,244,200,120,11,218,103,167,52,10,167,65,7,0,47,15,255,36,63,130,17,29,125,56,150,137,249,229,4,163,223,113,172,228,78,148,19,233,2,78,190,214,135,103,33,214,74,67,125,24,76,177,11,113,141,208,2,3,62,44,237,130,156,193,225,12,83,3,41,199,242,144,117,103,104,169,83,6,50,196,64,26,77,84,18,7,128,12,191,12,253,176,23,158,109,107,176,13,99,125,193,177,240,32,208,80,107,181,182,116,197,203,53,93,200,96,204,67,10,25,24,218,166,127,108,92,132,103,239,163,181,65,67,53,161,18,26,193,228,183,13,162,129,72,245,142,141,69,242,193,192,120,194,57,67,239,216,72,202,103,165,195,41,218,49,166,1,226,86,175,134,155,200,200,130,101,102,32,210,40,130,43,6,35,31,46,140,77,164,27,208,176,56,52,68,130,254,132,162,167,94,6,227,215,124,250,12,245,245,215,225,44,9,181,183,207,245,159,146,51,44,194,235,223,135,241,121,184,142,198,129,246,67,136,232,181,199,41,2,116,244,31,144,163,189,6,124,189,212,72,235,49,129,214,160,100,216,173,249,110,145,252,22,193,41,80,224,84,66,126,254,250,106,113,150,32,192,129,65,149,235,200,110,32,164,108,147,2,81,233,187,13,151,112,71,130,88,100,66,222,35,150,140,122,246,246,44,153,92,93,47,224,62,98,7,205,58,46,211,57,90,206,129,229,122,59,79,175,101,10,69,12,17,235,100,142,226,39,98,21,37,215,237,213,229,54,56,59,75,135,23,40,128,248,89,190,252,231,180,115,93,97,23,109,240,165,140,204,16,227,123,152,82,155,48,207,184,138,104,114,124,87,129,237,220,214,231,142,62,119,245,185,167,207,125,125,222,197,222,27,250,199,252,84,104,113,202,69,181,19,1,34,193,202,168,215,27,82,229,206,116,236,139,36,203,153,0,43,129,128,236,64,99,75,230,87,127,127,54,1,11,23,43,29,74,44,10,21,242,179,72,150,73,182,194,126,176,254,250,217,75,28,27,63,133,51,92,130,144,234,47,67,56,238,58,146,130,113,162,35,10,20,234,16,100,250,139,8,17,26,220,188,70,165,145,177,73,35,152,253,31,194,11,61,7,37,122,195,253,179,195,197,54,3,207,226,82,159,251,214,87,91,108,190,35,4,110,85,74,237,249,95,85,88,105,29,175,146,12,215,255,160,163,42,214,253,24,3,12,9,105,108,4,136,196,89,14,105,159,155,170,16,52,40,56,44,223,98,104,152,142,79,109,2,54,120,140,83,58,99,63,237,154,233,71,155,206,208,210,92,215,27,165,3,137,113,176,130,191,172,14,182,136,228,132,93,213,35,178,70,122,178,90,207,224,232,173,116,52,79,215,115,16,96,196,204,6,21,218,86,56,187,35,232,149,220,6,73,175,228,11,74,146,149,104,16,147,38,249,175,201,124,174,42,43,24,123,178,128,150,234,143,232,128,160,204,26,97,44,25,204,218,2,106,6,154,202,18,239,56,84,82,156,156,202,51,54,194,234,4,180,71,108,223,105,174,47,52,184,7,0,53,14,135,162,214,241,89,48,126,79,244,90,78,36,181,185,37,68,98,128,17,229,204,104,129,97,89,52,62,100,115,56,197,8,150,209,212,254,26,81,199,212,205,33,118,137,19,153,9,217,141,150,152,166,69,19,97,219,177,87,156,92,136,218,202,53,184,190,73,94,134,67,133,45,123,64,70,182,138,150,134,236,112,145,135,70,171,121,202,166,48,22,178,247,232,235,120,222,216,123,34,234,52,10,227,201,177,196,222,72,166,83,16,110,104,56,48,57,69,51,2,68,73,177,155,128,9,170,228,117,104,8,55,196,136,175,112,187,153,254,36,52,185,177,200,54,11,136,195,213,181,188,150,102,136,139,31,64,5,140,11,91,94,247,249,203,38,89,135,250,36,214,39,19,125,199,110,233,243,84,159,70,51,8,50,150,153,200,150,40,193,218,114,63,209,183,194,42,23,162,134,181,31,120,117,215,69,2,6,132,77,172,195,31,217,96,140,68,235,98,184,165,30,54,73,42,214,251,48,155,45,85,207,181,13,44,38,197,80,76,222,178,114,130,70,36,29,28,226,50,103,27,152,171,231,110,214,117,2,115,133,208,217,161,45,53,230,203,193,56,112,131,169,2,213,12,101,31,0,74,123,82,204,198,149,246,92,143,55,41,130,173,135,43,132,75,64,178,182,67,240,128,12,211,68,101,200,208,191,109,180,152,233,217,249,76,63,143,38,97,162,195,61,60,135,229,13,54,147,40,209,163,105,10,183,82,15,17,40,55,209,69,60,69,206,74,102,96,221,70,46,176,228,20,135,12,146,193,129,67,151,106,228,35,139,82,17,105,177,62,117,154,67,100,169,219,113,165,216,221,81,174,18,245,31,87,2,94,14,53,235,184,41,227,42,207,96,173,135,33,15,75,133,139,180,162,28,162,2,144,144,159,102,239,195,139,82,13,62,45,10,243,53,190,148,109,151,207,145,47,207,87,193,146,114,139,19,100,201,19,132,49,207,141,15,9,198,12,153,2,79,18,230,222,200,150,193,10,50,153,70,227,53,34,131,16,208,15,115,114,25,193,198,93,137,98,179,20,211,18,152,46,131,98,98,140,124,144,146,64,242,220,243,40,184,57,115,157,236,228,129,108,8,167,136,115,100,48,218,206,195,137,241,1,17,47,42,109,9,127,30,104,65,251,73,7,235,169,146,226,59,133,113,89,80,185,29,178,167,48,123,100,136,202,41,194,54,73,201,208,172,213,229,110,158,144,112,237,203,233,116,90,202,148,169,233,236,172,237,15,52,108,50,104,142,215,213,142,52,19,113,72,59,32,164,149,214,44,188,191,164,11,159,74,69,110,200,187,53,89,180,30,78,118,178,207,226,13,144,146,199,136,48,159,75,94,138,20,242,213,211,0,174,119,193,151,43,150,28,149,48,223,132,185,28,138,20,140,53,80,172,156,59,52,156,151,114,179,112,21,5,234,100,130,240,222,28,109,137,2,70,57,78,174,224,149,39,238,32,152,103,213,49,205,51,118,80,206,115,118,113,207,179,170,157,200,147,149,17,171,167,215,123,89,100,136,238,98,64,82,73,255,50,10,255,50,10,255,50,10,255,50,10,48,10,102,150,26,180,156,117,157,11,115,112,134,201,35,156,105,44,226,200,25,183,114,59,232,48,247,236,148,247,103,80,42,118,218,82,132,216,95,12,133,79,114,140,197,166,21,38,130,184,172,16,177,141,244,198,70,204,28,126,43,75,56,121,143,23,105,176,170,173,231,108,77,213,242,46,46,91,90,111,226,89,101,145,167,82,182,166,152,76,26,150,154,85,2,84,28,78,215,72,160,63,58,101,238,224,156,255,139,4,71,38,152,62,118,11,183,88,253,95,161,12,77,68,81,130,78,28,62,145,185,31,12,223,186,254,192,83,210,203,161,143,18,139,51,132,19,72,31,26,54,147,155,118,100,97,202,235,214,242,60,149,227,212,50,138,58,94,45,199,22,25,113,81,133,123,164,42,164,69,122,165,35,139,117,9,51,234,66,142,22,50,242,42,148,225,230,201,121,211,156,92,148,175,20,47,146,43,197,37,154,107,35,239,50,167,202,210,236,233,86,253,222,45,22,179,105,146,149,167,138,211,45,174,211,8,47,243,68,58,81,37,141,74,78,41,109,107,10,153,171,186,193,230,28,136,75,177,149,104,32,229,23,211,207,19,187,142,74,206,211,108,172,70,112,218,133,49,192,130,39,73,165,83,36,253,242,206,233,195,168,254,114,170,178,232,76,100,184,86,57,131,207,100,134,83,201,160,51,202,152,98,227,93,166,145,91,191,253,247,247,225,21,79,21,50,141,38,176,144,186,235,98,126,47,12,65,27,129,167,147,112,134,117,112,51,88,98,238,205,186,132,162,226,132,244,129,78,53,59,211,136,50,88,124,142,150,83,186,94,21,164,16,243,21,140,86,60,145,169,207,107,176,138,79,171,10,6,145,45,159,150,226,162,66,140,255,156,33,166,122,98,18,95,79,173,39,0,183,124,62,91,5,88,155,28,11,120,245,196,250,12,154,165,1,134,240,226,154,15,38,17,89,20,86,253,228,66,102,98,240,168,103,34,9,59,10,144,27,76,147,113,45,32,198,106,116,154,167,87,60,115,206,134,34,105,107,210,114,110,52,189,82,69,242,83,185,52,92,47,38,175,238,223,41,199,38,77,229,110,205,25,92,15,251,26,191,67,37,58,148,226,136,148,34,193,21,9,189,34,197,227,20,161,74,188,196,2,215,129,5,69,154,73,114,60,54,217,112,128,23,73,145,42,18,207,106,5,100,35,42,255,178,14,64,181,41,82,85,102,177,188,42,51,140,9,123,47,42,95,76,251,69,90,94,228,151,119,191,124,137,24,180,129,29,64,198,203,174,99,62,28,217,213,149,59,114,49,28,79,115,124,205,33,7,3,75,105,237,134,106,8,134,47,183,112,102,157,185,103,253,123,180,128,48,100,205,238,193,147,233,123,123,53,66,3,49,34,191,172,253,187,224,12,52,196,40,227,235,239,213,128,76,65,27,205,77,152,206,78,81,247,166,162,238,78,81,239,166,162,30,138,206,200,95,9,141,110,222,59,56,114,165,158,213,23,251,168,119,110,79,27,80,255,170,125,203,235,113,191,102,117,162,221,13,246,38,162,53,2,198,80,187,55,92,15,112,125,248,172,190,125,43,92,118,64,246,135,234,67,70,229,247,70,168,164,198,202,33,82,106,181,186,164,177,87,166,138,225,90,140,190,202,117,146,67,181,26,146,81,222,169,22,111,42,93,20,246,170,133,201,94,212,202,10,19,130,162,126,173,168,48,72,245,210,202,76,161,66,183,94,161,177,188,44,126,5,51,167,178,216,55,168,66,87,190,141,130,142,226,5,89,10,159,164,94,62,39,11,132,171,10,190,17,122,94,56,103,132,40,204,204,216,41,173,88,148,150,72,83,163,2,47,174,203,17,129,143,197,194,156,26,12,196,118,224,162,180,19,98,246,69,91,213,13,196,50,180,203,172,92,188,161,52,23,229,34,184,213,76,180,145,208,229,198,1,238,38,181,69,160,11,46,65,30,99,65,188,190,248,159,103,200,182,206,176,218,249,94,8,57,159,151,196,188,144,109,154,104,89,101,169,46,23,37,185,230,115,214,67,68,221,222,5,13,10,210,245,181,126,69,249,110,6,56,184,27,160,221,195,252,83,115,7,123,0,196,136,11,83,113,39,68,215,211,16,139,173,13,42,195,65,35,72,76,68,247,1,232,0,187,110,159,62,119,1,188,10,99,204,139,96,163,239,134,73,54,204,194,224,120,55,37,37,208,189,16,69,215,49,20,222,141,104,125,52,169,194,164,177,68,149,232,90,249,66,186,217,69,109,18,70,158,146,9,243,153,9,223,85,164,96,225,133,47,158,39,175,44,73,113,173,107,213,212,234,229,161,149,23,237,75,59,43,50,21,187,39,49,239,153,190,167,16,191,28,50,34,56,72,125,85,144,194,112,188,57,195,82,201,89,248,1,27,156,109,211,195,164,211,196,149,49,157,114,149,9,77,133,169,172,105,251,25,124,85,90,31,255,37,159,99,202,245,114,229,214,229,212,146,9,157,98,135,179,152,149,74,16,60,219,196,252,128,124,253,59,193,136,33,129,28,45,89,155,102,154,251,86,102,195,89,170,59,55,188,59,235,84,204,140,172,7,137,188,179,158,116,9,203,213,202,126,233,157,245,155,253,86,9,73,100,238,11,163,228,157,86,234,11,155,130,9,215,157,128,246,118,237,44,120,19,182,139,111,197,254,236,186,118,10,15,233,88,236,137,192,223,201,241,80,146,83,200,54,54,237,239,22,158,6,189,68,189,127,136,86,201,70,201,129,127,136,183,146,236,122,240,13,133,183,54,99,139,153,57,173,42,229,156,86,214,167,111,13,57,227,6,159,183,95,212,43,124,212,74,141,191,147,167,202,219,221,57,118,236,100,195,30,15,57,121,239,137,68,101,185,245,22,97,147,141,169,173,105,154,29,203,150,212,94,59,140,130,198,49,80,184,163,85,177,95,95,219,203,102,203,33,97,241,130,45,110,151,80,224,219,180,202,155,227,84,172,57,119,216,123,176,106,89,229,197,231,78,115,30,119,187,188,162,188,71,35,88,30,29,183,61,244,237,240,182,246,234,13,202,150,74,145,6,141,149,69,30,98,17,43,153,59,169,34,65,47,214,167,11,134,112,133,38,222,139,102,10,206,223,189,216,94,195,112,135,247,156,156,11,244,125,155,186,65,164,239,108,139,76,235,189,219,106,54,161,245,182,86,33,236,191,188,123,208,95,249,204,144,103,172,187,255,136,105,235,29,77,10,127,242,62,109,58,142,134,43,219,180,94,117,157,97,239,54,203,102,92,108,171,237,55,178,238,110,198,113,116,64,49,45,200,243,232,234,115,233,244,21,197,75,178,93,218,189,131,22,201,165,198,130,101,124,142,12,218,178,43,146,197,126,158,76,254,99,45,157,224,240,174,198,31,74,25,98,139,79,229,148,171,136,205,190,61,9,165,188,129,123,12,184,123,140,248,55,12,184,59,70,152,17,100,101,144,232,53,88,229,79,176,199,55,129,21,6,250,95,166,249,190,166,249,86,122,178,85,129,81,187,135,57,195,13,9,120,246,136,59,31,220,105,206,110,106,86,88,150,123,182,139,169,27,205,51,237,158,115,75,187,195,40,107,155,124,79,74,254,205,117,132,22,78,29,186,246,183,251,11,133,199,238,169,11,110,87,131,25,117,43,179,197,29,69,184,173,73,55,112,207,220,241,125,154,244,251,26,198,193,110,101,61,226,94,77,134,94,216,13,207,238,211,164,227,244,65,89,12,188,119,44,223,222,212,42,143,140,24,38,246,111,177,135,5,18,159,134,167,79,105,174,88,61,190,161,65,44,31,239,214,157,85,86,231,85,205,125,150,83,27,150,231,111,151,56,209,84,93,218,246,24,177,155,164,237,174,166,212,178,212,254,205,52,45,80,221,217,76,201,106,119,174,111,139,137,221,173,205,203,24,229,141,11,137,234,109,43,94,141,27,22,181,117,154,155,27,170,169,192,45,203,65,205,146,191,87,67,60,234,222,221,66,243,104,186,87,11,197,2,83,99,51,88,108,218,169,87,138,238,20,146,65,215,148,53,132,142,138,120,161,82,225,102,62,248,224,3,252,57,27,250,90,66,190,161,94,83,31,110,192,229,159,141,134,162,34,110,246,82,172,211,217,187,229,110,159,94,223,100,106,250,55,3,18,102,64,141,0,85,48,123,104,105,211,56,112,171,150,222,54,3,191,199,248,179,223,20,252,166,214,121,56,174,244,187,220,254,126,211,192,134,158,223,53,202,223,60,255,188,87,203,251,76,64,75,109,151,125,28,41,243,141,14,86,63,55,207,123,161,1,20,186,180,76,125,219,212,244,126,88,8,127,235,158,104,208,100,206,178,53,255,54,119,235,139,207,249,249,72,149,107,71,52,190,0,235,24,55,44,23,1,40,8,189,96,39,95,68,120,107,165,93,36,77,108,35,29,19,24,71,2,225,93,33,141,226,112,142,53,25,82,165,89,102,223,13,23,228,213,154,182,69,7,199,90,121,75,72,163,203,133,100,210,223,132,131,91,193,193,169,225,96,113,211,249,193,63,6,135,47,27,158,79,131,91,180,23,161,149,121,56,26,45,125,227,217,42,184,238,136,22,208,249,168,152,240,138,89,22,207,120,161,10,95,233,26,253,118,142,85,184,22,24,43,46,200,186,173,125,121,169,105,137,36,216,235,4,73,106,17,94,20,195,243,197,231,184,86,148,175,70,145,241,17,67,187,155,79,248,190,156,186,244,46,95,188,38,47,55,112,113,127,154,201,89,109,97,156,58,34,215,183,9,118,190,92,207,39,181,240,30,103,39,188,71,133,225,241,37,1,95,124,126,175,72,34,244,162,244,216,30,200,130,188,249,61,69,56,182,49,55,237,28,231,42,99,153,3,236,127,112,196,144,193,151,150,34,206,145,99,118,0,4,87,117,202,235,56,113,73,231,81,254,172,176,19,186,240,79,227,123,106,136,59,59,211,8,241,78,122,164,167,154,90,56,231,251,115,139,241,255,157,116,100,78,53,218,139,22,207,30,195,51,101,0,10,119,238,215,34,220,179,162,65,88,112,91,124,209,132,40,47,94,13,21,212,211,159,30,21,79,2,59,57,66,177,122,45,9,77,178,183,245,168,92,72,29,127,118,50,119,118,30,75,134,174,59,156,183,122,116,50,119,31,209,157,28,54,242,33,101,56,61,57,90,137,76,213,128,220,73,214,40,184,15,241,201,134,223,122,68,87,60,209,245,202,184,48,246,124,166,97,244,164,199,67,224,98,111,211,105,105,151,139,120,137,90,116,107,77,220,89,243,226,226,194,188,112,233,206,154,71,116,71,189,35,148,111,105,244,192,181,39,201,229,168,69,179,225,190,103,209,172,181,165,169,43,205,109,60,88,77,136,214,168,229,226,184,204,152,42,245,91,184,26,28,252,68,94,85,145,143,43,20,94,225,6,10,116,213,115,108,164,184,123,54,30,22,7,169,72,38,147,22,167,225,94,253,27,220,181,126,137,7,82,64,240,91,116,187,145,133,109,153,174,230,219,230,32,134,107,98,186,134,111,14,206,123,221,49,208,52,123,36,95,154,107,98,234,129,175,103,58,113,15,231,158,217,253,176,176,7,166,175,217,54,234,32,20,207,232,82,157,65,76,199,90,47,207,52,171,217,56,21,5,76,123,183,72,143,10,120,57,132,174,105,27,125,207,244,198,6,116,221,0,22,134,11,212,232,223,51,251,154,21,27,93,154,160,0,65,174,0,248,90,119,64,133,6,38,42,27,62,37,122,72,136,209,35,100,81,145,129,217,167,236,46,117,114,0,140,92,224,208,71,175,112,138,102,75,197,186,84,204,167,98,253,106,49,63,198,226,105,15,199,40,228,83,33,207,176,93,163,103,250,40,210,5,86,182,107,246,69,47,186,93,228,1,61,119,44,177,39,250,25,8,161,224,127,208,241,220,232,117,63,144,12,19,183,26,185,215,196,41,220,238,205,180,201,51,56,199,29,29,77,123,110,184,32,10,142,125,211,159,187,24,146,248,112,128,100,117,236,206,81,2,141,217,61,68,83,66,92,207,251,232,212,135,133,107,131,227,0,210,7,70,115,2,229,158,211,225,135,133,131,158,226,38,106,166,61,38,65,0,158,154,103,128,32,196,39,151,15,250,72,130,96,128,109,26,160,211,129,167,33,29,53,93,116,210,246,204,30,49,172,11,17,241,32,71,54,81,11,55,187,66,33,58,232,146,60,17,183,125,128,6,31,28,240,129,8,63,39,122,163,69,27,148,242,32,113,174,1,236,88,16,113,134,187,135,65,58,152,25,14,36,200,96,20,249,136,90,67,62,18,224,31,64,86,197,17,178,6,132,34,121,80,36,187,150,72,243,197,17,234,0,54,166,89,0,194,165,73,206,108,144,196,113,0,12,201,93,112,12,253,5,50,4,19,124,227,250,92,148,143,152,114,4,11,253,34,250,240,17,126,252,115,3,242,214,5,53,187,102,247,28,80,153,14,134,139,78,251,16,17,52,226,3,67,121,224,19,218,16,45,18,94,116,139,142,156,30,117,70,67,38,228,136,26,65,66,151,208,147,71,146,240,46,0,128,231,84,142,180,202,195,67,189,88,83,21,185,128,19,36,215,35,236,233,8,37,136,130,182,1,108,29,84,133,108,162,171,36,189,232,13,88,37,249,225,144,82,137,3,226,14,18,123,64,131,16,230,211,34,3,157,33,44,0,10,28,102,221,67,67,92,3,12,238,49,199,153,189,121,6,100,141,229,199,28,0,203,1,10,129,5,252,3,61,21,71,80,31,238,24,186,133,100,136,175,1,245,134,201,1,19,145,131,115,252,119,209,176,55,71,102,119,12,17,33,49,233,226,219,3,214,30,255,247,161,119,184,233,44,122,68,157,69,38,36,205,0,171,6,134,3,148,196,17,241,133,200,206,180,161,74,56,2,169,81,53,182,33,28,44,242,172,154,56,38,136,46,228,90,252,51,191,192,0,112,2,53,232,31,236,37,33,50,108,50,141,168,67,255,100,61,137,51,232,249,24,116,6,139,192,18,54,11,160,34,91,7,54,123,172,39,144,254,177,176,99,2,83,54,52,68,62,74,33,91,75,199,61,52,67,226,227,146,113,2,12,179,7,102,67,77,128,65,31,217,44,243,104,129,140,55,29,125,88,248,172,30,48,56,160,2,85,167,126,144,50,144,157,162,127,146,37,48,129,148,26,95,250,135,73,60,119,33,65,115,224,230,157,147,226,67,152,136,121,164,171,208,50,100,177,160,136,35,129,30,137,44,99,200,162,10,186,67,38,88,234,251,231,62,58,65,160,128,75,143,112,243,80,21,89,160,223,57,18,129,57,9,45,148,158,218,38,158,209,63,130,191,81,147,196,121,110,91,231,224,137,67,226,76,10,192,98,66,212,198,15,173,192,148,57,1,106,128,63,164,97,176,109,98,148,96,139,65,74,76,194,1,217,231,127,24,100,166,49,87,224,127,26,220,136,72,104,78,152,72,96,96,131,47,88,236,2,37,88,128,152,89,64,148,128,128,93,128,74,71,192,137,52,144,18,72,214,197,1,231,0,95,140,63,66,230,145,0,186,201,44,54,125,160,57,39,208,56,33,14,40,7,99,20,122,68,200,66,238,176,38,67,58,194,156,97,213,146,255,108,147,44,28,19,78,62,253,115,50,224,161,60,110,0,73,36,38,173,150,25,196,37,22,32,218,251,34,27,43,192,96,112,2,99,208,172,28,9,168,199,93,12,227,231,124,8,100,6,231,93,216,189,185,77,188,196,1,4,137,250,39,210,160,188,49,15,39,152,179,153,125,48,11,230,154,19,60,141,19,136,55,49,4,129,48,244,230,52,174,160,103,68,94,172,21,65,179,72,247,161,151,24,146,8,18,101,145,45,195,141,41,73,97,193,71,22,28,48,4,0,33,41,32,200,185,59,0,190,184,202,129,134,39,20,70,9,216,251,115,58,66,109,18,112,8,41,184,68,18,136,47,137,50,169,147,248,74,114,145,218,139,28,200,1,204,7,73,5,165,208,89,254,229,178,156,47,115,168,60,212,147,45,38,186,66,236,58,7,30,220,20,12,26,248,72,6,149,135,68,22,120,96,69,232,17,249,104,240,97,233,70,112,28,17,134,37,219,230,127,232,13,161,68,22,139,199,68,16,133,52,102,64,118,140,68,20,195,129,28,242,8,101,250,167,65,199,130,224,10,43,71,255,208,24,240,13,163,207,28,45,192,113,32,162,224,8,116,194,33,43,6,76,23,228,149,172,44,13,172,132,8,255,3,48,35,69,72,144,234,8,191,141,198,55,152,34,194,128,240,35,141,36,251,71,250,45,254,73,122,73,105,168,101,26,100,28,228,184,16,5,57,66,147,166,65,34,25,109,170,76,86,85,152,36,32,79,30,17,217,6,143,134,58,178,22,56,67,219,144,86,58,32,85,149,38,136,124,1,156,66,24,104,68,230,35,114,226,132,238,195,6,66,155,186,36,16,187,78,18,102,53,231,116,223,250,207,148,231,174,166,20,114,131,113,103,222,81,158,61,208,37,9,154,186,64,65,43,95,137,160,241,37,56,26,95,31,84,248,211,39,15,12,3,143,52,230,27,10,137,59,34,105,134,81,228,170,166,43,247,209,107,110,171,122,81,67,209,154,87,113,222,169,185,167,252,136,48,220,11,73,92,240,94,110,16,37,104,194,88,58,167,233,156,40,86,224,33,239,94,180,59,147,16,243,186,34,148,27,107,5,52,225,149,127,152,104,136,133,113,204,1,235,129,236,26,47,26,150,67,119,196,178,129,90,244,199,122,3,22,121,42,59,174,50,73,236,150,150,79,114,8,229,160,53,154,89,250,26,197,11,107,116,245,20,211,77,16,143,238,230,199,23,102,84,217,85,76,92,197,254,198,169,72,201,241,124,39,23,254,100,122,169,253,90,78,222,121,153,90,230,135,184,25,18,230,124,123,204,242,228,124,206,41,77,231,232,184,50,249,163,61,28,79,205,196,136,72,52,165,163,0,202,186,183,47,82,197,86,45,77,52,253,60,137,232,139,103,98,96,6,77,212,171,36,211,3,36,242,116,201,252,120,51,198,253,10,52,241,167,110,120,1,157,226,121,6,36,230,37,249,137,154,243,125,55,128,115,43,54,144,65,125,231,220,46,37,224,31,195,165,83,78,48,156,159,97,32,95,136,202,31,90,71,0,152,196,87,124,187,30,94,141,64,203,54,250,10,197,70,132,171,131,49,155,139,228,109,34,195,57,239,150,211,48,72,193,251,131,191,76,206,191,75,57,82,205,171,172,192,101,198,181,52,13,143,39,165,235,156,248,118,153,181,226,71,59,229,79,142,132,182,84,19,249,62,21,242,153,126,124,55,53,86,166,226,6,95,138,152,242,198,163,90,128,27,232,173,192,95,126,234,3,63,154,100,246,161,133,245,1,121,99,192,86,233,158,91,109,190,255,88,167,162,228,71,21,29,174,46,118,176,5,192,189,48,249,206,43,183,153,156,218,141,219,246,54,58,30,155,56,40,180,232,75,101,238,89,1,44,238,125,90,89,21,81,96,112,165,164,80,127,44,147,148,215,41,164,249,96,147,125,195,242,77,85,175,43,26,205,215,90,105,23,134,184,156,175,166,135,101,24,226,58,189,220,142,237,152,18,242,41,253,93,3,177,163,216,117,82,210,221,252,84,27,165,64,39,9,175,220,68,190,18,163,174,233,254,170,186,30,213,196,217,6,18,139,27,78,214,22,158,234,104,90,95,85,97,86,40,91,149,157,93,73,122,45,238,245,120,155,32,85,111,7,185,199,64,41,248,36,6,176,6,41,186,159,185,244,176,132,166,204,37,29,255,61,204,37,192,124,154,177,172,94,188,80,182,146,152,72,96,167,26,215,166,6,88,6,99,19,200,51,224,62,252,172,94,15,41,42,13,110,13,29,86,11,201,159,60,13,187,42,84,44,79,144,64,42,133,104,66,72,83,139,74,161,157,230,120,254,216,253,83,197,144,98,174,68,134,154,150,110,188,6,59,138,85,208,178,60,241,179,88,67,37,39,144,237,178,112,21,69,133,104,149,37,139,111,174,123,155,92,149,239,34,185,167,84,193,33,250,127,85,170,228,245,43,37,153,90,96,154,212,35,255,221,192,236,201,43,13,191,152,67,240,10,39,102,161,222,227,98,204,165,11,80,230,149,97,153,234,27,110,133,251,52,180,14,206,153,239,149,209,182,55,55,45,187,81,26,10,94,85,13,62,93,97,94,53,69,111,151,184,245,55,70,90,44,133,51,243,155,37,165,10,83,222,7,187,66,138,203,172,121,208,40,183,245,99,140,138,120,64,55,150,241,131,25,182,19,170,109,229,15,12,223,21,75,60,171,156,111,253,118,171,197,171,222,227,122,239,161,19,220,219,103,80,171,213,220,145,114,27,219,15,242,226,48,49,138,130,32,165,107,189,107,227,94,190,47,209,64,51,177,185,86,190,196,167,62,120,40,114,236,59,132,52,82,169,58,173,170,146,41,31,12,186,52,114,243,21,246,183,140,220,205,132,82,143,103,223,29,101,118,134,236,170,241,162,23,109,231,208,221,198,235,174,95,189,228,137,32,49,119,77,221,160,60,31,236,105,35,84,171,93,34,79,147,17,155,166,37,133,175,83,185,90,174,54,121,218,113,69,226,89,121,166,85,154,127,168,176,0,153,86,11,147,184,105,98,147,79,83,42,169,77,161,45,37,237,42,109,228,53,33,80,202,168,97,81,39,60,232,135,219,53,210,253,42,249,150,238,224,36,217,71,166,104,56,121,164,158,143,127,114,36,10,221,89,25,114,250,34,90,195,220,60,123,141,61,253,215,63,62,127,246,236,245,13,149,97,177,184,153,187,60,171,127,172,148,125,75,1,14,111,159,223,41,100,113,112,22,198,10,15,117,35,20,181,223,206,120,85,154,175,222,1,162,129,232,229,153,9,7,93,211,179,125,89,134,119,158,248,171,136,155,75,181,188,129,140,70,65,24,72,20,33,219,187,77,84,28,235,1,156,93,191,226,89,23,33,28,213,217,125,41,93,78,241,235,41,42,164,87,74,106,83,46,2,65,106,171,0,40,149,107,64,190,184,80,142,57,23,225,229,59,161,249,77,101,202,81,233,154,72,82,87,63,189,123,136,39,200,139,36,21,199,32,79,41,42,128,174,249,83,217,124,9,97,57,33,223,31,175,52,162,46,202,168,164,202,147,74,15,69,18,46,15,148,71,184,224,79,181,92,185,104,172,162,245,42,176,181,218,203,210,28,165,105,250,65,2,202,242,120,151,234,204,33,136,165,49,166,136,83,211,138,192,63,248,14,255,68,117,251,62,72,39,100,80,180,231,223,238,169,114,164,18,120,176,38,215,122,254,109,29,219,127,182,185,80,6,81,251,89,196,10,52,117,226,179,207,62,43,225,62,149,53,100,133,150,118,123,15,154,39,126,136,201,200,135,111,113,103,50,60,41,147,150,59,178,27,67,7,228,4,214,245,41,168,229,31,179,116,137,126,202,211,102,28,100,28,146,186,95,3,118,231,16,177,195,65,54,42,228,201,103,220,176,190,66,183,74,167,73,119,245,150,231,244,220,122,109,164,61,60,122,136,82,194,86,138,6,255,185,75,160,202,151,187,239,82,40,89,141,210,77,6,118,150,69,97,89,209,156,88,22,37,175,188,97,109,212,249,31,90,27,213,180,39,8,25,213,112,87,242,122,208,205,13,139,110,36,161,127,95,97,200,111,101,223,238,252,127,192,124,0,248,31,101,62,248,135,123,148,203,85,3,222,131,145,172,109,88,130,42,130,178,242,68,20,71,236,25,14,17,122,133,112,205,71,255,13,10,108,23,134,230,146,0,0}; +const uint8_t ELEGANT_HTML[10615] PROGMEM = {31,139,8,0,0,0,0,0,2,3,172,87,121,87,219,176,150,255,187,249,20,194,231,188,96,131,227,44,44,77,147,136,238,188,238,208,125,230,49,76,143,144,175,19,21,199,202,147,100,32,3,249,238,115,229,45,11,41,221,94,186,196,150,174,126,247,119,119,101,176,17,74,110,166,19,32,35,51,142,15,106,3,251,69,98,150,12,169,3,137,115,80,35,100,48,2,22,218,7,124,28,131,97,132,143,152,210,96,168,243,249,211,97,163,235,144,230,226,102,194,198,64,157,11,1,151,19,169,140,67,184,76,12,36,40,124,41,66,51,162,33,92,8,14,141,236,197,39,34,17,70,176,184,161,57,139,129,182,131,214,28,204,8,19,195,193,19,102,12,168,41,121,62,78,99,102,164,34,71,159,30,147,207,147,144,25,24,52,51,17,164,172,185,18,19,115,80,139,193,144,88,178,80,36,195,103,226,226,19,92,25,66,137,243,38,95,9,130,192,233,215,152,158,38,156,68,105,194,141,144,9,90,2,252,188,196,254,104,152,73,245,7,56,147,210,184,30,185,174,221,67,238,218,16,84,198,16,136,93,50,97,200,16,204,51,124,119,55,155,255,4,115,40,212,248,146,41,120,153,68,114,211,235,215,238,137,136,184,86,60,59,125,15,61,155,142,209,246,0,15,61,143,193,62,62,153,190,12,221,205,168,56,247,5,148,70,26,155,94,96,144,236,211,220,83,132,102,26,131,82,168,127,23,18,70,34,204,24,60,91,15,98,247,75,16,109,228,164,112,198,243,8,56,218,104,87,21,104,48,95,48,92,248,122,15,63,181,123,51,2,177,134,204,130,102,115,238,249,68,26,34,147,88,36,224,147,115,128,9,177,254,64,44,139,12,230,147,24,131,76,141,251,67,143,250,164,211,106,181,188,62,65,204,167,86,136,176,33,19,9,193,191,29,162,1,93,29,106,212,93,155,245,107,85,120,180,97,202,44,83,190,203,175,142,202,52,161,112,99,12,90,179,33,56,43,78,89,206,142,59,61,59,199,178,25,140,76,65,161,139,121,204,180,126,35,180,9,20,140,229,5,96,4,68,24,2,134,240,78,176,162,8,150,0,88,184,168,229,40,131,152,45,218,190,26,173,235,191,161,139,218,254,156,107,97,236,221,116,153,50,121,164,191,50,145,179,189,93,139,185,64,85,141,101,129,137,139,54,238,254,144,145,78,57,7,173,159,202,56,29,231,236,243,74,219,176,7,23,88,22,150,235,185,161,88,135,203,233,121,43,163,124,210,206,210,242,151,179,120,47,23,95,205,84,108,43,47,50,165,79,45,29,219,62,126,213,186,137,146,67,181,108,94,117,176,115,215,65,150,169,214,79,82,99,240,203,30,204,221,242,107,94,33,25,126,160,205,52,134,32,20,122,18,179,41,221,60,139,37,63,223,172,122,192,15,229,18,153,192,102,225,132,156,173,60,211,160,46,64,33,227,4,46,201,219,212,48,75,239,168,88,118,209,37,244,32,15,199,170,175,48,145,188,85,152,67,145,8,61,204,58,61,134,234,23,64,87,243,207,130,174,246,251,170,123,43,136,17,232,2,142,153,25,89,111,16,98,212,212,126,227,39,231,161,64,79,240,1,170,198,31,129,225,163,229,131,253,76,222,186,188,148,14,228,185,87,187,135,124,214,141,142,74,232,187,150,137,155,119,194,99,59,73,231,202,152,38,175,62,30,189,203,218,178,73,85,146,1,100,201,70,200,140,112,134,20,136,11,74,73,149,179,38,136,97,53,73,140,77,182,236,110,30,130,21,202,94,122,155,126,254,80,48,45,48,147,52,142,237,66,22,188,75,145,132,242,50,144,137,45,86,164,90,121,212,14,211,114,122,16,234,60,116,250,249,90,57,150,138,165,154,157,17,31,113,157,17,147,23,15,145,17,214,84,217,210,137,107,235,171,69,198,34,142,69,177,230,205,253,92,29,202,3,124,172,228,24,165,92,247,155,143,108,191,3,55,25,29,148,207,63,11,37,154,19,205,133,92,123,246,121,110,255,7,248,119,10,5,112,72,80,18,243,61,47,242,86,174,215,214,73,230,56,242,129,113,32,103,96,46,1,170,212,32,44,9,137,25,65,73,204,138,150,180,2,133,7,220,147,59,174,0,126,121,236,212,171,56,7,136,150,100,183,130,220,179,100,225,179,120,95,32,43,159,255,212,237,97,21,247,47,239,18,171,144,51,219,40,48,97,109,214,255,197,149,199,41,210,218,230,212,159,220,119,86,17,102,21,201,217,66,40,178,10,202,229,108,48,254,150,114,17,236,63,37,189,138,49,103,108,241,106,235,47,108,127,60,84,202,206,26,20,15,174,197,240,201,53,97,198,40,113,150,26,208,61,98,84,10,254,124,229,80,196,6,84,143,156,108,102,243,100,243,148,204,86,230,211,111,141,237,245,205,125,145,80,231,143,8,97,31,107,54,17,211,54,92,117,78,82,81,171,72,21,15,37,179,149,27,145,99,229,29,68,136,37,103,241,71,35,21,27,2,22,44,202,210,124,175,95,27,52,243,223,23,197,239,146,252,133,152,233,4,69,198,50,76,99,112,8,87,82,107,169,196,80,36,7,53,183,28,55,120,9,202,61,21,209,138,15,87,192,12,20,108,92,39,22,9,234,15,20,196,150,83,95,68,110,84,175,71,129,78,39,246,215,147,94,124,118,11,109,19,5,182,87,59,158,151,247,243,126,36,149,155,235,97,182,247,86,170,176,15,170,233,71,136,129,163,93,143,227,216,221,180,218,78,240,56,93,129,58,197,30,25,186,204,235,175,29,178,140,30,92,207,117,112,171,131,121,200,148,7,153,19,40,117,248,72,196,161,53,192,241,230,130,194,10,114,235,102,8,223,201,16,180,39,2,195,134,239,216,56,59,243,230,229,187,215,78,189,46,2,203,135,174,48,194,141,208,21,222,204,171,114,163,180,202,191,174,148,245,54,90,190,78,207,140,2,192,71,204,131,249,109,204,101,165,235,57,189,158,245,139,193,199,2,145,24,24,42,97,166,245,186,203,231,111,116,97,199,243,25,82,138,64,41,80,199,50,22,60,147,93,89,162,171,50,246,84,150,4,71,89,18,88,131,82,13,13,174,32,68,210,130,197,218,121,200,131,133,87,234,136,132,199,105,8,78,239,214,73,150,200,100,58,150,233,237,51,56,134,140,211,91,89,212,232,209,70,158,124,142,207,103,149,23,66,235,5,140,19,11,96,82,230,138,125,166,27,173,126,233,28,109,163,158,95,109,176,193,163,77,62,247,102,51,15,59,205,154,138,160,69,77,220,220,108,184,78,182,132,70,144,69,65,175,94,47,46,20,99,134,152,111,33,20,204,117,220,73,230,44,221,224,50,150,170,161,185,61,218,203,106,213,115,188,92,20,244,67,247,247,138,214,95,223,120,138,253,183,50,132,236,103,230,153,188,66,29,220,62,66,136,166,123,189,95,209,147,255,224,249,59,85,109,175,255,59,231,208,180,231,23,40,96,245,67,2,202,117,56,102,214,185,227,47,180,147,63,224,241,219,110,253,97,43,252,3,207,173,71,139,197,112,100,28,111,177,98,31,187,177,119,93,148,105,92,175,199,193,183,111,160,223,102,45,161,94,63,58,179,151,188,96,162,164,145,182,229,4,35,166,143,46,19,188,149,77,64,153,105,128,26,98,55,246,157,16,34,150,198,136,252,48,14,138,231,94,60,187,96,138,188,161,215,112,149,117,209,222,245,108,230,95,45,189,246,23,27,182,149,142,169,243,248,201,211,103,207,15,255,249,226,229,171,215,111,222,190,59,58,126,255,225,227,167,207,95,190,254,215,127,255,139,157,113,196,30,142,196,247,243,120,156,200,201,191,149,54,233,197,229,213,244,255,90,237,206,206,238,222,254,253,238,131,237,38,70,141,94,35,223,184,87,97,107,63,172,76,212,131,65,120,163,15,14,14,118,58,141,112,230,163,160,250,161,32,138,228,178,40,8,73,40,88,178,32,154,21,184,14,178,114,86,169,109,244,148,190,75,199,103,160,188,2,33,10,44,13,4,237,122,245,246,254,253,118,251,193,206,222,77,181,216,217,245,234,187,157,251,221,206,222,222,206,126,43,27,37,214,3,33,109,245,195,129,14,98,72,134,102,212,15,183,183,61,125,18,158,210,40,200,25,184,246,205,235,151,36,209,2,134,85,63,126,50,53,160,151,216,205,1,79,78,251,250,160,213,215,141,134,23,6,147,84,143,220,183,204,140,130,40,150,82,229,143,57,134,235,109,117,246,246,189,10,28,173,62,179,176,159,228,87,169,194,31,163,251,140,182,124,142,188,217,156,55,219,222,246,249,54,237,122,225,9,71,7,238,157,222,80,125,194,78,7,131,206,110,131,255,99,167,179,160,227,18,193,81,199,221,22,248,108,9,127,11,17,24,194,151,6,33,118,166,5,255,67,5,12,21,212,209,175,107,44,121,1,87,191,170,195,218,80,226,91,5,22,124,215,11,140,252,104,148,72,134,110,27,93,229,47,238,215,219,123,43,219,149,254,224,187,20,137,235,96,229,249,35,184,250,77,99,173,165,157,146,201,132,41,13,47,19,131,169,135,19,88,27,229,50,191,227,249,237,181,113,123,194,52,236,239,254,142,158,29,175,20,224,69,192,218,251,55,248,176,221,198,199,110,246,212,57,245,5,158,19,131,221,190,64,15,177,173,238,182,216,218,31,208,42,54,221,135,5,215,24,59,33,83,143,141,107,83,96,127,203,221,105,8,175,190,191,227,121,189,66,192,161,206,90,31,157,101,188,215,185,73,163,26,5,147,152,113,112,155,39,255,251,184,241,175,86,227,193,246,255,52,79,155,98,232,59,136,246,179,196,228,116,123,155,253,99,215,227,27,180,85,175,23,60,144,169,72,66,184,58,194,138,46,57,179,70,219,243,234,89,121,76,228,165,219,241,27,157,45,190,221,245,112,121,48,224,91,157,155,117,103,60,207,90,218,192,237,133,104,204,250,87,65,209,244,104,148,77,120,75,240,5,173,86,253,67,122,157,154,168,219,187,214,89,238,172,26,190,208,161,15,131,51,145,4,75,98,110,154,128,230,108,2,46,36,92,134,240,249,195,203,167,114,60,145,9,36,6,79,122,94,149,14,121,102,174,131,13,225,214,201,2,51,87,184,4,144,131,34,170,72,238,98,92,70,34,178,145,208,24,5,61,136,203,40,104,76,156,104,49,73,158,162,122,116,160,246,150,234,54,90,161,254,39,248,249,201,32,82,114,252,180,80,228,198,39,250,212,155,107,153,103,30,90,245,146,30,246,155,91,27,53,178,69,158,129,1,53,22,9,16,17,17,150,16,153,77,67,34,52,97,228,73,26,69,160,80,202,10,62,98,169,25,73,69,8,57,4,123,161,36,143,207,100,122,62,98,161,248,14,35,50,24,25,51,209,189,102,51,202,54,3,169,134,7,217,169,88,112,72,52,16,242,246,229,39,92,104,90,107,142,232,154,240,196,27,52,73,99,156,206,238,91,92,189,185,57,206,254,223,216,192,97,45,116,206,196,155,205,71,250,219,249,81,148,89,28,81,245,186,157,226,50,34,75,171,65,9,66,169,83,130,56,245,250,122,25,196,158,95,116,143,23,72,86,200,10,88,120,24,75,102,222,60,95,6,172,36,180,181,124,121,15,57,231,203,110,203,111,97,122,221,190,25,188,192,193,254,50,176,117,130,209,62,242,67,124,193,12,244,217,220,97,220,23,222,53,95,30,201,121,248,31,10,251,123,39,171,15,124,163,168,26,143,50,53,197,107,62,13,87,202,137,123,61,78,163,219,139,246,63,20,127,172,20,155,206,111,68,57,233,252,34,196,145,121,111,35,23,16,58,251,198,51,245,250,18,165,13,74,63,139,196,116,179,109,140,40,167,124,62,52,188,121,3,83,52,14,22,199,47,34,249,19,202,171,38,235,39,180,125,127,167,179,215,221,197,123,133,15,180,209,185,143,239,59,221,251,15,124,73,27,213,214,174,111,104,185,211,245,83,44,147,116,160,202,50,73,177,76,212,73,122,74,93,251,191,109,240,248,157,15,209,133,43,75,177,217,217,45,119,187,75,55,23,117,50,41,38,124,187,211,29,12,38,56,124,125,117,226,78,182,247,119,113,253,193,96,176,235,109,183,119,79,233,164,50,109,76,89,240,45,138,252,161,253,30,14,253,145,253,30,141,252,169,253,22,226,54,75,218,222,207,179,224,41,77,252,143,20,252,115,42,253,111,212,244,19,58,118,19,31,124,233,155,255,39,171,222,113,3,71,98,32,12,159,199,216,22,192,98,241,121,24,135,3,59,155,100,18,223,126,165,238,72,102,94,193,135,159,13,105,221,184,255,228,115,229,186,162,164,50,154,241,177,254,221,131,127,107,79,246,0,159,11,186,46,86,123,152,215,189,248,123,47,254,174,189,217,11,189,23,185,66,2,226,133,254,88,127,238,193,159,181,39,123,192,207,165,186,46,136,153,171,147,242,177,126,51,108,51,144,97,168,234,156,12,223,12,168,136,148,152,198,100,196,102,92,176,164,18,52,76,72,30,136,121,138,119,113,50,234,97,32,83,132,110,136,201,232,83,3,237,101,48,67,78,7,228,64,76,37,56,13,192,65,160,91,76,12,161,147,1,221,142,18,11,97,148,78,7,120,32,38,52,64,48,25,176,195,128,139,138,168,182,76,11,124,91,160,12,167,83,251,161,124,189,40,183,214,215,133,240,236,112,200,35,249,122,73,238,236,253,220,54,218,5,65,125,36,95,111,9,110,137,173,48,38,50,177,155,124,189,28,114,51,100,93,76,86,39,69,39,195,55,35,5,238,21,141,201,128,60,14,150,32,164,56,17,240,141,184,34,196,178,72,159,10,59,10,19,119,102,89,77,69,63,10,143,50,11,99,77,4,236,196,16,116,9,163,101,58,120,24,168,100,176,3,147,81,155,1,4,157,112,193,100,128,231,40,102,22,5,139,156,18,221,16,135,9,179,108,50,114,51,144,116,106,89,114,50,160,39,7,90,35,36,147,246,64,190,127,95,197,158,179,149,251,174,241,253,50,212,231,2,214,165,162,234,105,22,124,24,223,243,105,196,66,177,133,226,161,143,227,251,237,176,219,193,117,209,41,237,30,83,129,173,128,83,186,85,66,166,196,182,4,154,90,77,58,39,36,183,227,130,187,117,6,117,58,32,199,1,105,75,74,152,76,9,248,80,162,160,217,72,155,16,57,73,232,229,76,85,157,16,30,72,170,186,162,179,39,36,182,35,67,180,81,61,17,189,115,132,9,195,172,114,34,160,71,97,138,130,23,125,42,224,155,225,148,52,117,149,169,208,83,163,219,201,10,199,227,248,249,253,103,121,138,118,5,165,108,199,248,121,57,158,230,178,0,141,106,24,54,227,103,126,195,246,75,71,208,173,197,31,200,207,11,226,55,4,235,242,52,154,184,79,7,244,129,32,69,172,220,19,19,194,13,185,80,109,93,17,18,83,2,57,18,136,195,149,147,129,195,80,113,107,213,236,158,144,218,142,74,18,164,247,116,192,15,132,18,64,154,77,70,28,5,60,4,93,44,153,16,112,75,64,105,56,194,122,66,236,92,198,220,149,146,50,33,192,129,0,42,10,97,246,164,232,166,36,42,43,213,123,66,250,20,161,241,233,254,191,32,41,36,79,219,25,216,194,49,0,170,77,213,14,6,179,242,109,243,181,179,193,172,18,219,18,237,120,16,171,22,214,134,135,13,224,68,67,29,31,11,108,178,131,219,65,40,13,88,157,34,157,2,136,219,33,45,158,18,219,100,109,141,76,181,162,154,186,76,181,2,77,109,141,60,144,153,0,246,173,131,207,81,91,137,127,21,142,52,147,147,56,219,183,215,112,156,222,123,114,229,213,20,25,132,173,156,64,68,18,235,245,114,188,207,254,52,18,2,204,214,95,126,229,213,171,232,63,69,51,210,168,207,70,119,69,119,120,153,233,119,222,161,175,180,187,207,162,209,93,214,34,187,167,186,141,206,234,149,124,243,31,183,214,185,219,221,66,231,207,242,231,91,43,100,236,102,133,242,231,25,123,101,108,188,173,78,155,68,161,216,57,53,59,70,40,103,108,75,149,246,192,215,254,192,61,57,50,192,139,202,218,158,176,34,189,147,98,31,12,143,218,225,23,156,211,45,225,1,145,91,123,237,26,132,119,211,104,229,55,232,227,187,171,25,1,109,195,177,210,30,57,136,178,180,247,100,210,21,187,243,72,54,57,82,203,40,225,241,112,71,101,202,109,127,83,240,124,221,27,222,189,30,252,184,254,96,246,60,66,167,107,190,126,247,202,155,105,126,73,26,81,232,18,81,224,142,229,195,219,11,147,44,27,75,238,175,149,12,253,3,232,51,166,57,164,129,177,178,164,242,243,103,223,124,189,230,45,250,228,122,89,10,141,126,169,224,179,91,8,254,72,137,18,229,84,242,227,181,75,69,49,242,71,137,14,57,82,145,81,9,153,21,217,58,12,17,89,3,249,83,198,169,209,147,81,185,34,93,110,198,40,90,162,38,91,255,8,207,111,68,110,41,180,170,170,185,142,80,62,203,34,136,63,105,4,63,112,7,191,225,176,28,161,111,7,11,154,15,103,97,93,129,86,247,56,27,70,200,32,138,106,120,142,85,61,118,58,52,163,199,86,96,104,190,190,178,5,132,38,240,12,97,188,63,37,188,166,43,45,15,214,55,181,118,57,48,31,27,91,86,251,89,248,212,216,14,149,65,62,39,52,6,60,20,27,237,221,195,242,122,156,114,243,235,61,163,244,158,2,129,247,10,163,123,253,122,147,183,111,238,136,218,153,47,213,254,26,193,171,233,157,12,82,111,166,227,198,85,99,235,168,92,29,65,78,24,167,89,95,21,233,170,202,236,187,188,9,117,159,77,195,197,118,44,238,51,116,146,16,17,47,155,186,174,18,178,128,116,16,219,50,204,66,127,254,230,235,207,181,174,186,76,148,21,139,69,69,203,89,248,253,119,79,159,133,40,180,238,184,214,55,109,204,98,247,121,201,243,143,111,251,209,11,144,140,108,107,86,107,119,97,46,234,50,155,201,24,228,105,118,79,198,218,168,230,111,47,32,65,237,198,129,241,33,145,97,212,165,117,185,212,220,234,110,248,70,136,110,20,124,1,29,59,14,111,39,216,162,59,139,145,59,162,244,252,235,177,245,166,171,252,137,26,46,249,106,77,54,156,254,27,59,213,34,102,92,129,17,117,80,154,104,154,238,72,185,165,131,75,214,35,22,91,134,167,192,176,94,159,68,22,2,246,90,173,215,75,99,248,31,176,59,52,14,131,225,183,147,236,205,246,114,31,14,28,227,225,236,83,50,252,104,182,73,126,71,230,158,24,115,155,127,23,123,221,132,218,3,159,204,194,231,54,186,134,81,12,129,35,215,44,246,227,211,166,245,62,131,133,203,186,246,31,227,8,122,102,102,27,151,63,233,22,110,154,5,174,75,2,184,19,55,235,189,239,162,168,69,105,76,170,138,150,198,152,220,72,135,136,35,30,67,218,61,12,63,5,120,26,161,189,175,130,149,91,35,221,49,180,46,1,74,69,205,31,183,83,197,93,114,53,220,190,191,128,5,209,95,120,191,187,120,243,205,59,69,205,53,171,56,125,111,70,56,149,122,22,254,69,212,65,74,74,200,29,63,4,181,83,39,74,26,204,224,226,52,10,192,133,128,248,252,193,56,140,96,30,73,248,223,230,63,89,147,99,85,113,102,180,24,66,92,137,106,22,221,177,183,166,225,117,234,199,170,149,215,120,103,110,183,49,191,95,189,118,57,97,151,78,107,132,94,191,154,19,20,127,81,86,181,54,17,248,251,45,30,238,121,212,240,184,146,20,184,62,118,15,211,51,83,65,108,249,102,71,15,225,231,192,12,11,218,11,243,17,117,215,231,139,213,183,51,104,145,209,157,247,103,102,234,233,82,45,224,137,29,176,216,229,206,24,125,125,82,231,167,222,208,245,249,170,3,251,124,255,245,103,38,17,114,154,220,164,15,240,19,21,111,163,100,67,115,33,41,74,72,174,169,108,54,226,12,43,118,14,143,31,27,33,51,42,177,65,86,221,167,29,55,201,220,23,173,142,68,9,206,50,15,217,188,143,228,53,250,128,62,162,155,118,162,27,99,189,199,93,210,121,18,132,97,187,211,5,111,56,43,41,222,81,200,20,72,22,241,131,21,222,211,205,75,166,49,44,211,96,10,197,36,251,173,86,58,129,97,183,194,133,56,199,154,108,44,37,57,89,97,49,46,141,62,115,83,15,206,73,193,248,33,169,25,86,164,84,88,81,201,114,164,14,74,211,2,215,12,97,82,85,156,98,7,160,15,33,105,234,27,146,62,181,197,79,141,60,122,74,183,130,6,207,191,64,63,138,141,208,2,125,78,249,41,213,44,37,193,183,180,166,232,3,201,8,71,223,26,74,240,212,232,71,163,74,194,15,64,117,240,17,180,71,240,73,33,126,99,33,10,189,58,15,244,229,167,135,98,35,56,10,65,213,145,76,231,6,37,186,150,20,251,125,91,82,10,89,16,238,136,38,214,153,141,138,41,185,221,136,236,208,20,68,110,89,105,58,109,220,206,172,220,81,201,116,187,147,141,67,12,221,118,157,167,248,254,212,162,234,122,125,81,157,181,100,179,145,201,222,48,152,220,88,13,187,230,159,162,230,168,187,224,93,79,90,99,146,186,52,242,80,103,144,9,173,105,182,186,137,161,221,45,208,110,137,118,247,209,238,4,237,30,160,221,195,6,252,115,253,233,205,178,200,254,216,9,210,28,155,62,173,200,243,109,144,210,82,148,219,102,172,196,52,187,49,163,133,169,23,189,220,100,72,145,162,66,149,164,205,36,126,10,81,10,85,145,148,162,167,159,126,99,190,241,143,116,91,115,34,209,55,180,228,2,25,136,164,2,125,100,247,148,68,161,175,217,134,186,234,3,67,2,66,45,25,149,38,108,246,168,87,181,26,252,91,208,162,85,5,225,124,228,243,227,249,27,173,170,141,213,117,53,66,31,61,120,227,168,43,231,171,74,40,102,29,245,41,235,171,83,42,33,72,57,38,156,109,203,4,158,183,65,4,180,153,1,174,181,40,18,28,47,31,152,58,65,183,22,149,41,66,201,238,209,26,219,126,172,132,180,179,97,172,79,163,195,131,156,84,138,38,254,163,221,216,57,21,49,152,137,144,168,244,86,138,186,66,202,166,38,34,80,76,36,37,227,182,245,42,47,15,244,17,117,26,233,83,186,111,31,152,37,46,11,147,203,226,127,18,243,253,80,169,72,150,193,12,56,247,254,56,7,92,203,104,73,74,149,11,89,36,165,40,123,143,255,102,51,36,93,225,167,174,100,39,96,95,48,109,95,48,253,83,63,94,96,97,39,70,85,74,19,39,181,218,144,244,229,214,110,138,187,214,182,53,85,68,210,82,143,137,172,32,91,234,42,79,236,116,152,139,180,86,112,172,111,68,173,193,205,132,212,90,116,196,154,97,86,158,18,206,50,55,183,239,72,38,246,78,218,111,11,154,171,226,37,73,188,181,118,247,131,85,197,74,220,57,60,208,76,165,199,52,63,167,88,43,58,239,41,145,233,238,82,239,161,81,115,70,121,182,234,172,199,34,207,21,213,9,94,86,103,35,19,156,138,209,192,190,76,89,41,142,204,134,229,27,187,213,222,219,118,117,251,67,208,244,147,133,170,139,130,200,67,211,253,28,76,194,153,210,152,105,90,180,246,206,231,247,90,104,138,50,142,178,12,93,152,183,208,78,162,156,109,107,73,145,155,75,124,96,181,214,79,227,91,115,73,168,113,186,165,101,214,12,128,224,168,230,168,160,101,221,64,237,221,114,11,30,94,22,169,25,35,92,108,71,226,253,104,147,212,14,12,223,201,173,29,156,137,139,14,155,217,178,179,115,32,242,2,23,73,141,168,72,202,244,33,89,172,186,5,254,73,74,238,147,220,171,186,92,203,109,20,248,209,35,5,239,71,79,147,214,82,25,158,74,64,170,174,108,147,140,41,152,152,50,79,232,50,254,90,86,108,145,58,221,162,83,150,81,129,82,98,194,92,33,82,103,76,32,150,75,82,80,68,139,13,205,144,203,163,232,187,210,118,224,116,142,44,88,150,113,10,42,157,58,211,67,103,126,229,131,25,229,40,164,221,221,212,79,94,163,139,186,183,209,101,219,29,191,85,2,255,203,45,62,75,130,249,234,50,194,193,19,220,252,194,137,166,192,60,197,70,124,82,104,3,244,69,245,146,238,189,132,47,14,204,202,120,106,21,46,198,229,67,95,174,72,9,212,161,112,232,11,172,52,163,238,92,136,194,35,42,149,130,115,172,74,82,97,200,90,72,117,73,149,74,224,226,229,140,21,166,151,29,219,86,146,140,81,152,212,165,40,112,191,72,5,19,234,41,35,87,19,181,184,64,19,50,99,37,225,189,49,156,168,29,205,240,57,149,194,99,101,93,80,99,22,118,99,112,130,250,22,191,192,44,73,58,174,71,66,175,176,82,81,61,70,186,185,169,139,140,96,94,157,93,164,185,8,15,94,203,243,124,32,122,84,110,55,179,7,79,2,243,192,16,44,79,30,6,247,130,248,65,116,81,133,155,165,141,126,243,251,181,185,249,53,98,185,156,118,61,236,106,167,217,5,242,134,215,210,251,183,145,16,226,174,47,87,253,94,93,18,165,71,253,114,176,145,227,129,93,77,125,28,118,8,43,97,96,249,146,130,229,124,68,85,180,98,196,23,50,41,42,111,118,7,193,42,7,176,183,107,2,14,6,78,73,83,75,61,97,48,121,74,25,217,62,37,121,39,166,176,159,196,166,184,247,114,74,240,238,182,73,226,161,255,79,10,255,159,20,12,241,255,147,194,255,39,133,88,73,12,87,89,77,31,204,100,163,4,175,53,93,245,39,110,191,237,128,207,126,103,231,119,127,24,80,97,140,202,185,105,44,183,39,89,165,156,85,137,164,169,134,188,70,248,29,173,246,59,166,41,68,184,221,31,239,37,169,38,247,57,109,236,107,190,104,75,27,251,83,229,64,243,72,27,187,195,36,158,55,238,3,84,113,154,107,3,192,127,80,132,222,129,50,252,63,2,150,29,16,63,144,180,104,99,45,42,195,99,254,5,14,40,44,161,224,169,231,248,193,188,57,199,54,253,57,121,96,56,138,13,94,204,187,61,52,238,170,94,118,204,64,123,56,161,157,120,202,114,66,24,100,78,38,148,133,35,240,65,4,60,234,5,228,128,31,57,82,232,145,101,224,194,114,68,88,142,9,247,123,248,228,8,30,248,143,216,7,248,136,125,225,209,135,71,104,199,109,119,186,199,251,222,54,102,37,28,178,122,212,21,219,56,231,244,172,7,161,224,57,241,17,165,199,128,193,197,220,209,54,216,128,198,240,29,29,53,10,32,127,143,31,244,224,195,165,135,123,108,17,119,216,30,63,105,92,84,46,7,232,239,127,91,62,54,147,234,223,127,242,36,40,57,194,253,249,152,0,37,79,88,30,17,150,29,33,175,57,111,134,109,125,251,254,75,122,176,71,5,21,192,1,182,209,162,25,206,247,110,34,152,153,164,211,140,110,163,182,141,73,201,10,131,96,203,234,10,48,30,160,24,44,84,0,45,67,100,192,202,156,149,12,134,142,59,175,224,238,32,51,61,215,196,238,86,1,67,179,245,199,210,90,81,137,29,33,1,130,187,240,188,128,78,129,54,30,206,179,199,10,7,124,164,111,10,78,202,46,26,176,20,251,198,126,100,76,82,183,14,26,168,35,166,130,79,137,169,125,77,104,99,56,38,43,156,82,235,180,61,94,97,11,37,14,106,99,184,206,101,249,193,179,248,162,191,26,158,178,117,63,184,127,129,207,78,105,158,218,198,91,82,225,69,99,254,77,254,201,222,117,173,183,110,195,224,251,60,5,186,163,175,182,62,78,13,251,65,186,151,19,247,236,189,231,179,23,130,96,138,144,105,147,58,235,170,217,38,1,252,144,66,18,83,9,47,29,26,49,227,200,52,96,199,129,118,26,113,52,162,233,53,165,88,246,215,227,66,225,99,114,112,60,158,61,217,244,248,134,171,40,144,220,189,33,9,24,36,204,191,156,9,8,152,227,40,79,70,233,213,154,71,174,201,123,121,35,178,236,227,216,129,4,23,248,239,223,232,78,247,250,31,92,227,177,235,24,204,145,150,153,187,193,197,48,14,140,7,51,56,24,152,74,187,76,176,85,149,64,216,169,157,221,117,11,16,116,219,128,110,123,208,157,43,1,33,67,188,54,74,149,95,130,233,193,88,141,159,62,3,32,70,16,35,13,81,155,35,82,123,138,212,30,145,186,83,164,14,73,111,12,254,202,126,221,132,171,187,33,174,76,36,251,248,234,108,11,125,127,116,109,55,196,117,221,144,55,45,47,86,222,180,172,96,191,64,174,67,185,94,225,167,62,43,151,28,144,114,169,222,3,127,158,150,58,108,99,118,136,194,182,122,248,114,176,189,60,74,230,154,173,239,150,199,216,84,155,58,208,27,73,158,162,158,136,157,36,214,199,180,250,64,234,103,164,181,73,72,166,81,102,104,230,12,73,122,38,127,181,214,129,156,124,3,33,61,248,54,102,34,87,130,222,212,41,122,51,137,55,130,60,73,61,17,91,73,220,38,169,91,38,127,204,183,38,113,23,40,185,206,22,129,126,38,203,193,198,128,167,159,220,139,42,33,117,71,82,69,98,95,74,123,249,36,38,79,80,19,41,145,220,219,227,17,125,79,148,134,188,66,87,243,25,90,198,199,87,152,16,159,39,255,195,4,99,237,238,226,98,30,23,57,189,142,150,249,180,182,21,224,251,180,170,37,105,85,177,40,218,135,173,82,57,105,222,67,227,161,211,101,2,251,188,64,221,130,113,96,251,2,129,104,113,215,62,47,209,58,208,125,11,189,203,138,196,64,180,68,160,65,237,154,14,63,178,2,95,237,239,98,92,180,118,121,153,116,134,41,7,70,151,10,45,82,212,145,41,204,42,42,173,9,19,73,91,114,160,104,84,72,164,215,13,114,227,98,28,67,178,241,248,124,242,38,26,121,200,207,204,111,104,106,53,63,106,87,177,105,93,205,74,166,60,138,213,147,187,84,51,189,179,223,70,146,177,123,99,216,190,220,160,128,59,244,217,14,83,37,187,253,107,44,112,94,214,14,131,206,218,172,116,21,179,92,63,227,90,108,173,253,147,119,53,229,199,127,15,49,38,231,203,15,110,93,184,91,60,80,109,19,81,41,139,160,104,19,227,131,193,215,207,138,161,163,21,73,3,55,30,88,197,204,72,43,120,111,174,93,150,71,28,51,204,247,34,207,199,241,191,96,139,253,210,44,127,218,111,101,73,227,100,169,12,86,69,242,7,215,205,42,149,21,84,236,218,169,30,140,182,248,217,159,117,237,130,30,236,88,100,21,248,164,142,7,131,71,107,251,159,130,95,72,106,95,34,223,103,217,85,12,58,56,240,63,224,251,31,39,154,111,182,39,181,197,200,28,179,74,40,105,230,237,118,106,67,19,39,124,222,110,226,11,62,42,115,124,90,79,149,202,221,65,59,114,178,61,34,209,112,113,32,33,210,173,169,197,38,193,184,52,77,161,53,35,241,208,112,40,0,245,64,65,84,175,159,215,178,145,40,200,162,132,173,99,41,167,178,188,172,147,200,57,87,160,232,93,76,137,228,115,149,158,163,203,142,50,202,37,32,152,30,189,186,116,120,109,63,158,195,155,3,50,82,212,105,144,98,230,185,106,37,39,231,163,60,176,154,242,211,149,188,137,169,223,61,78,136,117,150,79,182,51,156,100,173,36,20,47,232,229,80,114,73,47,192,178,203,177,228,17,122,26,235,225,30,207,127,254,163,65,239,233,213,154,95,225,174,250,244,97,107,30,146,253,201,37,152,198,128,110,44,180,238,195,48,195,49,30,202,106,69,150,53,81,140,163,238,128,41,44,8,115,151,204,44,200,171,85,98,20,149,5,78,53,134,217,241,53,78,12,37,187,48,204,245,60,30,254,69,142,147,28,170,106,252,204,19,83,137,239,48,243,203,124,38,123,163,164,209,207,27,220,188,197,207,27,92,193,25,20,164,205,192,234,37,78,229,229,231,113,90,108,56,160,151,29,205,255,31,205,153,251,73,167,138,85,106,193,113,166,93,75,209,163,241,54,123,156,165,97,249,100,89,138,107,29,12,113,166,110,205,25,220,205,173,39,151,53,253,21,74,250,250,251,38,74,156,26,103,188,105,126,255,163,42,246,71,108,3,182,5,219,165,55,66,1,164,253,199,238,236,213,18,72,223,129,239,161,81,31,12,185,119,251,102,191,91,2,105,76,7,198,42,182,21,203,81,201,50,54,74,149,35,182,30,58,15,125,191,24,78,102,143,79,0,214,38,193,123,67,100,231,171,140,73,203,164,231,207,175,184,27,114,181,101,160,50,171,45,3,21,210,82,229,48,50,65,85,8,19,157,218,213,155,115,61,177,199,220,148,198,136,11,23,85,62,227,37,11,22,201,60,77,26,72,108,129,12,144,88,249,139,129,200,234,230,17,130,53,93,142,48,37,152,146,48,117,115,196,23,186,59,195,182,116,74,85,137,214,81,18,23,19,167,127,15,190,33,127,78,183,66,249,4,95,85,21,235,242,165,213,224,169,181,86,213,148,167,211,199,116,231,195,235,83,71,77,119,90,208,13,97,1,88,76,249,46,101,59,80,190,75,207,69,224,213,167,9,193,51,232,108,222,197,117,11,252,172,161,79,95,57,211,148,161,167,175,124,121,0,154,199,142,125,28,214,35,237,96,117,74,45,81,3,85,104,28,175,246,79,161,5,251,91,139,212,224,96,78,105,240,231,220,173,11,250,119,69,226,209,17,160,231,175,182,112,232,63,1,110,78,25,27,188,33,42,34,193,88,69,218,162,20,3,81,77,8,236,80,18,2,110,168,2,85,119,118,127,15,20,254,160,213,240,195,22,226,130,16,236,240,8,216,194,71,171,96,133,10,102,166,130,82,172,130,250,124,42,124,147,248,39,50,240,6,166,182,202,67,162,157,210,222,224,213,119,99,242,28,188,8,118,97,138,118,215,56,179,130,225,107,181,61,180,106,129,86,219,28,60,63,97,26,221,16,93,123,188,33,178,27,139,218,119,46,234,161,209,230,126,232,156,216,232,38,132,122,223,252,107,135,247,248,177,53,126,208,192,186,190,187,222,205,82,226,168,230,33,201,110,84,104,179,227,23,179,198,30,115,212,216,195,202,141,15,3,92,44,106,33,186,168,163,127,172,131,203,128,255,200,253,208,217,120,137,49,105,181,13,91,69,213,253,150,59,133,214,244,56,233,147,13,80,175,206,187,255,216,186,106,228,8,98,0,86,239,47,52,234,179,28,182,29,120,70,160,11,195,49,61,255,44,195,113,181,96,102,148,36,40,103,68,111,2,166,202,146,93,70,104,63,4,18,141,200,226,172,97,225,57,77,67,95,145,119,203,33,163,56,232,63,167,217,203,43,116,0,77,121,82,4,108,232,219,247,2,223,111,150,39,42,9,115,8,193,58,112,218,126,46,85,186,173,36,151,169,188,181,3,71,201,175,84,172,116,59,118,210,107,97,190,218,35,105,48,159,232,86,70,35,103,190,58,39,210,134,121,210,9,243,159,166,26,5,179,236,121,58,55,134,174,242,65,231,207,116,194,55,9,153,12,51,93,124,98,17,69,31,44,155,178,37,86,255,127,3,239,74,20,154,55,85,181,92,46,203,101,39,6,205,74,220,121,149,183,79,72,241,236,113,184,178,212,218,247,170,175,181,70,37,50,166,188,169,137,88,157,44,187,154,57,26,39,178,157,8,197,104,121,208,114,111,119,243,118,228,153,18,160,131,184,179,201,92,118,85,21,134,111,111,12,255,44,253,149,50,77,133,3,100,148,120,179,92,179,89,229,200,142,235,48,240,42,188,0,80,2,192,245,60,78,28,248,165,10,222,233,127,119,151,202,83,83,243,3,25,77,44,82,99,33,233,159,184,188,218,8,63,31,35,180,225,231,94,243,5,150,190,56,84,173,188,183,196,211,61,63,203,15,228,252,253,137,227,163,69,32,38,61,109,50,230,124,136,219,250,26,253,111,179,199,227,224,241,175,203,162,67,255,190,97,122,216,238,222,95,22,62,12,44,172,188,36,187,239,118,125,108,174,86,27,4,21,48,193,106,30,58,29,71,176,13,42,251,113,48,186,104,242,164,121,211,60,141,42,48,42,112,216,200,10,203,254,151,219,164,219,160,219,254,219,109,124,162,64,190,211,105,208,169,91,148,45,31,112,153,96,21,229,251,86,22,115,194,6,122,245,122,216,179,126,150,62,36,187,231,109,107,254,114,124,217,173,127,155,247,255,141,202,177,60,90,212,117,91,0,189,173,202,11,120,248,120,215,229,33,120,222,246,197,245,134,71,210,251,109,35,128,54,114,250,253,169,240,106,120,201,78,4,194,136,151,0,34,139,220,164,61,60,94,156,131,206,26,218,241,201,54,149,192,134,234,82,215,218,240,34,232,13,122,68,22,114,140,238,139,253,154,173,123,183,1,167,5,73,39,129,201,113,98,179,135,117,159,4,71,189,120,179,220,248,98,216,5,79,126,185,244,59,184,178,216,158,234,133,229,129,139,40,10,69,215,251,18,118,18,89,38,4,211,33,69,176,33,37,148,66,186,33,68,134,5,43,216,118,147,247,228,139,54,248,167,47,170,231,139,244,227,98,157,131,109,83,188,92,133,66,241,117,49,47,214,71,8,63,227,182,126,124,162,154,211,231,61,61,84,7,43,155,62,108,241,35,3,12,31,48,72,123,178,126,137,180,132,114,249,34,237,193,49,10,41,192,227,49,253,41,124,121,88,49,112,104,83,245,59,93,27,245,41,23,243,27,36,22,66,240,80,5,195,58,171,29,118,188,35,85,14,111,94,108,213,211,143,228,158,18,192,143,216,45,208,32,97,45,255,24,38,141,92,71,99,241,5,34,165,4,127,27,207,210,190,6,204,154,230,199,15,88,30,56,101,11,253,28,132,11,45,69,71,89,81,61,252,216,102,129,217,68,88,134,228,108,72,228,29,3,185,154,198,196,39,158,5,214,93,114,251,190,141,21,124,49,89,24,39,148,25,126,44,65,73,72,125,97,217,85,27,4,1,177,212,8,253,196,104,165,145,239,166,150,234,141,213,206,35,135,24,18,165,111,201,233,1,77,90,180,82,12,164,14,207,146,15,90,127,0,184,101,116,42,44,9,102,27,207,62,233,76,68,199,152,137,169,111,201,33,77,111,41,188,218,244,50,60,31,209,13,95,104,246,120,145,201,134,89,51,95,58,176,133,126,127,198,81,174,244,81,56,243,224,102,64,211,37,167,70,247,32,146,89,30,61,251,174,196,212,130,91,191,173,88,96,13,68,183,137,39,97,210,160,8,137,158,172,91,12,53,170,251,230,128,191,149,225,61,144,4,95,5,46,139,220,250,133,181,230,253,134,18,204,57,180,41,70,201,158,81,190,153,253,29,3,40,174,59,193,12,126,218,0,26,147,13,128,159,35,244,167,19,70,135,131,223,121,91,232,146,8,157,24,224,5,57,173,251,144,172,167,198,10,144,228,221,102,25,252,156,142,72,50,8,244,165,118,171,228,0,61,205,2,81,190,36,203,75,136,156,82,39,135,168,173,38,32,11,248,78,63,154,121,42,186,201,36,164,68,46,41,120,77,8,24,45,184,162,98,139,108,32,223,28,220,35,234,140,182,214,35,83,233,0,147,211,160,148,250,50,250,27,252,75,10,123,12,150,169,116,17,78,127,202,158,186,105,7,51,207,231,38,96,198,115,161,12,130,217,129,102,113,6,216,75,0,12,18,243,147,46,142,207,143,174,19,227,205,241,230,52,28,41,122,147,34,54,214,195,55,25,246,119,105,14,85,94,155,48,127,184,247,115,241,74,10,64,154,22,51,77,78,11,247,150,6,7,13,41,77,202,198,136,215,1,223,98,85,181,163,139,218,126,19,33,154,3,94,131,93,106,58,174,53,202,231,251,168,92,68,246,88,140,218,203,160,167,134,171,239,35,95,217,31,11,253,177,61,117,98,34,21,182,235,6,15,125,106,104,222,58,164,174,68,13,60,88,145,30,203,23,33,74,3,161,133,71,147,237,33,153,62,73,201,46,200,238,148,218,49,231,5,52,216,125,190,232,25,221,148,12,81,46,15,198,72,38,43,20,23,154,53,184,75,85,20,32,212,9,80,27,227,5,163,101,11,229,54,73,68,18,47,22,169,148,110,60,127,219,2,171,229,65,6,91,201,39,76,197,253,253,200,210,204,78,125,57,33,19,150,194,40,60,55,116,128,102,138,54,165,18,52,29,73,36,223,45,202,23,229,162,163,86,141,211,42,192,173,250,28,65,83,75,4,235,70,22,250,175,188,171,108,128,28,7,161,159,231,95,188,235,185,116,164,211,157,115,119,119,215,117,119,223,95,127,64,40,203,155,116,236,92,86,70,152,52,105,9,33,121,4,200,170,76,220,166,57,176,144,138,85,32,170,53,210,11,186,162,214,229,186,45,218,3,72,248,78,34,131,13,198,12,26,122,128,33,16,1,57,226,0,22,106,3,139,3,106,162,161,7,218,22,158,225,168,36,61,66,219,198,143,222,46,103,202,27,111,41,133,46,80,91,125,52,229,141,189,110,199,127,225,40,74,84,187,55,231,5,20,27,234,215,32,120,33,185,9,78,78,84,193,7,71,113,247,189,181,81,144,173,191,181,223,187,237,251,71,84,190,234,48,187,96,246,206,1,185,155,194,236,56,180,169,90,72,190,33,154,191,68,13,217,47,13,234,249,12,117,9,134,6,72,33,56,167,145,36,176,216,11,238,169,128,169,190,133,241,99,161,196,125,126,239,182,61,167,71,251,213,47,241,240,78,109,50,115,97,56,111,55,178,11,12,215,5,132,179,207,12,248,186,94,254,13,240,75,121,212,160,184,72,242,26,127,160,182,94,167,204,228,65,82,246,202,97,23,47,54,198,60,34,235,201,16,65,247,190,191,112,243,184,100,36,64,121,27,82,90,52,47,21,116,33,2,243,225,194,38,183,119,86,71,59,116,101,139,184,149,79,183,22,137,32,239,50,73,118,153,208,118,95,137,90,252,160,92,124,175,153,73,133,151,47,220,213,155,128,217,29,164,229,69,47,197,229,69,84,139,204,212,86,36,218,148,31,186,91,171,76,147,169,9,11,65,82,250,23,75,249,197,71,55,247,132,196,17,51,9,40,39,215,89,42,76,46,60,163,210,70,177,129,194,52,75,66,225,199,244,89,154,52,27,70,145,185,43,248,232,25,69,113,84,50,227,93,145,174,181,227,28,244,101,122,250,94,35,230,0,207,248,215,164,100,90,143,89,98,177,199,243,216,158,229,177,107,86,10,210,49,159,120,78,149,45,90,230,138,23,57,84,207,244,176,184,71,248,115,100,156,105,245,114,34,84,178,127,120,45,114,125,7,55,136,144,33,200,117,134,169,231,113,27,13,143,101,26,197,22,66,133,219,109,137,210,163,177,71,85,148,232,59,28,219,164,61,206,216,228,85,233,4,170,143,217,24,233,249,134,38,146,247,82,169,142,90,8,131,203,16,168,253,48,153,156,234,46,37,238,82,254,72,176,117,137,239,81,42,78,95,137,165,84,123,45,61,159,75,214,198,237,194,195,121,29,199,101,167,238,29,159,167,88,114,14,87,140,125,23,138,81,62,255,49,138,177,251,173,106,145,3,17,178,62,20,160,32,187,206,18,103,122,180,71,111,202,206,16,238,51,173,188,60,141,160,233,178,69,63,114,33,127,9,218,188,20,11,130,87,66,133,20,240,41,116,160,66,85,115,134,15,87,223,145,202,20,44,212,65,255,246,109,79,26,211,69,47,203,213,151,102,228,28,100,68,4,58,73,213,149,205,50,101,9,114,183,72,148,37,130,60,76,158,186,255,172,60,121,20,74,147,186,168,19,64,160,43,243,86,112,81,159,166,88,65,7,102,186,20,124,217,191,26,100,104,24,201,25,154,122,245,250,118,249,221,250,244,249,236,173,126,157,36,150,189,233,124,49,38,7,136,158,98,237,174,97,226,13,9,201,37,73,221,45,179,169,24,184,173,227,89,70,184,66,202,98,77,108,184,115,125,108,122,160,134,62,185,32,215,157,180,211,212,143,158,150,189,1,106,8,155,53,156,103,110,219,166,226,188,200,161,243,227,188,217,103,234,226,11,107,217,94,32,34,187,48,240,34,5,106,243,236,22,187,12,35,220,66,21,158,67,215,34,88,177,123,174,216,196,31,70,73,204,160,208,251,43,157,154,151,115,158,154,183,179,136,227,174,43,30,17,216,169,245,20,0,203,164,173,233,193,133,204,79,199,132,194,88,123,42,207,39,30,79,99,219,151,88,139,106,135,6,51,10,204,72,203,24,10,112,99,48,84,175,51,46,156,206,200,201,240,4,239,228,59,109,205,179,97,19,80,241,98,76,29,245,70,193,26,128,171,0,13,55,53,122,23,196,116,99,223,229,43,42,93,176,4,236,210,137,215,26,149,84,27,252,47,13,167,213,191,48,43,133,118,93,43,194,249,193,217,27,162,90,222,250,28,51,124,254,201,187,111,189,245,57,93,155,122,177,52,82,47,154,254,42,225,122,67,88,36,89,83,119,200,214,133,163,199,78,94,24,238,97,72,89,130,148,191,130,155,230,92,13,21,175,9,101,28,247,179,119,13,105,212,39,242,14,76,13,89,246,76,47,80,111,137,6,238,91,93,181,64,75,229,103,33,118,33,90,43,135,171,5,67,244,66,39,156,206,148,112,189,117,217,26,253,245,153,10,202,159,76,248,60,44,4,217,55,188,184,129,87,46,244,35,101,200,123,28,70,138,40,165,239,31,125,244,71,39,185,207,129,151,176,224,60,141,205,243,159,45,212,143,8,177,165,237,95,57,46,141,168,254,133,158,208,72,26,198,231,159,110,199,39,14,238,114,46,176,3,42,63,101,130,29,129,40,88,56,77,24,183,143,152,51,215,242,100,146,156,201,112,223,59,175,153,253,101,131,236,157,163,215,78,168,2,193,187,111,236,51,208,108,36,156,241,107,222,125,163,153,253,125,234,97,80,125,248,170,236,236,215,183,63,153,76,210,93,159,242,242,94,188,65,125,239,219,1,220,36,77,206,37,95,216,245,98,190,187,190,113,139,223,17,232,242,136,58,156,252,25,166,70,121,66,255,198,119,192,238,65,145,84,10,11,185,17,152,251,203,224,137,116,68,62,171,89,196,82,151,139,74,226,20,228,118,132,60,94,196,163,179,71,165,84,81,139,199,254,6,155,165,47,209,14,181,93,154,134,72,129,255,149,29,83,180,168,52,103,118,76,225,252,168,49,179,251,123,140,153,192,107,226,197,9,73,20,190,238,23,51,106,40,155,0,127,176,36,68,94,249,199,30,255,31,244,124,255,247,246,252,4,154,51,220,177,191,110,148,176,1,116,204,99,202,73,66,80,151,176,151,196,93,76,93,39,95,250,21,191,41,40,143,0,146,0,0}; diff --git a/Software/src/lib/ayushsharma82-ElegantOTA/src/elop.h b/Software/src/lib/ayushsharma82-ElegantOTA/src/elop.h index 525c16aa..9a41a44a 100644 --- a/Software/src/lib/ayushsharma82-ElegantOTA/src/elop.h +++ b/Software/src/lib/ayushsharma82-ElegantOTA/src/elop.h @@ -3,6 +3,6 @@ #include -extern const uint8_t ELEGANT_HTML[11640]; +extern const uint8_t ELEGANT_HTML[10615]; #endif From 6094a2c85b4593ae2e4c9f39e2c75bb4c4ea6f1b Mon Sep 17 00:00:00 2001 From: korhojoa Date: Sat, 13 Sep 2025 17:28:56 +0300 Subject: [PATCH 192/245] Add tool to easily replace OTA file --- Software/src/lib/update_ota_html_gzip.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 Software/src/lib/update_ota_html_gzip.py diff --git a/Software/src/lib/update_ota_html_gzip.py b/Software/src/lib/update_ota_html_gzip.py new file mode 100644 index 00000000..794fe8e2 --- /dev/null +++ b/Software/src/lib/update_ota_html_gzip.py @@ -0,0 +1,23 @@ +import json +from pathlib import Path +libpath = Path("ayushsharma82-ElegantOTA") +gzipped=libpath/"CurrentPlainHTML.txt.gz" +header=libpath/"src/elop.h" +cpp=libpath/"src/elop.cpp" +if not gzipped.exists(): + print(f"Please create {gzipped.resolve()} to replace OTA file.") + print(f"Example: zopfli -v --i10000 {libpath.resolve()}/CurrentPlainHTML.txt") + raise SystemExit(1) +gzipbytes=gzipped.read_bytes() +intlist = [int(one) for one in gzipbytes] +content = json.dumps(intlist).replace("[","{").replace("]","}").replace(" ", "") +headertext = header.read_text() +header.write_text(headertext[:1+headertext.find("[")]+str(len(gzipbytes))+headertext[headertext.find("]"):]) +cpptext = cpp.read_text() +first_bracket = cpptext.find("[") +second_bracket = cpptext.find("]") +corrected_bytes = cpptext[:1+first_bracket]+str(len(gzipbytes))+cpptext[second_bracket:] +cppout = corrected_bytes[:corrected_bytes.find("{")]+content+corrected_bytes[corrected_bytes.find(";"):]+"\n" +cpp.write_text(cppout) +print("File content updated from", gzipped.resolve()) +print("Bytes fixed:", cpptext[1+first_bracket:second_bracket], "to", len(gzipbytes)) From 9ee0dffb337d9d499e9f496fafba7a569bf9197c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 14 Sep 2025 22:45:03 +0300 Subject: [PATCH 193/245] Refactor Settings page with cards --- Software/src/battery/BATTERIES.cpp | 2 + .../src/devboard/webserver/settings_html.cpp | 136 +++++++++++++----- 2 files changed, 100 insertions(+), 38 deletions(-) diff --git a/Software/src/battery/BATTERIES.cpp b/Software/src/battery/BATTERIES.cpp index 9fb09e25..a444ed1a 100644 --- a/Software/src/battery/BATTERIES.cpp +++ b/Software/src/battery/BATTERIES.cpp @@ -18,6 +18,8 @@ std::vector supported_battery_types() { const char* name_for_chemistry(battery_chemistry_enum chem) { switch (chem) { + case battery_chemistry_enum::Autodetect: + return "Autodetect"; case battery_chemistry_enum::LFP: return "LFP"; case battery_chemistry_enum::NCA: diff --git a/Software/src/devboard/webserver/settings_html.cpp b/Software/src/devboard/webserver/settings_html.cpp index 55bddc4a..1d249e65 100644 --- a/Software/src/devboard/webserver/settings_html.cpp +++ b/Software/src/devboard/webserver/settings_html.cpp @@ -864,6 +864,21 @@ const char* getCANInterfaceName(CAN_Interface interface) { grid-column: span 2; } + .settings-card { + background-color: #3a4b54; /* Slightly lighter than main background */ + padding: 15px 20px; + margin-bottom: 20px; + border-radius: 20px; /* Less rounded than 50px for a more card-like feel */ + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); + } + .settings-card h3 { + color: #fff; + margin-top: 0; + margin-bottom: 15px; + padding-bottom: 8px; + border-bottom: 1px solid #4d5f69; + } + form .if-battery, form .if-inverter, form .if-charger, form .if-shunt { display: contents; } form[data-battery="0"] .if-battery { display: none; } form[data-inverter="0"] .if-inverter { display: none; } @@ -960,8 +975,12 @@ const char* getCANInterfaceName(CAN_Interface interface) {
-
- + + +
+

Battery config

+
+ + @@ -1023,12 +1042,29 @@ const char* getCANInterfaceName(CAN_Interface interface) {
+ + + +
+ + +
+ +
+
+ +
+

Inverter config

+
+
- %INVCOMM%
@@ -1067,12 +1103,19 @@ const char* getCANInterfaceName(CAN_Interface interface) {
+
+
+ +
+

Optional components config (optional)

+
+
- %CHGCOMM%
@@ -1082,11 +1125,21 @@ const char* getCANInterfaceName(CAN_Interface interface) {
- %SHUNTCOMM%
+
+
+ +
+

Hardware config

+
+ + + + @@ -1097,17 +1150,7 @@ const char* getCANInterfaceName(CAN_Interface interface) { %EQSTOP% - - - - - -
- -
@@ -1149,6 +1192,17 @@ const char* getCANInterfaceName(CAN_Interface interface) {
+ + +
+
+ +
+

Connectivity settings

+
+ @@ -1191,28 +1245,6 @@ const char* getCANInterfaceName(CAN_Interface interface) {
- - - - - - - - - - - - - - - - - - - - @@ -1240,6 +1272,34 @@ const char* getCANInterfaceName(CAN_Interface interface) {
+
+
+ +
+

Debug options

+
+ + + + + + + + + + + + + + + + + + + +
+
+
From 332e982beda6f1d4e4827a2e0b67b04084281126 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 14 Sep 2025 23:12:32 +0300 Subject: [PATCH 194/245] Move save button, update naming --- .../src/devboard/webserver/settings_html.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/Software/src/devboard/webserver/settings_html.cpp b/Software/src/devboard/webserver/settings_html.cpp index 1d249e65..6c91561c 100644 --- a/Software/src/devboard/webserver/settings_html.cpp +++ b/Software/src/devboard/webserver/settings_html.cpp @@ -977,6 +977,10 @@ const char* getCANInterfaceName(CAN_Interface interface) {
+
+

Settings saved. Reboot to take the new settings into use.

+

+

Battery config

@@ -1107,7 +1111,7 @@ const char* getCANInterfaceName(CAN_Interface interface) {
-

Optional components config (optional)

+

Optional components config

- +
@@ -1175,12 +1179,9 @@ const char* getCANInterfaceName(CAN_Interface interface) {
- + - - - @@ -1188,7 +1189,7 @@ const char* getCANInterfaceName(CAN_Interface interface) { - +
@@ -1255,6 +1256,8 @@ const char* getCANInterfaceName(CAN_Interface interface) { + + From 50d794d0dee09be91cabb8132b56348f4750a4ef Mon Sep 17 00:00:00 2001 From: Jonny Date: Sun, 14 Sep 2025 20:55:27 +0100 Subject: [PATCH 195/245] Restore previous SMA TRIPOWER behaviour to fix regression and potential issues --- Software/src/inverter/SMA-TRIPOWER-CAN.cpp | 58 +++++++++++++++------- Software/src/inverter/SMA-TRIPOWER-CAN.h | 11 ++++ 2 files changed, 52 insertions(+), 17 deletions(-) diff --git a/Software/src/inverter/SMA-TRIPOWER-CAN.cpp b/Software/src/inverter/SMA-TRIPOWER-CAN.cpp index 213c530b..172cd4c0 100644 --- a/Software/src/inverter/SMA-TRIPOWER-CAN.cpp +++ b/Software/src/inverter/SMA-TRIPOWER-CAN.cpp @@ -137,6 +137,17 @@ void SmaTripowerInverter::map_can_frame_to_variable(CAN_frame rx_frame) { } } +void SmaTripowerInverter::pushFrame(CAN_frame* frame, std::function callback) { + if (listLength >= 20) { + return; //TODO: scream. + } + framesToSend[listLength] = { + .frame = frame, + .callback = callback, + }; + listLength++; +} + void SmaTripowerInverter::transmit_can(unsigned long currentMillis) { // Send CAN Message only if we're enabled by inverter @@ -144,6 +155,18 @@ void SmaTripowerInverter::transmit_can(unsigned long currentMillis) { return; } + if (listLength > 0 && currentMillis - previousMillis250ms >= INTERVAL_250_MS) { + previousMillis250ms = currentMillis; + // Send next frame. + Frame frame = framesToSend[0]; + transmit_can_frame(frame.frame); + frame.callback(); + for (int i = 0; i < listLength - 1; i++) { + framesToSend[i] = framesToSend[i + 1]; + } + listLength--; + } + if (!pairing_completed) { return; } @@ -151,19 +174,19 @@ void SmaTripowerInverter::transmit_can(unsigned long currentMillis) { // Send CAN Message every 2s if (currentMillis - previousMillis2s >= INTERVAL_2_S) { previousMillis2s = currentMillis; - transmit_can_frame(&SMA_358); + pushFrame(&SMA_358); } // Send CAN Message every 10s if (currentMillis - previousMillis10s >= INTERVAL_10_S) { previousMillis10s = currentMillis; - transmit_can_frame(&SMA_518); - transmit_can_frame(&SMA_4D8); - transmit_can_frame(&SMA_3D8); + pushFrame(&SMA_518); + pushFrame(&SMA_4D8); + pushFrame(&SMA_3D8); } // Send CAN Message every 60s (potentially SMA_458 is not required for stable operation) if (currentMillis - previousMillis60s >= INTERVAL_60_S) { previousMillis60s = currentMillis; - transmit_can_frame(&SMA_458); + pushFrame(&SMA_458); } } @@ -172,17 +195,18 @@ void SmaTripowerInverter::completePairing() { } void SmaTripowerInverter::transmit_can_init() { + listLength = 0; // clear all frames - transmit_can_frame(&SMA_558); //Pairing start - Vendor - transmit_can_frame(&SMA_598); //Serial - transmit_can_frame(&SMA_5D8); //BYD - transmit_can_frame(&SMA_618_0); //BATTERY - transmit_can_frame(&SMA_618_1); //-Box Pr - transmit_can_frame(&SMA_618_2); //emium H - transmit_can_frame(&SMA_618_3); //VS - transmit_can_frame(&SMA_358); - transmit_can_frame(&SMA_3D8); - transmit_can_frame(&SMA_458); - transmit_can_frame(&SMA_4D8); - transmit_can_frame(&SMA_518); + pushFrame(&SMA_558); //Pairing start - Vendor + pushFrame(&SMA_598); //Serial + pushFrame(&SMA_5D8); //BYD + pushFrame(&SMA_618_0); //BATTERY + pushFrame(&SMA_618_1); //-Box Pr + pushFrame(&SMA_618_2); //emium H + pushFrame(&SMA_618_3); //VS + pushFrame(&SMA_358); + pushFrame(&SMA_3D8); + pushFrame(&SMA_458); + pushFrame(&SMA_4D8); + pushFrame(&SMA_518, [this]() { this->completePairing(); }); } diff --git a/Software/src/inverter/SMA-TRIPOWER-CAN.h b/Software/src/inverter/SMA-TRIPOWER-CAN.h index 7a18ea2a..15a660cc 100644 --- a/Software/src/inverter/SMA-TRIPOWER-CAN.h +++ b/Software/src/inverter/SMA-TRIPOWER-CAN.h @@ -4,6 +4,8 @@ #include "../devboard/hal/hal.h" #include "SmaInverterBase.h" +#include + class SmaTripowerInverter : public SmaInverterBase { public: const char* name() override { return Name; } @@ -20,6 +22,7 @@ class SmaTripowerInverter : public SmaInverterBase { const int THIRTY_MINUTES = 1200; void transmit_can_init(); + void pushFrame(CAN_frame* frame, std::function callback = []() {}); void completePairing(); unsigned long previousMillis250ms = 0; // will store last time a 250ms CAN Message was send @@ -28,6 +31,14 @@ class SmaTripowerInverter : public SmaInverterBase { unsigned long previousMillis10s = 0; // will store last time a 10s CAN Message was send unsigned long previousMillis60s = 0; // will store last time a 60s CAN Message was send + typedef struct { + CAN_frame* frame; + std::function callback; + } Frame; + + unsigned short listLength = 0; + Frame framesToSend[20]; + uint32_t inverter_time = 0; uint16_t inverter_voltage = 0; int16_t inverter_current = 0; From 980e450871ce975d7acde0eb4638f27ecf762f32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Mon, 15 Sep 2025 13:25:57 +0300 Subject: [PATCH 196/245] Update Software.cpp --- Software/Software.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/Software.cpp b/Software/Software.cpp index 19cb5382..6428e12e 100644 --- a/Software/Software.cpp +++ b/Software/Software.cpp @@ -34,7 +34,7 @@ #endif // The current software version, shown on webserver -const char* version_number = "9.0.RC10experimental"; +const char* version_number = "9.0.0"; // Interval timers volatile unsigned long currentMillis = 0; From 26126bae1a32f4f4e8bc01e3fbd2f380ca1b3b29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Mon, 15 Sep 2025 14:42:54 +0300 Subject: [PATCH 197/245] Tweak voltage limits for Zoe1 --- Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.h b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.h index 1e5d7631..baa759d0 100644 --- a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.h +++ b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.h @@ -34,10 +34,10 @@ class RenaultZoeGen1Battery : public CanBattery { private: RenaultZoeGen1HtmlRenderer renderer; - static const int MAX_PACK_VOLTAGE_DV = 4200; //5000 = 500.0V + static const int MAX_PACK_VOLTAGE_DV = 4040; //5000 = 500.0V static const int MIN_PACK_VOLTAGE_DV = 3000; static const int MAX_CELL_DEVIATION_MV = 150; - static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value + static const int MAX_CELL_VOLTAGE_MV = 4220; //Battery is put into emergency stop if one cell goes over this value static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value DATALAYER_BATTERY_TYPE* datalayer_battery; From 73c6821a9a4bf1c324036c4769801372269982d3 Mon Sep 17 00:00:00 2001 From: Jonny Date: Mon, 15 Sep 2025 20:23:35 +0100 Subject: [PATCH 198/245] Stop erroneous events when saving an empty SSID/pw --- Software/src/communication/nvm/comm_nvm.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Software/src/communication/nvm/comm_nvm.cpp b/Software/src/communication/nvm/comm_nvm.cpp index 8a47e178..58eba5c9 100644 --- a/Software/src/communication/nvm/comm_nvm.cpp +++ b/Software/src/communication/nvm/comm_nvm.cpp @@ -207,10 +207,12 @@ void store_settings() { } if (!settings.putString("SSID", String(ssid.c_str()))) { - set_event(EVENT_PERSISTENT_SAVE_INFO, 1); + if (ssid != "") + set_event(EVENT_PERSISTENT_SAVE_INFO, 1); } if (!settings.putString("PASSWORD", String(password.c_str()))) { - set_event(EVENT_PERSISTENT_SAVE_INFO, 2); + if (password != "") + set_event(EVENT_PERSISTENT_SAVE_INFO, 2); } if (!settings.putUInt("BATTERY_WH_MAX", datalayer.battery.info.total_capacity_Wh)) { From df52d067e78c742096434dfdf5b7124a70c7d16a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 16 Sep 2025 13:36:42 +0300 Subject: [PATCH 199/245] Update Software.cpp --- Software/Software.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/Software.cpp b/Software/Software.cpp index 6428e12e..5c65c717 100644 --- a/Software/Software.cpp +++ b/Software/Software.cpp @@ -34,7 +34,7 @@ #endif // The current software version, shown on webserver -const char* version_number = "9.0.0"; +const char* version_number = "9.0.dev"; // Interval timers volatile unsigned long currentMillis = 0; From 79964a0601a009a20f1c785d74e3384bfe71139b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 16 Sep 2025 14:44:30 +0300 Subject: [PATCH 200/245] Remove old method to disable webserver to save flash --- Software/Software.cpp | 16 +++++----------- Software/src/devboard/webserver/webserver.cpp | 3 --- Software/src/devboard/webserver/webserver.h | 2 -- 3 files changed, 5 insertions(+), 16 deletions(-) diff --git a/Software/Software.cpp b/Software/Software.cpp index 5c65c717..63f2026b 100644 --- a/Software/Software.cpp +++ b/Software/Software.cpp @@ -86,9 +86,7 @@ void connectivity_loop(void*) { // Init wifi init_WiFi(); - if (webserver_enabled) { - init_webserver(); - } + init_webserver(); if (mdns_enabled) { init_mDNS(); @@ -98,9 +96,7 @@ void connectivity_loop(void*) { START_TIME_MEASUREMENT(wifi); wifi_monitor(); - if (webserver_enabled) { - ota_monitor(); - } + ota_monitor(); END_TIME_MEASUREMENT_MAX(wifi, datalayer.system.status.wifi_task_10s_max_us); @@ -388,11 +384,9 @@ void core_loop(void*) { END_TIME_MEASUREMENT_MAX(comm, datalayer.system.status.time_comm_us); - if (webserver_enabled) { - START_TIME_MEASUREMENT(ota); - ElegantOTA.loop(); - END_TIME_MEASUREMENT_MAX(ota, datalayer.system.status.time_ota_us); - } + START_TIME_MEASUREMENT(ota); + ElegantOTA.loop(); + END_TIME_MEASUREMENT_MAX(ota, datalayer.system.status.time_ota_us); // Process currentMillis = millis(); diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 5a58b66a..70647e95 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -23,9 +23,6 @@ extern std::string http_username; extern std::string http_password; -bool webserver_enabled = - true; // Global flag to enable or disable the webserver //Old method to disable was with #ifdef WEBSERVER - bool webserver_auth = false; // Create AsyncWebServer object on port 80 diff --git a/Software/src/devboard/webserver/webserver.h b/Software/src/devboard/webserver/webserver.h index 6f3f5c45..6cbb956f 100644 --- a/Software/src/devboard/webserver/webserver.h +++ b/Software/src/devboard/webserver/webserver.h @@ -7,8 +7,6 @@ #include "../../lib/ayushsharma82-ElegantOTA/src/ElegantOTA.h" #include "../../lib/mathieucarbou-AsyncTCPSock/src/AsyncTCP.h" -extern bool webserver_enabled; - extern const char* version_number; // The current software version, shown on webserver // Common charger parameters From 5277665dd12149c6969149647ea970c1d1fd818f Mon Sep 17 00:00:00 2001 From: Jonny Date: Tue, 16 Sep 2025 16:11:13 +0100 Subject: [PATCH 201/245] Fix validation so that 8 character WiFi passwords work --- Software/src/devboard/webserver/webserver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 5a58b66a..404b5aed 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -621,7 +621,7 @@ void init_webserver() { def_route_with_auth("/updatePassword", server, HTTP_GET, [](AsyncWebServerRequest* request) { if (request->hasParam("value")) { String value = request->getParam("value")->value(); - if (value.length() > 8) { // Check if password is within the allowable length + if (value.length() >= 8) { // Password must be 8 characters or longer password = value.c_str(); store_settings(); request->send(200, "text/plain", "Updated successfully"); From 024446862459f81d928a14d0fe9d9c1a4e36d3ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 17 Sep 2025 23:06:30 +0300 Subject: [PATCH 202/245] Make contactor opening take 9s instead of 60s --- Software/src/battery/TESLA-BATTERY.cpp | 30 +++++++++++++------------- Software/src/battery/TESLA-BATTERY.h | 6 +++++- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/Software/src/battery/TESLA-BATTERY.cpp b/Software/src/battery/TESLA-BATTERY.cpp index 42226c9f..ff4208be 100644 --- a/Software/src/battery/TESLA-BATTERY.cpp +++ b/Software/src/battery/TESLA-BATTERY.cpp @@ -976,22 +976,22 @@ void TeslaBattery:: if ((datalayer.system.status.inverter_allows_contactor_closing == true) && (datalayer.battery.status.bms_status != FAULT) && (!datalayer.system.settings.equipment_stop_active)) { // Carry on: 0x221 DRIVE state & reset power down timer - vehicleState = 1; - powerDownTimer = 180; //0x221 50ms cyclic, 20 calls/second + vehicleState = CAR_DRIVE; + powerDownSeconds = 9; } else { // Faulted state, or inverter blocks contactor closing // Shut down: 0x221 ACCESSORY state for 3 seconds, followed by GOING_DOWN, then OFF - if (powerDownTimer <= 180 && powerDownTimer > 120) { - vehicleState = 2; //ACCESSORY - powerDownTimer--; + if (powerDownSeconds <= 9 && powerDownSeconds > 6) { + vehicleState = ACCESSORY; + powerDownSeconds--; } - if (powerDownTimer <= 120 && powerDownTimer > 60) { - vehicleState = 3; //GOING_DOWN - powerDownTimer--; + if (powerDownSeconds <= 6 && powerDownSeconds > 3) { + vehicleState = GOING_DOWN; + powerDownSeconds--; } - if (powerDownTimer <= 60 && powerDownTimer > 0) { - vehicleState = 0; //OFF - powerDownTimer--; + if (powerDownSeconds <= 3 && powerDownSeconds > 0) { + vehicleState = CAR_OFF; + powerDownSeconds--; } } @@ -2059,7 +2059,7 @@ void TeslaBattery::transmit_can(unsigned long currentMillis) { previousMillis50 = currentMillis; //0x221 VCFRONT_LVPowerState - if (vehicleState == 1) { // Drive + if (vehicleState == CAR_DRIVE) { switch (muxNumber_TESLA_221) { case 0: generateMuxFrameCounterChecksum(TESLA_221_DRIVE_Mux0, frameCounter_TESLA_221, 52, 4, 56, 8); @@ -2077,7 +2077,7 @@ void TeslaBattery::transmit_can(unsigned long currentMillis) { //Generate next new frame frameCounter_TESLA_221 = (frameCounter_TESLA_221 + 1) % 16; } - if (vehicleState == 2) { // Accessory + if (vehicleState == ACCESSORY) { switch (muxNumber_TESLA_221) { case 0: generateMuxFrameCounterChecksum(TESLA_221_ACCESSORY_Mux0, frameCounter_TESLA_221, 52, 4, 56, 8); @@ -2095,7 +2095,7 @@ void TeslaBattery::transmit_can(unsigned long currentMillis) { //Generate next new frame frameCounter_TESLA_221 = (frameCounter_TESLA_221 + 1) % 16; } - if (vehicleState == 3) { // Going down + if (vehicleState == GOING_DOWN) { switch (muxNumber_TESLA_221) { case 0: generateMuxFrameCounterChecksum(TESLA_221_GOING_DOWN_Mux0, frameCounter_TESLA_221, 52, 4, 56, 8); @@ -2113,7 +2113,7 @@ void TeslaBattery::transmit_can(unsigned long currentMillis) { //Generate next new frame frameCounter_TESLA_221 = (frameCounter_TESLA_221 + 1) % 16; } - if (vehicleState == 0) { // Off + if (vehicleState == CAR_OFF) { switch (muxNumber_TESLA_221) { case 0: generateMuxFrameCounterChecksum(TESLA_221_OFF_Mux0, frameCounter_TESLA_221, 52, 4, 56, 8); diff --git a/Software/src/battery/TESLA-BATTERY.h b/Software/src/battery/TESLA-BATTERY.h index 0bf4037f..91311b52 100644 --- a/Software/src/battery/TESLA-BATTERY.h +++ b/Software/src/battery/TESLA-BATTERY.h @@ -78,7 +78,11 @@ class TeslaBattery : public CanBattery { uint8_t muxNumber_TESLA_221 = 0; uint8_t frameCounter_TESLA_221 = 15; // Start at 15 for Mux 0 uint8_t vehicleState = 1; // "OFF": 0, "DRIVE": 1, "ACCESSORY": 2, "GOING_DOWN": 3 - uint16_t powerDownTimer = 180; // Car power down (i.e. contactor open) tracking timer, 3 seconds per sendingState + static const uint8_t CAR_OFF = 0; + static const uint8_t CAR_DRIVE = 1; + static const uint8_t ACCESSORY = 2; + static const uint8_t GOING_DOWN = 3; + uint8_t powerDownSeconds = 9; // Car power down (i.e. contactor open) tracking timer, 3 seconds per sendingState //0x2E1 VCFRONT_status, 6 mux tracker uint8_t muxNumber_TESLA_2E1 = 0; //0x334 UI From 15143d1384c5f3bae853c35dab1a6b4e4b505e68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 17 Sep 2025 23:23:55 +0300 Subject: [PATCH 203/245] Add configurable option for estimated SOC --- Software/src/battery/BATTERIES.cpp | 2 ++ Software/src/battery/BATTERIES.h | 2 +- Software/src/battery/KIA-E-GMP-BATTERY.cpp | 19 ++++++++++--------- Software/src/battery/KIA-E-GMP-BATTERY.h | 2 +- Software/src/communication/nvm/comm_nvm.cpp | 1 + .../src/devboard/webserver/settings_html.cpp | 14 ++++++++++++++ Software/src/devboard/webserver/webserver.cpp | 8 ++++---- 7 files changed, 33 insertions(+), 15 deletions(-) diff --git a/Software/src/battery/BATTERIES.cpp b/Software/src/battery/BATTERIES.cpp index a444ed1a..1c954d13 100644 --- a/Software/src/battery/BATTERIES.cpp +++ b/Software/src/battery/BATTERIES.cpp @@ -296,6 +296,8 @@ bool user_selected_tesla_GTW_rightHandDrive = true; uint16_t user_selected_tesla_GTW_mapRegion = 2; uint16_t user_selected_tesla_GTW_chassisType = 2; uint16_t user_selected_tesla_GTW_packEnergy = 1; +/* User-selected EGMP+others settings */ +bool user_selected_use_estimated_SOC = false; // Use 0V for user selected cell/pack voltage defaults (On boot will be replaced with saved values from NVM) uint16_t user_selected_max_pack_voltage_dV = 0; diff --git a/Software/src/battery/BATTERIES.h b/Software/src/battery/BATTERIES.h index 0324c189..cb24124e 100644 --- a/Software/src/battery/BATTERIES.h +++ b/Software/src/battery/BATTERIES.h @@ -62,7 +62,7 @@ extern uint16_t user_selected_max_pack_voltage_dV; extern uint16_t user_selected_min_pack_voltage_dV; extern uint16_t user_selected_max_cell_voltage_mV; extern uint16_t user_selected_min_cell_voltage_mV; - +extern bool user_selected_use_estimated_SOC; extern bool user_selected_LEAF_interlock_mandatory; extern bool user_selected_tesla_digital_HVIL; extern uint16_t user_selected_tesla_GTW_country; diff --git a/Software/src/battery/KIA-E-GMP-BATTERY.cpp b/Software/src/battery/KIA-E-GMP-BATTERY.cpp index 20f3cd8e..664d4867 100644 --- a/Software/src/battery/KIA-E-GMP-BATTERY.cpp +++ b/Software/src/battery/KIA-E-GMP-BATTERY.cpp @@ -114,16 +114,17 @@ uint8_t KiaEGmpBattery::calculateCRC(CAN_frame rx_frame, uint8_t length, uint8_t void KiaEGmpBattery::update_values() { -#ifdef ESTIMATE_SOC_FROM_CELLVOLTAGE - // Use the simplified pack-based SOC estimation with proper compensation - datalayer.battery.status.real_soc = estimateSOC(batteryVoltage, datalayer.battery.info.number_of_cells, batteryAmps); + if (user_selected_use_estimated_SOC) { + // Use the simplified pack-based SOC estimation with proper compensation + datalayer.battery.status.real_soc = + estimateSOC(batteryVoltage, datalayer.battery.info.number_of_cells, batteryAmps); - // For comparison or fallback, we can still calculate from min/max cell voltages - SOC_estimated_lowest = estimateSOCFromCell(CellVoltMin_mV); - SOC_estimated_highest = estimateSOCFromCell(CellVoltMax_mV); -#else - datalayer.battery.status.real_soc = (SOC_Display * 10); //increase SOC range from 0-100.0 -> 100.00 -#endif + // For comparison or fallback, we can still calculate from min/max cell voltages + SOC_estimated_lowest = estimateSOCFromCell(CellVoltMin_mV); + SOC_estimated_highest = estimateSOCFromCell(CellVoltMax_mV); + } else { + datalayer.battery.status.real_soc = (SOC_Display * 10); //increase SOC range from 0-100.0 -> 100.00 + } datalayer.battery.status.soh_pptt = (batterySOH * 10); //Increase decimals from 100.0% -> 100.00% diff --git a/Software/src/battery/KIA-E-GMP-BATTERY.h b/Software/src/battery/KIA-E-GMP-BATTERY.h index dfd0237c..f28b8529 100644 --- a/Software/src/battery/KIA-E-GMP-BATTERY.h +++ b/Software/src/battery/KIA-E-GMP-BATTERY.h @@ -3,7 +3,7 @@ #include "CanBattery.h" #include "KIA-E-GMP-HTML.h" -#define ESTIMATE_SOC_FROM_CELLVOLTAGE +extern bool user_selected_use_estimated_SOC; class KiaEGmpBattery : public CanBattery { public: diff --git a/Software/src/communication/nvm/comm_nvm.cpp b/Software/src/communication/nvm/comm_nvm.cpp index 58eba5c9..315c9fcc 100644 --- a/Software/src/communication/nvm/comm_nvm.cpp +++ b/Software/src/communication/nvm/comm_nvm.cpp @@ -104,6 +104,7 @@ void init_stored_settings() { user_selected_can_addon_crystal_frequency_mhz = settings.getUInt("CANFREQ", 8); user_selected_canfd_addon_crystal_frequency_mhz = settings.getUInt("CANFDFREQ", 40); user_selected_LEAF_interlock_mandatory = settings.getBool("INTERLOCKREQ", false); + user_selected_use_estimated_SOC = settings.getBool("SOCESTIMATED", false); user_selected_tesla_digital_HVIL = settings.getBool("DIGITALHVIL", false); user_selected_tesla_GTW_country = settings.getUInt("GTWCOUNTRY", 0); user_selected_tesla_GTW_rightHandDrive = settings.getBool("GTWRHD", false); diff --git a/Software/src/devboard/webserver/settings_html.cpp b/Software/src/devboard/webserver/settings_html.cpp index 6c91561c..8184bea4 100644 --- a/Software/src/devboard/webserver/settings_html.cpp +++ b/Software/src/devboard/webserver/settings_html.cpp @@ -251,6 +251,10 @@ String settings_processor(const String& var, BatteryEmulatorSettingsStore& setti return settings.getBool("DBLBTR") ? "checked" : ""; } + if (var == "SOCESTIMATED") { + return settings.getBool("SOCESTIMATED") ? "checked" : ""; + } + if (var == "CNTCTRL") { return settings.getBool("CNTCTRL") ? "checked" : ""; } @@ -912,6 +916,11 @@ const char* getCANInterfaceName(CAN_Interface interface) { display: contents; } + form .if-socestimated { display: none; } /* Integrations where you can turn on SOC estimation */ + form[data-battery="16"] .if-socestimated { + display: contents; + } + form .if-dblbtr { display: none; } form[data-dblbtr="true"] .if-dblbtr { display: contents; @@ -1022,6 +1031,11 @@ const char* getCANInterfaceName(CAN_Interface interface) {
+
+ + +
+
+ + + diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 9933da74..9350652d 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -506,6 +506,8 @@ void init_webserver() { } else if (p->name() == "SUBNET4") { auto type = atoi(p->value().c_str()); settings.saveUInt("SUBNET4", type); + } else if (p->name() == "APNAME") { + settings.saveString("APNAME", p->value().c_str()); } else if (p->name() == "APPASSWORD") { settings.saveString("APPASSWORD", p->value().c_str()); } else if (p->name() == "HOSTNAME") { diff --git a/Software/src/devboard/wifi/wifi.cpp b/Software/src/devboard/wifi/wifi.cpp index ab9831ee..9179f0e2 100644 --- a/Software/src/devboard/wifi/wifi.cpp +++ b/Software/src/devboard/wifi/wifi.cpp @@ -54,7 +54,7 @@ static uint16_t current_check_interval = WIFI_CHECK_INTERVAL; static bool connected_once = false; void init_WiFi() { - DEBUG_PRINTF("init_Wifi enabled=%d, apå=%d, ssid=%s, password=%s\n", wifi_enabled, wifiap_enabled, ssid.c_str(), + DEBUG_PRINTF("init_Wifi enabled=%d, ap=%d, ssid=%s, password=%s\n", wifi_enabled, wifiap_enabled, ssid.c_str(), password.c_str()); if (!custom_hostname.empty()) { From 983105ab6a079156ffbf963d3fdc963a52c015a0 Mon Sep 17 00:00:00 2001 From: Jonny Date: Tue, 23 Sep 2025 07:15:11 +0100 Subject: [PATCH 216/245] Enable QIO and 80mhz flash on Lilygo T-CAN485 build (50-80% perf improvement) --- platformio.ini | 3 +++ 1 file changed, 3 insertions(+) diff --git a/platformio.ini b/platformio.ini index f4a37b8f..ba16969a 100644 --- a/platformio.ini +++ b/platformio.ini @@ -26,6 +26,9 @@ platform = https://github.com/pioarduino/platform-espressif32/releases/download/ board = esp32dev monitor_speed = 115200 monitor_filters = default, time, log2file +board_build.flash_mode = qio +board_build.f_flash = 80000000 +board_build.arduino.memory_type = qio_qspi board_build.partitions = min_spiffs.csv framework = arduino build_flags = -I include -DHW_LILYGO From a0de6b092b2034ff512f653d7683c1e8829f6ccf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 23 Sep 2025 09:38:28 +0300 Subject: [PATCH 217/245] Enable QIO and 80MHZ flash on Stark CMR --- platformio.ini | 3 +++ 1 file changed, 3 insertions(+) diff --git a/platformio.ini b/platformio.ini index ba16969a..1afd6624 100644 --- a/platformio.ini +++ b/platformio.ini @@ -39,6 +39,9 @@ platform = https://github.com/pioarduino/platform-espressif32/releases/download/ board = esp32dev monitor_speed = 115200 monitor_filters = default, time, log2file, esp32_exception_decoder +board_build.flash_mode = qio +board_build.f_flash = 80000000 +board_build.arduino.memory_type = qio_qspi board_build.partitions = min_spiffs.csv framework = arduino build_flags = -I include -DHW_STARK From ed2ebb00fd5a4b541b2f68e0fad3a1656a8675af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 23 Sep 2025 10:01:21 +0300 Subject: [PATCH 218/245] Update Software.cpp --- Software/Software.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/Software.cpp b/Software/Software.cpp index 63f2026b..96ee0487 100644 --- a/Software/Software.cpp +++ b/Software/Software.cpp @@ -34,7 +34,7 @@ #endif // The current software version, shown on webserver -const char* version_number = "9.0.dev"; +const char* version_number = "9.1.0"; // Interval timers volatile unsigned long currentMillis = 0; From 48d416b5c4a964a3b6949f587fd8aabf4bb1b3fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 23 Sep 2025 10:28:46 +0300 Subject: [PATCH 219/245] Update Software.cpp --- Software/Software.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/Software.cpp b/Software/Software.cpp index 96ee0487..387a5dbe 100644 --- a/Software/Software.cpp +++ b/Software/Software.cpp @@ -34,7 +34,7 @@ #endif // The current software version, shown on webserver -const char* version_number = "9.1.0"; +const char* version_number = "9.1.dev"; // Interval timers volatile unsigned long currentMillis = 0; From 2ba937bd328378727e6c3b88fe3bcc9e61766c5a Mon Sep 17 00:00:00 2001 From: James Brookes Date: Tue, 23 Sep 2025 18:10:24 +0100 Subject: [PATCH 220/245] Fix incorrect user_selected_tesla_GTW_country value written to 7FF user_selected_tesla_GTW_rightHandDrive bit --- Software/src/battery/TESLA-BATTERY.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/src/battery/TESLA-BATTERY.cpp b/Software/src/battery/TESLA-BATTERY.cpp index ff4208be..08bd341f 100644 --- a/Software/src/battery/TESLA-BATTERY.cpp +++ b/Software/src/battery/TESLA-BATTERY.cpp @@ -2601,7 +2601,7 @@ void TeslaModel3YBattery::setup(void) { // Performs one time setup at startup //0x7FF GTW CAN frame values //Mux1 write_signal_value(&TESLA_7FF_Mux1, 16, 16, user_selected_tesla_GTW_country, false); - write_signal_value(&TESLA_7FF_Mux1, 11, 1, user_selected_tesla_GTW_country, false); + write_signal_value(&TESLA_7FF_Mux1, 11, 1, user_selected_tesla_GTW_rightHandDrive, false); //Mux3 write_signal_value(&TESLA_7FF_Mux3, 8, 4, user_selected_tesla_GTW_mapRegion, false); write_signal_value(&TESLA_7FF_Mux3, 18, 3, user_selected_tesla_GTW_chassisType, false); From d336b75f56f84ce5fb826f05c06b6bbb62d559d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 24 Sep 2025 22:54:53 +0300 Subject: [PATCH 221/245] Update valid cellvoltage filter --- Software/src/battery/KIA-E-GMP-BATTERY.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/src/battery/KIA-E-GMP-BATTERY.cpp b/Software/src/battery/KIA-E-GMP-BATTERY.cpp index 664d4867..47e6de23 100644 --- a/Software/src/battery/KIA-E-GMP-BATTERY.cpp +++ b/Software/src/battery/KIA-E-GMP-BATTERY.cpp @@ -72,7 +72,7 @@ uint16_t KiaEGmpBattery::selectSOC(uint16_t SOC_low, uint16_t SOC_high) { void KiaEGmpBattery::set_cell_voltages(CAN_frame rx_frame, int start, int length, int startCell) { for (size_t i = 0; i < length; i++) { - if ((rx_frame.data.u8[start + i] * 20) > 1000) { + if ((rx_frame.data.u8[start + i] * 20) > 1800) { datalayer.battery.status.cell_voltages_mV[startCell + i] = (rx_frame.data.u8[start + i] * 20); } } From 397e8d03a1023c2c474133b56d9badd6c95304ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 25 Sep 2025 00:00:35 +0300 Subject: [PATCH 222/245] Remove/shorten periodically logged items --- Software/src/battery/TESLA-BATTERY.cpp | 51 ++++---------------------- Software/src/devboard/wifi/wifi.cpp | 2 +- 2 files changed, 9 insertions(+), 44 deletions(-) diff --git a/Software/src/battery/TESLA-BATTERY.cpp b/Software/src/battery/TESLA-BATTERY.cpp index 08bd341f..17021812 100644 --- a/Software/src/battery/TESLA-BATTERY.cpp +++ b/Software/src/battery/TESLA-BATTERY.cpp @@ -996,18 +996,18 @@ void TeslaBattery:: } printFaultCodesIfActive(); - logging.printf("BMS Contactors State: "); + logging.printf("Contactor State: "); logging.printf(getBMSContactorState(battery_contactor)); // Display what state the BMS thinks the contactors are in - logging.printf(", HVIL: "); + logging.printf(" HVIL: "); logging.printf(getHvilStatusState(battery_hvil_status)); - logging.printf(", NegativeState: "); + logging.printf(" NegState: "); logging.printf(getContactorState(battery_packContNegativeState)); - logging.printf(", PositiveState: "); + logging.printf(" PosState: "); logging.println(getContactorState(battery_packContPositiveState)); - logging.printf("HVP Contactors setState: "); + logging.printf("Cont. setState: "); logging.printf( getContactorText(battery_packContactorSetState)); // Display what state the HVP has set the contactors to be in - logging.printf(", Closing blocked: "); + logging.printf(" Closing blocked: "); logging.printf(getNoYes(battery_packCtrsClosingBlocked)); if (battery_packContactorSetState == 5) { logging.printf(" (already CLOSED)"); @@ -1015,43 +1015,8 @@ void TeslaBattery:: logging.printf(", Pyrotest: "); logging.println(getNoYes(battery_pyroTestInProgress)); - logging.printf("Battery values: "); - logging.printf("Real SOC: "); - logging.print(battery_soc_ui / 10.0, 1); - logging.printf(", Battery voltage: "); - logging.print(battery_volts / 10.0, 1); - logging.printf("V"); - logging.printf(", Battery HV current: "); - logging.print(battery_amps / 10.0, 1); - logging.printf("A"); - logging.printf(", Fully charged?: "); - if (battery_full_charge_complete) - logging.printf("YES, "); - else - logging.printf("NO, "); - if (datalayer.battery.info.chemistry == battery_chemistry_enum::LFP) { - logging.printf("LFP chemistry detected!"); - } - logging.println(""); - logging.printf("Cellstats, Max: "); - logging.print(battery_cell_max_v); - logging.printf("mV (cell "); - logging.print(battery_BrickVoltageMaxNum); - logging.printf("), Min: "); - logging.print(battery_cell_min_v); - logging.printf("mV (cell "); - logging.print(battery_BrickVoltageMinNum); - logging.printf("), Imbalance: "); - logging.print(battery_cell_deviation_mV); - logging.println("mV."); - - logging.printf("High Voltage Output Pins: %.2f V, Low Voltage: %.2f V, DC/DC 12V current: %.2f A.\n", - (battery_dcdcHvBusVolt * 0.146484), (battery_dcdcLvBusVolt * 0.0390625), - (battery_dcdcLvOutputCurrent * 0.1)); - - logging.printf("PCS_ambientTemp: %.2f°C, DCDC_Temp: %.2f°C, ChgPhA: %.2f°C, ChgPhB: %.2f°C, ChgPhC: %.2f°C.\n", - PCS_ambientTemp * 0.1 + 40, PCS_dcdcTemp * 0.1 + 40, PCS_chgPhATemp * 0.1 + 40, - PCS_chgPhBTemp * 0.1 + 40, PCS_chgPhCTemp * 0.1 + 40); + logging.printf("HV: %.2f V, 12V: %.2f V, 12V current: %.2f A.\n", (battery_dcdcHvBusVolt * 0.146484), + (battery_dcdcLvBusVolt * 0.0390625), (battery_dcdcLvOutputCurrent * 0.1)); } void TeslaBattery::handle_incoming_can_frame(CAN_frame rx_frame) { diff --git a/Software/src/devboard/wifi/wifi.cpp b/Software/src/devboard/wifi/wifi.cpp index 9179f0e2..7d612fb0 100644 --- a/Software/src/devboard/wifi/wifi.cpp +++ b/Software/src/devboard/wifi/wifi.cpp @@ -108,7 +108,7 @@ void wifi_monitor() { if ((hasConnectedBefore && (currentMillis - lastWiFiCheck > current_check_interval)) || (!hasConnectedBefore && (currentMillis - lastWiFiCheck > INIT_WIFI_FULL_RECONNECT_INTERVAL))) { - DEBUG_PRINTF("Time to monitor Wi-Fi status: %d, %d, %d, %d, %d\n", hasConnectedBefore, currentMillis, lastWiFiCheck, + DEBUG_PRINTF("Wi-Fi status: %d, %d, %d, %d, %d\n", hasConnectedBefore, currentMillis, lastWiFiCheck, current_check_interval, INIT_WIFI_FULL_RECONNECT_INTERVAL); lastWiFiCheck = currentMillis; From 483d4300b13743d83d3966e34d23e5cf4ff83bdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 25 Sep 2025 00:00:59 +0300 Subject: [PATCH 223/245] Make sure user only enables one general logging method at once --- .../src/devboard/webserver/settings_html.cpp | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/Software/src/devboard/webserver/settings_html.cpp b/Software/src/devboard/webserver/settings_html.cpp index b5e6f813..eef9f425 100644 --- a/Software/src/devboard/webserver/settings_html.cpp +++ b/Software/src/devboard/webserver/settings_html.cpp @@ -1309,11 +1309,30 @@ const char* getCANInterfaceName(CAN_Interface interface) { + + - + - + From 132029169dadf8dc46a42199c22e29faac132081 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 25 Sep 2025 11:09:56 +0300 Subject: [PATCH 224/245] Raise limit to 2500 for filtering --- Software/src/battery/KIA-E-GMP-BATTERY.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/src/battery/KIA-E-GMP-BATTERY.cpp b/Software/src/battery/KIA-E-GMP-BATTERY.cpp index 47e6de23..2da329ed 100644 --- a/Software/src/battery/KIA-E-GMP-BATTERY.cpp +++ b/Software/src/battery/KIA-E-GMP-BATTERY.cpp @@ -72,7 +72,7 @@ uint16_t KiaEGmpBattery::selectSOC(uint16_t SOC_low, uint16_t SOC_high) { void KiaEGmpBattery::set_cell_voltages(CAN_frame rx_frame, int start, int length, int startCell) { for (size_t i = 0; i < length; i++) { - if ((rx_frame.data.u8[start + i] * 20) > 1800) { + if ((rx_frame.data.u8[start + i] * 20) > 2500) { datalayer.battery.status.cell_voltages_mV[startCell + i] = (rx_frame.data.u8[start + i] * 20); } } From 48411680c66637a69a9d53e2474f77a0863ce2bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 25 Sep 2025 12:04:36 +0300 Subject: [PATCH 225/245] Raise limit further --- Software/src/battery/KIA-E-GMP-BATTERY.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/src/battery/KIA-E-GMP-BATTERY.cpp b/Software/src/battery/KIA-E-GMP-BATTERY.cpp index 2da329ed..516dfa43 100644 --- a/Software/src/battery/KIA-E-GMP-BATTERY.cpp +++ b/Software/src/battery/KIA-E-GMP-BATTERY.cpp @@ -72,7 +72,7 @@ uint16_t KiaEGmpBattery::selectSOC(uint16_t SOC_low, uint16_t SOC_high) { void KiaEGmpBattery::set_cell_voltages(CAN_frame rx_frame, int start, int length, int startCell) { for (size_t i = 0; i < length; i++) { - if ((rx_frame.data.u8[start + i] * 20) > 2500) { + if ((rx_frame.data.u8[start + i] * 20) > 2600) { datalayer.battery.status.cell_voltages_mV[startCell + i] = (rx_frame.data.u8[start + i] * 20); } } From f48b4235c1438dbc664ec8a0cb065b2f3c81f729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 25 Sep 2025 15:30:52 +0300 Subject: [PATCH 226/245] Simplify DigitalHVIL sending to increase performance --- Software/src/battery/TESLA-BATTERY.cpp | 49 +++++++++----------------- Software/src/battery/TESLA-BATTERY.h | 3 +- 2 files changed, 19 insertions(+), 33 deletions(-) diff --git a/Software/src/battery/TESLA-BATTERY.cpp b/Software/src/battery/TESLA-BATTERY.cpp index 17021812..7c5042c0 100644 --- a/Software/src/battery/TESLA-BATTERY.cpp +++ b/Software/src/battery/TESLA-BATTERY.cpp @@ -1870,7 +1870,7 @@ void TeslaBattery::handle_incoming_can_frame(CAN_frame rx_frame) { stateMachineBMSQuery = 1; break; } - if (memcmp(&rx_frame.data.u8[0], "\x10", 1) == 0) { + if (rx_frame.data.u8[0] == 0x10) { //Received first data frame battery_partNumber[0] = rx_frame.data.u8[5]; battery_partNumber[1] = rx_frame.data.u8[6]; @@ -1879,7 +1879,7 @@ void TeslaBattery::handle_incoming_can_frame(CAN_frame rx_frame) { stateMachineBMSQuery = 2; break; } - if (memcmp(&rx_frame.data.u8[0], "\x21", 1) == 0) { + if (rx_frame.data.u8[0] == 0x21) { //Second part of part number after flow control battery_partNumber[3] = rx_frame.data.u8[1]; battery_partNumber[4] = rx_frame.data.u8[2]; @@ -1891,7 +1891,7 @@ void TeslaBattery::handle_incoming_can_frame(CAN_frame rx_frame) { logging.println("CAN UDS: Received BMS query second data frame"); break; } - if (memcmp(&rx_frame.data.u8[0], "\x22", 1) == 0) { + if (rx_frame.data.u8[0] == 0x22) { //Final part of part number battery_partNumber[10] = rx_frame.data.u8[1]; battery_partNumber[11] = rx_frame.data.u8[2]; @@ -1950,41 +1950,26 @@ CAN_frame can_msg_118[] = { {.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x118, .data = {0x6F, 0x8E, 0x30, 0x10, 0x00, 0x08, 0x00, 0x80}}, {.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x118, .data = {0x70, 0x8F, 0x30, 0x10, 0x00, 0x08, 0x00, 0x80}}}; -unsigned long lastSend1CF = 0; -unsigned long lastSend118 = 0; - -int index_1CF = 0; -int index_118 = 0; - void TeslaBattery::transmit_can(unsigned long currentMillis) { - if (user_selected_tesla_digital_HVIL) { //Special S/X? mode for 2024+ batteries - if ((datalayer.system.status.inverter_allows_contactor_closing) && (datalayer.battery.status.bms_status != FAULT)) { - if (currentMillis - lastSend1CF >= 10) { - transmit_can_frame(&can_msg_1CF[index_1CF]); - - index_1CF = (index_1CF + 1) % 8; - lastSend1CF = currentMillis; - } - - if (currentMillis - lastSend118 >= 10) { - transmit_can_frame(&can_msg_118[index_118]); - - index_118 = (index_118 + 1) % 16; - lastSend118 = currentMillis; - } - } else { - index_1CF = 0; - index_118 = 0; - } - } - //Send 10ms messages if (currentMillis - previousMillis10 >= INTERVAL_10_MS) { previousMillis10 = currentMillis; - //0x118 DI_systemStatus - transmit_can_frame(&TESLA_118); + if (user_selected_tesla_digital_HVIL) { //Special Digital HVIL mode for S/X 2024+ batteries + if ((datalayer.system.status.inverter_allows_contactor_closing) && + (datalayer.battery.status.bms_status != FAULT)) { + transmit_can_frame(&can_msg_1CF[index_1CF]); + index_1CF = (index_1CF + 1) % 8; + transmit_can_frame(&can_msg_118[index_118]); + index_118 = (index_118 + 1) % 16; + } + } else { //Normal handling of 118 message (Non digital HVIL version) + //0x118 DI_systemStatus + transmit_can_frame(&TESLA_118); + index_1CF = 0; //Stop broadcasting Digital HVIL 1CF and 118 to keep contactors open + index_118 = 0; + } //0x2E1 VCFRONT_status switch (muxNumber_TESLA_2E1) { diff --git a/Software/src/battery/TESLA-BATTERY.h b/Software/src/battery/TESLA-BATTERY.h index 91311b52..49dc11f2 100644 --- a/Software/src/battery/TESLA-BATTERY.h +++ b/Software/src/battery/TESLA-BATTERY.h @@ -461,7 +461,8 @@ class TeslaBattery : public CanBattery { .DLC = 8, .ID = 0x610, .data = {0x02, 0x10, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}}; // Define initial UDS request - + uint8_t index_1CF = 0; + uint8_t index_118 = 0; uint8_t stateMachineClearIsolationFault = 0xFF; uint8_t stateMachineBMSReset = 0xFF; uint8_t stateMachineSOCReset = 0xFF; From 3ef32795274acecc0c826171ffcaf3b899bb7673 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 25 Sep 2025 15:49:50 +0300 Subject: [PATCH 227/245] Remove unused codeblocks --- Software/src/battery/TESLA-BATTERY.cpp | 217 +------------------------ Software/src/battery/TESLA-BATTERY.h | 2 - 2 files changed, 4 insertions(+), 215 deletions(-) diff --git a/Software/src/battery/TESLA-BATTERY.cpp b/Software/src/battery/TESLA-BATTERY.cpp index 7c5042c0..7700e685 100644 --- a/Software/src/battery/TESLA-BATTERY.cpp +++ b/Software/src/battery/TESLA-BATTERY.cpp @@ -113,33 +113,6 @@ inline const char* getHvilStatusState(int index) { } } -inline const char* getBMSState(int index) { - switch (index) { - case 0: - return "STANDBY"; - case 1: - return "DRIVE"; - case 2: - return "SUPPORT"; - case 3: - return "CHARGE"; - case 4: - return "FEIM"; - case 5: - return "CLEAR_FAULT"; - case 6: - return "FAULT"; - case 7: - return "WELD"; - case 8: - return "TEST"; - case 9: - return "SNA"; - default: - return "UNKNOWN"; - } -} - inline const char* getBMSContactorState(int index) { switch (index) { case 0: @@ -161,174 +134,10 @@ inline const char* getBMSContactorState(int index) { } } -inline const char* getBMSHvState(int index) { - switch (index) { - case 0: - return "DOWN"; - case 1: - return "COMING_UP"; - case 2: - return "GOING_DOWN"; - case 3: - return "UP_FOR_DRIVE"; - case 4: - return "UP_FOR_CHARGE"; - case 5: - return "UP_FOR_DC_CHARGE"; - case 6: - return "UP"; - default: - return "UNKNOWN"; - } -} - -inline const char* getBMSUiChargeStatus(int index) { - switch (index) { - case 0: - return "DISCONNECTED"; - case 1: - return "NO_POWER"; - case 2: - return "ABOUT_TO_CHARGE"; - case 3: - return "CHARGING"; - case 4: - return "CHARGE_COMPLETE"; - case 5: - return "CHARGE_STOPPED"; - default: - return "UNKNOWN"; - } -} - -inline const char* getPCS_DcdcStatus(int index) { - switch (index) { - case 0: - return "IDLE"; - case 1: - return "ACTIVE"; - case 2: - return "FAULTED"; - default: - return "UNKNOWN"; - } -} - -inline const char* getPCS_DcdcMainState(int index) { - switch (index) { - case 0: - return "STANDBY"; - case 1: - return "12V_SUPPORT_ACTIVE"; - case 2: - return "PRECHARGE_STARTUP"; - case 3: - return "PRECHARGE_ACTIVE"; - case 4: - return "DIS_HVBUS_ACTIVE"; - case 5: - return "SHUTDOWN"; - case 6: - return "FAULTED"; - default: - return "UNKNOWN"; - } -} - -inline const char* getPCS_DcdcSubState(int index) { - switch (index) { - case 0: - return "PWR_UP_INIT"; - case 1: - return "STANDBY"; - case 2: - return "12V_SUPPORT_ACTIVE"; - case 3: - return "DIS_HVBUS"; - case 4: - return "PCHG_FAST_DIS_HVBUS"; - case 5: - return "PCHG_SLOW_DIS_HVBUS"; - case 6: - return "PCHG_DWELL_CHARGE"; - case 7: - return "PCHG_DWELL_WAIT"; - case 8: - return "PCHG_DI_RECOVERY_WAIT"; - case 9: - return "PCHG_ACTIVE"; - case 10: - return "PCHG_FLT_FAST_DIS_HVBUS"; - case 11: - return "SHUTDOWN"; - case 12: - return "12V_SUPPORT_FAULTED"; - case 13: - return "DIS_HVBUS_FAULTED"; - case 14: - return "PCHG_FAULTED"; - case 15: - return "CLEAR_FAULTS"; - case 16: - return "FAULTED"; - case 17: - return "NUM"; - default: - return "UNKNOWN"; - } -} - -inline const char* getBMSPowerLimitState(int index) { - switch (index) { - case 0: - return "NOT_CALCULATED_FOR_DRIVE"; - case 1: - return "CALCULATED_FOR_DRIVE"; - default: - return "UNKNOWN"; - } -} - -inline const char* getHVPStatus(int index) { - switch (index) { - case 0: - return "INVALID"; - case 1: - return "NOT_AVAILABLE"; - case 2: - return "STALE"; - case 3: - return "VALID"; - default: - return "UNKNOWN"; - } -} - -inline const char* getHVPContactor(int index) { - switch (index) { - case 0: - return "NOT_ACTIVE"; - case 1: - return "ACTIVE"; - case 2: - return "COMPLETED"; - default: - return "UNKNOWN"; - } -} - -inline const char* getFalseTrue(bool value) { - return value ? "True" : "False"; -} - inline const char* getNoYes(bool value) { return value ? "Yes" : "No"; } -inline const char* getFault(bool value) { - return value ? "ACTIVE" : "NOT_ACTIVE"; -} - // Clamp DLC to 0–8 bytes for classic CAN inline int getDataLen(uint8_t dlc) { return std::min(dlc, 8); @@ -660,8 +469,6 @@ void TeslaBattery:: datalayer.battery.status.cell_min_voltage_mV = battery_cell_min_v; - battery_cell_deviation_mV = (battery_cell_max_v - battery_cell_min_v); - /* Value mapping is completed. Start to check all safeties */ //INTERNAL_OPEN_FAULT - Someone disconnected a high voltage cable while battery was in use @@ -2024,8 +1831,6 @@ void TeslaBattery::transmit_can(unsigned long currentMillis) { default: break; } - //Generate next new frame - frameCounter_TESLA_221 = (frameCounter_TESLA_221 + 1) % 16; } if (vehicleState == ACCESSORY) { switch (muxNumber_TESLA_221) { @@ -2042,8 +1847,6 @@ void TeslaBattery::transmit_can(unsigned long currentMillis) { default: break; } - //Generate next new frame - frameCounter_TESLA_221 = (frameCounter_TESLA_221 + 1) % 16; } if (vehicleState == GOING_DOWN) { switch (muxNumber_TESLA_221) { @@ -2060,8 +1863,6 @@ void TeslaBattery::transmit_can(unsigned long currentMillis) { default: break; } - //Generate next new frame - frameCounter_TESLA_221 = (frameCounter_TESLA_221 + 1) % 16; } if (vehicleState == CAR_OFF) { switch (muxNumber_TESLA_221) { @@ -2078,23 +1879,13 @@ void TeslaBattery::transmit_can(unsigned long currentMillis) { default: break; } - //Generate next new frame - frameCounter_TESLA_221 = (frameCounter_TESLA_221 + 1) % 16; } + //Generate next new frame + frameCounter_TESLA_221 = (frameCounter_TESLA_221 + 1) % 16; //0x3C2 VCLEFT_switchStatus - switch (muxNumber_TESLA_3C2) { - case 0: - transmit_can_frame(&TESLA_3C2_Mux0); - muxNumber_TESLA_3C2++; - break; - case 1: - transmit_can_frame(&TESLA_3C2_Mux1); - muxNumber_TESLA_3C2 = 0; - break; - default: - break; - } + transmit_can_frame(muxNumber_TESLA_3C2 == 0 ? &TESLA_3C2_Mux0 : &TESLA_3C2_Mux1); + muxNumber_TESLA_3C2 = !muxNumber_TESLA_3C2; // Flip between sending Mux0 and Mux1 on each pass //0x39D IBST_status transmit_can_frame(&TESLA_39D); diff --git a/Software/src/battery/TESLA-BATTERY.h b/Software/src/battery/TESLA-BATTERY.h index 49dc11f2..9d599eba 100644 --- a/Software/src/battery/TESLA-BATTERY.h +++ b/Software/src/battery/TESLA-BATTERY.h @@ -467,10 +467,8 @@ class TeslaBattery : public CanBattery { uint8_t stateMachineBMSReset = 0xFF; uint8_t stateMachineSOCReset = 0xFF; uint8_t stateMachineBMSQuery = 0xFF; - uint16_t sendContactorClosingMessagesStill = 300; uint16_t battery_cell_max_v = 3300; uint16_t battery_cell_min_v = 3300; - uint16_t battery_cell_deviation_mV = 0; //contains the deviation between highest and lowest cell in mV bool cellvoltagesRead = false; //0x3d2: 978 BMS_kwhCounter uint32_t battery_total_discharge = 0; From 95ee6ff9ae6f6db0dd9c6ed73a7633c02c1ded4c Mon Sep 17 00:00:00 2001 From: James Brookes Date: Thu, 25 Sep 2025 19:03:54 +0100 Subject: [PATCH 228/245] Add events for BMS_a145_SW_SOC_Change, BMS reset and SOC reset --- Software/src/battery/TESLA-BATTERY.cpp | 64 ++++++++++++++++++-------- Software/src/devboard/utils/events.cpp | 13 +++++- Software/src/devboard/utils/events.h | 5 ++ 3 files changed, 61 insertions(+), 21 deletions(-) diff --git a/Software/src/battery/TESLA-BATTERY.cpp b/Software/src/battery/TESLA-BATTERY.cpp index 7700e685..bc7b3761 100644 --- a/Software/src/battery/TESLA-BATTERY.cpp +++ b/Software/src/battery/TESLA-BATTERY.cpp @@ -483,11 +483,17 @@ void TeslaBattery:: } else { clear_event(EVENT_BATTERY_FUSE); } + // Raise any informational Tesla BMS events in BE + if (BMS_a145_SW_SOC_Change == true) { // BMS has recalibrated pack SOC + set_event(EVENT_BATTERY_SOC_RECALIBRATION, 0); + } else { + clear_event(EVENT_BATTERY_SOC_RECALIBRATION); + } if (user_selected_tesla_GTW_chassisType > 1) { //{{0, "Model S"}, {1, "Model X"}, {2, "Model 3"}, {3, "Model Y"}}; - // Autodetect algoritm for chemistry on 3/Y packs. + // Autodetect algorithm for chemistry on 3/Y packs. // NCM/A batteries have 96s, LFP has 102-108s - // Drawback with this check is that it takes 3-5minutes before all cells have been counted! + // Drawback with this check is that it takes 3-5 minutes before all cells have been counted! if (datalayer.battery.info.number_of_cells > 101) { datalayer.battery.info.chemistry = battery_chemistry_enum::LFP; } @@ -528,23 +534,28 @@ void TeslaBattery:: //Start the BMS ECU reset statemachine, only if contactors are OPEN and BMS ECU allows it stateMachineBMSReset = 0; datalayer.battery.settings.user_requests_tesla_bms_reset = false; - logging.println("BMS reset requested"); + logging.println("INFO: BMS reset requested"); } else { logging.println("ERROR: BMS reset failed due to contactors not being open, or BMS ECU not allowing it"); stateMachineBMSReset = 0xFF; datalayer.battery.settings.user_requests_tesla_bms_reset = false; + set_event(EVENT_BMS_RESET_REQ_FAIL, 0); + clear_event(EVENT_BMS_RESET_REQ_FAIL); } } if (datalayer.battery.settings.user_requests_tesla_soc_reset) { - if (datalayer.battery.status.real_soc < 1500 || datalayer.battery.status.real_soc > 9000) { - //Start the SOC reset statemachine, only if SOC < 15% or > 90% + if ((datalayer.battery.status.real_soc < 1500 || datalayer.battery.status.real_soc > 9000) && + battery_contactor == 1) { + //Start the SOC reset statemachine, only if SOC less than 15% or greater than 90%, and contactors open stateMachineSOCReset = 0; datalayer.battery.settings.user_requests_tesla_soc_reset = false; - logging.println("SOC reset requested"); + logging.println("INFO: SOC reset requested"); } else { - logging.println("ERROR: SOC reset failed due to SOC not being less than 15 or greater than 90"); + logging.println("ERROR: SOC reset failed, SOC not < 15 or > 90"); stateMachineSOCReset = 0xFF; datalayer.battery.settings.user_requests_tesla_soc_reset = false; + set_event(EVENT_BATTERY_SOC_RESET_FAIL, 0); + clear_event(EVENT_BATTERY_SOC_RESET_FAIL); } } @@ -779,7 +790,7 @@ void TeslaBattery:: datalayer_extended.tesla.HVP_shuntBarTempStatus = HVP_shuntBarTempStatus; datalayer_extended.tesla.HVP_shuntAsicTempStatus = HVP_shuntAsicTempStatus; - //Safety checks for CAN message sesnding + //Safety checks for CAN message sending if ((datalayer.system.status.inverter_allows_contactor_closing == true) && (datalayer.battery.status.bms_status != FAULT) && (!datalayer.system.settings.equipment_stop_active)) { // Carry on: 0x221 DRIVE state & reset power down timer @@ -1667,10 +1678,10 @@ void TeslaBattery::handle_incoming_can_frame(CAN_frame rx_frame) { } */ break; - case 0x612: // CAN UDSs for BMS + case 0x612: // CAN UDS responses for BMS datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; //BMS Query - if (stateMachineBMSQuery != 0xFF && stateMachineBMSReset == 0xFF) { + if (stateMachineBMSQuery != 0xFF && stateMachineBMSReset == 0xFF && stateMachineSOCReset == 0xFF) { if (memcmp(rx_frame.data.u8, "\x02\x50\x03\xAA\xAA\xAA\xAA\xAA", 8) == 0) { //Received initial response, proceed to actual query logging.println("CAN UDS: Received BMS query initial handshake reply"); @@ -1713,15 +1724,28 @@ void TeslaBattery::handle_incoming_can_frame(CAN_frame rx_frame) { break; } } - //BMS Reset - if (stateMachineBMSQuery == 0xFF) { // Make sure this is reset request not query - if (memcmp(rx_frame.data.u8, "\x02\x67\x06\xAA\xAA\xAA\xAA\xAA", 8) == 0) { - logging.println("CAN UDS: ECU unlocked"); - } else if (memcmp(rx_frame.data.u8, "\x03\x7F\x11\x78\xAA\xAA\xAA\xAA", 8) == 0) { - logging.println("CAN UDS: ECU reset request successful but ECU busy, response pending"); - } else if (memcmp(rx_frame.data.u8, "\x02\x51\x01\xAA\xAA\xAA\xAA\xAA", 8) == 0) { - logging.println("CAN UDS: ECU reset positive response, 1 second downtime"); - } + //BMS ECU responses + if (memcmp(rx_frame.data.u8, "\x02\x67\x06\xAA\xAA\xAA\xAA\xAA", 8) == 0) { + logging.println("CAN UDS: BMS ECU unlocked"); + } + if (memcmp(rx_frame.data.u8, "\x03\x7F\x11\x78\xAA\xAA\xAA\xAA", 8) == 0) { + logging.println("CAN UDS: BMS ECU reset request successful but ECU busy, response pending"); + } + if (memcmp(rx_frame.data.u8, "\x02\x51\x01\xAA\xAA\xAA\xAA\xAA", 8) == 0) { + logging.println("CAN UDS: BMS ECU reset positive response, 1 second downtime"); + set_event(EVENT_BMS_RESET_REQ_SUCCESS, 0); + clear_event(EVENT_BMS_RESET_REQ_SUCCESS); + } + if (memcmp(rx_frame.data.u8, "\x05\x71\x01\x04\x07\x01\xAA\xAA", 8) == 0) { + logging.println("CAN UDS: BMS SOC reset accepted, resetting BMS ECU"); + set_event(EVENT_BATTERY_SOC_RESET_SUCCESS, 0); + clear_event(EVENT_BATTERY_SOC_RESET_SUCCESS); + stateMachineBMSReset = 6; // BMS ECU already unlocked etc. so we jump straight to reset + } + if (memcmp(rx_frame.data.u8, "\x05\x71\x01\x04\x07\x00\xAA\xAA", 8) == 0) { + logging.println("CAN UDS: BMS SOC reset failed"); + set_event(EVENT_BATTERY_SOC_RESET_FAIL, 0); + clear_event(EVENT_BATTERY_SOC_RESET_FAIL); } break; default: @@ -2308,7 +2332,7 @@ void TeslaBattery::printFaultCodesIfActive() { printDebugIfActive(BMS_a139_SW_DC_Link_V_Irrational, "ERROR: BMS_a139_SW_DC_Link_V_Irrational"); printDebugIfActive(BMS_a141_SW_BMB_Status_Warning, "ERROR: BMS_a141_SW_BMB_Status_Warning"); printDebugIfActive(BMS_a144_Hvp_Config_Mismatch, "ERROR: BMS_a144_Hvp_Config_Mismatch"); - printDebugIfActive(BMS_a145_SW_SOC_Change, "ERROR: BMS_a145_SW_SOC_Change"); + printDebugIfActive(BMS_a145_SW_SOC_Change, "INFO: BMS_a145_SW_SOC_Change"); printDebugIfActive(BMS_a146_SW_Brick_Overdischarged, "ERROR: BMS_a146_SW_Brick_Overdischarged"); printDebugIfActive(BMS_a149_SW_Missing_Config_Block, "ERROR: BMS_a149_SW_Missing_Config_Block"); printDebugIfActive(BMS_a151_SW_external_isolation, "ERROR: BMS_a151_SW_external_isolation"); diff --git a/Software/src/devboard/utils/events.cpp b/Software/src/devboard/utils/events.cpp index d6875aab..6af46dcf 100644 --- a/Software/src/devboard/utils/events.cpp +++ b/Software/src/devboard/utils/events.cpp @@ -67,6 +67,9 @@ void init_events(void) { events.entries[EVENT_BATTERY_UNDERVOLTAGE].level = EVENT_LEVEL_WARNING; events.entries[EVENT_BATTERY_VALUE_UNAVAILABLE].level = EVENT_LEVEL_WARNING; events.entries[EVENT_BATTERY_ISOLATION].level = EVENT_LEVEL_WARNING; + events.entries[EVENT_BATTERY_SOC_RECALIBRATION].level = EVENT_LEVEL_INFO; + events.entries[EVENT_BATTERY_SOC_RESET_SUCCESS].level = EVENT_LEVEL_INFO; + events.entries[EVENT_BATTERY_SOC_RESET_FAIL].level = EVENT_LEVEL_INFO; events.entries[EVENT_VOLTAGE_DIFFERENCE].level = EVENT_LEVEL_INFO; events.entries[EVENT_SOH_DIFFERENCE].level = EVENT_LEVEL_WARNING; events.entries[EVENT_SOH_LOW].level = EVENT_LEVEL_ERROR; @@ -124,6 +127,8 @@ void init_events(void) { events.entries[EVENT_EQUIPMENT_STOP].level = EVENT_LEVEL_ERROR; events.entries[EVENT_SD_INIT_FAILED].level = EVENT_LEVEL_WARNING; events.entries[EVENT_PERIODIC_BMS_RESET].level = EVENT_LEVEL_INFO; + events.entries[EVENT_BMS_RESET_REQ_SUCCESS].level = EVENT_LEVEL_INFO; + events.entries[EVENT_BMS_RESET_REQ_FAIL].level = EVENT_LEVEL_INFO; events.entries[EVENT_BATTERY_TEMP_DEVIATION_HIGH].level = EVENT_LEVEL_WARNING; events.entries[EVENT_GPIO_CONFLICT].level = EVENT_LEVEL_ERROR; events.entries[EVENT_GPIO_NOT_DEFINED].level = EVENT_LEVEL_ERROR; @@ -244,6 +249,8 @@ String get_event_message_string(EVENTS_ENUM_TYPE event) { return "Battery measurement unavailable. Check 12V power supply and battery wiring!"; case EVENT_BATTERY_ISOLATION: return "Battery reports isolation error. High voltage might be leaking to ground. Check battery!"; + case EVENT_BATTERY_SOC_RECALIBRATION: + return "The BMS updated the HV battery State of Charge (SOC) by more than 3% based on SocByOcv."; case EVENT_VOLTAGE_DIFFERENCE: return "Too large voltage diff between the batteries. Second battery cannot join the DC-link"; case EVENT_SOH_DIFFERENCE: @@ -361,7 +368,11 @@ String get_event_message_string(EVENTS_ENUM_TYPE event) { case EVENT_SD_INIT_FAILED: return "SD card initialization failed, check hardware. Power must be removed to reset the SD card."; case EVENT_PERIODIC_BMS_RESET: - return "BMS Reset Event Completed."; + return "BMS reset event completed."; + case EVENT_BMS_RESET_REQ_SUCCESS: + return "BMS reset request completed successfully."; + case EVENT_BMS_RESET_REQ_FAIL: + return "BMS reset request failed - check contactors are open."; case EVENT_GPIO_CONFLICT: return "GPIO Pin Conflict: The pin used by '" + esp32hal->failed_allocator() + "' is already allocated by '" + esp32hal->conflicting_allocator() + "'. Please check your configuration and assign different pins."; diff --git a/Software/src/devboard/utils/events.h b/Software/src/devboard/utils/events.h index 8f85b13b..3f4090be 100644 --- a/Software/src/devboard/utils/events.h +++ b/Software/src/devboard/utils/events.h @@ -49,6 +49,9 @@ XX(EVENT_BATTERY_ISOLATION) \ XX(EVENT_BATTERY_REQUESTS_HEAT) \ XX(EVENT_BATTERY_WARMED_UP) \ + XX(EVENT_BATTERY_SOC_RECALIBRATION) \ + XX(EVENT_BATTERY_SOC_RESET_SUCCESS) \ + XX(EVENT_BATTERY_SOC_RESET_FAIL) \ XX(EVENT_VOLTAGE_DIFFERENCE) \ XX(EVENT_SOH_DIFFERENCE) \ XX(EVENT_SOH_LOW) \ @@ -107,6 +110,8 @@ XX(EVENT_AUTOMATIC_PRECHARGE_FAILURE) \ XX(EVENT_SD_INIT_FAILED) \ XX(EVENT_PERIODIC_BMS_RESET) \ + XX(EVENT_BMS_RESET_REQ_SUCCESS) \ + XX(EVENT_BMS_RESET_REQ_FAIL) \ XX(EVENT_BATTERY_TEMP_DEVIATION_HIGH) \ XX(EVENT_GPIO_NOT_DEFINED) \ XX(EVENT_GPIO_CONFLICT) \ From c6b7ff82c06fec675fd40aed516643d83ee26d37 Mon Sep 17 00:00:00 2001 From: James Brookes Date: Thu, 25 Sep 2025 19:09:46 +0100 Subject: [PATCH 229/245] Alter BMS_a145_SW_SOC_Change event to once only --- Software/src/battery/TESLA-BATTERY.cpp | 6 ++++-- Software/src/battery/TESLA-BATTERY.h | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Software/src/battery/TESLA-BATTERY.cpp b/Software/src/battery/TESLA-BATTERY.cpp index bc7b3761..56a152b1 100644 --- a/Software/src/battery/TESLA-BATTERY.cpp +++ b/Software/src/battery/TESLA-BATTERY.cpp @@ -484,10 +484,12 @@ void TeslaBattery:: clear_event(EVENT_BATTERY_FUSE); } // Raise any informational Tesla BMS events in BE - if (BMS_a145_SW_SOC_Change == true) { // BMS has recalibrated pack SOC + if (BMS_a145_SW_SOC_Change == true && !BMS_SW_SOC_Change_Latch) { // BMS has newly recalibrated pack SOC + BMS_SW_SOC_Change_Latch = true; // Only set event once, BMS_a145 can be active for a while set_event(EVENT_BATTERY_SOC_RECALIBRATION, 0); - } else { clear_event(EVENT_BATTERY_SOC_RECALIBRATION); + } else if (BMS_a145_SW_SOC_Change == false) { + BMS_SW_SOC_Change_Latch = false; } if (user_selected_tesla_GTW_chassisType > 1) { //{{0, "Model S"}, {1, "Model X"}, {2, "Model 3"}, {3, "Model Y"}}; diff --git a/Software/src/battery/TESLA-BATTERY.h b/Software/src/battery/TESLA-BATTERY.h index 9d599eba..8abc15db 100644 --- a/Software/src/battery/TESLA-BATTERY.h +++ b/Software/src/battery/TESLA-BATTERY.h @@ -877,6 +877,7 @@ class TeslaBattery : public CanBattery { bool BMS_a141_SW_BMB_Status_Warning = false; bool BMS_a144_Hvp_Config_Mismatch = false; bool BMS_a145_SW_SOC_Change = false; + bool BMS_SW_SOC_Change_Latch = false; bool BMS_a146_SW_Brick_Overdischarged = false; bool BMS_a149_SW_Missing_Config_Block = false; bool BMS_a151_SW_external_isolation = false; From 29129037b08f65f6437f4c505468985acb667b17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 25 Sep 2025 23:44:46 +0300 Subject: [PATCH 230/245] Add more input field validation on Settings page --- .../src/devboard/webserver/settings_html.cpp | 63 ++++++++++++++----- Software/src/devboard/webserver/webserver.cpp | 3 +- 2 files changed, 50 insertions(+), 16 deletions(-) diff --git a/Software/src/devboard/webserver/settings_html.cpp b/Software/src/devboard/webserver/settings_html.cpp index eef9f425..fc59267b 100644 --- a/Software/src/devboard/webserver/settings_html.cpp +++ b/Software/src/devboard/webserver/settings_html.cpp @@ -1029,10 +1029,14 @@ const char* getCANInterfaceName(CAN_Interface interface) {
- + - +
@@ -1163,10 +1167,14 @@ const char* getCANInterfaceName(CAN_Interface interface) { - + - + +
- + - - + +
@@ -1226,16 +1240,26 @@ const char* getCANInterfaceName(CAN_Interface interface) { - + - + - + - + @@ -1271,11 +1295,20 @@ const char* getCANInterfaceName(CAN_Interface interface) {
- - + + + + - + + diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 9350652d..8bf9e71b 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -524,7 +524,8 @@ void init_webserver() { } else if (p->name() == "MQTTTOPIC") { settings.saveString("MQTTTOPIC", p->value().c_str()); } else if (p->name() == "MQTTTIMEOUT") { - settings.saveString("MQTTTIMEOUT", p->value().c_str()); + auto port = atoi(p->value().c_str()); + settings.saveUInt("MQTTTIMEOUT", port); } else if (p->name() == "MQTTOBJIDPREFIX") { settings.saveString("MQTTOBJIDPREFIX", p->value().c_str()); } else if (p->name() == "MQTTDEVICENAME") { From 04d9a362925eb51cf4b2cd15459bd91e725b25d0 Mon Sep 17 00:00:00 2001 From: James Brookes Date: Fri, 26 Sep 2025 10:38:56 +0100 Subject: [PATCH 231/245] Add event and logging text for SOC Reset --- Software/src/devboard/utils/events.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Software/src/devboard/utils/events.cpp b/Software/src/devboard/utils/events.cpp index 6af46dcf..57882def 100644 --- a/Software/src/devboard/utils/events.cpp +++ b/Software/src/devboard/utils/events.cpp @@ -251,6 +251,10 @@ String get_event_message_string(EVENTS_ENUM_TYPE event) { return "Battery reports isolation error. High voltage might be leaking to ground. Check battery!"; case EVENT_BATTERY_SOC_RECALIBRATION: return "The BMS updated the HV battery State of Charge (SOC) by more than 3% based on SocByOcv."; + case EVENT_BATTERY_SOC_RESET_SUCCESS: + return "SOC reset routine was successful."; + case EVENT_BATTERY_SOC_RESET_FAIL: + return "SOC reset routine failed - check SOC is < 15 or > 90, and contactors are open."; case EVENT_VOLTAGE_DIFFERENCE: return "Too large voltage diff between the batteries. Second battery cannot join the DC-link"; case EVENT_SOH_DIFFERENCE: From 4058050423c1081b502c4da0f00660afeab940a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Fri, 26 Sep 2025 13:45:39 +0300 Subject: [PATCH 232/245] Add more tooltips --- .../src/devboard/webserver/settings_html.cpp | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/Software/src/devboard/webserver/settings_html.cpp b/Software/src/devboard/webserver/settings_html.cpp index fc59267b..7f24b588 100644 --- a/Software/src/devboard/webserver/settings_html.cpp +++ b/Software/src/devboard/webserver/settings_html.cpp @@ -750,7 +750,7 @@ const char* getCANInterfaceName(CAN_Interface interface) { function editError(){alert('Invalid input');} - function editSSID(){var value=prompt('Enter new SSID:');if(value!==null){var xhr=new + function editSSID(){var value=prompt('Which SSID to connect to. Enter new SSID:');if(value!==null){var xhr=new XMLHttpRequest();xhr.onload=editComplete;xhr.onerror=editError;xhr.open('GET','/updateSSID?value='+encodeURIComponent(value),true);xhr.send();}} function editPassword(){var value=prompt('Enter new password:');if(value!==null){var xhr=new @@ -1041,7 +1041,8 @@ const char* getCANInterfaceName(CAN_Interface interface) {
- +
@@ -1056,20 +1057,25 @@ const char* getCANInterfaceName(CAN_Interface interface) {
- + - + - + - +
- +
@@ -1164,7 +1170,8 @@ const char* getCANInterfaceName(CAN_Interface interface) {
- + Date: Fri, 26 Sep 2025 19:29:50 +0300 Subject: [PATCH 233/245] Improve password/username entering --- .../src/devboard/webserver/settings_html.cpp | 37 ++++++++++++------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/Software/src/devboard/webserver/settings_html.cpp b/Software/src/devboard/webserver/settings_html.cpp index 7f24b588..f3f12f73 100644 --- a/Software/src/devboard/webserver/settings_html.cpp +++ b/Software/src/devboard/webserver/settings_html.cpp @@ -1248,14 +1248,14 @@ const char* getCANInterfaceName(CAN_Interface interface) { @@ -1304,14 +1304,18 @@ const char* getCANInterfaceName(CAN_Interface interface) {
- - + + - + - - +