From 7f16ee6fb033b3d75d01b91a19fe4bc16a713529 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Fri, 15 Nov 2024 20:07:19 +0200 Subject: [PATCH 1/9] Wrap digitalwrite and PWMwrite in own function --- Software/Software.ino | 105 ++++++++++++++++++++------------------- Software/USER_SETTINGS.h | 1 + 2 files changed, 56 insertions(+), 50 deletions(-) diff --git a/Software/Software.ino b/Software/Software.ino index babef4dd..bba1d277 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -120,6 +120,16 @@ MyTimer check_pause_2s(INTERVAL_2_S); enum State { DISCONNECTED, PRECHARGE, NEGATIVE, POSITIVE, PRECHARGE_OFF, COMPLETED, SHUTDOWN_REQUESTED }; State contactorStatus = DISCONNECTED; +#define ON 1 +#define OFF 0 + +#ifdef NC_CONTACTORS //Normally closed contactors use inverted logic + #undef ON + #define ON 0 + #undef OFF + #define OFF 1 +#endif + #define MAX_ALLOWED_FAULT_TICKS 1000 /* NOTE: modify the precharge time constant below to account for the resistance and capacitance of the target system. * t=3RC at minimum, t=5RC ideally @@ -127,20 +137,34 @@ State contactorStatus = DISCONNECTED; #define PRECHARGE_TIME_MS 160 #define NEGATIVE_CONTACTOR_TIME_MS 1000 #define POSITIVE_CONTACTOR_TIME_MS 2000 -#ifdef PWM_CONTACTOR_CONTROL #define PWM_Freq 20000 // 20 kHz frequency, beyond audible range #define PWM_Res 10 // 10 Bit resolution 0 to 1023, maps 'nicely' to 0% 100% -#define PWM_Hold_Duty 250 -#define PWM_Off_Duty 0 -#define PWM_On_Duty 1023 +#define PWM_HOLD_DUTY 250 +#define PWM_OFF_DUTY 0 +#define PWM_ON_DUTY 1023 #define POSITIVE_PWM_Ch 0 #define NEGATIVE_PWM_Ch 1 -#endif unsigned long prechargeStartTime = 0; unsigned long negativeStartTime = 0; unsigned long timeSpentInFaultedMode = 0; #endif +void set(uint8_t pin, bool direction, uint32_t pwm_freq = 0xFFFFFFFFFF){ + #ifdef PWM_CONTACTOR_CONTROL + if(pwm_freq != 0xFFFFFFFFFF) + { + ledcWrite(pin, pwm_freq); + return; + } + #endif + if(direction == 1){ + digitalWrite(pin, HIGH); + } + else{ // 0 + digitalWrite(pin, LOW); + } +} + #ifdef EQUIPMENT_STOP_BUTTON const unsigned long equipment_button_long_press_duration = 15000; // 15 seconds for long press in case of MOMENTARY_SWITCH @@ -497,27 +521,27 @@ void init_CAN() { void init_contactors() { // Init contactor pins #ifdef CONTACTOR_CONTROL -#ifndef PWM_CONTACTOR_CONTROL - pinMode(POSITIVE_CONTACTOR_PIN, OUTPUT); - digitalWrite(POSITIVE_CONTACTOR_PIN, LOW); - pinMode(NEGATIVE_CONTACTOR_PIN, OUTPUT); - digitalWrite(NEGATIVE_CONTACTOR_PIN, LOW); -#else +#ifdef PWM_CONTACTOR_CONTROL ledcAttachChannel(POSITIVE_CONTACTOR_PIN, PWM_Freq, PWM_Res, POSITIVE_PWM_Ch); // Setup PWM Channel Frequency and Resolution ledcAttachChannel(NEGATIVE_CONTACTOR_PIN, PWM_Freq, PWM_Res, NEGATIVE_PWM_Ch); // Setup PWM Channel Frequency and Resolution - ledcWrite(POSITIVE_CONTACTOR_PIN, PWM_Off_Duty); // Set Positive PWM to 0% - ledcWrite(NEGATIVE_CONTACTOR_PIN, PWM_Off_Duty); // Set Negative PWM to 0% + ledcWrite(POSITIVE_CONTACTOR_PIN, PWM_OFF_DUTY); // Set Positive PWM to 0% + ledcWrite(NEGATIVE_CONTACTOR_PIN, PWM_OFF_DUTY); // Set Negative PWM to 0% +#else //Normal CONTACTOR_CONTROL + pinMode(POSITIVE_CONTACTOR_PIN, OUTPUT); + set(POSITIVE_CONTACTOR_PIN, OFF); + pinMode(NEGATIVE_CONTACTOR_PIN, OUTPUT); + set(NEGATIVE_CONTACTOR_PIN, OFF); #endif pinMode(PRECHARGE_PIN, OUTPUT); - digitalWrite(PRECHARGE_PIN, LOW); -#endif + set(PRECHARGE_PIN, OFF); +#endif //CONTACTOR_CONTROL // Init BMS contactor #ifdef HW_STARK // TODO: Rewrite this so LilyGo can also handle this BMS contactor pinMode(BMS_POWER, OUTPUT); - digitalWrite(BMS_POWER, HIGH); -#endif + set(BMS_POWER, ON); +#endif //HW_STARK } void init_rs485() { @@ -751,14 +775,9 @@ void handle_contactors() { contactorStatus = DISCONNECTED; } if (contactorStatus == SHUTDOWN_REQUESTED) { - digitalWrite(PRECHARGE_PIN, LOW); -#ifndef PWM_CONTACTOR_CONTROL - digitalWrite(NEGATIVE_CONTACTOR_PIN, LOW); - digitalWrite(POSITIVE_CONTACTOR_PIN, LOW); -#else - ledcWrite(NEGATIVE_CONTACTOR_PIN, PWM_Off_Duty); - ledcWrite(POSITIVE_CONTACTOR_PIN, PWM_Off_Duty); -#endif + set(PRECHARGE_PIN, OFF); + set(NEGATIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY); + set(POSITIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY); set_event(EVENT_ERROR_OPEN_CONTACTOR, 0); datalayer.system.status.contactor_control_closed = false; return; // A fault scenario latches the contactor control. It is not possible to recover without a powercycle (and investigation why fault occured) @@ -766,14 +785,9 @@ void handle_contactors() { // After that, check if we are OK to start turning on the battery if (contactorStatus == DISCONNECTED) { - digitalWrite(PRECHARGE_PIN, LOW); -#ifndef PWM_CONTACTOR_CONTROL - digitalWrite(NEGATIVE_CONTACTOR_PIN, LOW); - digitalWrite(POSITIVE_CONTACTOR_PIN, LOW); -#else - ledcWrite(NEGATIVE_CONTACTOR_PIN, PWM_Off_Duty); - ledcWrite(POSITIVE_CONTACTOR_PIN, PWM_Off_Duty); -#endif + set(PRECHARGE_PIN, OFF); + set(NEGATIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY); + set(POSITIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY); if (datalayer.system.status.battery_allows_contactor_closing && datalayer.system.status.inverter_allows_contactor_closing) { @@ -783,8 +797,9 @@ void handle_contactors() { // In case the inverter requests contactors to open, set the state accordingly if (contactorStatus == COMPLETED) { - if (!datalayer.system.status.inverter_allows_contactor_closing) + if (!datalayer.system.status.inverter_allows_contactor_closing){ contactorStatus = DISCONNECTED; + } // Skip running the state machine below if it has already completed return; } @@ -793,18 +808,14 @@ void handle_contactors() { // Handle actual state machine. This first turns on Precharge, then Negative, then Positive, and finally turns OFF precharge switch (contactorStatus) { case PRECHARGE: - digitalWrite(PRECHARGE_PIN, HIGH); + set(PRECHARGE_PIN, ON); prechargeStartTime = currentTime; contactorStatus = NEGATIVE; break; case NEGATIVE: if (currentTime - prechargeStartTime >= PRECHARGE_TIME_MS) { -#ifndef PWM_CONTACTOR_CONTROL - digitalWrite(NEGATIVE_CONTACTOR_PIN, HIGH); -#else - ledcWrite(NEGATIVE_CONTACTOR_PIN, PWM_On_Duty); -#endif + set(NEGATIVE_CONTACTOR_PIN, ON, PWM_ON_DUTY); negativeStartTime = currentTime; contactorStatus = POSITIVE; } @@ -812,22 +823,16 @@ void handle_contactors() { case POSITIVE: if (currentTime - negativeStartTime >= NEGATIVE_CONTACTOR_TIME_MS) { -#ifndef PWM_CONTACTOR_CONTROL - digitalWrite(POSITIVE_CONTACTOR_PIN, HIGH); -#else - ledcWrite(POSITIVE_CONTACTOR_PIN, PWM_On_Duty); -#endif + set(POSITIVE_CONTACTOR_PIN, ON, PWM_ON_DUTY); contactorStatus = PRECHARGE_OFF; } break; case PRECHARGE_OFF: if (currentTime - negativeStartTime >= POSITIVE_CONTACTOR_TIME_MS) { - digitalWrite(PRECHARGE_PIN, LOW); -#ifdef PWM_CONTACTOR_CONTROL - ledcWrite(NEGATIVE_CONTACTOR_PIN, PWM_Hold_Duty); - ledcWrite(POSITIVE_CONTACTOR_PIN, PWM_Hold_Duty); -#endif + set(PRECHARGE_PIN, OFF); + set(NEGATIVE_CONTACTOR_PIN, PWM_HOLD_DUTY); + set(POSITIVE_CONTACTOR_PIN, PWM_HOLD_DUTY); contactorStatus = COMPLETED; datalayer.system.status.contactor_control_closed = true; } diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index c6351a12..be9dfece 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -59,6 +59,7 @@ //#define INTERLOCK_REQUIRED //Nissan LEAF specific setting, if enabled requires both high voltage conenctors to be seated before starting //#define CONTACTOR_CONTROL //Enable this line to have pins 25,32,33 handle automatic precharge/contactor+/contactor- closing sequence //#define PWM_CONTACTOR_CONTROL //Enable this line to use PWM for CONTACTOR_CONTROL, which lowers power consumption and heat generation. CONTACTOR_CONTROL must be enabled. +//#define NC_CONTACTORS //Enable this line to control normally closed contactors. CONTACTOR_CONTROL must be enabled for this option. Extremely rare setting! //#define DUAL_CAN //Enable this line to activate an isolated secondary CAN Bus using add-on MCP2515 chip (Needed for some inverters / double battery) #define CRYSTAL_FREQUENCY_MHZ 8 //DUAL_CAN option, what is your MCP2515 add-on boards crystal frequency? //#define CAN_FD //Enable this line to activate an isolated secondary CAN-FD bus using add-on MCP2518FD chip / Native CANFD on Stark board From cb4a830a5730df3ce6fefb7c313fc1f3b38d7d68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Fri, 15 Nov 2024 20:54:37 +0200 Subject: [PATCH 2/9] Add second contactor closing function --- Software/Software.ino | 53 +++++++++++++++++++----------- Software/USER_SETTINGS.h | 9 +++-- Software/src/devboard/hal/hw_3LB.h | 6 ++-- 3 files changed, 42 insertions(+), 26 deletions(-) diff --git a/Software/Software.ino b/Software/Software.ino index bba1d277..234fd5ab 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -123,11 +123,11 @@ State contactorStatus = DISCONNECTED; #define ON 1 #define OFF 0 -#ifdef NC_CONTACTORS //Normally closed contactors use inverted logic - #undef ON - #define ON 0 - #undef OFF - #define OFF 1 +#ifdef NC_CONTACTORS //Normally closed contactors use inverted logic +#undef ON +#define ON 0 +#undef OFF +#define OFF 1 #endif #define MAX_ALLOWED_FAULT_TICKS 1000 @@ -149,18 +149,16 @@ unsigned long negativeStartTime = 0; unsigned long timeSpentInFaultedMode = 0; #endif -void set(uint8_t pin, bool direction, uint32_t pwm_freq = 0xFFFFFFFFFF){ - #ifdef PWM_CONTACTOR_CONTROL - if(pwm_freq != 0xFFFFFFFFFF) - { +void set(uint8_t pin, bool direction, uint32_t pwm_freq = 0xFFFFFFFFFF) { +#ifdef PWM_CONTACTOR_CONTROL + if (pwm_freq != 0xFFFFFFFFFF) { ledcWrite(pin, pwm_freq); return; } - #endif - if(direction == 1){ +#endif + if (direction == 1) { digitalWrite(pin, HIGH); - } - else{ // 0 + } else { // 0 digitalWrite(pin, LOW); } } @@ -528,7 +526,7 @@ void init_contactors() { NEGATIVE_PWM_Ch); // Setup PWM Channel Frequency and Resolution ledcWrite(POSITIVE_CONTACTOR_PIN, PWM_OFF_DUTY); // Set Positive PWM to 0% ledcWrite(NEGATIVE_CONTACTOR_PIN, PWM_OFF_DUTY); // Set Negative PWM to 0% -#else //Normal CONTACTOR_CONTROL +#else //Normal CONTACTOR_CONTROL pinMode(POSITIVE_CONTACTOR_PIN, OUTPUT); set(POSITIVE_CONTACTOR_PIN, OFF); pinMode(NEGATIVE_CONTACTOR_PIN, OUTPUT); @@ -536,12 +534,18 @@ void init_contactors() { #endif pinMode(PRECHARGE_PIN, OUTPUT); set(PRECHARGE_PIN, OFF); -#endif //CONTACTOR_CONTROL +#endif //CONTACTOR_CONTROL +#ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY + pinMode(SECOND_POSITIVE_CONTACTOR_PIN, OUTPUT); + set(SECOND_POSITIVE_CONTACTOR_PIN, OFF); + pinMode(SECOND_NEGATIVE_CONTACTOR_PIN, OUTPUT); + set(SECOND_NEGATIVE_CONTACTOR_PIN, OFF); +#endif //CONTACTOR_CONTROL_DOUBLE_BATTERY // Init BMS contactor #ifdef HW_STARK // TODO: Rewrite this so LilyGo can also handle this BMS contactor pinMode(BMS_POWER, OUTPUT); - set(BMS_POWER, ON); -#endif //HW_STARK + digitalWrite(BMS_POWER, HIGH); +#endif //HW_STARK } void init_rs485() { @@ -797,9 +801,18 @@ void handle_contactors() { // In case the inverter requests contactors to open, set the state accordingly if (contactorStatus == COMPLETED) { - if (!datalayer.system.status.inverter_allows_contactor_closing){ + if (!datalayer.system.status.inverter_allows_contactor_closing) { contactorStatus = DISCONNECTED; } +#ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY + if (datalayer.system.status.battery2_allows_contactor_closing) { + set(SECOND_NEGATIVE_CONTACTOR_PIN, ON); + set(SECOND_POSITIVE_CONTACTOR_PIN, ON); + } else { + set(SECOND_NEGATIVE_CONTACTOR_PIN, OFF); + set(SECOND_POSITIVE_CONTACTOR_PIN, OFF); + } +#endif //CONTACTOR_CONTROL_DOUBLE_BATTERY // Skip running the state machine below if it has already completed return; } @@ -831,8 +844,8 @@ void handle_contactors() { case PRECHARGE_OFF: if (currentTime - negativeStartTime >= POSITIVE_CONTACTOR_TIME_MS) { set(PRECHARGE_PIN, OFF); - set(NEGATIVE_CONTACTOR_PIN, PWM_HOLD_DUTY); - set(POSITIVE_CONTACTOR_PIN, PWM_HOLD_DUTY); + set(NEGATIVE_CONTACTOR_PIN, ON, PWM_HOLD_DUTY); + set(POSITIVE_CONTACTOR_PIN, ON, PWM_HOLD_DUTY); contactorStatus = COMPLETED; datalayer.system.status.contactor_control_closed = true; } diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index be9dfece..392bd661 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -53,13 +53,16 @@ //#define HW_STARK //#define HW_3LB +/* Contactor settings. If you have a battery that does not activate contactors via CAN, configure this section */ +//#define CONTACTOR_CONTROL //Enable this line to have the emulator handle automatic precharge/contactor+/contactor- closing sequence (See wiki for pins) +//#define CONTACTOR_CONTROL_DOUBLE_BATTERY //Enable this line to have the emulator hardware control secondary set of contactors for double battery setups (See wiki for pins) +//#define PWM_CONTACTOR_CONTROL //Enable this line to use PWM for CONTACTOR_CONTROL, which lowers power consumption and heat generation. CONTACTOR_CONTROL must be enabled. +//#define NC_CONTACTORS //Enable this line to control normally closed contactors. CONTACTOR_CONTROL must be enabled for this option. Extremely rare setting! + /* Other options */ //#define DEBUG_VIA_USB //Enable this line to have the USB port output serial diagnostic data while program runs (WARNING, raises CPU load, do not use for production) //#define DEBUG_CAN_DATA //Enable this line to print incoming/outgoing CAN & CAN-FD messages to USB serial (WARNING, raises CPU load, do not use for production) //#define INTERLOCK_REQUIRED //Nissan LEAF specific setting, if enabled requires both high voltage conenctors to be seated before starting -//#define CONTACTOR_CONTROL //Enable this line to have pins 25,32,33 handle automatic precharge/contactor+/contactor- closing sequence -//#define PWM_CONTACTOR_CONTROL //Enable this line to use PWM for CONTACTOR_CONTROL, which lowers power consumption and heat generation. CONTACTOR_CONTROL must be enabled. -//#define NC_CONTACTORS //Enable this line to control normally closed contactors. CONTACTOR_CONTROL must be enabled for this option. Extremely rare setting! //#define DUAL_CAN //Enable this line to activate an isolated secondary CAN Bus using add-on MCP2515 chip (Needed for some inverters / double battery) #define CRYSTAL_FREQUENCY_MHZ 8 //DUAL_CAN option, what is your MCP2515 add-on boards crystal frequency? //#define CAN_FD //Enable this line to activate an isolated secondary CAN-FD bus using add-on MCP2518FD chip / Native CANFD on Stark board diff --git a/Software/src/devboard/hal/hw_3LB.h b/Software/src/devboard/hal/hw_3LB.h index 137d83f3..a54de2a4 100644 --- a/Software/src/devboard/hal/hw_3LB.h +++ b/Software/src/devboard/hal/hw_3LB.h @@ -52,9 +52,9 @@ #define NEGATIVE_CONTACTOR_PIN 33 #define PRECHARGE_PIN 25 -#define 2ND_POSITIVE_CONTACTOR_PIN 13 -#define 2ND_NEGATIVE_CONTACTOR_PIN 16 -#define 2ND_PRECHARGE_PIN 18 +#define SECOND_POSITIVE_CONTACTOR_PIN 13 +#define SECOND_NEGATIVE_CONTACTOR_PIN 16 +#define SECOND_PRECHARGE_PIN 18 // SMA CAN contactor pins #define INVERTER_CONTACTOR_ENABLE_PIN 36 From 21b801b98376e40f5375e6ccac17add0d37f9a41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Mon, 18 Nov 2024 13:16:35 +0200 Subject: [PATCH 3/9] Fix so that contactors open on second battery incase of FAULT --- Software/Software.ino | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Software/Software.ino b/Software/Software.ino index 234fd5ab..0e8b78bf 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -748,6 +748,8 @@ void check_interconnect_available() { clear_event(EVENT_VOLTAGE_DIFFERENCE); if (datalayer.battery.status.bms_status != FAULT) { // Only proceed if we are not in faulted state datalayer.system.status.battery2_allows_contactor_closing = true; + } else { // If main battery is in fault state, disengage the second battery + datalayer.system.status.battery2_allows_contactor_closing = false; } } else { //We are over 3.0V diff set_event(EVENT_VOLTAGE_DIFFERENCE, @@ -808,7 +810,7 @@ void handle_contactors() { if (datalayer.system.status.battery2_allows_contactor_closing) { set(SECOND_NEGATIVE_CONTACTOR_PIN, ON); set(SECOND_POSITIVE_CONTACTOR_PIN, ON); - } else { + } else { // Closing contactors on secondary battery not allowed set(SECOND_NEGATIVE_CONTACTOR_PIN, OFF); set(SECOND_POSITIVE_CONTACTOR_PIN, OFF); } From 40717942fb995b1fdb9ca7b616daae319b6843db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Mon, 18 Nov 2024 22:27:24 +0200 Subject: [PATCH 4/9] Make webserver reflect actual second battery control status --- Software/Software.ino | 6 ++++-- Software/src/datalayer/datalayer.h | 4 +++- Software/src/devboard/webserver/webserver.cpp | 15 ++++++++------- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/Software/Software.ino b/Software/Software.ino index 0e8b78bf..22d2d0eb 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -785,7 +785,7 @@ void handle_contactors() { set(NEGATIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY); set(POSITIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY); set_event(EVENT_ERROR_OPEN_CONTACTOR, 0); - datalayer.system.status.contactor_control_closed = false; + datalayer.system.status.contactors_engaged = false; return; // A fault scenario latches the contactor control. It is not possible to recover without a powercycle (and investigation why fault occured) } @@ -810,9 +810,11 @@ void handle_contactors() { if (datalayer.system.status.battery2_allows_contactor_closing) { set(SECOND_NEGATIVE_CONTACTOR_PIN, ON); set(SECOND_POSITIVE_CONTACTOR_PIN, ON); + datalayer.system.status.contactors_battery2_engaged = true; } else { // Closing contactors on secondary battery not allowed set(SECOND_NEGATIVE_CONTACTOR_PIN, OFF); set(SECOND_POSITIVE_CONTACTOR_PIN, OFF); + datalayer.system.status.contactors_battery2_engaged = false; } #endif //CONTACTOR_CONTROL_DOUBLE_BATTERY // Skip running the state machine below if it has already completed @@ -849,7 +851,7 @@ void handle_contactors() { set(NEGATIVE_CONTACTOR_PIN, ON, PWM_HOLD_DUTY); set(POSITIVE_CONTACTOR_PIN, ON, PWM_HOLD_DUTY); contactorStatus = COMPLETED; - datalayer.system.status.contactor_control_closed = true; + datalayer.system.status.contactors_engaged = true; } break; default: diff --git a/Software/src/datalayer/datalayer.h b/Software/src/datalayer/datalayer.h index 5b198158..150a4567 100644 --- a/Software/src/datalayer/datalayer.h +++ b/Software/src/datalayer/datalayer.h @@ -189,7 +189,9 @@ typedef struct { bool inverter_allows_contactor_closing = true; #ifdef CONTACTOR_CONTROL /** True if the contactor controlled by battery-emulator is closed */ - bool contactor_control_closed = false; + bool contactors_engaged = false; + /** True if the contactor controlled by battery-emulator is closed */ + bool contactors_battery2_engaged = false; #endif } DATALAYER_SYSTEM_STATUS_TYPE; diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 8eab7631..8bd4384c 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -733,7 +733,7 @@ String processor(const String& var) { #ifdef CONTACTOR_CONTROL content += "

