Convert .ino to .cpp file

This commit is contained in:
Daniel Öster 2025-09-01 23:50:17 +03:00
parent aba193ca85
commit 1cc4a021c2

View file

@ -1,5 +1,4 @@
/* Do not change any code below this line unless you are sure what you are doing */ #include <Arduino.h>
/* Only change battery specific settings in "USER_SETTINGS.h" */
#include "HardwareSerial.h" #include "HardwareSerial.h"
#include "USER_SETTINGS.h" #include "USER_SETTINGS.h"
#include "esp_system.h" #include "esp_system.h"
@ -51,130 +50,20 @@ TaskHandle_t connectivity_loop_task;
TaskHandle_t logging_loop_task; TaskHandle_t logging_loop_task;
TaskHandle_t mqtt_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; Logging logging;
// Initialization static std::list<Transmitter*> transmitters;
void setup() {
init_hal();
init_serial(); void register_transmitter(Transmitter* transmitter) {
transmitters.push_back(transmitter);
// We print this after setting up serial, so that is also printed if configured to do so DEBUG_PRINTF("transmitter registered, total: %d\n", transmitters.size());
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 // Initialization functions
void loop() {} void init_serial() {
// Init Serial monitor
void logging_loop(void*) { Serial.begin(115200);
while (!Serial) {}
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();
}
}
} }
void connectivity_loop(void*) { void connectivity_loop(void*) {
@ -205,146 +94,22 @@ void connectivity_loop(void*) {
} }
} }
void mqtt_loop(void*) { void logging_loop(void*) {
esp_task_wdt_add(NULL); // Register this task with WDT
init_logging_buffers();
init_sdcard();
while (true) { while (true) {
START_TIME_MEASUREMENT(mqtt); if (datalayer.system.info.SD_logging_active) {
mqtt_loop(); write_log_to_sdcard();
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.CAN_SD_logging_active) {
write_can_frame_to_sdcard();
}
} }
} }
static std::list<Transmitter*> 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() { void check_interconnect_available() {
if (datalayer.battery.status.voltage_dV == 0 || datalayer.battery2.status.voltage_dV == 0) { if (datalayer.battery.status.voltage_dV == 0 || datalayer.battery2.status.voltage_dV == 0) {
return; // Both voltage values need to be available to start check return; // Both voltage values need to be available to start check
@ -589,3 +354,229 @@ void check_reset_reason() {
break; 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);
}
}