mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-04 18:29:48 +02:00
Merge branch 'main' into feature/RELION-LV-protocol
This commit is contained in:
commit
e89d69ba3b
17 changed files with 271 additions and 172 deletions
|
@ -70,7 +70,7 @@ class KiaHyundai64Battery : public CanBattery {
|
||||||
uint16_t CellVoltMin_mV = 3700;
|
uint16_t CellVoltMin_mV = 3700;
|
||||||
uint16_t allowedDischargePower = 0;
|
uint16_t allowedDischargePower = 0;
|
||||||
uint16_t allowedChargePower = 0;
|
uint16_t allowedChargePower = 0;
|
||||||
uint16_t batteryVoltage = 0;
|
uint16_t batteryVoltage = 3700;
|
||||||
uint16_t inverterVoltageFrameHigh = 0;
|
uint16_t inverterVoltageFrameHigh = 0;
|
||||||
uint16_t inverterVoltage = 0;
|
uint16_t inverterVoltage = 0;
|
||||||
uint16_t cellvoltages_mv[98];
|
uint16_t cellvoltages_mv[98];
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
|
|
||||||
void VolvoSpaBattery::
|
void VolvoSpaBattery::
|
||||||
update_values() { //This function maps all the values fetched via CAN to the correct parameters used for the inverter
|
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
|
// Update webserver datalayer
|
||||||
datalayer_extended.VolvoPolestar.soc_bms = SOC_BMS;
|
datalayer_extended.VolvoPolestar.soc_bms = SOC_BMS;
|
||||||
|
@ -88,6 +87,7 @@ void VolvoSpaBattery::
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_LOG
|
#ifdef DEBUG_LOG
|
||||||
|
uint8_t cnt = 0;
|
||||||
logging.print("BMS reported SOC%: ");
|
logging.print("BMS reported SOC%: ");
|
||||||
logging.println(SOC_BMS);
|
logging.println(SOC_BMS);
|
||||||
logging.print("Calculated SOC%: ");
|
logging.print("Calculated SOC%: ");
|
||||||
|
@ -127,6 +127,7 @@ void VolvoSpaBattery::
|
||||||
logging.print(cell_voltages[cnt++]);
|
logging.print(cell_voltages[cnt++]);
|
||||||
logging.print(",");
|
logging.print(",");
|
||||||
}
|
}
|
||||||
|
cnt = 0;
|
||||||
logging.println(";");
|
logging.println(";");
|
||||||
#endif
|
#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
|
(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]);
|
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) &&
|
} 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
|
(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[5] << 8) | rx_frame.data.u8[6]);
|
||||||
cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8);
|
cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8);
|
||||||
transmit_can_frame(&VOLVO_FlowControl); // Send flow control
|
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) &&
|
} 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
|
(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
|
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++] = 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[2] << 8) | rx_frame.data.u8[3];
|
||||||
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
|
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
|
transmit_can_frame(&VOLVO_SOH_Req); //Send SOH read request
|
||||||
}
|
}
|
||||||
rxConsecutiveFrames = 0;
|
rxConsecutiveFrames = false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -314,7 +330,7 @@ void VolvoSpaBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||||
void VolvoSpaBattery::readCellVoltages() {
|
void VolvoSpaBattery::readCellVoltages() {
|
||||||
battery_request_idx = 0;
|
battery_request_idx = 0;
|
||||||
batteryModuleNumber = 0x10;
|
batteryModuleNumber = 0x10;
|
||||||
rxConsecutiveFrames = 0;
|
rxConsecutiveFrames = false;
|
||||||
VOLVO_CELL_U_Req.data.u8[3] = batteryModuleNumber++;
|
VOLVO_CELL_U_Req.data.u8[3] = batteryModuleNumber++;
|
||||||
transmit_can_frame(&VOLVO_CELL_U_Req); //Send cell voltage read request for first module
|
transmit_can_frame(&VOLVO_CELL_U_Req); //Send cell voltage read request for first module
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ class VolvoSpaBattery : public CanBattery {
|
||||||
uint16_t HvBattPwrLimChrgSlowAgi = 0; //0x177
|
uint16_t HvBattPwrLimChrgSlowAgi = 0; //0x177
|
||||||
uint8_t batteryModuleNumber = 0x10; // First battery module
|
uint8_t batteryModuleNumber = 0x10; // First battery module
|
||||||
uint8_t battery_request_idx = 0;
|
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
|
uint16_t min_max_voltage[2]; //contains cell min[0] and max[1] values in mV
|
||||||
uint8_t cellcounter = 0;
|
uint8_t cellcounter = 0;
|
||||||
uint16_t cell_voltages[108]; //array with all the cellvoltages
|
uint16_t cell_voltages[108]; //array with all the cellvoltages
|
||||||
|
@ -75,21 +75,17 @@ class VolvoSpaBattery : public CanBattery {
|
||||||
.ext_ID = false,
|
.ext_ID = false,
|
||||||
.DLC = 8,
|
.DLC = 8,
|
||||||
.ID = 0x536,
|
.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
|
.data = {0x00, 0x40, 0x40, 0x01, 0x00, 0x00, 0x00, 0x00}}; //Network manage frame
|
||||||
|
|
||||||
CAN_frame VOLVO_140_CLOSE = {.FD = false,
|
CAN_frame VOLVO_140_CLOSE = {.FD = false,
|
||||||
.ext_ID = false,
|
.ext_ID = false,
|
||||||
.DLC = 8,
|
.DLC = 8,
|
||||||
.ID = 0x140,
|
.ID = 0x140,
|
||||||
.data = {0x00, 0x02, 0x00, 0xB7, 0xFF, 0x03, 0xFF, 0x82}}; //Close contactors message
|
.data = {0x00, 0x02, 0x00, 0xB7, 0xFF, 0x03, 0xFF, 0x82}}; //Close contactors message
|
||||||
|
|
||||||
CAN_frame VOLVO_140_OPEN = {.FD = false,
|
CAN_frame VOLVO_140_OPEN = {.FD = false,
|
||||||
.ext_ID = false,
|
.ext_ID = false,
|
||||||
.DLC = 8,
|
.DLC = 8,
|
||||||
.ID = 0x140,
|
.ID = 0x140,
|
||||||
.data = {0x00, 0x02, 0x00, 0x9E, 0xFF, 0x03, 0xFF, 0x82}}; //Open contactor message
|
.data = {0x00, 0x02, 0x00, 0x9E, 0xFF, 0x03, 0xFF, 0x82}}; //Open contactor message
|
||||||
|
|
||||||
CAN_frame VOLVO_372 = {
|
CAN_frame VOLVO_372 = {
|
||||||
.FD = false,
|
.FD = false,
|
||||||
.ext_ID = false,
|
.ext_ID = false,
|
||||||
|
@ -117,6 +113,12 @@ class VolvoSpaBattery : public CanBattery {
|
||||||
.DLC = 8,
|
.DLC = 8,
|
||||||
.ID = 0x735,
|
.ID = 0x735,
|
||||||
.data = {0x03, 0x22, 0xF4, 0x42, 0x00, 0x00, 0x00, 0x00}}; //BECM supply voltage request frame
|
.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,
|
CAN_frame VOLVO_DTC_Erase = {.FD = false,
|
||||||
.ext_ID = false,
|
.ext_ID = false,
|
||||||
.DLC = 8,
|
.DLC = 8,
|
||||||
|
|
|
@ -9,11 +9,11 @@ class VolvoSpaHtmlRenderer : public BatteryHtmlRenderer {
|
||||||
public:
|
public:
|
||||||
String get_status_html() {
|
String get_status_html() {
|
||||||
String content;
|
String content;
|
||||||
|
content += "</h4><h4>BECM reported number of DTCs: " + String(datalayer_extended.VolvoPolestar.DTCcount) + "</h4>";
|
||||||
content += "<h4>BECM reported SOC: " + String(datalayer_extended.VolvoPolestar.soc_bms) + "</h4>";
|
content += "<h4>BECM reported SOC: " + String(datalayer_extended.VolvoPolestar.soc_bms / 10.0) + " %</h4>";
|
||||||
content += "<h4>Calculated SOC: " + String(datalayer_extended.VolvoPolestar.soc_calc) + "</h4>";
|
content += "<h4>Calculated SOC: " + String(datalayer_extended.VolvoPolestar.soc_calc / 10.0) + " %</h4>";
|
||||||
content += "<h4>Rescaled SOC: " + String(datalayer_extended.VolvoPolestar.soc_rescaled / 10) + "</h4>";
|
content += "<h4>Rescaled SOC: " + String(datalayer_extended.VolvoPolestar.soc_rescaled / 10.0) + " %</h4>";
|
||||||
content += "<h4>BECM reported SOH: " + String(datalayer_extended.VolvoPolestar.soh_bms) + "</h4>";
|
content += "<h4>BECM reported SOH: " + String(datalayer_extended.VolvoPolestar.soh_bms / 100.0) + " %</h4>";
|
||||||
content += "<h4>BECM supply voltage: " + String(datalayer_extended.VolvoPolestar.BECMsupplyVoltage) + " mV</h4>";
|
content += "<h4>BECM supply voltage: " + String(datalayer_extended.VolvoPolestar.BECMsupplyVoltage) + " mV</h4>";
|
||||||
|
|
||||||
content += "<h4>HV voltage: " + String(datalayer_extended.VolvoPolestar.BECMBatteryVoltage) + " V</h4>";
|
content += "<h4>HV voltage: " + String(datalayer_extended.VolvoPolestar.BECMBatteryVoltage) + " V</h4>";
|
||||||
|
@ -31,7 +31,54 @@ class VolvoSpaHtmlRenderer : public BatteryHtmlRenderer {
|
||||||
content +=
|
content +=
|
||||||
"<h4>Charge power limit slow aging: " + String(datalayer_extended.VolvoPolestar.HvBattPwrLimChrgSlowAgi) +
|
"<h4>Charge power limit slow aging: " + String(datalayer_extended.VolvoPolestar.HvBattPwrLimChrgSlowAgi) +
|
||||||
" kW</h4>";
|
" kW</h4>";
|
||||||
|
content += "<h4>HVIL Circuit A status: ";
|
||||||
|
switch (datalayer_extended.VolvoPolestar.HVILstatusBits & 0x01) {
|
||||||
|
case 0x01:
|
||||||
|
content += String("Open");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
content += String("Not valid");
|
||||||
|
}
|
||||||
|
content += "<h4>HVIL Circuit B status: ";
|
||||||
|
switch (datalayer_extended.VolvoPolestar.HVILstatusBits & 0x02) {
|
||||||
|
case 0x02:
|
||||||
|
content += String("Open");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
content += String("Closed");
|
||||||
|
}
|
||||||
|
content += "<h4>HVIL Circuit C status: ";
|
||||||
|
switch (datalayer_extended.VolvoPolestar.HVILstatusBits & 0x04) {
|
||||||
|
case 0x04:
|
||||||
|
content += String("Open");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
content += String("Closed");
|
||||||
|
}
|
||||||
|
content += "<h4>Positive contactor status: ";
|
||||||
|
switch (datalayer_extended.VolvoPolestar.HVILstatusBits & 0x08) {
|
||||||
|
case 0x08:
|
||||||
|
content += String("Open");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
content += String("Closed");
|
||||||
|
}
|
||||||
|
content += "<h4>Precharge Contactor status: ";
|
||||||
|
switch (datalayer_extended.VolvoPolestar.HVILstatusBits & 0x10) {
|
||||||
|
case 0x10:
|
||||||
|
content += String("Open");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
content += String("Closed");
|
||||||
|
}
|
||||||
|
content += "<h4>Negative Contactor status: ";
|
||||||
|
switch (datalayer_extended.VolvoPolestar.HVILstatusBits & 0x20) {
|
||||||
|
case 0x20:
|
||||||
|
content += String("Open");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
content += String("Closed");
|
||||||
|
}
|
||||||
content += "<h4>HV system relay status: ";
|
content += "<h4>HV system relay status: ";
|
||||||
switch (datalayer_extended.VolvoPolestar.HVSysRlySts) {
|
switch (datalayer_extended.VolvoPolestar.HVSysRlySts) {
|
||||||
case 0:
|
case 0:
|
||||||
|
|
|
@ -55,7 +55,7 @@ const int OFF = 0;
|
||||||
#define OFF 1
|
#define OFF 1
|
||||||
#endif //NC_CONTACTORS
|
#endif //NC_CONTACTORS
|
||||||
|
|
||||||
#define MAX_ALLOWED_FAULT_TICKS 1000
|
#define MAX_ALLOWED_FAULT_TICKS 1000 //1000 = 10 seconds
|
||||||
#define NEGATIVE_CONTACTOR_TIME_MS \
|
#define NEGATIVE_CONTACTOR_TIME_MS \
|
||||||
500 // Time after negative contactor is turned on, to start precharge (not actual precharge time!)
|
500 // Time after negative contactor is turned on, to start precharge (not actual precharge time!)
|
||||||
#define PRECHARGE_COMPLETED_TIME_MS \
|
#define PRECHARGE_COMPLETED_TIME_MS \
|
||||||
|
@ -192,7 +192,7 @@ void handle_contactors() {
|
||||||
set(negPin, OFF, PWM_OFF_DUTY);
|
set(negPin, OFF, PWM_OFF_DUTY);
|
||||||
set(posPin, OFF, PWM_OFF_DUTY);
|
set(posPin, OFF, PWM_OFF_DUTY);
|
||||||
set_event(EVENT_ERROR_OPEN_CONTACTOR, 0);
|
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)
|
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(prechargePin, OFF);
|
||||||
set(negPin, OFF, PWM_OFF_DUTY);
|
set(negPin, OFF, PWM_OFF_DUTY);
|
||||||
set(posPin, 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 &&
|
if (datalayer.system.status.inverter_allows_contactor_closing &&
|
||||||
datalayer.system.status.inverter_allows_contactor_closing &&
|
|
||||||
!datalayer.system.settings.equipment_stop_active) {
|
!datalayer.system.settings.equipment_stop_active) {
|
||||||
contactorStatus = START_PRECHARGE;
|
contactorStatus = START_PRECHARGE;
|
||||||
}
|
}
|
||||||
|
@ -263,7 +262,7 @@ void handle_contactors() {
|
||||||
set(posPin, ON, PWM_HOLD_DUTY);
|
set(posPin, ON, PWM_HOLD_DUTY);
|
||||||
dbg_contactors("PRECHARGE_OFF");
|
dbg_contactors("PRECHARGE_OFF");
|
||||||
contactorStatus = COMPLETED;
|
contactorStatus = COMPLETED;
|
||||||
datalayer.system.status.contactors_engaged = true;
|
datalayer.system.status.contactors_engaged = 1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -310,8 +310,8 @@ struct DATALAYER_SYSTEM_STATUS_TYPE {
|
||||||
/** True if the inverter allows for the contactors to close */
|
/** True if the inverter allows for the contactors to close */
|
||||||
bool inverter_allows_contactor_closing = true;
|
bool inverter_allows_contactor_closing = true;
|
||||||
|
|
||||||
/** True if the contactor controlled by battery-emulator is closed */
|
/** 0 if starting up, 1 if contactors engaged, 2 if the contactors controlled by battery-emulator is opened */
|
||||||
bool contactors_engaged = false;
|
uint8_t contactors_engaged = 0;
|
||||||
/** True if the contactor controlled by battery-emulator is closed. Determined by check_interconnect_available(); if voltage is OK */
|
/** True if the contactor controlled by battery-emulator is closed. Determined by check_interconnect_available(); if voltage is OK */
|
||||||
bool contactors_battery2_engaged = false;
|
bool contactors_battery2_engaged = false;
|
||||||
|
|
||||||
|
|
|
@ -783,6 +783,8 @@ struct DATALAYER_INFO_VOLVO_POLESTAR {
|
||||||
uint8_t HVSysDCRlySts1 = 0;
|
uint8_t HVSysDCRlySts1 = 0;
|
||||||
uint8_t HVSysDCRlySts2 = 0;
|
uint8_t HVSysDCRlySts2 = 0;
|
||||||
uint8_t HVSysIsoRMonrSts = 0;
|
uint8_t HVSysIsoRMonrSts = 0;
|
||||||
|
uint8_t DTCcount = 0;
|
||||||
|
uint8_t HVILstatusBits = 0;
|
||||||
/** User requesting DTC reset via WebUI*/
|
/** User requesting DTC reset via WebUI*/
|
||||||
bool UserRequestDTCreset = false;
|
bool UserRequestDTCreset = false;
|
||||||
/** User requesting DTC readout via WebUI*/
|
/** User requesting DTC readout via WebUI*/
|
||||||
|
|
|
@ -38,7 +38,16 @@ class StarkHal : public Esp32Hal {
|
||||||
virtual gpio_num_t CAN_SE_PIN() { return GPIO_NUM_NC; }
|
virtual gpio_num_t CAN_SE_PIN() { return GPIO_NUM_NC; }
|
||||||
|
|
||||||
// CANFD_ADDON defines for MCP2517
|
// 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_SDI() { return GPIO_NUM_5; }
|
||||||
virtual gpio_num_t MCP2517_SDO() { return GPIO_NUM_34; }
|
virtual gpio_num_t MCP2517_SDO() { return GPIO_NUM_34; }
|
||||||
virtual gpio_num_t MCP2517_CS() { return GPIO_NUM_18; }
|
virtual gpio_num_t MCP2517_CS() { return GPIO_NUM_18; }
|
||||||
|
|
|
@ -141,7 +141,9 @@ SensorConfig batterySensorConfigTemplate[] = {
|
||||||
{"balancing_active_cells", "Balancing Active Cells", "", "", "", always}};
|
{"balancing_active_cells", "Balancing Active Cells", "", "", "", always}};
|
||||||
|
|
||||||
SensorConfig globalSensorConfigTemplate[] = {{"bms_status", "BMS Status", "", "", "", always},
|
SensorConfig globalSensorConfigTemplate[] = {{"bms_status", "BMS Status", "", "", "", always},
|
||||||
{"pause_status", "Pause Status", "", "", "", always}};
|
{"pause_status", "Pause Status", "", "", "", always},
|
||||||
|
{"event_level", "Event Level", "", "", "", always},
|
||||||
|
{"emulator_status", "Emulator Status", "", "", "", always}};
|
||||||
|
|
||||||
static std::list<SensorConfig> sensorConfigs;
|
static std::list<SensorConfig> sensorConfigs;
|
||||||
|
|
||||||
|
@ -311,6 +313,10 @@ static bool publish_common_info(void) {
|
||||||
set_battery_attributes(doc, datalayer.battery2, "_2", battery2->supports_charged_energy());
|
set_battery_attributes(doc, datalayer.battery2, "_2", battery2->supports_charged_energy());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
doc["event_level"] = get_event_level_string(get_event_level());
|
||||||
|
doc["emulator_status"] = get_emulator_status_string(get_emulator_status());
|
||||||
|
|
||||||
serializeJson(doc, mqtt_msg);
|
serializeJson(doc, mqtt_msg);
|
||||||
if (mqtt_publish(state_topic.c_str(), mqtt_msg, false) == false) {
|
if (mqtt_publish(state_topic.c_str(), mqtt_msg, false) == false) {
|
||||||
#ifdef DEBUG_LOG
|
#ifdef DEBUG_LOG
|
||||||
|
|
|
@ -14,6 +14,7 @@ typedef struct {
|
||||||
static EVENT_TYPE events;
|
static EVENT_TYPE events;
|
||||||
static const char* EVENTS_ENUM_TYPE_STRING[] = {EVENTS_ENUM_TYPE(GENERATE_STRING)};
|
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* EVENTS_LEVEL_TYPE_STRING[] = {EVENTS_LEVEL_TYPE(GENERATE_STRING)};
|
||||||
|
static const char* EMULATOR_STATUS_STRING[] = {EMULATOR_STATUS(GENERATE_STRING)};
|
||||||
|
|
||||||
/* Local function prototypes */
|
/* Local function prototypes */
|
||||||
static void set_event(EVENTS_ENUM_TYPE event, uint8_t data, bool latched);
|
static void set_event(EVENTS_ENUM_TYPE event, uint8_t data, bool latched);
|
||||||
|
@ -270,8 +271,8 @@ String get_event_message_string(EVENTS_ENUM_TYPE event) {
|
||||||
case EVENT_INTERFACE_MISSING:
|
case EVENT_INTERFACE_MISSING:
|
||||||
return "Configuration trying to use CAN interface not baked into the software. Recompile software!";
|
return "Configuration trying to use CAN interface not baked into the software. Recompile software!";
|
||||||
case EVENT_ERROR_OPEN_CONTACTOR:
|
case EVENT_ERROR_OPEN_CONTACTOR:
|
||||||
return "Too much time spent in error state. Opening contactors, not safe to continue charging. "
|
return "Too much time spent in error state. Opening contactors, not safe to continue. "
|
||||||
"Check other error code for reason!";
|
"Check other active ERROR code for reason. Reboot emulator after problem is solved!";
|
||||||
case EVENT_MODBUS_INVERTER_MISSING:
|
case EVENT_MODBUS_INVERTER_MISSING:
|
||||||
return "Modbus inverter has not sent any data. Inspect communication wiring!";
|
return "Modbus inverter has not sent any data. Inspect communication wiring!";
|
||||||
case EVENT_NO_ENABLE_DETECTED:
|
case EVENT_NO_ENABLE_DETECTED:
|
||||||
|
@ -393,6 +394,11 @@ const char* get_event_level_string(EVENTS_ENUM_TYPE event) {
|
||||||
return EVENTS_LEVEL_TYPE_STRING[events.entries[event].level] + 12;
|
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) {
|
const EVENTS_STRUCT_TYPE* get_event_pointer(EVENTS_ENUM_TYPE event) {
|
||||||
return &events.entries[event];
|
return &events.entries[event];
|
||||||
}
|
}
|
||||||
|
@ -401,6 +407,27 @@ EVENTS_LEVEL_TYPE get_event_level(void) {
|
||||||
return events.level;
|
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 */
|
/* Local functions */
|
||||||
|
|
||||||
static void set_event(EVENTS_ENUM_TYPE event, uint8_t data, bool latched) {
|
static void set_event(EVENTS_ENUM_TYPE event, uint8_t data, bool latched) {
|
||||||
|
|
|
@ -126,6 +126,14 @@ typedef enum { EVENTS_ENUM_TYPE(GENERATE_ENUM) } EVENTS_ENUM_TYPE;
|
||||||
|
|
||||||
typedef enum { EVENTS_LEVEL_TYPE(GENERATE_ENUM) } EVENTS_LEVEL_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 {
|
typedef enum {
|
||||||
EVENT_STATE_PENDING = 0,
|
EVENT_STATE_PENDING = 0,
|
||||||
EVENT_STATE_INACTIVE,
|
EVENT_STATE_INACTIVE,
|
||||||
|
@ -151,8 +159,11 @@ struct EventData {
|
||||||
const char* get_event_enum_string(EVENTS_ENUM_TYPE event);
|
const char* get_event_enum_string(EVENTS_ENUM_TYPE event);
|
||||||
String get_event_message_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_ENUM_TYPE event);
|
||||||
|
const char* get_event_level_string(EVENTS_LEVEL_TYPE event_level);
|
||||||
|
|
||||||
EVENTS_LEVEL_TYPE get_event_level(void);
|
EVENTS_LEVEL_TYPE get_event_level(void);
|
||||||
|
EMULATOR_STATUS get_emulator_status();
|
||||||
|
const char* get_emulator_status_string(EMULATOR_STATUS status);
|
||||||
|
|
||||||
void init_events(void);
|
void init_events(void);
|
||||||
void set_event_latched(EVENTS_ENUM_TYPE event, uint8_t data);
|
void set_event_latched(EVENTS_ENUM_TYPE event, uint8_t data);
|
||||||
|
|
|
@ -32,10 +32,6 @@ void led_exe(void) {
|
||||||
led->exe();
|
led->exe();
|
||||||
}
|
}
|
||||||
|
|
||||||
led_color led_get_color() {
|
|
||||||
return led->color;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LED::exe(void) {
|
void LED::exe(void) {
|
||||||
|
|
||||||
// Update brightness
|
// Update brightness
|
||||||
|
@ -53,27 +49,21 @@ void LED::exe(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set color
|
// Set color
|
||||||
switch (get_event_level()) {
|
switch (get_emulator_status()) {
|
||||||
case EVENT_LEVEL_INFO:
|
case EMULATOR_STATUS::STATUS_OK:
|
||||||
color = led_color::GREEN;
|
|
||||||
pixels.setPixelColor(COLOR_GREEN(brightness)); // Green pulsing LED
|
pixels.setPixelColor(COLOR_GREEN(brightness)); // Green pulsing LED
|
||||||
break;
|
break;
|
||||||
case EVENT_LEVEL_WARNING:
|
case EMULATOR_STATUS::STATUS_WARNING:
|
||||||
color = led_color::YELLOW;
|
|
||||||
pixels.setPixelColor(COLOR_YELLOW(brightness)); // Yellow pulsing LED
|
pixels.setPixelColor(COLOR_YELLOW(brightness)); // Yellow pulsing LED
|
||||||
break;
|
break;
|
||||||
case EVENT_LEVEL_DEBUG:
|
case EMULATOR_STATUS::STATUS_ERROR:
|
||||||
case EVENT_LEVEL_UPDATE:
|
|
||||||
color = led_color::BLUE;
|
|
||||||
pixels.setPixelColor(COLOR_BLUE(brightness)); // Blue pulsing LED
|
|
||||||
break;
|
|
||||||
case EVENT_LEVEL_ERROR:
|
|
||||||
color = led_color::RED;
|
|
||||||
pixels.setPixelColor(COLOR_RED(esp32hal->LED_MAX_BRIGHTNESS())); // Red LED full brightness
|
pixels.setPixelColor(COLOR_RED(esp32hal->LED_MAX_BRIGHTNESS())); // Red LED full brightness
|
||||||
break;
|
break;
|
||||||
default:
|
case EMULATOR_STATUS::STATUS_UPDATING:
|
||||||
|
pixels.setPixelColor(COLOR_BLUE(brightness)); // Blue pulsing LED
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
pixels.show(); // This sends the updated pixel color to the hardware.
|
pixels.show(); // This sends the updated pixel color to the hardware.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,6 @@
|
||||||
|
|
||||||
class LED {
|
class LED {
|
||||||
public:
|
public:
|
||||||
led_color color = led_color::GREEN;
|
|
||||||
|
|
||||||
LED(gpio_num_t pin, uint8_t maxBrightness)
|
LED(gpio_num_t pin, uint8_t maxBrightness)
|
||||||
: pixels(pin), max_brightness(maxBrightness), brightness(maxBrightness), mode(led_mode_enum::CLASSIC) {}
|
: pixels(pin), max_brightness(maxBrightness), brightness(maxBrightness), mode(led_mode_enum::CLASSIC) {}
|
||||||
|
|
||||||
|
@ -31,6 +29,5 @@ class LED {
|
||||||
|
|
||||||
bool led_init(void);
|
bool led_init(void);
|
||||||
void led_exe(void);
|
void led_exe(void);
|
||||||
led_color led_get_color(void);
|
|
||||||
|
|
||||||
#endif // LED_H_
|
#endif // LED_H_
|
||||||
|
|
|
@ -21,7 +21,6 @@ enum class comm_interface {
|
||||||
Highest
|
Highest
|
||||||
};
|
};
|
||||||
|
|
||||||
enum led_color { GREEN, YELLOW, RED, BLUE };
|
|
||||||
enum led_mode_enum { CLASSIC, FLOW, HEARTBEAT };
|
enum led_mode_enum { CLASSIC, FLOW, HEARTBEAT };
|
||||||
enum PrechargeState {
|
enum PrechargeState {
|
||||||
AUTO_PRECHARGE_IDLE,
|
AUTO_PRECHARGE_IDLE,
|
||||||
|
|
|
@ -836,6 +836,28 @@ String processor(const String& var) {
|
||||||
content += "button:hover { background-color: #3A4A52; }";
|
content += "button:hover { background-color: #3A4A52; }";
|
||||||
content += "h2 { font-size: 1.2em; margin: 0.3em 0 0.5em 0; }";
|
content += "h2 { font-size: 1.2em; margin: 0.3em 0 0.5em 0; }";
|
||||||
content += "h4 { margin: 0.6em 0; line-height: 1.2; }";
|
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 += "</style>";
|
content += "</style>";
|
||||||
|
|
||||||
// Compact header
|
// Compact header
|
||||||
|
@ -943,21 +965,18 @@ String processor(const String& var) {
|
||||||
content += "<div style='background-color: ";
|
content += "<div style='background-color: ";
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (led_get_color()) {
|
switch (get_emulator_status()) {
|
||||||
case led_color::GREEN:
|
case EMULATOR_STATUS::STATUS_OK:
|
||||||
content += "#2D3F2F;";
|
content += "#2D3F2F;";
|
||||||
break;
|
break;
|
||||||
case led_color::YELLOW:
|
case EMULATOR_STATUS::STATUS_WARNING:
|
||||||
content += "#F5CC00;";
|
content += "#F5CC00;";
|
||||||
break;
|
break;
|
||||||
case led_color::BLUE:
|
case EMULATOR_STATUS::STATUS_ERROR:
|
||||||
content += "#2B35AF;"; // Blue in test mode
|
|
||||||
break;
|
|
||||||
case led_color::RED:
|
|
||||||
content += "#A70107;";
|
content += "#A70107;";
|
||||||
break;
|
break;
|
||||||
default: // Some new color, make background green
|
case EMULATOR_STATUS::STATUS_UPDATING:
|
||||||
content += "#2D3F2F;";
|
content += "#2B35AF;"; // Blue in test mode
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1115,58 +1134,6 @@ String processor(const String& var) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
content += "<h4>Battery allows contactor closing: ";
|
|
||||||
if (datalayer.system.status.battery_allows_contactor_closing == true) {
|
|
||||||
content += "<span>✓</span>";
|
|
||||||
} else {
|
|
||||||
content += "<span style='color: red;'>✕</span>";
|
|
||||||
}
|
|
||||||
|
|
||||||
content += " Inverter allows contactor closing: ";
|
|
||||||
if (datalayer.system.status.inverter_allows_contactor_closing == true) {
|
|
||||||
content += "<span>✓</span></h4>";
|
|
||||||
} else {
|
|
||||||
content += "<span style='color: red;'>✕</span></h4>";
|
|
||||||
}
|
|
||||||
if (emulator_pause_status == NORMAL)
|
|
||||||
content += "<h4>Power status: " + String(get_emulator_pause_status().c_str()) + " </h4>";
|
|
||||||
else
|
|
||||||
content += "<h4 style='color: red;'>Power status: " + String(get_emulator_pause_status().c_str()) + " </h4>";
|
|
||||||
|
|
||||||
if (contactor_control_enabled) {
|
|
||||||
content += "<h4>Contactors controlled by emulator, state: ";
|
|
||||||
if (datalayer.system.status.contactors_engaged) {
|
|
||||||
content += "<span style='color: green;'>ON</span>";
|
|
||||||
} else {
|
|
||||||
content += "<span style='color: red;'>OFF</span>";
|
|
||||||
}
|
|
||||||
content += "</h4>";
|
|
||||||
if (contactor_control_enabled_double_battery) {
|
|
||||||
if (pwm_contactor_control) {
|
|
||||||
content += "<h4>Cont. Neg.: ";
|
|
||||||
if (datalayer.system.status.contactors_battery2_engaged) {
|
|
||||||
content += "<span style='color: green;'>Economized</span>";
|
|
||||||
content += " Cont. Pos.: ";
|
|
||||||
content += "<span style='color: green;'>Economized</span>";
|
|
||||||
} else {
|
|
||||||
content += "<span style='color: red;'>✕</span>";
|
|
||||||
content += " Cont. Pos.: ";
|
|
||||||
content += "<span style='color: red;'>✕</span>";
|
|
||||||
}
|
|
||||||
} 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 += "<h4>Cont. Neg.: ";
|
|
||||||
if (digitalRead(esp32hal->SECOND_BATTERY_CONTACTORS_PIN()) == HIGH) {
|
|
||||||
content += "<span style='color: green;'>✓</span>";
|
|
||||||
} else {
|
|
||||||
content += "<span style='color: red;'>✕</span>";
|
|
||||||
}
|
|
||||||
} //no PWM_CONTACTOR_CONTROL
|
|
||||||
content += "</h4>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close the block
|
// Close the block
|
||||||
content += "</div>";
|
content += "</div>";
|
||||||
|
|
||||||
|
@ -1264,71 +1231,85 @@ String processor(const String& var) {
|
||||||
} else { // > 0
|
} else { // > 0
|
||||||
content += "<h4>Battery charging!</h4>";
|
content += "<h4>Battery charging!</h4>";
|
||||||
}
|
}
|
||||||
|
|
||||||
content += "<h4>Automatic contactor closing allowed:</h4>";
|
|
||||||
content += "<h4>Battery: ";
|
|
||||||
if (datalayer.system.status.battery2_allowed_contactor_closing == true) {
|
|
||||||
content += "<span>✓</span>";
|
|
||||||
} else {
|
|
||||||
content += "<span style='color: red;'>✕</span>";
|
|
||||||
}
|
|
||||||
|
|
||||||
content += " Inverter: ";
|
|
||||||
if (datalayer.system.status.inverter_allows_contactor_closing == true) {
|
|
||||||
content += "<span>✓</span></h4>";
|
|
||||||
} else {
|
|
||||||
content += "<span style='color: red;'>✕</span></h4>";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (emulator_pause_status == NORMAL)
|
|
||||||
content += "<h4>Power status: " + String(get_emulator_pause_status().c_str()) + " </h4>";
|
|
||||||
else
|
|
||||||
content += "<h4 style='color: red;'>Power status: " + String(get_emulator_pause_status().c_str()) + " </h4>";
|
|
||||||
|
|
||||||
if (contactor_control_enabled) {
|
|
||||||
content += "<h4>Contactors controlled by emulator, state: ";
|
|
||||||
if (datalayer.system.status.contactors_battery2_engaged) {
|
|
||||||
content += "<span style='color: green;'>ON</span>";
|
|
||||||
} else {
|
|
||||||
content += "<span style='color: red;'>OFF</span>";
|
|
||||||
}
|
|
||||||
content += "</h4>";
|
|
||||||
|
|
||||||
if (contactor_control_enabled_double_battery) {
|
|
||||||
content += "<h4>Cont. Neg.: ";
|
|
||||||
if (pwm_contactor_control) {
|
|
||||||
if (datalayer.system.status.contactors_battery2_engaged) {
|
|
||||||
content += "<span style='color: green;'>Economized</span>";
|
|
||||||
content += " Cont. Pos.: ";
|
|
||||||
content += "<span style='color: green;'>Economized</span>";
|
|
||||||
} else {
|
|
||||||
content += "<span style='color: red;'>✕</span>";
|
|
||||||
content += " Cont. Pos.: ";
|
|
||||||
content += "<span style='color: red;'>✕</span>";
|
|
||||||
}
|
|
||||||
} 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 += "<span style='color: green;'>✓</span>";
|
|
||||||
} else {
|
|
||||||
content += "<span style='color: red;'>✕</span>";
|
|
||||||
}
|
|
||||||
|
|
||||||
content += " Cont. Pos.: ";
|
|
||||||
if (digitalRead(SECOND_POSITIVE_CONTACTOR_PIN) == HIGH) {
|
|
||||||
content += "<span style='color: green;'>✓</span>";
|
|
||||||
} else {
|
|
||||||
content += "<span style='color: red;'>✕</span>";
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
content += "</h4>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
content += "</div>";
|
content += "</div>";
|
||||||
content += "</div>";
|
content += "</div>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Block for Contactor status and component request status
|
||||||
|
// Start a new block with gray background color
|
||||||
|
content += "<div style='background-color: #333; padding: 10px; margin-bottom: 10px;border-radius: 50px'>";
|
||||||
|
|
||||||
|
if (emulator_pause_status == NORMAL) {
|
||||||
|
content += "<h4>Power status: " + String(get_emulator_pause_status().c_str()) + " </h4>";
|
||||||
|
} else {
|
||||||
|
content += "<h4 style='color: red;'>Power status: " + String(get_emulator_pause_status().c_str()) + " </h4>";
|
||||||
|
}
|
||||||
|
|
||||||
|
content += "<h4>Emulator allows contactor closing: ";
|
||||||
|
if (datalayer.battery.status.bms_status == FAULT) {
|
||||||
|
content += "<span style='color: red;'>✕</span>";
|
||||||
|
} else {
|
||||||
|
content += "<span>✓</span>";
|
||||||
|
}
|
||||||
|
content += " Inverter allows contactor closing: ";
|
||||||
|
if (datalayer.system.status.inverter_allows_contactor_closing == true) {
|
||||||
|
content += "<span>✓</span></h4>";
|
||||||
|
} else {
|
||||||
|
content += "<span style='color: red;'>✕</span></h4>";
|
||||||
|
}
|
||||||
|
if (battery2) {
|
||||||
|
content += "<h4>Secondary battery allowed to join ";
|
||||||
|
if (datalayer.system.status.battery2_allowed_contactor_closing == true) {
|
||||||
|
content += "<span>✓</span>";
|
||||||
|
} else {
|
||||||
|
content += "<span style='color: red;'>✕ (voltage mismatch)</span>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!contactor_control_enabled) {
|
||||||
|
content += "<div class=\"tooltip\">";
|
||||||
|
content += "<h4>Contactors not fully controlled via emulator <span style=\"color:orange\">[?]</span></h4>";
|
||||||
|
content +=
|
||||||
|
"<span class=\"tooltiptext\">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!</span>";
|
||||||
|
content += "</div>";
|
||||||
|
} else { //contactor_control_enabled TRUE
|
||||||
|
content += "<div class=\"tooltip\"><h4>Contactors controlled by emulator, state: ";
|
||||||
|
if (datalayer.system.status.contactors_engaged == 0) {
|
||||||
|
content += "<span style='color: green;'>PRECHARGE</span>";
|
||||||
|
} else if (datalayer.system.status.contactors_engaged == 1) {
|
||||||
|
content += "<span style='color: green;'>ON</span>";
|
||||||
|
} else if (datalayer.system.status.contactors_engaged == 2) {
|
||||||
|
content += "<span style='color: red;'>OFF</span>";
|
||||||
|
content += "<span class=\"tooltip-icon\"> [!]</span>";
|
||||||
|
content +=
|
||||||
|
"<span class=\"tooltiptext\">Emulator spent too much time in critical FAULT event. Investigate event "
|
||||||
|
"causing this via Events page. Reboot required to resume operation!</span>";
|
||||||
|
}
|
||||||
|
content += "</h4></div>";
|
||||||
|
if (contactor_control_enabled_double_battery && battery2) {
|
||||||
|
content += "<h4>Secondary battery contactor, state: ";
|
||||||
|
if (pwm_contactor_control) {
|
||||||
|
if (datalayer.system.status.contactors_battery2_engaged) {
|
||||||
|
content += "<span style='color: green;'>Economized</span>";
|
||||||
|
} else {
|
||||||
|
content += "<span style='color: red;'>OFF</span>";
|
||||||
|
}
|
||||||
|
} 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
|
||||||
|
if (digitalRead(esp32hal->SECOND_BATTERY_CONTACTORS_PIN()) == HIGH) {
|
||||||
|
content += "<span style='color: green;'>ON</span>";
|
||||||
|
} else {
|
||||||
|
content += "<span style='color: red;'>OFF</span>";
|
||||||
|
}
|
||||||
|
} //no PWM_CONTACTOR_CONTROL
|
||||||
|
content += "</h4>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the block
|
||||||
|
content += "</div>";
|
||||||
|
|
||||||
if (charger) {
|
if (charger) {
|
||||||
// Start a new block with orange background color
|
// Start a new block with orange background color
|
||||||
|
|
|
@ -14,3 +14,5 @@ void pinMode(uint8_t pin, uint8_t mode) {}
|
||||||
int max(int a, int b) {
|
int max(int a, int b) {
|
||||||
return (a > b) ? a : b;
|
return (a > b) ? a : b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ESPClass ESP;
|
||||||
|
|
|
@ -24,4 +24,15 @@ void delay(unsigned long ms);
|
||||||
void delayMicroseconds(unsigned long us);
|
void delayMicroseconds(unsigned long us);
|
||||||
int max(int a, int b);
|
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
|
#endif
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue