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/task.h"
#include "src/communication/Transmitter.h"
#include "src/communication/can/comm_can.h"
#include "src/communication/contactorcontrol/comm_contactorcontrol.h"
#include "src/communication/equipmentstopbutton/comm_equipmentstopbutton.h"
@ -105,9 +106,8 @@ void setup() {
#ifdef EQUIPMENT_STOP_BUTTON
init_equipment_stop_button();
#endif
#ifdef CAN_SHUNT_SELECTED
setup_can_shunt();
#endif
// BOOT button at runtime is used as an input for various things
pinMode(0, INPUT_PULLUP);
@ -203,6 +203,12 @@ void mqtt_loop(void*) {
}
#endif
static std::list<Transmitter*> transmitters;
void register_transmitter(Transmitter* transmitter) {
transmitters.push_back(transmitter);
}
void core_loop(void*) {
esp_task_wdt_add(NULL); // Register this task with WDT
TickType_t xLastWakeTime = xTaskGetTickCount();
@ -255,15 +261,24 @@ void core_loop(void*) {
START_TIME_MEASUREMENT(time_values);
#endif
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) {
update_values_battery2();
battery2->update_values();
check_interconnect_available();
}
update_calculated_values();
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
END_TIME_MEASUREMENT_MAX(time_values, datalayer.system.status.time_values_us);
#endif
@ -271,12 +286,12 @@ void core_loop(void*) {
#ifdef FUNCTION_TIME_MEASUREMENT
START_TIME_MEASUREMENT(cantx);
#endif
// Output
transmit_can(currentMillis); // Send CAN messages to all components
#ifdef RS485_BATTERY_SELECTED
transmit_rs485(currentMillis);
#endif // RS485_BATTERY_SELECTED
// 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);
@ -508,12 +523,6 @@ void update_calculated_values() {
lastMillisOverflowCheck = currentMillis;
}
void update_values_inverter() {
if (inverter) {
inverter->update_values();
}
}
void check_reset_reason() {
esp_reset_reason_t reason = esp_reset_reason();
switch (reason) {

View file

@ -41,41 +41,21 @@ void setup_battery() {
#endif
}
void update_values_battery() {
battery->update_values();
}
// transmit_can_battery is called once and we need to
// 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) {
((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) {
((CanBattery*)battery2)->handle_incoming_can_frame(rx_frame);
}
#ifdef RS485_BATTERY_SELECTED
void transmit_rs485() {
((RS485Battery*)battery)->transmit_rs485();
}
void receive_RS485() {
((RS485Battery*)battery)->receive_RS485();
}
#endif
#endif

View file

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

View file

@ -1,41 +1,9 @@
#include "../include.h"
#ifdef BMW_SBOX
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.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 reversed = 0;
for (int i = 0; i < 8; i++) {
@ -64,7 +32,7 @@ uint8_t calculateCRC(CAN_frame CAN) {
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();
if (rx_frame.ID == 0x200) {
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? **/
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);
datalayer.system.info.shunt_protocol[63] = '\0';
}

View file

@ -1,22 +1,67 @@
#ifndef BMW_SBOX_CONTROL_H
#define BMW_SBOX_CONTROL_H
#include "../include.h"
#define CAN_SHUNT_SELECTED
void transmit_can(CAN_frame* tx_frame, int interface);
#define CAN_SHUNT_SELECTED
#define SELECTED_SHUNT_CLASS BmwSbox
#include "Shunt.h"
class BmwSbox : public CanShunt {
public:
void setup();
void transmit_can(unsigned long currentMillis);
void handle_incoming_can_frame(CAN_frame rx_frame);
private:
/** Minimum input voltage required to enable relay control **/
#define MINIMUM_INPUT_VOLTAGE 250
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. **/
#define MAX_PRECHARGE_RESISTOR_VOLTAGE_PERCENT 0.99
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
*/
#define 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
#define CONTACTOR_CONTROL_T3 2000 // Precharge relay lead time after positive contactor has been engaged
static const int CONTACTOR_CONTROL_T1 = 5000; // Time before negative contactor engages and precharging starts
static const int CONTACTOR_CONTROL_T2 =
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

View file

@ -3,22 +3,31 @@
#include "Battery.h"
#include "src/communication/Transmitter.h"
#include "src/devboard/utils/types.h"
// Abstract base class for batteries using the CAN bus
class CanBattery : public Battery {
class CanBattery : public Battery, Transmitter {
public:
virtual void handle_incoming_can_frame(CAN_frame rx_frame) = 0;
virtual void transmit_can(unsigned long currentMillis) = 0;
String interface_name() { return getCANInterfaceName(can_interface); }
void transmit(unsigned long currentMillis) { transmit_can(currentMillis); }
protected:
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

View file

@ -3,15 +3,20 @@
#include "Battery.h"
#include "src/communication/Transmitter.h"
#include "src/devboard/utils/types.h"
// Abstract base class for batteries using the RS485 interface
class RS485Battery : public Battery {
class RS485Battery : public Battery, Transmitter {
public:
virtual void receive_RS485() = 0;
virtual void transmit_rs485() = 0;
virtual void transmit_rs485(unsigned long currentMillis) = 0;
String interface_name() { return "RS485"; }
void transmit(unsigned long currentMillis) { transmit_rs485(currentMillis); }
RS485Battery() { register_transmitter(this); }
};
#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 "../datalayer/datalayer.h"
#include "src/communication/Transmitter.h"
enum class ChargerType { NissanLeaf, ChevyVolt };
@ -36,13 +37,19 @@ class Charger {
};
// Base class for chargers on a CAN bus
class CanCharger : public Charger {
class CanCharger : public Charger, Transmitter {
public:
virtual void map_can_frame_to_variable(CAN_frame rx_frame) = 0;
virtual void transmit_can(unsigned long currentMillis) = 0;
void transmit(unsigned long currentMillis) {
if (allowed_to_send_CAN) {
transmit_can(currentMillis);
}
}
protected:
CanCharger(ChargerType type) : Charger(type) {}
CanCharger(ChargerType type) : Charger(type) { register_transmitter(this); }
};
#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
}
// 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) {
if (!allowed_to_send_CAN) {
return;

View file

@ -37,16 +37,6 @@ void init_CAN();
*/
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
*

View file

@ -5,11 +5,20 @@
#include "src/devboard/utils/types.h"
class CanInverterProtocol : public InverterProtocol {
class CanInverterProtocol : public InverterProtocol, Transmitter {
public:
virtual const char* interface_name() { return getCANInterfaceName(can_config.inverter); }
virtual void transmit_can(unsigned long currentMillis) = 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

View file

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

View file

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