Contactors controlled by Battery-Emulator: "; - if (datalayer.system.status.contactor_control_closed) { + if (datalayer.system.status.contactors_engaged) { content += "ON"; } else { content += "OFF"; @@ -853,34 +853,35 @@ String processor(const String& var) { #ifdef CONTACTOR_CONTROL content += "

Contactors controlled by Battery-Emulator: "; - if (datalayer.system.status.contactor_control_closed) { + if (datalayer.system.status.contactors_battery2_engaged) { content += "ON"; } else { content += "OFF"; } content += "

"; - +#ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY content += "

Pre Charge: "; - if (digitalRead(PRECHARGE_PIN) == HIGH) { + if (digitalRead(SECOND_PRECHARGE_PIN) == HIGH) { content += ""; } else { content += ""; } content += " Cont. Neg.: "; - if (digitalRead(NEGATIVE_CONTACTOR_PIN) == HIGH) { + if (digitalRead(SECOND_NEGATIVE_CONTACTOR_PIN) == HIGH) { content += ""; } else { content += ""; } content += " Cont. Pos.: "; - if (digitalRead(POSITIVE_CONTACTOR_PIN) == HIGH) { + if (digitalRead(SECOND_POSITIVE_CONTACTOR_PIN) == HIGH) { content += ""; } else { content += ""; } content += "

