mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-03 01:39:30 +02:00
Convert .ino to .cpp file
This commit is contained in:
parent
aba193ca85
commit
1cc4a021c2
1 changed files with 247 additions and 256 deletions
|
@ -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 <Arduino.h>
|
||||
#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<Transmitter*> 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,144 +94,20 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
if (datalayer.system.info.SD_logging_active) {
|
||||
write_log_to_sdcard();
|
||||
}
|
||||
|
||||
// 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 (datalayer.system.info.CAN_SD_logging_active) {
|
||||
write_can_frame_to_sdcard();
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue