Introduce Transmitter interface for generic comm transmit and Shunt

class
This commit is contained in:
Jaakko Haakana 2025-06-02 23:44:18 +03:00
parent dbe3de7422
commit 94cc5effa4
16 changed files with 181 additions and 134 deletions

View file

@ -9,6 +9,7 @@
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "src/communication/Transmitter.h"
#include "src/communication/can/comm_can.h" #include "src/communication/can/comm_can.h"
#include "src/communication/contactorcontrol/comm_contactorcontrol.h" #include "src/communication/contactorcontrol/comm_contactorcontrol.h"
#include "src/communication/equipmentstopbutton/comm_equipmentstopbutton.h" #include "src/communication/equipmentstopbutton/comm_equipmentstopbutton.h"
@ -105,9 +106,8 @@ void setup() {
#ifdef EQUIPMENT_STOP_BUTTON #ifdef EQUIPMENT_STOP_BUTTON
init_equipment_stop_button(); init_equipment_stop_button();
#endif #endif
#ifdef CAN_SHUNT_SELECTED
setup_can_shunt(); setup_can_shunt();
#endif
// BOOT button at runtime is used as an input for various things // BOOT button at runtime is used as an input for various things
pinMode(0, INPUT_PULLUP); pinMode(0, INPUT_PULLUP);
@ -203,6 +203,12 @@ void mqtt_loop(void*) {
} }
#endif #endif
static std::list<Transmitter*> transmitters;
void register_transmitter(Transmitter* transmitter) {
transmitters.push_back(transmitter);
}
void core_loop(void*) { void core_loop(void*) {
esp_task_wdt_add(NULL); // Register this task with WDT esp_task_wdt_add(NULL); // Register this task with WDT
TickType_t xLastWakeTime = xTaskGetTickCount(); TickType_t xLastWakeTime = xTaskGetTickCount();
@ -254,16 +260,25 @@ void core_loop(void*) {
#ifdef FUNCTION_TIME_MEASUREMENT #ifdef FUNCTION_TIME_MEASUREMENT
START_TIME_MEASUREMENT(time_values); START_TIME_MEASUREMENT(time_values);
#endif #endif
update_pause_state(); // Check if we are OK to send CAN or need to pause update_pause_state(); // Check if we are OK to send CAN or need to pause
update_values_battery(); // Fetch battery values
// Fetch battery values
if (battery) {
battery->update_values();
}
if (battery2) { if (battery2) {
update_values_battery2(); battery2->update_values();
check_interconnect_available(); check_interconnect_available();
} }
update_calculated_values(); update_calculated_values();
update_machineryprotection(); // Check safeties update_machineryprotection(); // Check safeties
update_values_inverter(); // Update values heading towards inverter
// Update values heading towards inverter
if (inverter) {
inverter->update_values();
}
#ifdef FUNCTION_TIME_MEASUREMENT #ifdef FUNCTION_TIME_MEASUREMENT
END_TIME_MEASUREMENT_MAX(time_values, datalayer.system.status.time_values_us); END_TIME_MEASUREMENT_MAX(time_values, datalayer.system.status.time_values_us);
#endif #endif
@ -271,12 +286,12 @@ void core_loop(void*) {
#ifdef FUNCTION_TIME_MEASUREMENT #ifdef FUNCTION_TIME_MEASUREMENT
START_TIME_MEASUREMENT(cantx); START_TIME_MEASUREMENT(cantx);
#endif #endif
// Output
transmit_can(currentMillis); // Send CAN messages to all components
#ifdef RS485_BATTERY_SELECTED // Let all transmitter objects send their messages
transmit_rs485(currentMillis); for (auto& transmitter : transmitters) {
#endif // RS485_BATTERY_SELECTED transmitter->transmit(currentMillis);
}
#ifdef FUNCTION_TIME_MEASUREMENT #ifdef FUNCTION_TIME_MEASUREMENT
END_TIME_MEASUREMENT_MAX(cantx, datalayer.system.status.time_cantx_us); END_TIME_MEASUREMENT_MAX(cantx, datalayer.system.status.time_cantx_us);
END_TIME_MEASUREMENT_MAX(all, datalayer.system.status.core_task_10s_max_us); END_TIME_MEASUREMENT_MAX(all, datalayer.system.status.core_task_10s_max_us);
@ -508,12 +523,6 @@ void update_calculated_values() {
lastMillisOverflowCheck = currentMillis; lastMillisOverflowCheck = currentMillis;
} }
void update_values_inverter() {
if (inverter) {
inverter->update_values();
}
}
void check_reset_reason() { void check_reset_reason() {
esp_reset_reason_t reason = esp_reset_reason(); esp_reset_reason_t reason = esp_reset_reason();
switch (reason) { switch (reason) {

View file

@ -41,41 +41,21 @@ void setup_battery() {
#endif #endif
} }
void update_values_battery() {
battery->update_values();
}
// transmit_can_battery is called once and we need to // transmit_can_battery is called once and we need to
// call both batteries. // call both batteries.
void transmit_can_battery(unsigned long currentMillis) {
((CanBattery*)battery)->transmit_can(currentMillis);
if (battery2) {
((CanBattery*)battery2)->transmit_can(currentMillis);
}
}
void handle_incoming_can_frame_battery(CAN_frame rx_frame) { void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
((CanBattery*)battery)->handle_incoming_can_frame(rx_frame); ((CanBattery*)battery)->handle_incoming_can_frame(rx_frame);
} }
void update_values_battery2() {
battery2->update_values();
}
void handle_incoming_can_frame_battery2(CAN_frame rx_frame) { void handle_incoming_can_frame_battery2(CAN_frame rx_frame) {
((CanBattery*)battery2)->handle_incoming_can_frame(rx_frame); ((CanBattery*)battery2)->handle_incoming_can_frame(rx_frame);
} }
#ifdef RS485_BATTERY_SELECTED #ifdef RS485_BATTERY_SELECTED
void transmit_rs485() {
((RS485Battery*)battery)->transmit_rs485();
}
void receive_RS485() { void receive_RS485() {
((RS485Battery*)battery)->receive_RS485(); ((RS485Battery*)battery)->receive_RS485();
} }
#endif #endif
#endif #endif

View file

@ -12,7 +12,6 @@ extern Battery* battery2;
#ifdef BMW_SBOX #ifdef BMW_SBOX
#include "BMW-SBOX.h" #include "BMW-SBOX.h"
void handle_incoming_can_frame_shunt(CAN_frame rx_frame); void handle_incoming_can_frame_shunt(CAN_frame rx_frame);
void transmit_can_shunt(unsigned long currentMillis);
void setup_can_shunt(); void setup_can_shunt();
#endif #endif

View file

@ -1,41 +1,9 @@
#include "../include.h" #include "../include.h"
#ifdef BMW_SBOX #ifdef BMW_SBOX
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "BMW-SBOX.h" #include "BMW-SBOX.h"
#define MAX_ALLOWED_FAULT_TICKS 1000
enum SboxState { DISCONNECTED, PRECHARGE, NEGATIVE, POSITIVE, PRECHARGE_OFF, COMPLETED, SHUTDOWN_REQUESTED };
SboxState contactorStatus = DISCONNECTED;
unsigned long prechargeStartTime = 0;
unsigned long negativeStartTime = 0;
unsigned long positiveStartTime = 0;
unsigned long timeSpentInFaultedMode = 0;
unsigned long LastMsgTime = 0; // will store last time a 20ms CAN Message was send
unsigned long LastAvgTime = 0; // Last current storage time
unsigned long ShuntLastSeen = 0;
uint32_t avg_mA_array[10];
uint32_t avg_sum;
uint8_t k; //avg array pointer
uint8_t CAN100_cnt = 0;
CAN_frame SBOX_100 = {.FD = false,
.ext_ID = false,
.DLC = 4,
.ID = 0x100,
.data = {0x55, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00,
0x00}}; // Byte 0: relay control, Byte 1: counter 0-E, Byte 4: CRC
CAN_frame SBOX_300 = {.FD = false,
.ext_ID = false,
.DLC = 4,
.ID = 0x300,
.data = {0xFF, 0xFE, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}}; // Static frame
uint8_t reverse_bits(uint8_t byte) { uint8_t reverse_bits(uint8_t byte) {
uint8_t reversed = 0; uint8_t reversed = 0;
for (int i = 0; i < 8; i++) { for (int i = 0; i < 8; i++) {
@ -64,7 +32,7 @@ uint8_t calculateCRC(CAN_frame CAN) {
return crc; return crc;
} }
void handle_incoming_can_frame_shunt(CAN_frame rx_frame) { void BmwSbox::handle_incoming_can_frame(CAN_frame rx_frame) {
unsigned long currentTime = millis(); unsigned long currentTime = millis();
if (rx_frame.ID == 0x200) { if (rx_frame.ID == 0x200) {
ShuntLastSeen = currentTime; ShuntLastSeen = currentTime;
@ -100,7 +68,7 @@ void handle_incoming_can_frame_shunt(CAN_frame rx_frame) {
} }
} }
void transmit_can_shunt(unsigned long currentMillis) { void BmwSbox::transmit_can(unsigned long currentMillis) {
/** Shunt can frames seen? **/ /** Shunt can frames seen? **/
if (ShuntLastSeen + 1000 < currentMillis) { if (ShuntLastSeen + 1000 < currentMillis) {
@ -210,7 +178,7 @@ void transmit_can_shunt(unsigned long currentMillis) {
} }
} }
void setup_can_shunt() { void BmwSbox::setup() {
strncpy(datalayer.system.info.shunt_protocol, "BMW SBOX", 63); strncpy(datalayer.system.info.shunt_protocol, "BMW SBOX", 63);
datalayer.system.info.shunt_protocol[63] = '\0'; datalayer.system.info.shunt_protocol[63] = '\0';
} }

View file

@ -1,22 +1,67 @@
#ifndef BMW_SBOX_CONTROL_H #ifndef BMW_SBOX_CONTROL_H
#define BMW_SBOX_CONTROL_H #define BMW_SBOX_CONTROL_H
#include "../include.h" #include "../include.h"
#define CAN_SHUNT_SELECTED #define CAN_SHUNT_SELECTED
void transmit_can(CAN_frame* tx_frame, int interface); #define SELECTED_SHUNT_CLASS BmwSbox
/** Minimum input voltage required to enable relay control **/ #include "Shunt.h"
#define MINIMUM_INPUT_VOLTAGE 250
/** Minimum required percentage of input voltage at the output port to engage the positive relay. **/ class BmwSbox : public CanShunt {
/** Prevents engagement of the positive contactor if the precharge resistor is faulty. **/ public:
#define MAX_PRECHARGE_RESISTOR_VOLTAGE_PERCENT 0.99 void setup();
void transmit_can(unsigned long currentMillis);
void handle_incoming_can_frame(CAN_frame rx_frame);
/* NOTE: modify the T2 time constant below to account for the resistance and capacitance of the target system. private:
/** Minimum input voltage required to enable relay control **/
static const int MINIMUM_INPUT_VOLTAGE = 250;
/** Minimum required percentage of input voltage at the output port to engage the positive relay. **/
/** Prevents engagement of the positive contactor if the precharge resistor is faulty. **/
static constexpr const double MAX_PRECHARGE_RESISTOR_VOLTAGE_PERCENT = 0.99;
/* NOTE: modify the T2 time constant below to account for the resistance and capacitance of the target system.
* t=3RC at minimum, t=5RC ideally * t=3RC at minimum, t=5RC ideally
*/ */
#define CONTACTOR_CONTROL_T1 5000 // Time before negative contactor engages and precharging starts static const int CONTACTOR_CONTROL_T1 = 5000; // Time before negative contactor engages and precharging starts
#define CONTACTOR_CONTROL_T2 5000 // Precharge time before precharge resistor is bypassed by positive contactor static const int CONTACTOR_CONTROL_T2 =
#define CONTACTOR_CONTROL_T3 2000 // Precharge relay lead time after positive contactor has been engaged 5000; // Precharge time before precharge resistor is bypassed by positive contactor
static const int CONTACTOR_CONTROL_T3 = 2000; // Precharge relay lead time after positive contactor has been engaged
static const int MAX_ALLOWED_FAULT_TICKS = 1000;
enum SboxState { DISCONNECTED, PRECHARGE, NEGATIVE, POSITIVE, PRECHARGE_OFF, COMPLETED, SHUTDOWN_REQUESTED };
SboxState contactorStatus = DISCONNECTED;
unsigned long prechargeStartTime = 0;
unsigned long negativeStartTime = 0;
unsigned long positiveStartTime = 0;
unsigned long timeSpentInFaultedMode = 0;
unsigned long LastMsgTime = 0; // will store last time a 20ms CAN Message was send
unsigned long LastAvgTime = 0; // Last current storage time
unsigned long ShuntLastSeen = 0;
uint32_t avg_mA_array[10];
uint32_t avg_sum;
uint8_t k; //avg array pointer
uint8_t CAN100_cnt = 0;
CAN_frame SBOX_100 = {.FD = false,
.ext_ID = false,
.DLC = 4,
.ID = 0x100,
.data = {0x55, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00,
0x00}}; // Byte 0: relay control, Byte 1: counter 0-E, Byte 4: CRC
CAN_frame SBOX_300 = {.FD = false,
.ext_ID = false,
.DLC = 4,
.ID = 0x300,
.data = {0xFF, 0xFE, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}}; // Static frame
};
#endif #endif

View file

@ -3,22 +3,31 @@
#include "Battery.h" #include "Battery.h"
#include "src/communication/Transmitter.h"
#include "src/devboard/utils/types.h" #include "src/devboard/utils/types.h"
// Abstract base class for batteries using the CAN bus // Abstract base class for batteries using the CAN bus
class CanBattery : public Battery { class CanBattery : public Battery, Transmitter {
public: public:
virtual void handle_incoming_can_frame(CAN_frame rx_frame) = 0; virtual void handle_incoming_can_frame(CAN_frame rx_frame) = 0;
virtual void transmit_can(unsigned long currentMillis) = 0; virtual void transmit_can(unsigned long currentMillis) = 0;
String interface_name() { return getCANInterfaceName(can_interface); } String interface_name() { return getCANInterfaceName(can_interface); }
void transmit(unsigned long currentMillis) { transmit_can(currentMillis); }
protected: protected:
CAN_Interface can_interface; CAN_Interface can_interface;
CanBattery() { can_interface = can_config.battery; } CanBattery() {
can_interface = can_config.battery;
register_transmitter(this);
}
CanBattery(CAN_Interface interface) { can_interface = interface; } CanBattery(CAN_Interface interface) {
can_interface = interface;
register_transmitter(this);
}
}; };
#endif #endif

View file

@ -3,15 +3,20 @@
#include "Battery.h" #include "Battery.h"
#include "src/communication/Transmitter.h"
#include "src/devboard/utils/types.h" #include "src/devboard/utils/types.h"
// Abstract base class for batteries using the RS485 interface // Abstract base class for batteries using the RS485 interface
class RS485Battery : public Battery { class RS485Battery : public Battery, Transmitter {
public: public:
virtual void receive_RS485() = 0; virtual void receive_RS485() = 0;
virtual void transmit_rs485() = 0; virtual void transmit_rs485(unsigned long currentMillis) = 0;
String interface_name() { return "RS485"; } String interface_name() { return "RS485"; }
void transmit(unsigned long currentMillis) { transmit_rs485(currentMillis); }
RS485Battery() { register_transmitter(this); }
}; };
#endif #endif

View file

@ -0,0 +1,31 @@
#ifndef _SHUNT_H
#define _SHUNT_H
#include "../communication/can/comm_can.h"
#include "src/communication/Transmitter.h"
#include "src/devboard/utils/types.h"
class CanShunt : public Transmitter {
public:
virtual void setup() = 0;
virtual void transmit_can(unsigned long currentMillis) = 0;
virtual void handle_incoming_can_frame(CAN_frame rx_frame) = 0;
void transmit(unsigned long currentMillis) {
if (allowed_to_send_CAN) {
transmit_can(currentMillis);
}
}
protected:
CAN_Interface can_interface;
CanShunt() {
can_interface = can_config.battery;
register_transmitter(this);
}
};
extern CanShunt* shunt;
#endif

View file

@ -0,0 +1,16 @@
#include "../include.h"
#include "Shunt.h"
CanShunt* shunt = nullptr;
void setup_can_shunt() {
#if defined(CAN_SHUNT_SELECTED) && defined(SELECTED_SHUNT_CLASS)
shunt = new SELECTED_SHUNT_CLASS();
#endif
}
void handle_incoming_can_frame_shunt(CAN_frame rx_frame) {
if (shunt) {
shunt->handle_incoming_can_frame(rx_frame);
}
}

View file

@ -4,6 +4,7 @@
#include "src/devboard/utils/types.h" #include "src/devboard/utils/types.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "src/communication/Transmitter.h"
enum class ChargerType { NissanLeaf, ChevyVolt }; enum class ChargerType { NissanLeaf, ChevyVolt };
@ -36,13 +37,19 @@ class Charger {
}; };
// Base class for chargers on a CAN bus // Base class for chargers on a CAN bus
class CanCharger : public Charger { class CanCharger : public Charger, Transmitter {
public: public:
virtual void map_can_frame_to_variable(CAN_frame rx_frame) = 0; virtual void map_can_frame_to_variable(CAN_frame rx_frame) = 0;
virtual void transmit_can(unsigned long currentMillis) = 0; virtual void transmit_can(unsigned long currentMillis) = 0;
void transmit(unsigned long currentMillis) {
if (allowed_to_send_CAN) {
transmit_can(currentMillis);
}
}
protected: protected:
CanCharger(ChargerType type) : Charger(type) {} CanCharger(ChargerType type) : Charger(type) { register_transmitter(this); }
}; };
#endif #endif

View file

@ -0,0 +1,11 @@
#ifndef _TRANSMITTER_H
#define _TRANSMITTER_H
class Transmitter {
public:
virtual void transmit(unsigned long currentMillis) = 0;
};
void register_transmitter(Transmitter* transmitter);
#endif

View file

@ -104,30 +104,6 @@ void init_CAN() {
#endif // CANFD_ADDON #endif // CANFD_ADDON
} }
// Transmit functions
void transmit_can(unsigned long currentMillis) {
if (!allowed_to_send_CAN) {
return; //Global block of CAN messages
}
#ifndef RS485_BATTERY_SELECTED
transmit_can_battery(currentMillis);
#endif
#ifdef CAN_INVERTER_SELECTED
transmit_can_inverter(currentMillis);
#endif // CAN_INVERTER_SELECTED
if (charger) {
charger->transmit_can(currentMillis);
}
#ifdef CAN_SHUNT_SELECTED
transmit_can_shunt(currentMillis);
#endif // CAN_SHUNT_SELECTED
}
void transmit_can_frame(CAN_frame* tx_frame, int interface) { void transmit_can_frame(CAN_frame* tx_frame, int interface) {
if (!allowed_to_send_CAN) { if (!allowed_to_send_CAN) {
return; return;

View file

@ -37,16 +37,6 @@ void init_CAN();
*/ */
void transmit_can_frame(); void transmit_can_frame();
/**
* @brief Send CAN messages to all components
*
* @param[in] void
* @param[in] unsigned long currentMillis
*
* @return void
*/
void transmit_can(unsigned long currentMillis);
/** /**
* @brief Receive CAN messages from all interfaces * @brief Receive CAN messages from all interfaces
* *

View file

@ -5,11 +5,20 @@
#include "src/devboard/utils/types.h" #include "src/devboard/utils/types.h"
class CanInverterProtocol : public InverterProtocol { class CanInverterProtocol : public InverterProtocol, Transmitter {
public: public:
virtual const char* interface_name() { return getCANInterfaceName(can_config.inverter); } virtual const char* interface_name() { return getCANInterfaceName(can_config.inverter); }
virtual void transmit_can(unsigned long currentMillis) = 0; virtual void transmit_can(unsigned long currentMillis) = 0;
virtual void map_can_frame_to_variable(CAN_frame rx_frame) = 0; virtual void map_can_frame_to_variable(CAN_frame rx_frame) = 0;
void transmit(unsigned long currentMillis) {
if (allowed_to_send_CAN) {
transmit_can(currentMillis);
}
}
protected:
CanInverterProtocol() { register_transmitter(this); }
}; };
#endif #endif

View file

@ -34,17 +34,10 @@ void setup_inverter() {
} }
#ifdef CAN_INVERTER_SELECTED #ifdef CAN_INVERTER_SELECTED
void update_values_can_inverter() {
can_inverter->update_values();
}
void map_can_frame_to_variable_inverter(CAN_frame rx_frame) { void map_can_frame_to_variable_inverter(CAN_frame rx_frame) {
can_inverter->map_can_frame_to_variable(rx_frame); can_inverter->map_can_frame_to_variable(rx_frame);
} }
void transmit_can_inverter(unsigned long currentMillis) {
can_inverter->transmit_can(currentMillis);
}
#endif #endif
#ifdef RS485_INVERTER_SELECTED #ifdef RS485_INVERTER_SELECTED

View file

@ -34,7 +34,6 @@ extern InverterProtocol* inverter;
void setup_inverter(); void setup_inverter();
#ifdef CAN_INVERTER_SELECTED #ifdef CAN_INVERTER_SELECTED
void update_values_can_inverter();
void map_can_frame_to_variable_inverter(CAN_frame rx_frame); void map_can_frame_to_variable_inverter(CAN_frame rx_frame);
void transmit_can_inverter(unsigned long currentMillis); void transmit_can_inverter(unsigned long currentMillis);
#endif #endif