"; -#endif +#endif // CONTACTOR_CONTROL_DOUBLE_BATTERY +#endif // CONTACTOR_CONTROL content += ""; content += ""; From e681ca3191e74c858bc2532e98b862c8a0d54723 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Mon, 18 Nov 2024 22:31:25 +0200 Subject: [PATCH 5/9] Avoid voltage check to pass with init value --- Software/src/battery/NISSAN-LEAF-BATTERY.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp index f32a9ba6..f5165a72 100644 --- a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp +++ b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp @@ -138,7 +138,7 @@ static uint16_t battery2_GIDS = 273; //Startup in 24kWh mode static uint16_t battery2_MAX = 0; static uint16_t battery2_Max_GIDS = 273; //Startup in 24kWh mode static uint16_t battery2_StateOfHealth = 99; //State of health % -static uint16_t battery2_Total_Voltage2 = 740; //Battery voltage (0-450V) [0.5V/bit, so actual range 0-800] +static uint16_t battery2_Total_Voltage2 = 690; //Battery voltage (0-450V) [0.5V/bit, so actual range 0-800] static int16_t battery2_Current2 = 0; //Battery current (-400-200A) [0.5A/bit, so actual range -800-400] static int16_t battery2_HistData_Temperature_MAX = 6; //-40 to 86*C static int16_t battery2_HistData_Temperature_MIN = 5; //-40 to 86*C From f2cbc26f5eab222c2d35df12a47f82faca180fdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Mon, 18 Nov 2024 22:35:30 +0200 Subject: [PATCH 6/9] Add inverter check for battery2 --- Software/Software.ino | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Software/Software.ino b/Software/Software.ino index 22d2d0eb..1ca4d6d4 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -807,7 +807,8 @@ void handle_contactors() { contactorStatus = DISCONNECTED; } #ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY - if (datalayer.system.status.battery2_allows_contactor_closing) { + if (datalayer.system.status.battery2_allows_contactor_closing && + datalayer.system.status.inverter_allows_contactor_closing) { set(SECOND_NEGATIVE_CONTACTOR_PIN, ON); set(SECOND_POSITIVE_CONTACTOR_PIN, ON); datalayer.system.status.contactors_battery2_engaged = true; From 2352cf035e93ad1da821d37e4115ccb5a23e7850 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Mon, 18 Nov 2024 22:48:55 +0200 Subject: [PATCH 7/9] Branch out contactor_battery2 handling --- Software/Software.ino | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/Software/Software.ino b/Software/Software.ino index 1ca4d6d4..e97a6779 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -763,6 +763,10 @@ void handle_contactors() { datalayer.system.status.inverter_allows_contactor_closing = digitalRead(INVERTER_CONTACTOR_ENABLE_PIN); #endif +#ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY + handle_contactors_battery2(); +#endif + #ifdef CONTACTOR_CONTROL // First check if we have any active errors, incase we do, turn off the battery if (datalayer.battery.status.bms_status == FAULT) { @@ -803,21 +807,10 @@ void handle_contactors() { // In case the inverter requests contactors to open, set the state accordingly if (contactorStatus == COMPLETED) { + //Incase inverter requests contactors to open, make state machine jump to Disconnected state if (!datalayer.system.status.inverter_allows_contactor_closing) { contactorStatus = DISCONNECTED; } -#ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY - if (datalayer.system.status.battery2_allows_contactor_closing && - datalayer.system.status.inverter_allows_contactor_closing) { - set(SECOND_NEGATIVE_CONTACTOR_PIN, ON); - set(SECOND_POSITIVE_CONTACTOR_PIN, ON); - datalayer.system.status.contactors_battery2_engaged = true; - } else { // Closing contactors on secondary battery not allowed - set(SECOND_NEGATIVE_CONTACTOR_PIN, OFF); - set(SECOND_POSITIVE_CONTACTOR_PIN, OFF); - datalayer.system.status.contactors_battery2_engaged = false; - } -#endif //CONTACTOR_CONTROL_DOUBLE_BATTERY // Skip running the state machine below if it has already completed return; } @@ -861,6 +854,21 @@ void handle_contactors() { #endif // CONTACTOR_CONTROL } +#ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY +void handle_contactors_battery2() { + if ((contactorStatus == COMPLETED) && datalayer.system.status.battery2_allows_contactor_closing && + datalayer.system.status.inverter_allows_contactor_closing) { + set(SECOND_NEGATIVE_CONTACTOR_PIN, ON); + set(SECOND_POSITIVE_CONTACTOR_PIN, ON); + datalayer.system.status.contactors_battery2_engaged = true; + } else { // Closing contactors on secondary battery not allowed + set(SECOND_NEGATIVE_CONTACTOR_PIN, OFF); + set(SECOND_POSITIVE_CONTACTOR_PIN, OFF); + datalayer.system.status.contactors_battery2_engaged = false; + } +} +#endif //CONTACTOR_CONTROL_DOUBLE_BATTERY + void update_calculated_values() { /* Calculate allowed charge/discharge currents*/ if (datalayer.battery.status.voltage_dV > 10) { From 7fa3a160e118b90e28a14aac8739560848765bd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 20 Nov 2024 21:49:18 +0200 Subject: [PATCH 8/9] Squash bugs related to estop and datalayer --- Software/Software.ino | 26 ++++++++------------ Software/src/battery/NISSAN-LEAF-BATTERY.cpp | 14 ++++------- Software/src/datalayer/datalayer.h | 2 +- 3 files changed, 16 insertions(+), 26 deletions(-) diff --git a/Software/Software.ino b/Software/Software.ino index e97a6779..3d800713 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -57,7 +57,7 @@ const char* version_number = "7.7.dev"; // Interval settings uint16_t intervalUpdateValues = INTERVAL_1_S; // Interval at which to update inverter values / Modbus registers -unsigned long previousMillis10ms = 50; +unsigned long previousMillis10ms = 0; unsigned long previousMillisUpdateVal = 0; // CAN parameters @@ -302,9 +302,6 @@ void core_loop(void* task_time_us) { previousMillis10ms = millis(); led_exe(); handle_contactors(); // Take care of startup precharge/contactor closing -#ifdef DOUBLE_BATTERY - check_interconnect_available(); -#endif } END_TIME_MEASUREMENT_MAX(time_10ms, datalayer.system.status.time_10ms_us); @@ -314,6 +311,7 @@ void core_loop(void* task_time_us) { update_values_battery(); // Fetch battery values #ifdef DOUBLE_BATTERY update_values_battery2(); + check_interconnect_available(); #endif update_calculated_values(); #ifndef SERIAL_LINK_RECEIVER @@ -536,6 +534,7 @@ void init_contactors() { set(PRECHARGE_PIN, OFF); #endif //CONTACTOR_CONTROL #ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY + pinMode(SECOND_POSITIVE_CONTACTOR_PIN, OUTPUT); set(SECOND_POSITIVE_CONTACTOR_PIN, OFF); pinMode(SECOND_NEGATIVE_CONTACTOR_PIN, OUTPUT); @@ -775,15 +774,11 @@ void handle_contactors() { timeSpentInFaultedMode = 0; } - //handle contactor control SHUTDOWN_REQUESTED vs DISCONNECTED - if (timeSpentInFaultedMode > MAX_ALLOWED_FAULT_TICKS || - (datalayer.system.settings.equipment_stop_active && contactorStatus != SHUTDOWN_REQUESTED)) { + //handle contactor control SHUTDOWN_REQUESTED + if (timeSpentInFaultedMode > MAX_ALLOWED_FAULT_TICKS) { contactorStatus = SHUTDOWN_REQUESTED; - datalayer.system.settings.equipment_stop_active = true; - } - if (contactorStatus == SHUTDOWN_REQUESTED && !datalayer.system.settings.equipment_stop_active) { - contactorStatus = DISCONNECTED; } + if (contactorStatus == SHUTDOWN_REQUESTED) { set(PRECHARGE_PIN, OFF); set(NEGATIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY); @@ -800,15 +795,15 @@ void handle_contactors() { set(POSITIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY); if (datalayer.system.status.battery_allows_contactor_closing && - datalayer.system.status.inverter_allows_contactor_closing) { + datalayer.system.status.inverter_allows_contactor_closing && !datalayer.system.settings.equipment_stop_active) { contactorStatus = PRECHARGE; } } // In case the inverter requests contactors to open, set the state accordingly if (contactorStatus == COMPLETED) { - //Incase inverter requests contactors to open, make state machine jump to Disconnected state - if (!datalayer.system.status.inverter_allows_contactor_closing) { + //Incase inverter (or estop) requests contactors to open, make state machine jump to Disconnected state (recoverable) + if (!datalayer.system.status.inverter_allows_contactor_closing || datalayer.system.settings.equipment_stop_active) { contactorStatus = DISCONNECTED; } // Skip running the state machine below if it has already completed @@ -856,8 +851,7 @@ void handle_contactors() { #ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY void handle_contactors_battery2() { - if ((contactorStatus == COMPLETED) && datalayer.system.status.battery2_allows_contactor_closing && - datalayer.system.status.inverter_allows_contactor_closing) { + if ((contactorStatus == COMPLETED) && datalayer.system.status.battery2_allows_contactor_closing) { set(SECOND_NEGATIVE_CONTACTOR_PIN, ON); set(SECOND_POSITIVE_CONTACTOR_PIN, ON); datalayer.system.status.contactors_battery2_engaged = true; diff --git a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp index f5165a72..90fb6365 100644 --- a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp +++ b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp @@ -136,10 +136,10 @@ static uint16_t battery2_TEMP = 0; //Temporary value used in s static uint16_t battery2_Wh_Remaining = 0; //Amount of energy in battery, in Wh static uint16_t battery2_GIDS = 273; //Startup in 24kWh mode static uint16_t battery2_MAX = 0; -static uint16_t battery2_Max_GIDS = 273; //Startup in 24kWh mode -static uint16_t battery2_StateOfHealth = 99; //State of health % -static uint16_t battery2_Total_Voltage2 = 690; //Battery voltage (0-450V) [0.5V/bit, so actual range 0-800] -static int16_t battery2_Current2 = 0; //Battery current (-400-200A) [0.5A/bit, so actual range -800-400] +static uint16_t battery2_Max_GIDS = 273; //Startup in 24kWh mode +static uint16_t battery2_StateOfHealth = 99; //State of health % +static uint16_t battery2_Total_Voltage2 = 0; //Battery voltage (0-450V) [0.5V/bit, so actual range 0-800] +static int16_t battery2_Current2 = 0; //Battery current (-400-200A) [0.5A/bit, so actual range -800-400] static int16_t battery2_HistData_Temperature_MAX = 6; //-40 to 86*C static int16_t battery2_HistData_Temperature_MIN = 5; //-40 to 86*C static int16_t battery2_AverageTemperature = 6; //Only available on ZE0, in celcius, -40 to +55 @@ -507,11 +507,7 @@ void receive_can_battery2(CAN_frame rx_frame) { battery2_Relay_Cut_Request = ((rx_frame.data.u8[1] & 0x18) >> 3); battery2_Failsafe_Status = (rx_frame.data.u8[1] & 0x07); battery2_MainRelayOn_flag = (bool)((rx_frame.data.u8[3] & 0x20) >> 5); - if (battery2_MainRelayOn_flag) { - datalayer.system.status.battery2_allows_contactor_closing = true; - } else { - datalayer.system.status.battery2_allows_contactor_closing = false; - } + //battery2_allows_contactor_closing written by check_interconnect_available(); battery2_Full_CHARGE_flag = (bool)((rx_frame.data.u8[3] & 0x10) >> 4); battery2_Interlock = (bool)((rx_frame.data.u8[3] & 0x08) >> 3); break; diff --git a/Software/src/datalayer/datalayer.h b/Software/src/datalayer/datalayer.h index 150a4567..528179a6 100644 --- a/Software/src/datalayer/datalayer.h +++ b/Software/src/datalayer/datalayer.h @@ -190,7 +190,7 @@ typedef struct { #ifdef CONTACTOR_CONTROL /** True if the contactor controlled by battery-emulator is closed */ bool contactors_engaged = false; - /** True if the contactor controlled by battery-emulator is closed */ + /** True if the contactor controlled by battery-emulator is closed. Determined by check_interconnect_available(); if voltage is OK */ bool contactors_battery2_engaged = false; #endif } DATALAYER_SYSTEM_STATUS_TYPE; From 95981d8365a53eab3aa56be8bab6f65655127f54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sat, 23 Nov 2024 23:59:18 +0200 Subject: [PATCH 9/9] Refactor check_interconnect --- Software/Software.ino | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Software/Software.ino b/Software/Software.ino index 3d800713..c49b72f4 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -743,16 +743,18 @@ void check_interconnect_available() { return; // Both voltage values need to be available to start check } - if (abs(datalayer.battery.status.voltage_dV - datalayer.battery2.status.voltage_dV) < 30) { // If we are within 3.0V + uint16_t voltage_diff = abs(datalayer.battery.status.voltage_dV - datalayer.battery2.status.voltage_dV); + + if (voltage_diff <= 30) { // If we are within 3.0V between the batteries clear_event(EVENT_VOLTAGE_DIFFERENCE); - if (datalayer.battery.status.bms_status != FAULT) { // Only proceed if we are not in faulted state - datalayer.system.status.battery2_allows_contactor_closing = true; - } else { // If main battery is in fault state, disengage the second battery + if (datalayer.battery.status.bms_status == FAULT) { + // If main battery is in fault state, disengage the second battery datalayer.system.status.battery2_allows_contactor_closing = false; + } else { // If main battery is OK, allow second battery to join + datalayer.system.status.battery2_allows_contactor_closing = true; } - } else { //We are over 3.0V diff - set_event(EVENT_VOLTAGE_DIFFERENCE, - (uint8_t)(abs(datalayer.battery.status.voltage_dV - datalayer.battery2.status.voltage_dV) / 10)); + } else { //Voltage between the two packs is too large + set_event(EVENT_VOLTAGE_DIFFERENCE, (uint8_t)(voltage_diff / 10)); } } #endif //DOUBLE_BATTERY