mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-06 03:50:13 +02:00
Merge pull request #619 from dalathegreat/feature/double-automatic-contactor
Feature: Double contactor support + NC support
This commit is contained in:
commit
9e74fcd032
6 changed files with 120 additions and 90 deletions
|
@ -57,7 +57,7 @@ const char* version_number = "7.8.dev";
|
||||||
|
|
||||||
// Interval settings
|
// Interval settings
|
||||||
uint16_t intervalUpdateValues = INTERVAL_1_S; // Interval at which to update inverter values / Modbus registers
|
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;
|
unsigned long previousMillisUpdateVal = 0;
|
||||||
|
|
||||||
// CAN parameters
|
// CAN parameters
|
||||||
|
@ -118,6 +118,16 @@ MyTimer check_pause_2s(INTERVAL_2_S);
|
||||||
enum State { DISCONNECTED, PRECHARGE, NEGATIVE, POSITIVE, PRECHARGE_OFF, COMPLETED, SHUTDOWN_REQUESTED };
|
enum State { DISCONNECTED, PRECHARGE, NEGATIVE, POSITIVE, PRECHARGE_OFF, COMPLETED, SHUTDOWN_REQUESTED };
|
||||||
State contactorStatus = DISCONNECTED;
|
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
|
#define MAX_ALLOWED_FAULT_TICKS 1000
|
||||||
/* NOTE: modify the precharge time constant below to account for the resistance and capacitance of the target system.
|
/* 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
|
* t=3RC at minimum, t=5RC ideally
|
||||||
|
@ -125,20 +135,32 @@ State contactorStatus = DISCONNECTED;
|
||||||
#define PRECHARGE_TIME_MS 160
|
#define PRECHARGE_TIME_MS 160
|
||||||
#define NEGATIVE_CONTACTOR_TIME_MS 1000
|
#define NEGATIVE_CONTACTOR_TIME_MS 1000
|
||||||
#define POSITIVE_CONTACTOR_TIME_MS 2000
|
#define POSITIVE_CONTACTOR_TIME_MS 2000
|
||||||
#ifdef PWM_CONTACTOR_CONTROL
|
|
||||||
#define PWM_Freq 20000 // 20 kHz frequency, beyond audible range
|
#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_Res 10 // 10 Bit resolution 0 to 1023, maps 'nicely' to 0% 100%
|
||||||
#define PWM_Hold_Duty 250
|
#define PWM_HOLD_DUTY 250
|
||||||
#define PWM_Off_Duty 0
|
#define PWM_OFF_DUTY 0
|
||||||
#define PWM_On_Duty 1023
|
#define PWM_ON_DUTY 1023
|
||||||
#define POSITIVE_PWM_Ch 0
|
#define POSITIVE_PWM_Ch 0
|
||||||
#define NEGATIVE_PWM_Ch 1
|
#define NEGATIVE_PWM_Ch 1
|
||||||
#endif
|
|
||||||
unsigned long prechargeStartTime = 0;
|
unsigned long prechargeStartTime = 0;
|
||||||
unsigned long negativeStartTime = 0;
|
unsigned long negativeStartTime = 0;
|
||||||
unsigned long timeSpentInFaultedMode = 0;
|
unsigned long timeSpentInFaultedMode = 0;
|
||||||
#endif
|
#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
|
#ifdef EQUIPMENT_STOP_BUTTON
|
||||||
const unsigned long equipment_button_long_press_duration =
|
const unsigned long equipment_button_long_press_duration =
|
||||||
15000; // 15 seconds for long press in case of MOMENTARY_SWITCH
|
15000; // 15 seconds for long press in case of MOMENTARY_SWITCH
|
||||||
|
@ -280,9 +302,6 @@ void core_loop(void* task_time_us) {
|
||||||
previousMillis10ms = millis();
|
previousMillis10ms = millis();
|
||||||
led_exe();
|
led_exe();
|
||||||
handle_contactors(); // Take care of startup precharge/contactor closing
|
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);
|
END_TIME_MEASUREMENT_MAX(time_10ms, datalayer.system.status.time_10ms_us);
|
||||||
|
|
||||||
|
@ -292,6 +311,7 @@ void core_loop(void* task_time_us) {
|
||||||
update_values_battery(); // Fetch battery values
|
update_values_battery(); // Fetch battery values
|
||||||
#ifdef DOUBLE_BATTERY
|
#ifdef DOUBLE_BATTERY
|
||||||
update_values_battery2();
|
update_values_battery2();
|
||||||
|
check_interconnect_available();
|
||||||
#endif
|
#endif
|
||||||
update_calculated_values();
|
update_calculated_values();
|
||||||
#ifndef SERIAL_LINK_RECEIVER
|
#ifndef SERIAL_LINK_RECEIVER
|
||||||
|
@ -497,27 +517,34 @@ void init_CAN() {
|
||||||
void init_contactors() {
|
void init_contactors() {
|
||||||
// Init contactor pins
|
// Init contactor pins
|
||||||
#ifdef CONTACTOR_CONTROL
|
#ifdef CONTACTOR_CONTROL
|
||||||
#ifndef PWM_CONTACTOR_CONTROL
|
#ifdef PWM_CONTACTOR_CONTROL
|
||||||
pinMode(POSITIVE_CONTACTOR_PIN, OUTPUT);
|
|
||||||
digitalWrite(POSITIVE_CONTACTOR_PIN, LOW);
|
|
||||||
pinMode(NEGATIVE_CONTACTOR_PIN, OUTPUT);
|
|
||||||
digitalWrite(NEGATIVE_CONTACTOR_PIN, LOW);
|
|
||||||
#else
|
|
||||||
ledcAttachChannel(POSITIVE_CONTACTOR_PIN, PWM_Freq, PWM_Res,
|
ledcAttachChannel(POSITIVE_CONTACTOR_PIN, PWM_Freq, PWM_Res,
|
||||||
POSITIVE_PWM_Ch); // Setup PWM Channel Frequency and Resolution
|
POSITIVE_PWM_Ch); // Setup PWM Channel Frequency and Resolution
|
||||||
ledcAttachChannel(NEGATIVE_CONTACTOR_PIN, PWM_Freq, PWM_Res,
|
ledcAttachChannel(NEGATIVE_CONTACTOR_PIN, PWM_Freq, PWM_Res,
|
||||||
NEGATIVE_PWM_Ch); // Setup PWM Channel Frequency and Resolution
|
NEGATIVE_PWM_Ch); // Setup PWM Channel Frequency and Resolution
|
||||||
ledcWrite(POSITIVE_CONTACTOR_PIN, PWM_Off_Duty); // Set Positive 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%
|
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
|
#endif
|
||||||
pinMode(PRECHARGE_PIN, OUTPUT);
|
pinMode(PRECHARGE_PIN, OUTPUT);
|
||||||
digitalWrite(PRECHARGE_PIN, LOW);
|
set(PRECHARGE_PIN, OFF);
|
||||||
#endif
|
#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
|
// Init BMS contactor
|
||||||
#ifdef HW_STARK // TODO: Rewrite this so LilyGo can also handle this BMS contactor
|
#ifdef HW_STARK // TODO: Rewrite this so LilyGo can also handle this BMS contactor
|
||||||
pinMode(BMS_POWER, OUTPUT);
|
pinMode(BMS_POWER, OUTPUT);
|
||||||
digitalWrite(BMS_POWER, HIGH);
|
digitalWrite(BMS_POWER, HIGH);
|
||||||
#endif
|
#endif //HW_STARK
|
||||||
}
|
}
|
||||||
|
|
||||||
void init_rs485() {
|
void init_rs485() {
|
||||||
|
@ -695,14 +722,18 @@ void check_interconnect_available() {
|
||||||
return; // Both voltage values need to be available to start check
|
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);
|
clear_event(EVENT_VOLTAGE_DIFFERENCE);
|
||||||
if (datalayer.battery.status.bms_status != FAULT) { // Only proceed if we are not in faulted state
|
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;
|
datalayer.system.status.battery2_allows_contactor_closing = true;
|
||||||
}
|
}
|
||||||
} else { //We are over 3.0V diff
|
} else { //Voltage between the two packs is too large
|
||||||
set_event(EVENT_VOLTAGE_DIFFERENCE,
|
set_event(EVENT_VOLTAGE_DIFFERENCE, (uint8_t)(voltage_diff / 10));
|
||||||
(uint8_t)(abs(datalayer.battery.status.voltage_dV - datalayer.battery2.status.voltage_dV) / 10));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif //DOUBLE_BATTERY
|
#endif //DOUBLE_BATTERY
|
||||||
|
@ -712,6 +743,10 @@ void handle_contactors() {
|
||||||
datalayer.system.status.inverter_allows_contactor_closing = digitalRead(INVERTER_CONTACTOR_ENABLE_PIN);
|
datalayer.system.status.inverter_allows_contactor_closing = digitalRead(INVERTER_CONTACTOR_ENABLE_PIN);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY
|
||||||
|
handle_contactors_battery2();
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef CONTACTOR_CONTROL
|
#ifdef CONTACTOR_CONTROL
|
||||||
// First check if we have any active errors, incase we do, turn off the battery
|
// First check if we have any active errors, incase we do, turn off the battery
|
||||||
if (datalayer.battery.status.bms_status == FAULT) {
|
if (datalayer.battery.status.bms_status == FAULT) {
|
||||||
|
@ -720,50 +755,38 @@ void handle_contactors() {
|
||||||
timeSpentInFaultedMode = 0;
|
timeSpentInFaultedMode = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//handle contactor control SHUTDOWN_REQUESTED vs DISCONNECTED
|
//handle contactor control SHUTDOWN_REQUESTED
|
||||||
if (timeSpentInFaultedMode > MAX_ALLOWED_FAULT_TICKS ||
|
if (timeSpentInFaultedMode > MAX_ALLOWED_FAULT_TICKS) {
|
||||||
(datalayer.system.settings.equipment_stop_active && contactorStatus != SHUTDOWN_REQUESTED)) {
|
|
||||||
contactorStatus = SHUTDOWN_REQUESTED;
|
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) {
|
if (contactorStatus == SHUTDOWN_REQUESTED) {
|
||||||
digitalWrite(PRECHARGE_PIN, LOW);
|
set(PRECHARGE_PIN, OFF);
|
||||||
#ifndef PWM_CONTACTOR_CONTROL
|
set(NEGATIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY);
|
||||||
digitalWrite(NEGATIVE_CONTACTOR_PIN, LOW);
|
set(POSITIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY);
|
||||||
digitalWrite(POSITIVE_CONTACTOR_PIN, LOW);
|
|
||||||
#else
|
|
||||||
ledcWrite(NEGATIVE_CONTACTOR_PIN, PWM_Off_Duty);
|
|
||||||
ledcWrite(POSITIVE_CONTACTOR_PIN, PWM_Off_Duty);
|
|
||||||
#endif
|
|
||||||
set_event(EVENT_ERROR_OPEN_CONTACTOR, 0);
|
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)
|
return; // A fault scenario latches the contactor control. It is not possible to recover without a powercycle (and investigation why fault occured)
|
||||||
}
|
}
|
||||||
|
|
||||||
// After that, check if we are OK to start turning on the battery
|
// After that, check if we are OK to start turning on the battery
|
||||||
if (contactorStatus == DISCONNECTED) {
|
if (contactorStatus == DISCONNECTED) {
|
||||||
digitalWrite(PRECHARGE_PIN, LOW);
|
set(PRECHARGE_PIN, OFF);
|
||||||
#ifndef PWM_CONTACTOR_CONTROL
|
set(NEGATIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY);
|
||||||
digitalWrite(NEGATIVE_CONTACTOR_PIN, LOW);
|
set(POSITIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY);
|
||||||
digitalWrite(POSITIVE_CONTACTOR_PIN, LOW);
|
|
||||||
#else
|
|
||||||
ledcWrite(NEGATIVE_CONTACTOR_PIN, PWM_Off_Duty);
|
|
||||||
ledcWrite(POSITIVE_CONTACTOR_PIN, PWM_Off_Duty);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (datalayer.system.status.battery_allows_contactor_closing &&
|
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;
|
contactorStatus = PRECHARGE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// In case the inverter requests contactors to open, set the state accordingly
|
// In case the inverter requests contactors to open, set the state accordingly
|
||||||
if (contactorStatus == COMPLETED) {
|
if (contactorStatus == COMPLETED) {
|
||||||
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;
|
contactorStatus = DISCONNECTED;
|
||||||
|
}
|
||||||
// Skip running the state machine below if it has already completed
|
// Skip running the state machine below if it has already completed
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -772,18 +795,14 @@ void handle_contactors() {
|
||||||
// Handle actual state machine. This first turns on Precharge, then Negative, then Positive, and finally turns OFF precharge
|
// Handle actual state machine. This first turns on Precharge, then Negative, then Positive, and finally turns OFF precharge
|
||||||
switch (contactorStatus) {
|
switch (contactorStatus) {
|
||||||
case PRECHARGE:
|
case PRECHARGE:
|
||||||
digitalWrite(PRECHARGE_PIN, HIGH);
|
set(PRECHARGE_PIN, ON);
|
||||||
prechargeStartTime = currentTime;
|
prechargeStartTime = currentTime;
|
||||||
contactorStatus = NEGATIVE;
|
contactorStatus = NEGATIVE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NEGATIVE:
|
case NEGATIVE:
|
||||||
if (currentTime - prechargeStartTime >= PRECHARGE_TIME_MS) {
|
if (currentTime - prechargeStartTime >= PRECHARGE_TIME_MS) {
|
||||||
#ifndef PWM_CONTACTOR_CONTROL
|
set(NEGATIVE_CONTACTOR_PIN, ON, PWM_ON_DUTY);
|
||||||
digitalWrite(NEGATIVE_CONTACTOR_PIN, HIGH);
|
|
||||||
#else
|
|
||||||
ledcWrite(NEGATIVE_CONTACTOR_PIN, PWM_On_Duty);
|
|
||||||
#endif
|
|
||||||
negativeStartTime = currentTime;
|
negativeStartTime = currentTime;
|
||||||
contactorStatus = POSITIVE;
|
contactorStatus = POSITIVE;
|
||||||
}
|
}
|
||||||
|
@ -791,24 +810,18 @@ void handle_contactors() {
|
||||||
|
|
||||||
case POSITIVE:
|
case POSITIVE:
|
||||||
if (currentTime - negativeStartTime >= NEGATIVE_CONTACTOR_TIME_MS) {
|
if (currentTime - negativeStartTime >= NEGATIVE_CONTACTOR_TIME_MS) {
|
||||||
#ifndef PWM_CONTACTOR_CONTROL
|
set(POSITIVE_CONTACTOR_PIN, ON, PWM_ON_DUTY);
|
||||||
digitalWrite(POSITIVE_CONTACTOR_PIN, HIGH);
|
|
||||||
#else
|
|
||||||
ledcWrite(POSITIVE_CONTACTOR_PIN, PWM_On_Duty);
|
|
||||||
#endif
|
|
||||||
contactorStatus = PRECHARGE_OFF;
|
contactorStatus = PRECHARGE_OFF;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PRECHARGE_OFF:
|
case PRECHARGE_OFF:
|
||||||
if (currentTime - negativeStartTime >= POSITIVE_CONTACTOR_TIME_MS) {
|
if (currentTime - negativeStartTime >= POSITIVE_CONTACTOR_TIME_MS) {
|
||||||
digitalWrite(PRECHARGE_PIN, LOW);
|
set(PRECHARGE_PIN, OFF);
|
||||||
#ifdef PWM_CONTACTOR_CONTROL
|
set(NEGATIVE_CONTACTOR_PIN, ON, PWM_HOLD_DUTY);
|
||||||
ledcWrite(NEGATIVE_CONTACTOR_PIN, PWM_Hold_Duty);
|
set(POSITIVE_CONTACTOR_PIN, ON, PWM_HOLD_DUTY);
|
||||||
ledcWrite(POSITIVE_CONTACTOR_PIN, PWM_Hold_Duty);
|
|
||||||
#endif
|
|
||||||
contactorStatus = COMPLETED;
|
contactorStatus = COMPLETED;
|
||||||
datalayer.system.status.contactor_control_closed = true;
|
datalayer.system.status.contactors_engaged = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -817,6 +830,20 @@ void handle_contactors() {
|
||||||
#endif // CONTACTOR_CONTROL
|
#endif // CONTACTOR_CONTROL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY
|
||||||
|
void handle_contactors_battery2() {
|
||||||
|
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;
|
||||||
|
} 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() {
|
void update_calculated_values() {
|
||||||
/* Calculate allowed charge/discharge currents*/
|
/* Calculate allowed charge/discharge currents*/
|
||||||
if (datalayer.battery.status.voltage_dV > 10) {
|
if (datalayer.battery.status.voltage_dV > 10) {
|
||||||
|
|
|
@ -55,12 +55,16 @@
|
||||||
//#define HW_STARK
|
//#define HW_STARK
|
||||||
//#define HW_3LB
|
//#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 */
|
/* 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_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 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 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 DUAL_CAN //Enable this line to activate an isolated secondary CAN Bus using add-on MCP2515 chip (Needed for some inverters / double battery)
|
//#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 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
|
//#define CAN_FD //Enable this line to activate an isolated secondary CAN-FD bus using add-on MCP2518FD chip / Native CANFD on Stark board
|
||||||
|
|
|
@ -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_Wh_Remaining = 0; //Amount of energy in battery, in Wh
|
||||||
static uint16_t battery2_GIDS = 273; //Startup in 24kWh mode
|
static uint16_t battery2_GIDS = 273; //Startup in 24kWh mode
|
||||||
static uint16_t battery2_MAX = 0;
|
static uint16_t battery2_MAX = 0;
|
||||||
static uint16_t battery2_Max_GIDS = 273; //Startup in 24kWh mode
|
static uint16_t battery2_Max_GIDS = 273; //Startup in 24kWh mode
|
||||||
static uint16_t battery2_StateOfHealth = 99; //State of health %
|
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 = 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_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_MAX = 6; //-40 to 86*C
|
||||||
static int16_t battery2_HistData_Temperature_MIN = 5; //-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
|
static int16_t battery2_AverageTemperature = 6; //Only available on ZE0, in celcius, -40 to +55
|
||||||
|
@ -514,11 +514,7 @@ void receive_can_battery2(CAN_frame rx_frame) {
|
||||||
battery2_Relay_Cut_Request = ((rx_frame.data.u8[1] & 0x18) >> 3);
|
battery2_Relay_Cut_Request = ((rx_frame.data.u8[1] & 0x18) >> 3);
|
||||||
battery2_Failsafe_Status = (rx_frame.data.u8[1] & 0x07);
|
battery2_Failsafe_Status = (rx_frame.data.u8[1] & 0x07);
|
||||||
battery2_MainRelayOn_flag = (bool)((rx_frame.data.u8[3] & 0x20) >> 5);
|
battery2_MainRelayOn_flag = (bool)((rx_frame.data.u8[3] & 0x20) >> 5);
|
||||||
if (battery2_MainRelayOn_flag) {
|
//battery2_allows_contactor_closing written by check_interconnect_available();
|
||||||
datalayer.system.status.battery2_allows_contactor_closing = true;
|
|
||||||
} else {
|
|
||||||
datalayer.system.status.battery2_allows_contactor_closing = false;
|
|
||||||
}
|
|
||||||
battery2_Full_CHARGE_flag = (bool)((rx_frame.data.u8[3] & 0x10) >> 4);
|
battery2_Full_CHARGE_flag = (bool)((rx_frame.data.u8[3] & 0x10) >> 4);
|
||||||
battery2_Interlock = (bool)((rx_frame.data.u8[3] & 0x08) >> 3);
|
battery2_Interlock = (bool)((rx_frame.data.u8[3] & 0x08) >> 3);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -192,7 +192,9 @@ typedef struct {
|
||||||
bool inverter_allows_contactor_closing = true;
|
bool inverter_allows_contactor_closing = true;
|
||||||
#ifdef CONTACTOR_CONTROL
|
#ifdef CONTACTOR_CONTROL
|
||||||
/** True if the contactor controlled by battery-emulator is closed */
|
/** 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. Determined by check_interconnect_available(); if voltage is OK */
|
||||||
|
bool contactors_battery2_engaged = false;
|
||||||
#endif
|
#endif
|
||||||
} DATALAYER_SYSTEM_STATUS_TYPE;
|
} DATALAYER_SYSTEM_STATUS_TYPE;
|
||||||
|
|
||||||
|
|
|
@ -52,9 +52,9 @@
|
||||||
#define NEGATIVE_CONTACTOR_PIN 33
|
#define NEGATIVE_CONTACTOR_PIN 33
|
||||||
#define PRECHARGE_PIN 25
|
#define PRECHARGE_PIN 25
|
||||||
|
|
||||||
#define 2ND_POSITIVE_CONTACTOR_PIN 13
|
#define SECOND_POSITIVE_CONTACTOR_PIN 13
|
||||||
#define 2ND_NEGATIVE_CONTACTOR_PIN 16
|
#define SECOND_NEGATIVE_CONTACTOR_PIN 16
|
||||||
#define 2ND_PRECHARGE_PIN 18
|
#define SECOND_PRECHARGE_PIN 18
|
||||||
|
|
||||||
// SMA CAN contactor pins
|
// SMA CAN contactor pins
|
||||||
#define INVERTER_CONTACTOR_ENABLE_PIN 36
|
#define INVERTER_CONTACTOR_ENABLE_PIN 36
|
||||||
|
|
|
@ -642,7 +642,7 @@ String processor(const String& var) {
|
||||||
|
|
||||||
#ifdef CONTACTOR_CONTROL
|
#ifdef CONTACTOR_CONTROL
|
||||||
content += "<h4>Contactors controlled by Battery-Emulator: ";
|
content += "<h4>Contactors controlled by Battery-Emulator: ";
|
||||||
if (datalayer.system.status.contactor_control_closed) {
|
if (datalayer.system.status.contactors_engaged) {
|
||||||
content += "<span style='color: green;'>ON</span>";
|
content += "<span style='color: green;'>ON</span>";
|
||||||
} else {
|
} else {
|
||||||
content += "<span style='color: red;'>OFF</span>";
|
content += "<span style='color: red;'>OFF</span>";
|
||||||
|
@ -773,34 +773,35 @@ String processor(const String& var) {
|
||||||
|
|
||||||
#ifdef CONTACTOR_CONTROL
|
#ifdef CONTACTOR_CONTROL
|
||||||
content += "<h4>Contactors controlled by Battery-Emulator: ";
|
content += "<h4>Contactors controlled by Battery-Emulator: ";
|
||||||
if (datalayer.system.status.contactor_control_closed) {
|
if (datalayer.system.status.contactors_battery2_engaged) {
|
||||||
content += "<span style='color: green;'>ON</span>";
|
content += "<span style='color: green;'>ON</span>";
|
||||||
} else {
|
} else {
|
||||||
content += "<span style='color: red;'>OFF</span>";
|
content += "<span style='color: red;'>OFF</span>";
|
||||||
}
|
}
|
||||||
content += "</h4>";
|
content += "</h4>";
|
||||||
|
#ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY
|
||||||
content += "<h4>Pre Charge: ";
|
content += "<h4>Pre Charge: ";
|
||||||
if (digitalRead(PRECHARGE_PIN) == HIGH) {
|
if (digitalRead(SECOND_PRECHARGE_PIN) == HIGH) {
|
||||||
content += "<span style='color: green;'>✓</span>";
|
content += "<span style='color: green;'>✓</span>";
|
||||||
} else {
|
} else {
|
||||||
content += "<span style='color: red;'>✕</span>";
|
content += "<span style='color: red;'>✕</span>";
|
||||||
}
|
}
|
||||||
content += " Cont. Neg.: ";
|
content += " Cont. Neg.: ";
|
||||||
if (digitalRead(NEGATIVE_CONTACTOR_PIN) == HIGH) {
|
if (digitalRead(SECOND_NEGATIVE_CONTACTOR_PIN) == HIGH) {
|
||||||
content += "<span style='color: green;'>✓</span>";
|
content += "<span style='color: green;'>✓</span>";
|
||||||
} else {
|
} else {
|
||||||
content += "<span style='color: red;'>✕</span>";
|
content += "<span style='color: red;'>✕</span>";
|
||||||
}
|
}
|
||||||
|
|
||||||
content += " Cont. Pos.: ";
|
content += " Cont. Pos.: ";
|
||||||
if (digitalRead(POSITIVE_CONTACTOR_PIN) == HIGH) {
|
if (digitalRead(SECOND_POSITIVE_CONTACTOR_PIN) == HIGH) {
|
||||||
content += "<span style='color: green;'>✓</span>";
|
content += "<span style='color: green;'>✓</span>";
|
||||||
} else {
|
} else {
|
||||||
content += "<span style='color: red;'>✕</span>";
|
content += "<span style='color: red;'>✕</span>";
|
||||||
}
|
}
|
||||||
content += "</h4>";
|
content += "</h4>";
|
||||||
#endif
|
#endif // CONTACTOR_CONTROL_DOUBLE_BATTERY
|
||||||
|
#endif // CONTACTOR_CONTROL
|
||||||
|
|
||||||
content += "</div>";
|
content += "</div>";
|
||||||
content += "</div>";
|
content += "</div>";
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue