mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-03 09:49:32 +02:00
Initial hal changes
This commit is contained in:
parent
d6cbd1bcdd
commit
12b402f994
77 changed files with 924 additions and 672 deletions
|
@ -70,6 +70,12 @@ Logging logging;
|
|||
|
||||
// Initialization
|
||||
void setup() {
|
||||
init_hal();
|
||||
|
||||
if (!led_init()) {
|
||||
return;
|
||||
}
|
||||
|
||||
init_serial();
|
||||
|
||||
// We print this after setting up serial, such that is also printed to serial with DEBUG_VIA_USB set.
|
||||
|
@ -89,23 +95,32 @@ void setup() {
|
|||
&logging_loop_task, WIFI_CORE);
|
||||
#endif
|
||||
|
||||
init_CAN();
|
||||
if (!init_CAN()) {
|
||||
return;
|
||||
}
|
||||
|
||||
init_contactors();
|
||||
if (!init_contactors()) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef PRECHARGE_CONTROL
|
||||
init_precharge_control();
|
||||
#endif // PRECHARGE_CONTROL
|
||||
if (!init_precharge_control()) {
|
||||
return;
|
||||
}
|
||||
|
||||
setup_charger();
|
||||
setup_inverter();
|
||||
|
||||
if (!setup_inverter()) {
|
||||
return;
|
||||
}
|
||||
setup_battery();
|
||||
|
||||
init_rs485();
|
||||
if (!init_rs485()) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef EQUIPMENT_STOP_BUTTON
|
||||
init_equipment_stop_button();
|
||||
#endif
|
||||
if (!init_equipment_stop_button()) {
|
||||
return;
|
||||
}
|
||||
|
||||
setup_can_shunt();
|
||||
// BOOT button at runtime is used as an input for various things
|
||||
|
@ -115,9 +130,9 @@ void setup() {
|
|||
|
||||
// Initialize Task Watchdog for subscribed tasks
|
||||
esp_task_wdt_config_t wdt_config = {
|
||||
.timeout_ms = INTERVAL_5_S, // If task hangs for longer than this, reboot
|
||||
.idle_core_mask = (1 << CORE_FUNCTION_CORE) | (1 << WIFI_CORE), // Watch both cores
|
||||
.trigger_panic = true // Enable panic reset on timeout
|
||||
.timeout_ms = INTERVAL_5_S, // If task hangs for longer than this, reboot
|
||||
.idle_core_mask = (1 << esp32hal->CORE_FUNCTION_CORE()) | (1 << esp32hal->WIFI_CORE()), // Watch both cores
|
||||
.trigger_panic = true // Enable panic reset on timeout
|
||||
};
|
||||
|
||||
// Start tasks
|
||||
|
@ -126,11 +141,11 @@ void setup() {
|
|||
init_mqtt();
|
||||
|
||||
xTaskCreatePinnedToCore((TaskFunction_t)&mqtt_loop, "mqtt_loop", 4096, NULL, TASK_MQTT_PRIO, &mqtt_loop_task,
|
||||
WIFI_CORE);
|
||||
esp32hal->WIFI_CORE());
|
||||
#endif
|
||||
|
||||
xTaskCreatePinnedToCore((TaskFunction_t)&core_loop, "core_loop", 4096, NULL, TASK_CORE_PRIO, &main_loop_task,
|
||||
CORE_FUNCTION_CORE);
|
||||
esp32hal->CORE_FUNCTION_CORE());
|
||||
#ifdef PERIODIC_BMS_RESET_AT
|
||||
bmsResetTimeOffset = getTimeOffsetfromNowUntil(PERIODIC_BMS_RESET_AT);
|
||||
if (bmsResetTimeOffset == 0) {
|
||||
|
@ -213,15 +228,13 @@ void core_loop(void*) {
|
|||
esp_task_wdt_add(NULL); // Register this task with WDT
|
||||
TickType_t xLastWakeTime = xTaskGetTickCount();
|
||||
const TickType_t xFrequency = pdMS_TO_TICKS(1); // Convert 1ms to ticks
|
||||
led_init();
|
||||
|
||||
while (true) {
|
||||
|
||||
START_TIME_MEASUREMENT(all);
|
||||
START_TIME_MEASUREMENT(comm);
|
||||
#ifdef EQUIPMENT_STOP_BUTTON
|
||||
|
||||
monitor_equipment_stop_button();
|
||||
#endif
|
||||
|
||||
// Input, Runs as fast as possible
|
||||
receive_can(); // Receive CAN messages
|
||||
|
@ -237,7 +250,8 @@ void core_loop(void*) {
|
|||
// Process
|
||||
currentMillis = millis();
|
||||
if (currentMillis - previousMillis10ms >= INTERVAL_10_MS) {
|
||||
if ((currentMillis - previousMillis10ms >= INTERVAL_10_MS_DELAYED) && (currentMillis > BOOTUP_TIME)) {
|
||||
if ((currentMillis - previousMillis10ms >= INTERVAL_10_MS_DELAYED) &&
|
||||
(milliseconds(currentMillis) > esp32hal->BOOTUP_TIME())) {
|
||||
set_event(EVENT_TASK_OVERRUN, (currentMillis - previousMillis10ms));
|
||||
}
|
||||
previousMillis10ms = currentMillis;
|
||||
|
|
|
@ -52,13 +52,6 @@ const char* ha_device_id =
|
|||
#endif // MQTT_MANUAL_TOPIC_OBJECT_NAME
|
||||
#endif // USE_MQTT
|
||||
|
||||
#ifdef EQUIPMENT_STOP_BUTTON
|
||||
// Equipment stop button behavior. Use NC button for safety reasons.
|
||||
//LATCHING_SWITCH - Normally closed (NC), latching switch. When pressed it activates e-stop
|
||||
//MOMENTARY_SWITCH - Short press to activate e-stop, long 15s press to deactivate. E-stop is persistent between reboots
|
||||
volatile STOP_BUTTON_BEHAVIOR equipment_stop_behavior = LATCHING_SWITCH;
|
||||
#endif
|
||||
|
||||
/* Charger settings (Optional, when using generator charging) */
|
||||
volatile float CHARGER_SET_HV = 384; // Reasonably appropriate 4.0v per cell charging of a 96s pack
|
||||
volatile float CHARGER_MAX_HV = 420; // Max permissible output (VDC) of charger
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
/* There are also some options for battery limits and extra functionality */
|
||||
/* To edit battery specific limits, see also the USER_SETTINGS.cpp file*/
|
||||
|
||||
#define COMMON_IMAGE
|
||||
|
||||
/* Select battery used */
|
||||
//#define BMW_I3_BATTERY
|
||||
//#define BMW_IX_BATTERY
|
||||
|
@ -70,18 +72,18 @@
|
|||
//#define SUNGROW_CAN //Enable this line to emulate a "Sungrow SBR064" over CAN bus
|
||||
|
||||
/* Select hardware used for Battery-Emulator */
|
||||
//#define HW_LILYGO
|
||||
#define HW_LILYGO
|
||||
//#define HW_STARK
|
||||
//#define HW_3LB
|
||||
//#define HW_DEVKIT
|
||||
|
||||
/* Contactor settings. If you have a battery that does not activate contactors via CAN, configure this section */
|
||||
#define PRECHARGE_TIME_MS 500 //Precharge time in milliseconds. Modify to suit your inverter (See wiki for more info)
|
||||
//#define CONTACTOR_CONTROL //Enable this line to have the emulator handle automatic precharge/contactor+/contactor- closing sequence (See wiki for pins)
|
||||
#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!
|
||||
//#define PERIODIC_BMS_RESET //Enable to have the emulator powercycle the connected battery every 24hours via GPIO. Useful for some batteries like Nissan LEAF
|
||||
#define PERIODIC_BMS_RESET //Enable to have the emulator powercycle the connected battery every 24hours via GPIO. Useful for some batteries like Nissan LEAF
|
||||
//#define REMOTE_BMS_RESET //Enable to allow the emulator to remotely trigger a powercycle of the battery via MQTT. Useful for some batteries like Nissan LEAF
|
||||
// PERIODIC_BMS_RESET_AT Uses NTP server, internet required. In 24 Hour format WITHOUT leading 0. e.g 0230 should be 230. Time Zone is set in USER_SETTINGS.cpp
|
||||
//#define PERIODIC_BMS_RESET_AT 525
|
||||
|
@ -101,6 +103,7 @@
|
|||
//#define EQUIPMENT_STOP_BUTTON // Enable this to allow an equipment stop button connected to the Battery-Emulator to disengage the battery
|
||||
//#define LFP_CHEMISTRY //Tesla specific setting, enable this line to startup in LFP mode
|
||||
//#define INTERLOCK_REQUIRED //Nissan LEAF specific setting, if enabled requires both high voltage conenctors to be seated before starting
|
||||
|
||||
//#define LOG_TO_SD //Enable this line to log diagnostic data to SD card (WARNING, raises CPU load, do not use for production)
|
||||
//#define LOG_CAN_TO_SD //Enable this line to log incoming/outgoing CAN & CAN-FD messages to SD card (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)
|
||||
|
@ -193,9 +196,16 @@ extern volatile float CHARGER_END_A;
|
|||
|
||||
extern volatile unsigned long long bmsResetTimeOffset;
|
||||
|
||||
#include "src/communication/equipmentstopbutton/comm_equipmentstopbutton.h"
|
||||
|
||||
// Equipment stop button behavior. Use NC button for safety reasons.
|
||||
//LATCHING_SWITCH - Normally closed (NC), latching switch. When pressed it activates e-stop
|
||||
//MOMENTARY_SWITCH - Short press to activate e-stop, long 15s press to deactivate. E-stop is persistent between reboots
|
||||
|
||||
#ifdef EQUIPMENT_STOP_BUTTON
|
||||
typedef enum { LATCHING_SWITCH = 0, MOMENTARY_SWITCH = 1 } STOP_BUTTON_BEHAVIOR;
|
||||
extern volatile STOP_BUTTON_BEHAVIOR equipment_stop_behavior;
|
||||
const STOP_BUTTON_BEHAVIOR stop_button_default_behavior = STOP_BUTTON_BEHAVIOR::MOMENTARY_SWITCH;
|
||||
#else
|
||||
const STOP_BUTTON_BEHAVIOR stop_button_default_behavior = STOP_BUTTON_BEHAVIOR::NOT_CONNECTED;
|
||||
#endif
|
||||
|
||||
#ifdef WIFICONFIG
|
||||
|
|
|
@ -35,10 +35,8 @@ extern const char* name_for_battery_type(BatteryType type) {
|
|||
return BydAttoBattery::Name;
|
||||
case BatteryType::CellPowerBms:
|
||||
return CellPowerBms::Name;
|
||||
#ifdef CHADEMO_PIN_2 // Only support chademo for certain platforms
|
||||
case BatteryType::Chademo:
|
||||
return ChademoBattery::Name;
|
||||
#endif
|
||||
case BatteryType::CmfaEv:
|
||||
return CmfaEvBattery::Name;
|
||||
case BatteryType::Foxess:
|
||||
|
@ -124,10 +122,8 @@ Battery* create_battery(BatteryType type) {
|
|||
return new BydAttoBattery();
|
||||
case BatteryType::CellPowerBms:
|
||||
return new CellPowerBms();
|
||||
#ifdef CHADEMO_PIN_2 // Only support chademo for certain platforms
|
||||
case BatteryType::Chademo:
|
||||
return new ChademoBattery();
|
||||
#endif
|
||||
case BatteryType::CmfaEv:
|
||||
return new CmfaEvBattery();
|
||||
case BatteryType::Foxess:
|
||||
|
@ -210,7 +206,7 @@ void setup_battery() {
|
|||
break;
|
||||
case BatteryType::BmwI3:
|
||||
battery2 = new BmwI3Battery(&datalayer.battery2, &datalayer.system.status.battery2_allowed_contactor_closing,
|
||||
can_config.battery_double, WUP_PIN2);
|
||||
can_config.battery_double, esp32hal->WUP_PIN2());
|
||||
break;
|
||||
case BatteryType::KiaHyundai64:
|
||||
battery2 = new KiaHyundai64Battery(&datalayer.battery2, &datalayer_extended.KiaHyundai64_2,
|
||||
|
|
|
@ -508,6 +508,10 @@ void BmwI3Battery::transmit_can(unsigned long currentMillis) {
|
|||
}
|
||||
|
||||
void BmwI3Battery::setup(void) { // Performs one time setup at startup
|
||||
if (!esp32hal->alloc_pins(Name, wakeup_pin)) {
|
||||
return;
|
||||
}
|
||||
|
||||
strncpy(datalayer.system.info.battery_protocol, Name, 63);
|
||||
datalayer.system.info.battery_protocol[63] = '\0';
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ class BmwI3Battery : public CanBattery {
|
|||
public:
|
||||
// Use this constructor for the second battery.
|
||||
BmwI3Battery(DATALAYER_BATTERY_TYPE* datalayer_ptr, bool* contactor_closing_allowed_ptr, CAN_Interface targetCan,
|
||||
int wakeup)
|
||||
gpio_num_t wakeup)
|
||||
: CanBattery(targetCan) {
|
||||
datalayer_battery = datalayer_ptr;
|
||||
contactor_closing_allowed = contactor_closing_allowed_ptr;
|
||||
|
@ -32,7 +32,7 @@ class BmwI3Battery : public CanBattery {
|
|||
datalayer_battery = &datalayer.battery;
|
||||
allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing;
|
||||
contactor_closing_allowed = nullptr;
|
||||
wakeup_pin = WUP_PIN1;
|
||||
wakeup_pin = esp32hal->WUP_PIN1();
|
||||
}
|
||||
|
||||
virtual void setup(void);
|
||||
|
@ -70,7 +70,7 @@ class BmwI3Battery : public CanBattery {
|
|||
// If not null, this battery listens to this boolean to determine whether contactor closing is allowed
|
||||
bool* contactor_closing_allowed;
|
||||
|
||||
int wakeup_pin;
|
||||
gpio_num_t wakeup_pin;
|
||||
|
||||
unsigned long previousMillis20 = 0; // will store last time a 20ms CAN Message was send
|
||||
unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
#include "../include.h"
|
||||
#include "CHADEMO-SHUNTS.h"
|
||||
|
||||
#ifdef CHADEMO_PIN_2 // Only support chademo for certain platforms
|
||||
|
||||
/* CHADEMO handling runs at 6.25 times the rate of most other code, so, rather than the
|
||||
* default value of 12 (for 12 iterations of the 5s value update loop) * 5 for a 60s timeout,
|
||||
* instead use 75 for 75*0.8s = 60s
|
||||
|
@ -644,10 +642,11 @@ void ChademoBattery::transmit_can(unsigned long currentMillis) {
|
|||
*/
|
||||
void ChademoBattery::handle_chademo_sequence() {
|
||||
|
||||
precharge_low = digitalRead(PRECHARGE_PIN) == LOW;
|
||||
positive_high = digitalRead(POSITIVE_CONTACTOR_PIN) == HIGH;
|
||||
precharge_low = digitalRead(precharge) == LOW;
|
||||
positive_high = digitalRead(positive_contactor) == HIGH;
|
||||
contactors_ready = precharge_low && positive_high;
|
||||
vehicle_permission = digitalRead(CHADEMO_PIN_4);
|
||||
|
||||
vehicle_permission = digitalRead(pin4);
|
||||
|
||||
/* ------------------- State override conditions checks ------------------- */
|
||||
/* ------------------------------------------------------------------------------ */
|
||||
|
@ -670,8 +669,8 @@ void ChademoBattery::handle_chademo_sequence() {
|
|||
switch (CHADEMO_Status) {
|
||||
case CHADEMO_IDLE:
|
||||
/* this is where we can unlock connector */
|
||||
digitalWrite(CHADEMO_LOCK, LOW);
|
||||
plug_inserted = digitalRead(CHADEMO_PIN_7);
|
||||
digitalWrite(pin_lock, LOW);
|
||||
plug_inserted = digitalRead(pin7);
|
||||
|
||||
if (!plug_inserted) {
|
||||
#ifdef DEBUG_LOG
|
||||
|
@ -698,7 +697,7 @@ void ChademoBattery::handle_chademo_sequence() {
|
|||
/* If connection is detectable, jumpstart handshake by
|
||||
* indicate that the EVSE is ready to begin
|
||||
*/
|
||||
digitalWrite(CHADEMO_PIN_2, HIGH);
|
||||
digitalWrite(pin2, HIGH);
|
||||
|
||||
/* State change to initializing. We will re-enter the handler upon receipt of CAN */
|
||||
CHADEMO_Status = CHADEMO_INIT;
|
||||
|
@ -744,7 +743,7 @@ void ChademoBattery::handle_chademo_sequence() {
|
|||
// that pin 4 (j) reads high
|
||||
if (vehicle_permission) {
|
||||
//lock connector here
|
||||
digitalWrite(CHADEMO_LOCK, HIGH);
|
||||
digitalWrite(pin_lock, HIGH);
|
||||
|
||||
//TODO spec requires test to validate solenoid has indeed engaged.
|
||||
// example uses a comparator/current consumption check around solenoid
|
||||
|
@ -774,7 +773,7 @@ void ChademoBattery::handle_chademo_sequence() {
|
|||
if (x102_chg_session.s.status.StatusVehicleChargingEnabled) {
|
||||
if (get_measured_voltage() < 20) {
|
||||
|
||||
digitalWrite(CHADEMO_PIN_10, HIGH);
|
||||
digitalWrite(pin10, HIGH);
|
||||
evse_permission = true;
|
||||
} else {
|
||||
logging.println("Insulation check measures > 20v ");
|
||||
|
@ -892,8 +891,8 @@ void ChademoBattery::handle_chademo_sequence() {
|
|||
*/
|
||||
if (get_measured_current() <= 5 && get_measured_voltage() <= 10) {
|
||||
/* welding detection ideally here */
|
||||
digitalWrite(CHADEMO_PIN_10, LOW);
|
||||
digitalWrite(CHADEMO_PIN_2, LOW);
|
||||
digitalWrite(pin10, LOW);
|
||||
digitalWrite(pin2, LOW);
|
||||
CHADEMO_Status = CHADEMO_IDLE;
|
||||
}
|
||||
|
||||
|
@ -910,8 +909,8 @@ void ChademoBattery::handle_chademo_sequence() {
|
|||
#ifdef DEBUG_LOG
|
||||
logging.println("CHADEMO fault encountered, tearing down to make safe");
|
||||
#endif
|
||||
digitalWrite(CHADEMO_PIN_10, LOW);
|
||||
digitalWrite(CHADEMO_PIN_2, LOW);
|
||||
digitalWrite(pin10, LOW);
|
||||
digitalWrite(pin2, LOW);
|
||||
evse_permission = false;
|
||||
vehicle_permission = false;
|
||||
x209_sent = false;
|
||||
|
@ -931,14 +930,18 @@ void ChademoBattery::handle_chademo_sequence() {
|
|||
|
||||
void ChademoBattery::setup(void) { // Performs one time setup at startup
|
||||
|
||||
pinMode(CHADEMO_PIN_2, OUTPUT);
|
||||
digitalWrite(CHADEMO_PIN_2, LOW);
|
||||
pinMode(CHADEMO_PIN_10, OUTPUT);
|
||||
digitalWrite(CHADEMO_PIN_10, LOW);
|
||||
pinMode(CHADEMO_LOCK, OUTPUT);
|
||||
digitalWrite(CHADEMO_LOCK, LOW);
|
||||
pinMode(CHADEMO_PIN_4, INPUT);
|
||||
pinMode(CHADEMO_PIN_7, INPUT);
|
||||
if (!esp32hal->alloc_pins("CHADEMO", pin2, pin10, pin4, pin7, pin_lock)) {
|
||||
return;
|
||||
}
|
||||
|
||||
pinMode(pin2, OUTPUT);
|
||||
digitalWrite(pin2, LOW);
|
||||
pinMode(pin10, OUTPUT);
|
||||
digitalWrite(pin10, LOW);
|
||||
pinMode(pin_lock, OUTPUT);
|
||||
digitalWrite(pin_lock, LOW);
|
||||
pinMode(pin4, INPUT);
|
||||
pinMode(pin7, INPUT);
|
||||
|
||||
strncpy(datalayer.system.info.battery_protocol, Name, 63);
|
||||
datalayer.system.info.battery_protocol[63] = '\0';
|
||||
|
@ -989,5 +992,3 @@ void ChademoBattery::setup(void) { // Performs one time setup at startup
|
|||
|
||||
setupMillis = millis();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -13,6 +13,18 @@
|
|||
|
||||
class ChademoBattery : public CanBattery {
|
||||
public:
|
||||
ChademoBattery() {
|
||||
pin2 = esp32hal->CHADEMO_PIN_2();
|
||||
pin10 = esp32hal->CHADEMO_PIN_10();
|
||||
pin4 = esp32hal->CHADEMO_PIN_4();
|
||||
pin7 = esp32hal->CHADEMO_PIN_7();
|
||||
pin_lock = esp32hal->CHADEMO_LOCK();
|
||||
|
||||
// Assuming these are initialized by contactor control module.
|
||||
precharge = esp32hal->PRECHARGE_PIN();
|
||||
positive_contactor = esp32hal->POSITIVE_CONTACTOR_PIN();
|
||||
}
|
||||
|
||||
virtual void setup(void);
|
||||
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
|
||||
virtual void update_values();
|
||||
|
@ -21,6 +33,8 @@ class ChademoBattery : public CanBattery {
|
|||
static constexpr char* Name = "Chademo V2X mode";
|
||||
|
||||
private:
|
||||
gpio_num_t pin2, pin10, pin4, pin7, pin_lock, precharge, positive_contactor;
|
||||
|
||||
void process_vehicle_charging_minimums(CAN_frame rx_frame);
|
||||
void process_vehicle_charging_maximums(CAN_frame rx_frame);
|
||||
void process_vehicle_charging_session(CAN_frame rx_frame);
|
||||
|
|
|
@ -70,7 +70,14 @@ void DalyBms::setup(void) { // Performs one time setup at startup
|
|||
datalayer.battery.info.total_capacity_Wh = BATTERY_WH_MAX;
|
||||
datalayer.system.status.battery_allows_contactor_closing = true;
|
||||
|
||||
Serial2.begin(baud_rate(), SERIAL_8N1, RS485_RX_PIN, RS485_TX_PIN);
|
||||
auto rx_pin = esp32hal->RS485_RX_PIN();
|
||||
auto tx_pin = esp32hal->RS485_TX_PIN();
|
||||
|
||||
if (!esp32hal->alloc_pins(Name, rx_pin, tx_pin)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Serial2.begin(baud_rate(), SERIAL_8N1, rx_pin, tx_pin);
|
||||
}
|
||||
|
||||
uint8_t calculate_checksum(uint8_t buff[12]) {
|
||||
|
|
|
@ -8,7 +8,4 @@ class CanReceiver {
|
|||
virtual void receive_can_frame(CAN_frame* rx_frame) = 0;
|
||||
};
|
||||
|
||||
// Register a receiver object for a given CAN interface
|
||||
void register_can_receiver(CanReceiver* receiver, CAN_Interface interface);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#include "comm_can.h"
|
||||
#include <map>
|
||||
#include "../../include.h"
|
||||
#include "../../lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h"
|
||||
#include "../../lib/pierremolinaro-acan2515/ACAN2515.h"
|
||||
#include "src/devboard/sdcard/sdcard.h"
|
||||
|
||||
// Parameters
|
||||
|
@ -11,100 +13,159 @@ volatile bool send_ok_2515 = 0;
|
|||
volatile bool send_ok_2518 = 0;
|
||||
static unsigned long previousMillis10 = 0;
|
||||
|
||||
#ifdef USE_CANFD_INTERFACE_AS_CLASSIC_CAN
|
||||
const bool use_canfd_as_can_default = true;
|
||||
#else
|
||||
const bool use_canfd_as_can_default = false;
|
||||
#endif
|
||||
bool use_canfd_as_can = use_canfd_as_can_default;
|
||||
|
||||
void map_can_frame_to_variable(CAN_frame* rx_frame, CAN_Interface interface);
|
||||
|
||||
#ifdef CAN_ADDON
|
||||
static std::multimap<CAN_Interface, CanReceiver*> can_receivers;
|
||||
|
||||
void register_can_receiver(CanReceiver* receiver, CAN_Interface interface) {
|
||||
can_receivers.insert({interface, receiver});
|
||||
}
|
||||
|
||||
static const uint32_t QUARTZ_FREQUENCY = CRYSTAL_FREQUENCY_MHZ * 1000000UL; //MHZ configured in USER_SETTINGS.h
|
||||
SPIClass SPI2515;
|
||||
ACAN2515 can(MCP2515_CS, SPI2515, MCP2515_INT);
|
||||
|
||||
ACAN2515* can2515;
|
||||
|
||||
//ACAN2515 can(MCP2515_CS, SPI2515, MCP2515_INT);
|
||||
static ACAN2515_Buffer16 gBuffer;
|
||||
#endif //CAN_ADDON
|
||||
#ifdef CANFD_ADDON
|
||||
|
||||
SPIClass SPI2517;
|
||||
ACAN2517FD canfd(MCP2517_CS, SPI2517, MCP2517_INT);
|
||||
#endif //CANFD_ADDON
|
||||
//ACAN2517FD canfd(MCP2517_CS, SPI2517, MCP2517_INT);
|
||||
ACAN2517FD* canfd;
|
||||
|
||||
// Initialization functions
|
||||
|
||||
void init_CAN() {
|
||||
// CAN pins
|
||||
#ifdef CAN_SE_PIN
|
||||
pinMode(CAN_SE_PIN, OUTPUT);
|
||||
digitalWrite(CAN_SE_PIN, LOW);
|
||||
#endif // CAN_SE_PIN
|
||||
CAN_cfg.speed = CAN_SPEED_500KBPS;
|
||||
bool init_CAN() {
|
||||
|
||||
if (can_receivers.find(CAN_NATIVE) != can_receivers.end()) {
|
||||
auto se_pin = esp32hal->CAN_SE_PIN();
|
||||
auto tx_pin = esp32hal->CAN_TX_PIN();
|
||||
auto rx_pin = esp32hal->CAN_RX_PIN();
|
||||
|
||||
if (se_pin != GPIO_NUM_NC) {
|
||||
if (!esp32hal->alloc_pins("CAN", se_pin)) {
|
||||
return false;
|
||||
}
|
||||
pinMode(se_pin, OUTPUT);
|
||||
digitalWrite(se_pin, LOW);
|
||||
}
|
||||
|
||||
CAN_cfg.speed = CAN_SPEED_500KBPS;
|
||||
#ifdef NATIVECAN_250KBPS // Some component is requesting lower CAN speed
|
||||
CAN_cfg.speed = CAN_SPEED_250KBPS;
|
||||
CAN_cfg.speed = CAN_SPEED_250KBPS;
|
||||
#endif // NATIVECAN_250KBPS
|
||||
CAN_cfg.tx_pin_id = CAN_TX_PIN;
|
||||
CAN_cfg.rx_pin_id = CAN_RX_PIN;
|
||||
CAN_cfg.rx_queue = xQueueCreate(rx_queue_size, sizeof(CAN_frame_t));
|
||||
// Init CAN Module
|
||||
ESP32Can.CANInit();
|
||||
|
||||
#ifdef CAN_ADDON
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Dual CAN Bus (ESP32+MCP2515) selected");
|
||||
#endif // DEBUG_LOG
|
||||
gBuffer.initWithSize(25);
|
||||
SPI2515.begin(MCP2515_SCK, MCP2515_MISO, MCP2515_MOSI);
|
||||
ACAN2515Settings settings2515(QUARTZ_FREQUENCY, 500UL * 1000UL); // CAN bit rate 500 kb/s
|
||||
settings2515.mRequestedMode = ACAN2515Settings::NormalMode;
|
||||
const uint16_t errorCode2515 = can.begin(settings2515, [] { can.isr(); });
|
||||
if (errorCode2515 == 0) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Can ok");
|
||||
#endif // DEBUG_LOG
|
||||
} else {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.print("Error Can: 0x");
|
||||
logging.println(errorCode2515, HEX);
|
||||
#endif // DEBUG_LOG
|
||||
set_event(EVENT_CANMCP2515_INIT_FAILURE, (uint8_t)errorCode2515);
|
||||
}
|
||||
#endif // CAN_ADDON
|
||||
if (!esp32hal->alloc_pins("CAN", tx_pin, rx_pin)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef CANFD_ADDON
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("CAN FD add-on (ESP32+MCP2517) selected");
|
||||
#endif // DEBUG_LOG
|
||||
SPI2517.begin(MCP2517_SCK, MCP2517_SDO, MCP2517_SDI);
|
||||
ACAN2517FDSettings settings2517(CANFD_ADDON_CRYSTAL_FREQUENCY_MHZ, 500 * 1000,
|
||||
DataBitRateFactor::x4); // Arbitration bit rate: 500 kbit/s, data bit rate: 2 Mbit/s
|
||||
#ifdef USE_CANFD_INTERFACE_AS_CLASSIC_CAN
|
||||
settings2517.mRequestedMode = ACAN2517FDSettings::Normal20B; // ListenOnly / Normal20B / NormalFD
|
||||
#else // not USE_CANFD_INTERFACE_AS_CLASSIC_CAN
|
||||
settings2517.mRequestedMode = ACAN2517FDSettings::NormalFD; // ListenOnly / Normal20B / NormalFD
|
||||
#endif // USE_CANFD_INTERFACE_AS_CLASSIC_CAN
|
||||
const uint32_t errorCode2517 = canfd.begin(settings2517, [] { canfd.isr(); });
|
||||
canfd.poll();
|
||||
if (errorCode2517 == 0) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.print("Bit Rate prescaler: ");
|
||||
logging.println(settings2517.mBitRatePrescaler);
|
||||
logging.print("Arbitration Phase segment 1: ");
|
||||
logging.print(settings2517.mArbitrationPhaseSegment1);
|
||||
logging.print(" segment 2: ");
|
||||
logging.print(settings2517.mArbitrationPhaseSegment2);
|
||||
logging.print(" SJW: ");
|
||||
logging.println(settings2517.mArbitrationSJW);
|
||||
logging.print("Actual Arbitration Bit Rate: ");
|
||||
logging.print(settings2517.actualArbitrationBitRate());
|
||||
logging.print(" bit/s");
|
||||
logging.print(" (Exact:");
|
||||
logging.println(settings2517.exactArbitrationBitRate() ? "yes)" : "no)");
|
||||
logging.print("Arbitration Sample point: ");
|
||||
logging.print(settings2517.arbitrationSamplePointFromBitStart());
|
||||
logging.println("%");
|
||||
#endif // DEBUG_LOG
|
||||
} else {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.print("CAN-FD Configuration error 0x");
|
||||
logging.println(errorCode2517, HEX);
|
||||
#endif // DEBUG_LOG
|
||||
set_event(EVENT_CANMCP2517FD_INIT_FAILURE, (uint8_t)errorCode2517);
|
||||
CAN_cfg.tx_pin_id = tx_pin;
|
||||
CAN_cfg.rx_pin_id = rx_pin;
|
||||
CAN_cfg.rx_queue = xQueueCreate(rx_queue_size, sizeof(CAN_frame_t));
|
||||
// Init CAN Module
|
||||
ESP32Can.CANInit();
|
||||
}
|
||||
#endif // CANFD_ADDON
|
||||
|
||||
if (can_receivers.find(CAN_ADDON_MCP2515) != can_receivers.end()) {
|
||||
auto cs_pin = esp32hal->MCP2515_CS();
|
||||
auto int_pin = esp32hal->MCP2515_INT();
|
||||
auto sck_pin = esp32hal->MCP2515_SCK();
|
||||
auto miso_pin = esp32hal->MCP2515_MISO();
|
||||
auto mosi_pin = esp32hal->MCP2515_MOSI();
|
||||
|
||||
if (!esp32hal->alloc_pins("CAN", cs_pin, int_pin, sck_pin, miso_pin, mosi_pin)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Dual CAN Bus (ESP32+MCP2515) selected");
|
||||
#endif // DEBUG_LOG
|
||||
gBuffer.initWithSize(25);
|
||||
|
||||
can2515 = new ACAN2515(cs_pin, SPI2515, int_pin);
|
||||
|
||||
SPI2515.begin(sck_pin, miso_pin, mosi_pin);
|
||||
ACAN2515Settings settings2515(QUARTZ_FREQUENCY, 500UL * 1000UL); // CAN bit rate 500 kb/s
|
||||
settings2515.mRequestedMode = ACAN2515Settings::NormalMode;
|
||||
const uint16_t errorCode2515 = can2515->begin(settings2515, [] { can2515->isr(); });
|
||||
if (errorCode2515 == 0) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Can ok");
|
||||
#endif // DEBUG_LOG
|
||||
} else {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.print("Error Can: 0x");
|
||||
logging.println(errorCode2515, HEX);
|
||||
#endif // DEBUG_LOG
|
||||
set_event(EVENT_CANMCP2515_INIT_FAILURE, (uint8_t)errorCode2515);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (can_receivers.find(CANFD_NATIVE) != can_receivers.end() ||
|
||||
can_receivers.find(CANFD_ADDON_MCP2518) != can_receivers.end()) {
|
||||
auto cs_pin = esp32hal->MCP2517_CS();
|
||||
auto int_pin = esp32hal->MCP2517_INT();
|
||||
auto sck_pin = esp32hal->MCP2517_SCK();
|
||||
auto sdo_pin = esp32hal->MCP2517_SDO();
|
||||
auto sdi_pin = esp32hal->MCP2517_SDI();
|
||||
|
||||
if (!esp32hal->alloc_pins("CAN", cs_pin, int_pin, sck_pin, sdo_pin, sdi_pin)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
canfd = new ACAN2517FD(cs_pin, SPI2517, int_pin);
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("CAN FD add-on (ESP32+MCP2517) selected");
|
||||
#endif // DEBUG_LOG
|
||||
SPI2517.begin(sck_pin, sdo_pin, sdi_pin);
|
||||
ACAN2517FDSettings settings2517(
|
||||
CANFD_ADDON_CRYSTAL_FREQUENCY_MHZ, 500 * 1000,
|
||||
DataBitRateFactor::x4); // Arbitration bit rate: 500 kbit/s, data bit rate: 2 Mbit/s
|
||||
|
||||
// ListenOnly / Normal20B / NormalFD
|
||||
settings2517.mRequestedMode = use_canfd_as_can ? ACAN2517FDSettings::Normal20B : ACAN2517FDSettings::NormalFD;
|
||||
|
||||
const uint32_t errorCode2517 = canfd->begin(settings2517, [] { canfd->isr(); });
|
||||
canfd->poll();
|
||||
if (errorCode2517 == 0) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.print("Bit Rate prescaler: ");
|
||||
logging.println(settings2517.mBitRatePrescaler);
|
||||
logging.print("Arbitration Phase segment 1: ");
|
||||
logging.print(settings2517.mArbitrationPhaseSegment1);
|
||||
logging.print(" segment 2: ");
|
||||
logging.print(settings2517.mArbitrationPhaseSegment2);
|
||||
logging.print(" SJW: ");
|
||||
logging.println(settings2517.mArbitrationSJW);
|
||||
logging.print("Actual Arbitration Bit Rate: ");
|
||||
logging.print(settings2517.actualArbitrationBitRate());
|
||||
logging.print(" bit/s");
|
||||
logging.print(" (Exact:");
|
||||
logging.println(settings2517.exactArbitrationBitRate() ? "yes)" : "no)");
|
||||
logging.print("Arbitration Sample point: ");
|
||||
logging.print(settings2517.arbitrationSamplePointFromBitStart());
|
||||
logging.println("%");
|
||||
#endif // DEBUG_LOG
|
||||
} else {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.print("CAN-FD Configuration error 0x");
|
||||
logging.println(errorCode2517, HEX);
|
||||
#endif // DEBUG_LOG
|
||||
set_event(EVENT_CANMCP2517FD_INIT_FAILURE, (uint8_t)errorCode2517);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void transmit_can_frame(CAN_frame* tx_frame, int interface) {
|
||||
|
@ -133,7 +194,6 @@ void transmit_can_frame(CAN_frame* tx_frame, int interface) {
|
|||
}
|
||||
break;
|
||||
case CAN_ADDON_MCP2515: {
|
||||
#ifdef CAN_ADDON
|
||||
//Struct with ACAN2515 library format, needed to use the MCP2515 library for CAN2
|
||||
CANMessage MCP2515Frame;
|
||||
MCP2515Frame.id = tx_frame->ID;
|
||||
|
@ -144,17 +204,13 @@ void transmit_can_frame(CAN_frame* tx_frame, int interface) {
|
|||
MCP2515Frame.data[i] = tx_frame->data.u8[i];
|
||||
}
|
||||
|
||||
send_ok_2515 = can.tryToSend(MCP2515Frame);
|
||||
send_ok_2515 = can2515->tryToSend(MCP2515Frame);
|
||||
if (!send_ok_2515) {
|
||||
datalayer.system.info.can_2515_send_fail = true;
|
||||
}
|
||||
#else // Interface not compiled, and settings try to use it
|
||||
set_event(EVENT_INTERFACE_MISSING, interface);
|
||||
#endif //CAN_ADDON
|
||||
} break;
|
||||
case CANFD_NATIVE:
|
||||
case CANFD_ADDON_MCP2518: {
|
||||
#ifdef CANFD_ADDON
|
||||
CANFDMessage MCP2518Frame;
|
||||
if (tx_frame->FD) {
|
||||
MCP2518Frame.type = CANFDMessage::CANFD_WITH_BIT_RATE_SWITCH;
|
||||
|
@ -167,13 +223,10 @@ void transmit_can_frame(CAN_frame* tx_frame, int interface) {
|
|||
for (uint8_t i = 0; i < MCP2518Frame.len; i++) {
|
||||
MCP2518Frame.data[i] = tx_frame->data.u8[i];
|
||||
}
|
||||
send_ok_2518 = canfd.tryToSend(MCP2518Frame);
|
||||
send_ok_2518 = canfd->tryToSend(MCP2518Frame);
|
||||
if (!send_ok_2518) {
|
||||
datalayer.system.info.can_2518_send_fail = true;
|
||||
}
|
||||
#else // Interface not compiled, and settings try to use it
|
||||
set_event(EVENT_INTERFACE_MISSING, interface);
|
||||
#endif //CANFD_ADDON
|
||||
} break;
|
||||
default:
|
||||
// Invalid interface sent with function call. TODO: Raise event that coders messed up
|
||||
|
@ -184,12 +237,14 @@ void transmit_can_frame(CAN_frame* tx_frame, int interface) {
|
|||
// Receive functions
|
||||
void receive_can() {
|
||||
receive_frame_can_native(); // Receive CAN messages from native CAN port
|
||||
#ifdef CAN_ADDON
|
||||
receive_frame_can_addon(); // Receive CAN messages on add-on MCP2515 chip
|
||||
#endif // CAN_ADDON
|
||||
#ifdef CANFD_ADDON
|
||||
receive_frame_canfd_addon(); // Receive CAN-FD messages.
|
||||
#endif // CANFD_ADDON
|
||||
|
||||
if (can2515) {
|
||||
receive_frame_can_addon(); // Receive CAN messages on add-on MCP2515 chip
|
||||
}
|
||||
|
||||
if (canfd) {
|
||||
receive_frame_canfd_addon(); // Receive CAN-FD messages.
|
||||
}
|
||||
}
|
||||
|
||||
void receive_frame_can_native() { // This section checks if we have a complete CAN message incoming on native CAN port
|
||||
|
@ -211,13 +266,12 @@ void receive_frame_can_native() { // This section checks if we have a complete
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef CAN_ADDON
|
||||
void receive_frame_can_addon() { // This section checks if we have a complete CAN message incoming on add-on CAN port
|
||||
CAN_frame rx_frame; // Struct with our CAN format
|
||||
CANMessage MCP2515frame; // Struct with ACAN2515 library format, needed to use the MCP2515 library
|
||||
|
||||
if (can.available()) {
|
||||
can.receive(MCP2515frame);
|
||||
if (can2515->available()) {
|
||||
can2515->receive(MCP2515frame);
|
||||
|
||||
rx_frame.ID = MCP2515frame.id;
|
||||
rx_frame.ext_ID = MCP2515frame.ext ? CAN_frame_ext : CAN_frame_std;
|
||||
|
@ -230,14 +284,12 @@ void receive_frame_can_addon() { // This section checks if we have a complete C
|
|||
map_can_frame_to_variable(&rx_frame, CAN_ADDON_MCP2515);
|
||||
}
|
||||
}
|
||||
#endif // CAN_ADDON
|
||||
|
||||
#ifdef CANFD_ADDON
|
||||
void receive_frame_canfd_addon() { // This section checks if we have a complete CAN-FD message incoming
|
||||
CANFDMessage MCP2518frame;
|
||||
int count = 0;
|
||||
while (canfd.available() && count++ < 16) {
|
||||
canfd.receive(MCP2518frame);
|
||||
while (canfd->available() && count++ < 16) {
|
||||
canfd->receive(MCP2518frame);
|
||||
|
||||
CAN_frame rx_frame;
|
||||
rx_frame.ID = MCP2518frame.id;
|
||||
|
@ -249,7 +301,6 @@ void receive_frame_canfd_addon() { // This section checks if we have a complete
|
|||
map_can_frame_to_variable(&rx_frame, CANFD_NATIVE);
|
||||
}
|
||||
}
|
||||
#endif // CANFD_ADDON
|
||||
|
||||
// Support functions
|
||||
void print_can_frame(CAN_frame frame, frameDirection msgDir) {
|
||||
|
@ -276,12 +327,6 @@ void print_can_frame(CAN_frame frame, frameDirection msgDir) {
|
|||
}
|
||||
}
|
||||
|
||||
static std::multimap<CAN_Interface, CanReceiver*> can_receivers;
|
||||
|
||||
void register_can_receiver(CanReceiver* receiver, CAN_Interface interface) {
|
||||
can_receivers.insert({interface, receiver});
|
||||
}
|
||||
|
||||
void map_can_frame_to_variable(CAN_frame* rx_frame, CAN_Interface interface) {
|
||||
if (interface !=
|
||||
CANFD_NATIVE) { //Avoid printing twice due to receive_frame_canfd_addon sending to both FD interfaces
|
||||
|
|
|
@ -8,24 +8,23 @@
|
|||
#include "../../devboard/utils/value_mapping.h"
|
||||
#include "../../lib/ESP32Async-ESPAsyncWebServer/src/ESPAsyncWebServer.h"
|
||||
#include "../../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||
#ifdef CAN_ADDON
|
||||
#include "../../lib/pierremolinaro-acan2515/ACAN2515.h"
|
||||
#endif //CAN_ADDON
|
||||
#ifdef CANFD_ADDON
|
||||
#include "../../lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h"
|
||||
#endif //CANFD_ADDON
|
||||
|
||||
extern bool use_canfd_as_can;
|
||||
|
||||
void dump_can_frame(CAN_frame& frame, frameDirection msgDir);
|
||||
void transmit_can_frame(CAN_frame* tx_frame, int interface);
|
||||
|
||||
// Register a receiver object for a given CAN interface
|
||||
void register_can_receiver(CanReceiver* receiver, CAN_Interface interface);
|
||||
|
||||
/**
|
||||
* @brief Initialization function for CAN.
|
||||
* @brief Initializes all CAN interfaces requested earlier by other modules (see register_can_receiver)
|
||||
*
|
||||
* @param[in] void
|
||||
*
|
||||
* @return void
|
||||
* @return true if CAN interfaces were initialized successfully, false otherwise.
|
||||
*/
|
||||
void init_CAN();
|
||||
bool init_CAN();
|
||||
|
||||
/**
|
||||
* @brief Receive CAN messages from all interfaces
|
||||
|
|
|
@ -92,41 +92,57 @@ void set(uint8_t pin, bool direction, uint32_t pwm_freq = 0xFFFF) {
|
|||
|
||||
// Initialization functions
|
||||
|
||||
void init_contactors() {
|
||||
const char* contactors = "Contactors";
|
||||
|
||||
bool init_contactors() {
|
||||
// Init contactor pins
|
||||
if (contactor_control_enabled) {
|
||||
auto posPin = esp32hal->POSITIVE_CONTACTOR_PIN();
|
||||
auto negPin = esp32hal->NEGATIVE_CONTACTOR_PIN();
|
||||
auto precPin = esp32hal->PRECHARGE_PIN();
|
||||
|
||||
if (!esp32hal->alloc_pins(contactors, posPin, negPin, precPin)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pwm_contactor_control) {
|
||||
// Setup PWM Channel Frequency and Resolution
|
||||
ledcAttachChannel(POSITIVE_CONTACTOR_PIN, PWM_Freq, PWM_Res, PWM_Positive_Channel);
|
||||
ledcAttachChannel(NEGATIVE_CONTACTOR_PIN, PWM_Freq, PWM_Res, PWM_Negative_Channel);
|
||||
ledcAttachChannel(posPin, PWM_Freq, PWM_Res, PWM_Positive_Channel);
|
||||
ledcAttachChannel(negPin, PWM_Freq, PWM_Res, PWM_Negative_Channel);
|
||||
// Set all pins OFF (0% PWM)
|
||||
ledcWrite(POSITIVE_CONTACTOR_PIN, PWM_OFF_DUTY);
|
||||
ledcWrite(NEGATIVE_CONTACTOR_PIN, PWM_OFF_DUTY);
|
||||
ledcWrite(posPin, PWM_OFF_DUTY);
|
||||
ledcWrite(negPin, PWM_OFF_DUTY);
|
||||
} else { //Normal CONTACTOR_CONTROL
|
||||
pinMode(POSITIVE_CONTACTOR_PIN, OUTPUT);
|
||||
set(POSITIVE_CONTACTOR_PIN, OFF);
|
||||
pinMode(NEGATIVE_CONTACTOR_PIN, OUTPUT);
|
||||
set(NEGATIVE_CONTACTOR_PIN, OFF);
|
||||
pinMode(posPin, OUTPUT);
|
||||
set(posPin, OFF);
|
||||
pinMode(negPin, OUTPUT);
|
||||
set(negPin, OFF);
|
||||
} // Precharge never has PWM regardless of setting
|
||||
pinMode(PRECHARGE_PIN, OUTPUT);
|
||||
set(PRECHARGE_PIN, OFF);
|
||||
pinMode(precPin, OUTPUT);
|
||||
set(precPin, OFF);
|
||||
}
|
||||
if (contactor_control_enabled_double_battery) {
|
||||
pinMode(SECOND_BATTERY_CONTACTORS_PIN, OUTPUT);
|
||||
set(SECOND_BATTERY_CONTACTORS_PIN, OFF);
|
||||
}
|
||||
// Init BMS contactor
|
||||
#if defined HW_STARK || defined HW_3LB // This hardware has dedicated pin, always enable on start
|
||||
pinMode(BMS_POWER, OUTPUT); //LilyGo is omitted from this, only enabled if user selects PERIODIC_BMS_RESET
|
||||
digitalWrite(BMS_POWER, HIGH);
|
||||
#endif // HW with dedicated BMS pins
|
||||
|
||||
#ifdef BMS_POWER
|
||||
if (periodic_bms_reset || remote_bms_reset) {
|
||||
pinMode(BMS_POWER, OUTPUT);
|
||||
digitalWrite(BMS_POWER, HIGH);
|
||||
if (contactor_control_enabled_double_battery) {
|
||||
auto second_contactors = esp32hal->SECOND_BATTERY_CONTACTORS_PIN();
|
||||
if (!esp32hal->alloc_pins(contactors, second_contactors)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pinMode(second_contactors, OUTPUT);
|
||||
set(second_contactors, OFF);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Init BMS contactor
|
||||
if (periodic_bms_reset || remote_bms_reset || esp32hal->always_enable_bms_power()) {
|
||||
auto pin = esp32hal->BMS_POWER();
|
||||
if (!esp32hal->alloc_pins("BMS power", pin)) {
|
||||
return false;
|
||||
}
|
||||
pinMode(pin, OUTPUT);
|
||||
digitalWrite(pin, HIGH);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void dbg_contactors(const char* state) {
|
||||
|
@ -144,9 +160,14 @@ void handle_contactors() {
|
|||
datalayer.system.status.inverter_allows_contactor_closing = inverter->allows_contactor_closing();
|
||||
}
|
||||
|
||||
#ifdef BMS_POWER
|
||||
handle_BMSpower(); // Some batteries need to be periodically power cycled
|
||||
#endif
|
||||
auto posPin = esp32hal->POSITIVE_CONTACTOR_PIN();
|
||||
auto negPin = esp32hal->NEGATIVE_CONTACTOR_PIN();
|
||||
auto prechargePin = esp32hal->PRECHARGE_PIN();
|
||||
auto bms_power_pin = esp32hal->BMS_POWER();
|
||||
|
||||
if (bms_power_pin != GPIO_NUM_NC) {
|
||||
handle_BMSpower(); // Some batteries need to be periodically power cycled
|
||||
}
|
||||
|
||||
#ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY
|
||||
handle_contactors_battery2();
|
||||
|
@ -166,9 +187,9 @@ void handle_contactors() {
|
|||
}
|
||||
|
||||
if (contactorStatus == SHUTDOWN_REQUESTED) {
|
||||
set(PRECHARGE_PIN, OFF);
|
||||
set(NEGATIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY);
|
||||
set(POSITIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY);
|
||||
set(prechargePin, OFF);
|
||||
set(negPin, OFF, PWM_OFF_DUTY);
|
||||
set(posPin, OFF, PWM_OFF_DUTY);
|
||||
set_event(EVENT_ERROR_OPEN_CONTACTOR, 0);
|
||||
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)
|
||||
|
@ -176,9 +197,9 @@ void handle_contactors() {
|
|||
|
||||
// After that, check if we are OK to start turning on the battery
|
||||
if (contactorStatus == DISCONNECTED) {
|
||||
set(PRECHARGE_PIN, OFF);
|
||||
set(NEGATIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY);
|
||||
set(POSITIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY);
|
||||
set(prechargePin, OFF);
|
||||
set(negPin, OFF, PWM_OFF_DUTY);
|
||||
set(posPin, OFF, PWM_OFF_DUTY);
|
||||
datalayer.system.status.contactors_engaged = false;
|
||||
|
||||
if (datalayer.system.status.battery_allows_contactor_closing &&
|
||||
|
@ -210,7 +231,7 @@ void handle_contactors() {
|
|||
// Handle actual state machine. This first turns on Negative, then Precharge, then Positive, and finally turns OFF precharge
|
||||
switch (contactorStatus) {
|
||||
case START_PRECHARGE:
|
||||
set(NEGATIVE_CONTACTOR_PIN, ON, PWM_ON_DUTY);
|
||||
set(negPin, ON, PWM_ON_DUTY);
|
||||
dbg_contactors("NEGATIVE");
|
||||
prechargeStartTime = currentTime;
|
||||
contactorStatus = PRECHARGE;
|
||||
|
@ -218,7 +239,7 @@ void handle_contactors() {
|
|||
|
||||
case PRECHARGE:
|
||||
if (currentTime - prechargeStartTime >= NEGATIVE_CONTACTOR_TIME_MS) {
|
||||
set(PRECHARGE_PIN, ON);
|
||||
set(prechargePin, ON);
|
||||
dbg_contactors("PRECHARGE");
|
||||
negativeStartTime = currentTime;
|
||||
contactorStatus = POSITIVE;
|
||||
|
@ -227,7 +248,7 @@ void handle_contactors() {
|
|||
|
||||
case POSITIVE:
|
||||
if (currentTime - negativeStartTime >= PRECHARGE_TIME_MS) {
|
||||
set(POSITIVE_CONTACTOR_PIN, ON, PWM_ON_DUTY);
|
||||
set(posPin, ON, PWM_ON_DUTY);
|
||||
dbg_contactors("POSITIVE");
|
||||
prechargeCompletedTime = currentTime;
|
||||
contactorStatus = PRECHARGE_OFF;
|
||||
|
@ -236,9 +257,9 @@ void handle_contactors() {
|
|||
|
||||
case PRECHARGE_OFF:
|
||||
if (currentTime - prechargeCompletedTime >= PRECHARGE_COMPLETED_TIME_MS) {
|
||||
set(PRECHARGE_PIN, OFF);
|
||||
set(NEGATIVE_CONTACTOR_PIN, ON, PWM_HOLD_DUTY);
|
||||
set(POSITIVE_CONTACTOR_PIN, ON, PWM_HOLD_DUTY);
|
||||
set(prechargePin, OFF);
|
||||
set(negPin, ON, PWM_HOLD_DUTY);
|
||||
set(posPin, ON, PWM_HOLD_DUTY);
|
||||
dbg_contactors("PRECHARGE_OFF");
|
||||
contactorStatus = COMPLETED;
|
||||
datalayer.system.status.contactors_engaged = true;
|
||||
|
@ -269,9 +290,11 @@ This makes the BMS recalculate all SOC% and avoid memory leaks
|
|||
During that time we also set the emulator state to paused in order to not try and send CAN messages towards the battery
|
||||
Feature is only used if user has enabled PERIODIC_BMS_RESET in the USER_SETTINGS */
|
||||
|
||||
#ifdef BMS_POWER
|
||||
void handle_BMSpower() {
|
||||
if (periodic_bms_reset || remote_bms_reset) {
|
||||
auto bms_power_pin = esp32hal->BMS_POWER();
|
||||
auto bms2_power_pin = esp32hal->BMS2_POWER();
|
||||
|
||||
// Get current time
|
||||
currentTime = millis();
|
||||
|
||||
|
@ -285,10 +308,10 @@ void handle_BMSpower() {
|
|||
// If power has been removed for 30 seconds, restore the power
|
||||
if (datalayer.system.status.BMS_reset_in_progress && currentTime - lastPowerRemovalTime >= powerRemovalDuration) {
|
||||
// Reapply power to the BMS
|
||||
digitalWrite(BMS_POWER, HIGH);
|
||||
#ifdef BMS_2_POWER
|
||||
digitalWrite(BMS_2_POWER, HIGH); // Same for battery 2
|
||||
#endif
|
||||
digitalWrite(bms_power_pin, HIGH);
|
||||
if (bms2_power_pin != GPIO_NUM_NC) {
|
||||
digitalWrite(bms2_power_pin, HIGH); // Same for battery 2
|
||||
}
|
||||
bmsPowerOnTime = currentTime;
|
||||
datalayer.system.status.BMS_reset_in_progress = false; // Reset the power removal flag
|
||||
datalayer.system.status.BMS_startup_in_progress = true; // Set the BMS warmup flag
|
||||
|
@ -303,10 +326,12 @@ void handle_BMSpower() {
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void start_bms_reset() {
|
||||
if (periodic_bms_reset || remote_bms_reset) {
|
||||
auto bms_power_pin = esp32hal->BMS_POWER();
|
||||
auto bms2_power_pin = esp32hal->BMS2_POWER();
|
||||
|
||||
if (!datalayer.system.status.BMS_reset_in_progress) {
|
||||
lastPowerRemovalTime = currentTime; // Record the time when BMS reset was started
|
||||
// we are now resetting at the correct time. We don't need to offset anymore
|
||||
|
@ -319,12 +344,11 @@ void start_bms_reset() {
|
|||
// We try to keep contactors engaged during this pause, and just ramp power down to 0.
|
||||
setBatteryPause(true, false, false, false);
|
||||
|
||||
#ifdef BMS_POWER
|
||||
digitalWrite(BMS_POWER, LOW); // Remove power by setting the BMS power pin to LOW
|
||||
#endif
|
||||
#ifdef BMS_2_POWER
|
||||
digitalWrite(BMS_2_POWER, LOW); // Same for battery 2
|
||||
#endif
|
||||
digitalWrite(bms_power_pin, LOW); // Remove power by setting the BMS power pin to LOW
|
||||
|
||||
if (bms2_power_pin != GPIO_NUM_NC) {
|
||||
digitalWrite(bms2_power_pin, LOW); // Same for battery 2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,9 +36,9 @@ void start_bms_reset();
|
|||
*
|
||||
* @param[in] void
|
||||
*
|
||||
* @return void
|
||||
* @return true if contactor init was successful, false otherwise.
|
||||
*/
|
||||
void init_contactors();
|
||||
bool init_contactors();
|
||||
|
||||
/**
|
||||
* @brief Handle contactors
|
||||
|
|
|
@ -1,33 +1,45 @@
|
|||
#include "comm_equipmentstopbutton.h"
|
||||
#include "../../include.h"
|
||||
|
||||
STOP_BUTTON_BEHAVIOR equipment_stop_behavior = stop_button_default_behavior;
|
||||
|
||||
// Parameters
|
||||
#ifdef EQUIPMENT_STOP_BUTTON
|
||||
const unsigned long equipment_button_long_press_duration =
|
||||
15000; // 15 seconds for long press in case of MOMENTARY_SWITCH
|
||||
const unsigned long equipment_button_debounce_duration = 200; // 200ms for debouncing the button
|
||||
unsigned long timeSincePress = 0; // Variable to store the time since the last press
|
||||
DebouncedButton equipment_stop_button; // Debounced button object
|
||||
#endif // EQUIPMENT_STOP_BUTTON
|
||||
|
||||
// Initialization functions
|
||||
#ifdef EQUIPMENT_STOP_BUTTON
|
||||
void init_equipment_stop_button() {
|
||||
bool init_equipment_stop_button() {
|
||||
if (equipment_stop_behavior == STOP_BUTTON_BEHAVIOR::NOT_CONNECTED) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto pin = esp32hal->EQUIPMENT_STOP_PIN();
|
||||
if (!esp32hal->alloc_pins("Equipment stop button", pin)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//using external pullup resistors NC
|
||||
pinMode(EQUIPMENT_STOP_PIN, INPUT);
|
||||
pinMode(pin, INPUT);
|
||||
// Initialize the debounced button with NC switch type and equipment_button_debounce_duration debounce time
|
||||
initDebouncedButton(equipment_stop_button, EQUIPMENT_STOP_PIN, NC, equipment_button_debounce_duration);
|
||||
initDebouncedButton(equipment_stop_button, pin, NC, equipment_button_debounce_duration);
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif // EQUIPMENT_STOP_BUTTON
|
||||
|
||||
// Main functions
|
||||
|
||||
#ifdef EQUIPMENT_STOP_BUTTON
|
||||
void monitor_equipment_stop_button() {
|
||||
|
||||
if (equipment_stop_behavior == STOP_BUTTON_BEHAVIOR::NOT_CONNECTED) {
|
||||
return;
|
||||
}
|
||||
|
||||
ButtonState changed_state = debounceButton(equipment_stop_button, timeSincePress);
|
||||
|
||||
if (equipment_stop_behavior == LATCHING_SWITCH) {
|
||||
if (equipment_stop_behavior == STOP_BUTTON_BEHAVIOR::LATCHING_SWITCH) {
|
||||
if (changed_state == PRESSED) {
|
||||
// Changed to ON – initiating equipment stop.
|
||||
setBatteryPause(true, false, true);
|
||||
|
@ -35,7 +47,7 @@ void monitor_equipment_stop_button() {
|
|||
// Changed to OFF – ending equipment stop.
|
||||
setBatteryPause(false, false, false);
|
||||
}
|
||||
} else if (equipment_stop_behavior == MOMENTARY_SWITCH) {
|
||||
} else if (equipment_stop_behavior == STOP_BUTTON_BEHAVIOR::MOMENTARY_SWITCH) {
|
||||
if (changed_state == RELEASED) { // button is released
|
||||
|
||||
if (timeSincePress < equipment_button_long_press_duration) {
|
||||
|
@ -48,4 +60,3 @@ void monitor_equipment_stop_button() {
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif // EQUIPMENT_STOP_BUTTON
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
#ifndef _COMM_EQUIPMENTSTOPBUTTON_H_
|
||||
#define _COMM_EQUIPMENTSTOPBUTTON_H_
|
||||
|
||||
#include "../../include.h"
|
||||
|
||||
#ifdef EQUIPMENT_STOP_BUTTON
|
||||
#include "../../devboard/utils/debounce_button.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Initialization of equipment stop button
|
||||
|
@ -14,7 +10,7 @@
|
|||
*
|
||||
* @return void
|
||||
*/
|
||||
void init_equipment_stop_button();
|
||||
bool init_equipment_stop_button();
|
||||
|
||||
/**
|
||||
* @brief Monitor equipment stop button
|
||||
|
@ -25,4 +21,8 @@ void init_equipment_stop_button();
|
|||
*/
|
||||
void monitor_equipment_stop_button();
|
||||
|
||||
enum class STOP_BUTTON_BEHAVIOR { NOT_CONNECTED = 0, LATCHING_SWITCH = 1, MOMENTARY_SWITCH = 2 };
|
||||
|
||||
extern STOP_BUTTON_BEHAVIOR equipment_stop_behavior;
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "comm_nvm.h"
|
||||
#include "../../communication/can/comm_can.h"
|
||||
#include "../../include.h"
|
||||
#include "../contactorcontrol/comm_contactorcontrol.h"
|
||||
|
||||
|
@ -75,12 +76,14 @@ void init_stored_settings() {
|
|||
user_selected_battery_type = (BatteryType)settings.getUInt("BATTTYPE", (int)BatteryType::None);
|
||||
user_selected_inverter_protocol = (InverterProtocolType)settings.getUInt("INVTYPE", (int)InverterProtocolType::None);
|
||||
user_selected_charger_type = (ChargerType)settings.getUInt("CHGTYPE", (int)ChargerType::None);
|
||||
equipment_stop_behavior = (STOP_BUTTON_BEHAVIOR)settings.getUInt("EQSTOP", (int)STOP_BUTTON_BEHAVIOR::NOT_CONNECTED);
|
||||
user_selected_second_battery = settings.getBool("DBLBTR", false);
|
||||
contactor_control_enabled = settings.getBool("CNTCTRL", false);
|
||||
contactor_control_enabled_double_battery = settings.getBool("CNTCTRLDBL", false);
|
||||
pwm_contactor_control = settings.getBool("PWMCNTCTRL", false);
|
||||
periodic_bms_reset = settings.getBool("PERBMSRESET", false);
|
||||
remote_bms_reset = settings.getBool("REMBMSRESET", false);
|
||||
use_canfd_as_can = settings.getBool("CANFDASCAN", false);
|
||||
#endif
|
||||
|
||||
settings.end();
|
||||
|
|
|
@ -2,7 +2,15 @@
|
|||
#include "../../datalayer/datalayer.h"
|
||||
#include "../../datalayer/datalayer_extended.h"
|
||||
#include "../../include.h"
|
||||
|
||||
#ifdef PRECHARGE_CONTROL
|
||||
const bool precharge_control_enabled_default = true;
|
||||
#else
|
||||
const bool precharge_control_enabled_default = false;
|
||||
#endif
|
||||
|
||||
bool precharge_control_enabled = precharge_control_enabled_default;
|
||||
|
||||
// Parameters
|
||||
#define MAX_PRECHARGE_TIME_MS 15000 // Maximum time precharge may be enabled
|
||||
#define Precharge_default_PWM_Freq 11000
|
||||
|
@ -25,19 +33,36 @@ static int32_t prev_external_voltage = 20000;
|
|||
|
||||
// Initialization functions
|
||||
|
||||
void init_precharge_control() {
|
||||
bool init_precharge_control() {
|
||||
if (!precharge_control_enabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Setup PWM Channel Frequency and Resolution
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("Precharge control initialised\n");
|
||||
#endif
|
||||
pinMode(HIA4V1_PIN, OUTPUT);
|
||||
digitalWrite(HIA4V1_PIN, LOW);
|
||||
pinMode(INVERTER_DISCONNECT_CONTACTOR_PIN, OUTPUT);
|
||||
digitalWrite(INVERTER_DISCONNECT_CONTACTOR_PIN, LOW);
|
||||
|
||||
auto hia4v1_pin = esp32hal->HIA4V1_PIN();
|
||||
auto inverter_disconnect_contactor_pin = esp32hal->INVERTER_DISCONNECT_CONTACTOR_PIN();
|
||||
|
||||
if (!esp32hal->alloc_pins("Precharge control", hia4v1_pin, inverter_disconnect_contactor_pin)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pinMode(hia4v1_pin, OUTPUT);
|
||||
digitalWrite(hia4v1_pin, LOW);
|
||||
pinMode(inverter_disconnect_contactor_pin, OUTPUT);
|
||||
digitalWrite(inverter_disconnect_contactor_pin, LOW);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Main functions
|
||||
void handle_precharge_control(unsigned long currentMillis) {
|
||||
auto hia4v1_pin = esp32hal->HIA4V1_PIN();
|
||||
auto inverter_disconnect_contactor_pin = esp32hal->INVERTER_DISCONNECT_CONTACTOR_PIN();
|
||||
|
||||
int32_t target_voltage = datalayer.battery.status.voltage_dV;
|
||||
int32_t external_voltage = datalayer_extended.meb.BMS_voltage_intermediate_dV;
|
||||
|
||||
|
@ -49,14 +74,14 @@ void handle_precharge_control(unsigned long currentMillis) {
|
|||
break;
|
||||
case AUTO_PRECHARGE_START:
|
||||
freq = Precharge_default_PWM_Freq;
|
||||
ledcAttachChannel(HIA4V1_PIN, freq, Precharge_PWM_Res, PWM_Precharge_Channel);
|
||||
ledcWriteTone(HIA4V1_PIN, freq); // Set frequency and set dutycycle to 50%
|
||||
ledcAttachChannel(hia4v1_pin, freq, Precharge_PWM_Res, PWM_Precharge_Channel);
|
||||
ledcWriteTone(hia4v1_pin, freq); // Set frequency and set dutycycle to 50%
|
||||
prechargeStartTime = currentMillis;
|
||||
datalayer.system.status.precharge_status = AUTO_PRECHARGE_PRECHARGING;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("Precharge: Starting sequence\n");
|
||||
#endif
|
||||
digitalWrite(INVERTER_DISCONNECT_CONTACTOR_PIN, OFF);
|
||||
digitalWrite(inverter_disconnect_contactor_pin, OFF);
|
||||
break;
|
||||
|
||||
case AUTO_PRECHARGE_PRECHARGING:
|
||||
|
@ -84,24 +109,24 @@ void handle_precharge_control(unsigned long currentMillis) {
|
|||
logging.printf("Precharge: Target: %d V Extern: %d V Frequency: %u\n", target_voltage / 10,
|
||||
external_voltage / 10, freq);
|
||||
#endif
|
||||
ledcWriteTone(HIA4V1_PIN, freq);
|
||||
ledcWriteTone(hia4v1_pin, freq);
|
||||
}
|
||||
|
||||
if ((datalayer.battery.status.real_bms_status != BMS_STANDBY &&
|
||||
datalayer.battery.status.real_bms_status != BMS_ACTIVE) ||
|
||||
datalayer.battery.status.bms_status != ACTIVE || datalayer.system.settings.equipment_stop_active) {
|
||||
pinMode(HIA4V1_PIN, OUTPUT);
|
||||
digitalWrite(HIA4V1_PIN, LOW);
|
||||
digitalWrite(INVERTER_DISCONNECT_CONTACTOR_PIN, ON);
|
||||
pinMode(hia4v1_pin, OUTPUT);
|
||||
digitalWrite(hia4v1_pin, LOW);
|
||||
digitalWrite(inverter_disconnect_contactor_pin, ON);
|
||||
datalayer.system.status.precharge_status = AUTO_PRECHARGE_IDLE;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("Precharge: Disabling Precharge bms not standby/active or equipment stop\n");
|
||||
#endif
|
||||
} else if (currentMillis - prechargeStartTime >= MAX_PRECHARGE_TIME_MS ||
|
||||
datalayer.battery.status.real_bms_status == BMS_FAULT) {
|
||||
pinMode(HIA4V1_PIN, OUTPUT);
|
||||
digitalWrite(HIA4V1_PIN, LOW);
|
||||
digitalWrite(INVERTER_DISCONNECT_CONTACTOR_PIN, ON);
|
||||
pinMode(hia4v1_pin, OUTPUT);
|
||||
digitalWrite(hia4v1_pin, LOW);
|
||||
digitalWrite(inverter_disconnect_contactor_pin, ON);
|
||||
datalayer.system.status.precharge_status = AUTO_PRECHARGE_OFF;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("Precharge: Disabled (timeout reached / BMS fault) -> AUTO_PRECHARGE_OFF\n");
|
||||
|
@ -110,9 +135,9 @@ void handle_precharge_control(unsigned long currentMillis) {
|
|||
|
||||
// Add event
|
||||
} else if (datalayer.system.status.battery_allows_contactor_closing) {
|
||||
pinMode(HIA4V1_PIN, OUTPUT);
|
||||
digitalWrite(HIA4V1_PIN, LOW);
|
||||
digitalWrite(INVERTER_DISCONNECT_CONTACTOR_PIN, ON);
|
||||
pinMode(hia4v1_pin, OUTPUT);
|
||||
digitalWrite(hia4v1_pin, LOW);
|
||||
digitalWrite(inverter_disconnect_contactor_pin, ON);
|
||||
datalayer.system.status.precharge_status = AUTO_PRECHARGE_COMPLETED;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("Precharge: Disabled (contacts closed) -> COMPLETED\n");
|
||||
|
@ -134,8 +159,8 @@ void handle_precharge_control(unsigned long currentMillis) {
|
|||
!datalayer.system.status.inverter_allows_contactor_closing ||
|
||||
datalayer.system.settings.equipment_stop_active || datalayer.battery.status.bms_status != FAULT) {
|
||||
datalayer.system.status.precharge_status = AUTO_PRECHARGE_IDLE;
|
||||
pinMode(HIA4V1_PIN, OUTPUT);
|
||||
digitalWrite(HIA4V1_PIN, LOW);
|
||||
pinMode(hia4v1_pin, OUTPUT);
|
||||
digitalWrite(hia4v1_pin, LOW);
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("Precharge: equipment stop activated -> IDLE\n");
|
||||
#endif
|
||||
|
@ -146,4 +171,3 @@ void handle_precharge_control(unsigned long currentMillis) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
#endif // PRECHARGE_CONTROL
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
*
|
||||
* @return void
|
||||
*/
|
||||
void init_precharge_control();
|
||||
bool init_precharge_control();
|
||||
|
||||
/**
|
||||
* @brief Handle contactors
|
||||
|
|
|
@ -3,19 +3,30 @@
|
|||
|
||||
#include <list>
|
||||
|
||||
void init_rs485() {
|
||||
#ifdef RS485_EN_PIN
|
||||
pinMode(RS485_EN_PIN, OUTPUT);
|
||||
digitalWrite(RS485_EN_PIN, HIGH);
|
||||
#endif // RS485_EN_PIN
|
||||
#ifdef RS485_SE_PIN
|
||||
pinMode(RS485_SE_PIN, OUTPUT);
|
||||
digitalWrite(RS485_SE_PIN, HIGH);
|
||||
#endif // RS485_SE_PIN
|
||||
#ifdef PIN_5V_EN
|
||||
pinMode(PIN_5V_EN, OUTPUT);
|
||||
digitalWrite(PIN_5V_EN, HIGH);
|
||||
#endif // PIN_5V_EN
|
||||
bool init_rs485() {
|
||||
|
||||
auto en_pin = esp32hal->RS485_EN_PIN();
|
||||
auto se_pin = esp32hal->RS485_SE_PIN();
|
||||
auto pin_5v_en = esp32hal->PIN_5V_EN();
|
||||
|
||||
if (!esp32hal->alloc_pins_ignore_unused("RS485", en_pin, se_pin, pin_5v_en)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (en_pin != GPIO_NUM_NC) {
|
||||
pinMode(en_pin, OUTPUT);
|
||||
digitalWrite(en_pin, HIGH);
|
||||
}
|
||||
|
||||
if (se_pin != GPIO_NUM_NC) {
|
||||
pinMode(se_pin, OUTPUT);
|
||||
digitalWrite(se_pin, HIGH);
|
||||
}
|
||||
|
||||
if (pin_5v_en != GPIO_NUM_NC) {
|
||||
pinMode(pin_5v_en, OUTPUT);
|
||||
digitalWrite(pin_5v_en, HIGH);
|
||||
}
|
||||
|
||||
// Inverters and batteries are expected to initialize their serial port in their setup-function
|
||||
// for RS485 or Modbus comms.
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
*
|
||||
* @param[in] void
|
||||
*
|
||||
* @return void
|
||||
* @return true if init was successful, false otherwise.
|
||||
*/
|
||||
void init_rs485();
|
||||
bool init_rs485();
|
||||
|
||||
// Defines an interface for any object that needs to receive a signal to handle RS485 comm.
|
||||
// Can be extended later for more complex operation.
|
||||
|
|
|
@ -227,8 +227,6 @@ typedef struct {
|
|||
float CPU_temperature = 0;
|
||||
/** array with type of battery used, for displaying on webserver */
|
||||
char battery_protocol[64] = {0};
|
||||
/** array with type of inverter protocol used, for displaying on webserver */
|
||||
char inverter_protocol[64] = {0};
|
||||
/** array with type of battery used, for displaying on webserver */
|
||||
char shunt_protocol[64] = {0};
|
||||
/** array with type of inverter brand used, for displaying on webserver */
|
||||
|
|
30
Software/src/devboard/hal/hal.cpp
Normal file
30
Software/src/devboard/hal/hal.cpp
Normal file
|
@ -0,0 +1,30 @@
|
|||
#include "hal.h"
|
||||
|
||||
#include "../../../USER_SETTINGS.h"
|
||||
|
||||
#include "hw_3LB.h"
|
||||
#include "hw_devkit.h"
|
||||
#include "hw_lilygo.h"
|
||||
#include "hw_stark.h"
|
||||
|
||||
extern Esp32Hal* esp32hal;
|
||||
|
||||
void init_hal() {
|
||||
#if defined(HW_LILYGO)
|
||||
esp32hal = new LilyGoHal();
|
||||
#elif defined(HW_STARK)
|
||||
esp32hal = new StarkHal();
|
||||
#elif defined(HW_3LB)
|
||||
esp32hal = new ThreeLBHal();
|
||||
#elif defined(HW_DEVKIT)
|
||||
esp32hal = new DevKitHal();
|
||||
#else
|
||||
#error "No HW defined."
|
||||
#endif
|
||||
}
|
||||
|
||||
unsigned long millis();
|
||||
|
||||
bool Esp32Hal::system_booted_up() {
|
||||
return milliseconds(millis()) > BOOTUP_TIME();
|
||||
}
|
|
@ -1,16 +1,144 @@
|
|||
#ifndef _HAL_H_
|
||||
#define _HAL_H_
|
||||
|
||||
#include "../../../USER_SETTINGS.h"
|
||||
#include <soc/gpio_num.h>
|
||||
#include <chrono>
|
||||
#include "../../../src/devboard/utils/types.h"
|
||||
|
||||
#if defined(HW_LILYGO)
|
||||
#include "hw_lilygo.h"
|
||||
#elif defined(HW_STARK)
|
||||
#include "hw_stark.h"
|
||||
#elif defined(HW_3LB)
|
||||
#include "hw_3LB.h"
|
||||
#elif defined(HW_DEVKIT)
|
||||
#include "hw_devkit.h"
|
||||
#endif
|
||||
class Esp32Hal {
|
||||
public:
|
||||
virtual const char* name() = 0;
|
||||
|
||||
// Time it takes before system is considered fully started up.
|
||||
virtual duration BOOTUP_TIME() = 0;
|
||||
virtual bool system_booted_up();
|
||||
|
||||
// Core assignment
|
||||
virtual int CORE_FUNCTION_CORE() { return 1; }
|
||||
virtual int MODBUS_CORE() { return 0; }
|
||||
virtual int WIFI_CORE() { return 0; }
|
||||
|
||||
template <typename... Pins>
|
||||
bool alloc_pins(const char* name, Pins... pins) {
|
||||
std::vector<gpio_num_t> requested_pins = {static_cast<gpio_num_t>(pins)...};
|
||||
|
||||
for (gpio_num_t pin : requested_pins) {
|
||||
if (pin < 0) {
|
||||
// Event: {name} attempted to allocate pin that wasn't defined for the selected HW.
|
||||
return false;
|
||||
}
|
||||
|
||||
auto it = allocated_pins.find(pin);
|
||||
if (it != allocated_pins.end()) {
|
||||
// Event: GPIO conflict for pin {pin} between name and it->second.
|
||||
//std::cerr << "Pin " << pin << " already allocated to \"" << it->second << "\".\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (gpio_num_t pin : requested_pins) {
|
||||
allocated_pins[pin] = name;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Helper to forward vector to variadic template
|
||||
template <typename Vec, size_t... Is>
|
||||
bool alloc_pins_from_vector(const char* name, const Vec& pins, std::index_sequence<Is...>) {
|
||||
return alloc_pins(name, pins[Is]...);
|
||||
}
|
||||
|
||||
template <typename... Pins>
|
||||
bool alloc_pins_ignore_unused(const char* name, Pins... pins) {
|
||||
std::vector<gpio_num_t> valid_pins;
|
||||
for (gpio_num_t pin : std::vector<gpio_num_t>{static_cast<gpio_num_t>(pins)...}) {
|
||||
if (pin != GPIO_NUM_NC) {
|
||||
valid_pins.push_back(pin);
|
||||
}
|
||||
}
|
||||
|
||||
return alloc_pins_from_vector(name, valid_pins, std::make_index_sequence<sizeof...(pins)>{});
|
||||
}
|
||||
|
||||
virtual bool always_enable_bms_power() { return false; }
|
||||
|
||||
virtual gpio_num_t PIN_5V_EN() { return GPIO_NUM_NC; }
|
||||
virtual gpio_num_t RS485_EN_PIN() { return GPIO_NUM_NC; }
|
||||
virtual gpio_num_t RS485_TX_PIN() { return GPIO_NUM_NC; }
|
||||
virtual gpio_num_t RS485_RX_PIN() { return GPIO_NUM_NC; }
|
||||
virtual gpio_num_t RS485_SE_PIN() { return GPIO_NUM_NC; }
|
||||
|
||||
virtual gpio_num_t CAN_TX_PIN() { return GPIO_NUM_NC; }
|
||||
virtual gpio_num_t CAN_RX_PIN() { return GPIO_NUM_NC; }
|
||||
virtual gpio_num_t CAN_SE_PIN() { return GPIO_NUM_NC; }
|
||||
|
||||
// CAN_ADDON
|
||||
// SCK input of MCP2515
|
||||
virtual gpio_num_t MCP2515_SCK() { return GPIO_NUM_NC; }
|
||||
// SDI input of MCP2515
|
||||
virtual gpio_num_t MCP2515_MOSI() { return GPIO_NUM_NC; }
|
||||
// SDO output of MCP2515
|
||||
virtual gpio_num_t MCP2515_MISO() { return GPIO_NUM_NC; }
|
||||
// CS input of MCP2515
|
||||
virtual gpio_num_t MCP2515_CS() { return GPIO_NUM_NC; }
|
||||
// INT output of MCP2515
|
||||
virtual gpio_num_t MCP2515_INT() { return GPIO_NUM_NC; }
|
||||
|
||||
// CANFD_ADDON defines for MCP2517
|
||||
virtual gpio_num_t MCP2517_SCK() { return GPIO_NUM_NC; }
|
||||
virtual gpio_num_t MCP2517_SDI() { return GPIO_NUM_NC; }
|
||||
virtual gpio_num_t MCP2517_SDO() { return GPIO_NUM_NC; }
|
||||
virtual gpio_num_t MCP2517_CS() { return GPIO_NUM_NC; }
|
||||
virtual gpio_num_t MCP2517_INT() { return GPIO_NUM_NC; }
|
||||
|
||||
// CHAdeMO support pin dependencies
|
||||
virtual gpio_num_t CHADEMO_PIN_2() { return GPIO_NUM_NC; }
|
||||
virtual gpio_num_t CHADEMO_PIN_10() { return GPIO_NUM_NC; }
|
||||
virtual gpio_num_t CHADEMO_PIN_7() { return GPIO_NUM_NC; }
|
||||
virtual gpio_num_t CHADEMO_PIN_4() { return GPIO_NUM_NC; }
|
||||
virtual gpio_num_t CHADEMO_LOCK() { return GPIO_NUM_NC; }
|
||||
|
||||
// Contactor handling
|
||||
virtual gpio_num_t POSITIVE_CONTACTOR_PIN() { return GPIO_NUM_NC; }
|
||||
virtual gpio_num_t NEGATIVE_CONTACTOR_PIN() { return GPIO_NUM_NC; }
|
||||
virtual gpio_num_t PRECHARGE_PIN() { return GPIO_NUM_NC; }
|
||||
virtual gpio_num_t BMS_POWER() { return GPIO_NUM_NC; }
|
||||
virtual gpio_num_t BMS2_POWER() { return GPIO_NUM_NC; }
|
||||
virtual gpio_num_t SECOND_BATTERY_CONTACTORS_PIN() { return GPIO_NUM_NC; }
|
||||
|
||||
// Automatic precharging
|
||||
virtual gpio_num_t HIA4V1_PIN() { return GPIO_NUM_NC; }
|
||||
virtual gpio_num_t INVERTER_DISCONNECT_CONTACTOR_PIN() { return GPIO_NUM_NC; }
|
||||
|
||||
// SMA CAN contactor pins
|
||||
virtual gpio_num_t INVERTER_CONTACTOR_ENABLE_PIN() { return GPIO_NUM_NC; }
|
||||
|
||||
virtual gpio_num_t INVERTER_CONTACTOR_ENABLE_LED_PIN() { return GPIO_NUM_NC; }
|
||||
|
||||
// SD card
|
||||
virtual gpio_num_t SD_MISO_PIN() { return GPIO_NUM_NC; }
|
||||
virtual gpio_num_t SD_MOSI_PIN() { return GPIO_NUM_NC; }
|
||||
virtual gpio_num_t SD_SCLK_PIN() { return GPIO_NUM_NC; }
|
||||
virtual gpio_num_t SD_CS_PIN() { return GPIO_NUM_NC; }
|
||||
|
||||
// LED
|
||||
virtual gpio_num_t LED_PIN() { return GPIO_NUM_NC; }
|
||||
virtual uint8_t LED_MAX_BRIGHTNESS() { return 40; }
|
||||
|
||||
// Equipment stop pin
|
||||
virtual gpio_num_t EQUIPMENT_STOP_PIN() { return GPIO_NUM_NC; }
|
||||
|
||||
// Battery wake up pins
|
||||
virtual gpio_num_t WUP_PIN1() { return GPIO_NUM_NC; }
|
||||
virtual gpio_num_t WUP_PIN2() { return GPIO_NUM_NC; }
|
||||
|
||||
private:
|
||||
std::unordered_map<gpio_num_t, std::string> allocated_pins;
|
||||
};
|
||||
|
||||
extern Esp32Hal* esp32hal;
|
||||
|
||||
void init_hal();
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
#ifndef __HW_3LB_H__
|
||||
#define __HW_3LB_H__
|
||||
|
||||
// Board boot-up time
|
||||
#define BOOTUP_TIME 1000 // Time in ms it takes before system is considered fully started up
|
||||
#include "hal.h"
|
||||
|
||||
// Core assignment
|
||||
#define CORE_FUNCTION_CORE 1
|
||||
#define MODBUS_CORE 0
|
||||
#define WIFI_CORE 0
|
||||
class ThreeLBHal : public Esp32Hal {
|
||||
public:
|
||||
const char* name() { return "3LB board"; }
|
||||
// Board boot-up time
|
||||
duration BOOTUP_TIME() { return milliseconds(1000); }
|
||||
};
|
||||
|
||||
// RS485
|
||||
/*// RS485
|
||||
//#define PIN_5V_EN 16
|
||||
//#define RS485_EN_PIN 17 // 17 /RE
|
||||
#define RS485_TX_PIN 1 // 21
|
||||
|
@ -78,7 +79,7 @@
|
|||
#define WUP_PIN1 GPIO_NUM_25 // Wake up pin for battery 1
|
||||
#define WUP_PIN2 GPIO_NUM_32 // Wake up pin for battery 2
|
||||
|
||||
/* ----- Error checks below, don't change (can't be moved to separate file) ----- */
|
||||
// ----- Error checks below, don't change (can't be moved to separate file) -----
|
||||
#ifndef HW_CONFIGURED
|
||||
#define HW_CONFIGURED
|
||||
#else
|
||||
|
@ -91,18 +92,6 @@
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef EQUIPMENT_STOP_BUTTON
|
||||
#ifdef CAN_ADDON
|
||||
#error EQUIPMENT_STOP_BUTTON and CAN_ADDON cannot coexist due to overlapping GPIO pin usage
|
||||
#endif
|
||||
#ifdef CANFD_ADDON
|
||||
#error EQUIPMENT_STOP_BUTTON and CANFD_ADDON cannot coexist due to overlapping GPIO pin usage
|
||||
#endif
|
||||
#ifdef CHADEMO_BATTERY
|
||||
#error EQUIPMENT_STOP_BUTTON and CHADEMO_BATTERY cannot coexist due to overlapping GPIO pin usage
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef BMW_I3_BATTERY
|
||||
#if defined(CONTACTOR_CONTROL) && defined(WUP_PIN1)
|
||||
#error GPIO PIN 25 cannot be used for both BMWi3 Wakeup and contactor control. Disable CONTACTOR_CONTROL
|
||||
|
@ -111,5 +100,5 @@
|
|||
#error GPIO PIN 32 cannot be used for both BMWi3 Wakeup and contactor control. Disable CONTACTOR_CONTROL
|
||||
#endif
|
||||
#endif
|
||||
|
||||
*/
|
||||
#endif
|
||||
|
|
|
@ -14,10 +14,11 @@ The pin layout below supports the following:
|
|||
// Board boot-up time
|
||||
#define BOOTUP_TIME 1000 // Time in ms it takes before system is considered fully started up
|
||||
|
||||
// Core assignment
|
||||
#define CORE_FUNCTION_CORE 1
|
||||
#define MODBUS_CORE 0
|
||||
#define WIFI_CORE 0
|
||||
class DevKitHal : public Esp32Hal {
|
||||
public:
|
||||
const char* name() { return "ESP32 DevKit V1"; }
|
||||
duration BOOTUP_TIME() { return milliseconds(1000); }
|
||||
};
|
||||
|
||||
// RS485
|
||||
#define RS485_TX_PIN GPIO_NUM_1
|
||||
|
|
|
@ -1,82 +1,84 @@
|
|||
#ifndef __HW_LILYGO_H__
|
||||
#define __HW_LILYGO_H__
|
||||
|
||||
// Board boot-up time
|
||||
#define BOOTUP_TIME 1000 // Time in ms it takes before system is considered fully started up
|
||||
#include "hal.h"
|
||||
|
||||
// Core assignment
|
||||
#define CORE_FUNCTION_CORE 1
|
||||
#define MODBUS_CORE 0
|
||||
#define WIFI_CORE 0
|
||||
class LilyGoHal : public Esp32Hal {
|
||||
public:
|
||||
const char* name() { return "LilyGo T-CAN485"; }
|
||||
duration BOOTUP_TIME() { return milliseconds(1000); }
|
||||
|
||||
// RS485
|
||||
#define PIN_5V_EN 16
|
||||
#define RS485_EN_PIN 17 // 17 /RE
|
||||
#define RS485_TX_PIN 22 // 21
|
||||
#define RS485_RX_PIN 21 // 22
|
||||
#define RS485_SE_PIN 19 // 22 /SHDN
|
||||
virtual gpio_num_t PIN_5V_EN() { return GPIO_NUM_16; }
|
||||
virtual gpio_num_t RS485_EN_PIN() { return GPIO_NUM_17; }
|
||||
virtual gpio_num_t RS485_TX_PIN() { return GPIO_NUM_22; }
|
||||
virtual gpio_num_t RS485_RX_PIN() { return GPIO_NUM_21; }
|
||||
virtual gpio_num_t RS485_SE_PIN() { return GPIO_NUM_19; }
|
||||
|
||||
// CAN settings. CAN_2 is not defined as it can be either MCP2515 or MCP2517, defined by the user settings
|
||||
#define CAN_1_TYPE ESP32CAN
|
||||
virtual gpio_num_t CAN_TX_PIN() { return GPIO_NUM_27; }
|
||||
virtual gpio_num_t CAN_RX_PIN() { return GPIO_NUM_26; }
|
||||
virtual gpio_num_t CAN_SE_PIN() { return GPIO_NUM_23; }
|
||||
|
||||
// CAN1 PIN mappings, do not change these unless you are adding on extra hardware to the PCB
|
||||
#define CAN_TX_PIN GPIO_NUM_27
|
||||
#define CAN_RX_PIN GPIO_NUM_26
|
||||
#define CAN_SE_PIN 23
|
||||
// CAN_ADDON
|
||||
// SCK input of MCP2515
|
||||
virtual gpio_num_t MCP2515_SCK() { return GPIO_NUM_12; }
|
||||
// SDI input of MCP2515
|
||||
virtual gpio_num_t MCP2515_MOSI() { return GPIO_NUM_5; }
|
||||
// SDO output of MCP2515
|
||||
virtual gpio_num_t MCP2515_MISO() { return GPIO_NUM_34; }
|
||||
// CS input of MCP2515
|
||||
virtual gpio_num_t MCP2515_CS() { return GPIO_NUM_18; }
|
||||
// INT output of MCP2515
|
||||
virtual gpio_num_t MCP2515_INT() { return GPIO_NUM_35; }
|
||||
|
||||
// CAN2 defines below
|
||||
// CANFD_ADDON defines for MCP2517
|
||||
virtual gpio_num_t MCP2517_SCK() { return GPIO_NUM_12; }
|
||||
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_CS() { return GPIO_NUM_18; }
|
||||
virtual gpio_num_t MCP2517_INT() { return GPIO_NUM_35; }
|
||||
|
||||
// CAN_ADDON defines
|
||||
#define MCP2515_SCK 12 // SCK input of MCP2515
|
||||
#define MCP2515_MOSI 5 // SDI input of MCP2515
|
||||
#define MCP2515_MISO 34 // SDO output of MCP2515 | Pin 34 is input only, without pullup/down resistors
|
||||
#define MCP2515_CS 18 // CS input of MCP2515
|
||||
#define MCP2515_INT 35 // INT output of MCP2515 | | Pin 35 is input only, without pullup/down resistors
|
||||
// CHAdeMO support pin dependencies
|
||||
virtual gpio_num_t CHADEMO_PIN_2() { return GPIO_NUM_12; }
|
||||
virtual gpio_num_t CHADEMO_PIN_10() { return GPIO_NUM_5; }
|
||||
virtual gpio_num_t CHADEMO_PIN_7() { return GPIO_NUM_34; }
|
||||
virtual gpio_num_t CHADEMO_PIN_4() { return GPIO_NUM_35; }
|
||||
virtual gpio_num_t CHADEMO_LOCK() { return GPIO_NUM_18; }
|
||||
|
||||
// CANFD_ADDON defines
|
||||
#define MCP2517_SCK 12 // SCK input of MCP2517
|
||||
#define MCP2517_SDI 5 // SDI input of MCP2517
|
||||
#define MCP2517_SDO 34 // SDO output of MCP2517
|
||||
#define MCP2517_CS 18 // CS input of MCP2517
|
||||
#define MCP2517_INT 35 // INT output of MCP2517
|
||||
// Contactor handling
|
||||
virtual gpio_num_t POSITIVE_CONTACTOR_PIN() { return GPIO_NUM_32; }
|
||||
virtual gpio_num_t NEGATIVE_CONTACTOR_PIN() { return GPIO_NUM_33; }
|
||||
virtual gpio_num_t PRECHARGE_PIN() { return GPIO_NUM_25; }
|
||||
virtual gpio_num_t BMS_POWER() { return GPIO_NUM_18; }
|
||||
//virtual gpio_num_t BMS2_POWER() { return GPIO_NUM_NC; }
|
||||
virtual gpio_num_t SECOND_BATTERY_CONTACTORS_PIN() { return GPIO_NUM_15; }
|
||||
|
||||
// CHAdeMO support pin dependencies
|
||||
#define CHADEMO_PIN_2 12
|
||||
#define CHADEMO_PIN_10 5
|
||||
#define CHADEMO_PIN_7 34
|
||||
#define CHADEMO_PIN_4 35
|
||||
#define CHADEMO_LOCK 18
|
||||
// Automatic precharging
|
||||
virtual gpio_num_t HIA4V1_PIN() { return GPIO_NUM_25; }
|
||||
virtual gpio_num_t INVERTER_DISCONNECT_CONTACTOR_PIN() { return GPIO_NUM_32; }
|
||||
|
||||
// Contactor handling
|
||||
#define POSITIVE_CONTACTOR_PIN 32
|
||||
#define NEGATIVE_CONTACTOR_PIN 33
|
||||
#define PRECHARGE_PIN 25
|
||||
#define BMS_POWER 18 // Note, this pin collides with CAN add-ons and Chademo
|
||||
#define SECOND_BATTERY_CONTACTORS_PIN 15 //Note, this pin collides with SD card pins
|
||||
// SMA CAN contactor pins
|
||||
virtual gpio_num_t INVERTER_CONTACTOR_ENABLE_PIN() { return GPIO_NUM_5; }
|
||||
|
||||
// Automatic precharging
|
||||
#define HIA4V1_PIN 25
|
||||
#define INVERTER_DISCONNECT_CONTACTOR_PIN 32
|
||||
// virtual gpio_num_t INVERTER_CONTACTOR_ENABLE_LED_PIN() { return GPIO_NUM_NC; }
|
||||
|
||||
// SMA CAN contactor pins
|
||||
#define INVERTER_CONTACTOR_ENABLE_PIN 5
|
||||
// SD card
|
||||
virtual gpio_num_t SD_MISO_PIN() { return GPIO_NUM_2; }
|
||||
virtual gpio_num_t SD_MOSI_PIN() { return GPIO_NUM_15; }
|
||||
virtual gpio_num_t SD_SCLK_PIN() { return GPIO_NUM_14; }
|
||||
virtual gpio_num_t SD_CS_PIN() { return GPIO_NUM_13; }
|
||||
|
||||
// SD card
|
||||
#define SD_MISO_PIN 2
|
||||
#define SD_MOSI_PIN 15
|
||||
#define SD_SCLK_PIN 14
|
||||
#define SD_CS_PIN 13
|
||||
// LED
|
||||
virtual gpio_num_t LED_PIN() { return GPIO_NUM_4; }
|
||||
|
||||
// LED
|
||||
#define LED_PIN 4
|
||||
#define LED_MAX_BRIGHTNESS 40
|
||||
// Equipment stop pin
|
||||
virtual gpio_num_t EQUIPMENT_STOP_PIN() { return GPIO_NUM_35; }
|
||||
|
||||
// Equipment stop pin
|
||||
#define EQUIPMENT_STOP_PIN 35
|
||||
// Battery wake up pins
|
||||
virtual gpio_num_t WUP_PIN1() { return GPIO_NUM_25; }
|
||||
virtual gpio_num_t WUP_PIN2() { return GPIO_NUM_32; }
|
||||
};
|
||||
|
||||
// BMW_I3_BATTERY wake up pin
|
||||
#define WUP_PIN1 GPIO_NUM_25 // Wake up pin for battery 1
|
||||
#define WUP_PIN2 GPIO_NUM_32 // Wake up pin for battery 2
|
||||
#define HalClass LilyGoHal
|
||||
|
||||
/* ----- Error checks below, don't change (can't be moved to separate file) ----- */
|
||||
#ifndef HW_CONFIGURED
|
||||
|
@ -85,42 +87,4 @@
|
|||
#error Multiple HW defined! Please select a single HW
|
||||
#endif
|
||||
|
||||
#if defined(CAN_ADDON) && defined(CANFD_ADDON)
|
||||
// Check that user did not try to use dual can and fd-can on same hardware pins
|
||||
#error CAN_ADDON AND CANFD_ADDON CANNOT BE USED SIMULTANEOUSLY
|
||||
#endif
|
||||
|
||||
#if defined(SMA_BYD_H_CAN) || defined(SMA_BYD_HVS_CAN) || defined(SMA_TRIPOWER_CAN)
|
||||
#if defined(CAN_ADDON) || defined(CANFD_ADDON)
|
||||
#error Pin 5 used by both Enable line and for CAN-ADDON. Please reconfigure this, and remove this line to proceed
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef CHADEMO_BATTERY
|
||||
#ifdef CAN_ADDON
|
||||
#error CHADEMO and CAN_ADDON cannot coexist due to overlapping GPIO pin usage
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef EQUIPMENT_STOP_BUTTON
|
||||
#ifdef CAN_ADDON
|
||||
#error EQUIPMENT_STOP_BUTTON and CAN_ADDON cannot coexist due to overlapping GPIO pin usage
|
||||
#endif
|
||||
#ifdef CANFD_ADDON
|
||||
#error EQUIPMENT_STOP_BUTTON and CANFD_ADDON cannot coexist due to overlapping GPIO pin usage
|
||||
#endif
|
||||
#ifdef CHADEMO_BATTERY
|
||||
#error EQUIPMENT_STOP_BUTTON and CHADEMO_BATTERY cannot coexist due to overlapping GPIO pin usage
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef BMW_I3_BATTERY
|
||||
#if defined(CONTACTOR_CONTROL) && defined(WUP_PIN1)
|
||||
#error GPIO PIN 25 cannot be used for both BMWi3 Wakeup and contactor control. Disable CONTACTOR_CONTROL
|
||||
#endif
|
||||
#if defined(CONTACTOR_CONTROL) && defined(WUP_PIN2)
|
||||
#error GPIO PIN 32 cannot be used for both BMWi3 Wakeup and contactor control. Disable CONTACTOR_CONTROL
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#ifndef __HW_STARK_H__
|
||||
#define __HW_STARK_H__
|
||||
|
||||
#include "hal.h"
|
||||
|
||||
/*
|
||||
Stark CMR v1 - DIN-rail module with 4 power outputs, 1 x rs485, 1 x can and 1 x can-fd channel.
|
||||
For more information on this board visit the project discord or contact johan@redispose.se
|
||||
|
@ -15,15 +17,13 @@ GPIOs on extra header
|
|||
* GPIO 15 (JTAG TDO)
|
||||
*/
|
||||
|
||||
// Board boot-up time
|
||||
#define BOOTUP_TIME 5000 // Time in ms it takes before system is considered fully started up
|
||||
class StarkHal : public Esp32Hal {
|
||||
public:
|
||||
const char* name() { return "Stark CMR Module"; }
|
||||
duration BOOTUP_TIME() { return milliseconds(5000); }
|
||||
};
|
||||
|
||||
// Core assignment
|
||||
#define CORE_FUNCTION_CORE 1
|
||||
#define MODBUS_CORE 0
|
||||
#define WIFI_CORE 0
|
||||
|
||||
// RS485
|
||||
/*// RS485
|
||||
// #define PIN_5V_EN 16 // Not needed, GPIO 16 has hardware pullup for PSRAM compatibility
|
||||
// #define RS485_EN_PIN 17 // Not needed, GPIO 17 is used as SCK input of MCP2517
|
||||
#define RS485_TX_PIN 22
|
||||
|
@ -70,7 +70,7 @@ GPIOs on extra header
|
|||
#define WUP_PIN1 GPIO_NUM_25 // Wake up pin for battery 1
|
||||
#define WUP_PIN2 GPIO_NUM_32 // Wake up pin for battery 2
|
||||
|
||||
/* ----- Error checks below, don't change (can't be moved to separate file) ----- */
|
||||
// ----- Error checks below, don't change (can't be moved to separate file) -----
|
||||
#ifndef HW_CONFIGURED
|
||||
#define HW_CONFIGURED
|
||||
#else
|
||||
|
@ -85,5 +85,6 @@ GPIOs on extra header
|
|||
#error GPIO PIN 32 cannot be used for both BMWi3 Wakeup and contactor control. Disable CONTACTOR_CONTROL
|
||||
#endif
|
||||
#endif // BMW_I3_BATTERY
|
||||
*/
|
||||
|
||||
#endif // __HW_STARK_H__
|
||||
|
|
|
@ -246,13 +246,13 @@ static bool publish_common_info(void) {
|
|||
doc["pause_status"] = get_emulator_pause_status();
|
||||
|
||||
//only publish these values if BMS is active and we are comunication with the battery (can send CAN messages to the battery)
|
||||
if (datalayer.battery.status.CAN_battery_still_alive && allowed_to_send_CAN && millis() > BOOTUP_TIME) {
|
||||
if (datalayer.battery.status.CAN_battery_still_alive && allowed_to_send_CAN && esp32hal->system_booted_up()) {
|
||||
set_battery_attributes(doc, datalayer.battery, "", battery->supports_charged_energy());
|
||||
}
|
||||
|
||||
if (battery2) {
|
||||
//only publish these values if BMS is active and we are comunication with the battery (can send CAN messages to the battery)
|
||||
if (datalayer.battery2.status.CAN_battery_still_alive && allowed_to_send_CAN && millis() > BOOTUP_TIME) {
|
||||
if (datalayer.battery2.status.CAN_battery_still_alive && allowed_to_send_CAN && esp32hal->system_booted_up()) {
|
||||
set_battery_attributes(doc, datalayer.battery2, "_2", battery2->supports_charged_energy());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
#include "sdcard.h"
|
||||
#include "freertos/ringbuf.h"
|
||||
|
||||
#if defined(SD_CS_PIN) && defined(SD_SCLK_PIN) && defined(SD_MOSI_PIN) && \
|
||||
defined(SD_MISO_PIN) // ensure code is only compiled if all SD card pins are defined
|
||||
|
||||
File can_log_file;
|
||||
File log_file;
|
||||
RingbufHandle_t can_bufferHandle;
|
||||
|
@ -185,11 +182,20 @@ void init_logging_buffers() {
|
|||
#endif // defined(LOG_TO_SD)
|
||||
}
|
||||
|
||||
void init_sdcard() {
|
||||
bool init_sdcard() {
|
||||
|
||||
pinMode(SD_MISO_PIN, INPUT_PULLUP);
|
||||
auto miso_pin = esp32hal->SD_MISO_PIN();
|
||||
auto mosi_pin = esp32hal->SD_MOSI_PIN();
|
||||
auto miso_pin = esp32hal->SD_MISO_PIN();
|
||||
auto sclk_pin = esp32hal->SD_SCLK_PIN();
|
||||
|
||||
SD_MMC.setPins(SD_SCLK_PIN, SD_MOSI_PIN, SD_MISO_PIN);
|
||||
if (!esp32hal->alloc_pins("SD Card", miso_pin, mosi_pin)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pinMode(miso_pin, INPUT_PULLUP);
|
||||
|
||||
SD_MMC.setPins(sclk_pin, mosi_pin, miso_pin);
|
||||
if (!SD_MMC.begin("/root", true, true, SDMMC_FREQ_HIGHSPEED)) {
|
||||
set_event_latched(EVENT_SD_INIT_FAILED, 0);
|
||||
#ifdef DEBUG_LOG
|
||||
|
@ -208,6 +214,8 @@ void init_sdcard() {
|
|||
#ifdef DEBUG_LOG
|
||||
log_sdcard_details();
|
||||
#endif // DEBUG_LOG
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void log_sdcard_details() {
|
||||
|
@ -245,4 +253,3 @@ void log_sdcard_details() {
|
|||
logging.println(" MB");
|
||||
}
|
||||
}
|
||||
#endif // defined(SD_CS_PIN) && defined(SD_SCLK_PIN) && defined(SD_MOSI_PIN) && defined(SD_MISO_PIN)
|
||||
|
|
|
@ -6,14 +6,12 @@
|
|||
#include "../hal/hal.h"
|
||||
#include "../utils/events.h"
|
||||
|
||||
#if defined(SD_CS_PIN) && defined(SD_SCLK_PIN) && defined(SD_MOSI_PIN) && \
|
||||
defined(SD_MISO_PIN) // ensure code is only compiled if all SD card pins are defined
|
||||
#define CAN_LOG_FILE "/canlog.txt"
|
||||
#define LOG_FILE "/log.txt"
|
||||
|
||||
void init_logging_buffers();
|
||||
|
||||
void init_sdcard();
|
||||
bool init_sdcard();
|
||||
void log_sdcard_details();
|
||||
|
||||
void add_can_frame_to_buffer(CAN_frame frame, frameDirection msgDir);
|
||||
|
@ -29,5 +27,4 @@ void pause_log_writing();
|
|||
void add_log_to_buffer(const uint8_t* buffer, size_t size);
|
||||
void write_log_to_sdcard();
|
||||
|
||||
#endif // defined(SD_CS_PIN) && defined(SD_SCLK_PIN) && defined(SD_MOSI_PIN) && defined(SD_MISO_PIN)
|
||||
#endif // SDCARD_H
|
||||
|
|
|
@ -16,16 +16,23 @@ static const float heartbeat_peak1 = 0.80;
|
|||
static const float heartbeat_peak2 = 0.55;
|
||||
static const float heartbeat_deviation = 0.05;
|
||||
|
||||
static LED led(datalayer.battery.status.led_mode);
|
||||
static LED* led;
|
||||
|
||||
void led_init(void) {
|
||||
led.init();
|
||||
bool led_init(void) {
|
||||
if (!esp32hal->alloc_pins("LED", esp32hal->LED_PIN())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
led = new LED(datalayer.battery.status.led_mode, esp32hal->LED_PIN(), esp32hal->LED_MAX_BRIGHTNESS());
|
||||
led->init();
|
||||
|
||||
return true;
|
||||
}
|
||||
void led_exe(void) {
|
||||
led.exe();
|
||||
led->exe();
|
||||
}
|
||||
led_color led_get_color() {
|
||||
return led.color;
|
||||
return led->color;
|
||||
}
|
||||
|
||||
void LED::exe(void) {
|
||||
|
@ -61,7 +68,7 @@ void LED::exe(void) {
|
|||
break;
|
||||
case EVENT_LEVEL_ERROR:
|
||||
color = led_color::RED;
|
||||
pixels.setPixelColor(0, COLOR_RED(LED_MAX_BRIGHTNESS)); // Red LED full brightness
|
||||
pixels.setPixelColor(0, COLOR_RED(esp32hal->LED_MAX_BRIGHTNESS())); // Red LED full brightness
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -126,7 +133,7 @@ void LED::heartbeat_run(void) {
|
|||
brightness_f = map_float(period_pct, 0.55f, 1.00f, heartbeat_base + heartbeat_deviation * 2, heartbeat_base);
|
||||
}
|
||||
|
||||
brightness = (uint8_t)(brightness_f * LED_MAX_BRIGHTNESS);
|
||||
brightness = (uint8_t)(brightness_f * esp32hal->LED_MAX_BRIGHTNESS());
|
||||
}
|
||||
|
||||
uint8_t LED::up_down(float middle_point_f) {
|
||||
|
@ -138,7 +145,7 @@ uint8_t LED::up_down(float middle_point_f) {
|
|||
if (ms < middle_point) {
|
||||
brightness = map_uint16(ms, 0, middle_point, 0, max_brightness);
|
||||
} else {
|
||||
brightness = LED_MAX_BRIGHTNESS - map_uint16(ms, middle_point, LED_PERIOD_MS, 0, max_brightness);
|
||||
brightness = esp32hal->LED_MAX_BRIGHTNESS() - map_uint16(ms, middle_point, LED_PERIOD_MS, 0, max_brightness);
|
||||
}
|
||||
return CONSTRAIN(brightness, 0, max_brightness);
|
||||
}
|
||||
|
|
|
@ -8,14 +8,14 @@ class LED {
|
|||
public:
|
||||
led_color color = led_color::GREEN;
|
||||
|
||||
LED()
|
||||
: pixels(1, LED_PIN, NEO_GRB),
|
||||
max_brightness(LED_MAX_BRIGHTNESS),
|
||||
brightness(LED_MAX_BRIGHTNESS),
|
||||
LED(gpio_num_t pin, uint8_t maxBrightness)
|
||||
: pixels(1, pin, NEO_GRB),
|
||||
max_brightness(maxBrightness),
|
||||
brightness(maxBrightness),
|
||||
mode(led_mode_enum::CLASSIC) {}
|
||||
|
||||
LED(led_mode_enum mode)
|
||||
: pixels(1, LED_PIN, NEO_GRB), max_brightness(LED_MAX_BRIGHTNESS), brightness(LED_MAX_BRIGHTNESS), mode(mode) {}
|
||||
LED(led_mode_enum mode, gpio_num_t pin, uint8_t maxBrightness)
|
||||
: pixels(1, pin, NEO_GRB), max_brightness(maxBrightness), brightness(maxBrightness), mode(mode) {}
|
||||
|
||||
void exe(void);
|
||||
void init(void) { pixels.begin(); }
|
||||
|
@ -33,7 +33,7 @@ class LED {
|
|||
uint8_t up_down(float middle_point_f);
|
||||
};
|
||||
|
||||
void led_init(void);
|
||||
bool led_init(void);
|
||||
void led_exe(void);
|
||||
led_color led_get_color(void);
|
||||
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
#ifndef _TYPES_H_
|
||||
#define _TYPES_H_
|
||||
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
|
||||
using milliseconds = std::chrono::milliseconds;
|
||||
using duration = std::chrono::duration<unsigned long, std::ratio<1, 1000>>;
|
||||
|
||||
enum bms_status_enum { STANDBY = 0, INACTIVE = 1, DARKSTART = 2, ACTIVE = 3, FAULT = 4, UPDATING = 5 };
|
||||
enum real_bms_status_enum { BMS_DISCONNECTED = 0, BMS_STANDBY = 1, BMS_ACTIVE = 2, BMS_FAULT = 3 };
|
||||
enum battery_chemistry_enum { NCA, NMC, LFP };
|
||||
|
@ -42,7 +46,20 @@ enum PrechargeState {
|
|||
#define CAN_STILL_ALIVE 60
|
||||
// Set by battery each time we get a CAN message. Decrements every second. When reaching 0, sets event
|
||||
|
||||
typedef enum { CAN_NATIVE = 0, CANFD_NATIVE = 1, CAN_ADDON_MCP2515 = 2, CANFD_ADDON_MCP2518 = 3 } CAN_Interface;
|
||||
enum CAN_Interface {
|
||||
// Native CAN port on the LilyGo & Stark hardware
|
||||
CAN_NATIVE = 0,
|
||||
|
||||
// Native CANFD port on the Stark CMR hardware
|
||||
CANFD_NATIVE = 1,
|
||||
|
||||
// Add-on CAN MCP2515 connected to GPIO pins
|
||||
CAN_ADDON_MCP2515 = 2,
|
||||
|
||||
// Add-on CAN-FD MCP2518 connected to GPIO pins
|
||||
CANFD_ADDON_MCP2518 = 3
|
||||
};
|
||||
|
||||
extern const char* getCANInterfaceName(CAN_Interface interface);
|
||||
|
||||
/* CAN Frame structure */
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include <Arduino.h>
|
||||
#include "../../../src/communication/contactorcontrol/comm_contactorcontrol.h"
|
||||
#include "../../charger/CHARGERS.h"
|
||||
#include "../../communication/can/comm_can.h"
|
||||
#include "../../communication/nvm/comm_nvm.h"
|
||||
#include "../../datalayer/datalayer.h"
|
||||
#include "../../include.h"
|
||||
|
@ -67,6 +68,19 @@ void render_checkbox(String& content, const char* label, bool enabled, const cha
|
|||
content += " value='on'/>";
|
||||
}
|
||||
|
||||
const char* name_for_button_type(STOP_BUTTON_BEHAVIOR behavior) {
|
||||
switch (behavior) {
|
||||
case STOP_BUTTON_BEHAVIOR::LATCHING_SWITCH:
|
||||
return "Latching";
|
||||
case STOP_BUTTON_BEHAVIOR::MOMENTARY_SWITCH:
|
||||
return "Momentary";
|
||||
case STOP_BUTTON_BEHAVIOR::NOT_CONNECTED:
|
||||
return "Not connected";
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
String settings_processor(const String& var) {
|
||||
if (var == "X") {
|
||||
String content = "";
|
||||
|
@ -116,6 +130,12 @@ String settings_processor(const String& var) {
|
|||
options_for_enum((ChargerType)settings.getUInt("CHGTYPE", (int)ChargerType::None), name_for_charger_type);
|
||||
content += "</select>";
|
||||
|
||||
content += "<label>Equipment stop button: </label><select style='max-width: 250px;' name='EQSTOP'>";
|
||||
content +=
|
||||
options_for_enum((STOP_BUTTON_BEHAVIOR)settings.getUInt("EQSTOP", (int)STOP_BUTTON_BEHAVIOR::NOT_CONNECTED),
|
||||
name_for_button_type);
|
||||
content += "</select>";
|
||||
|
||||
// TODO: Generalize settings: define settings in one place and use the definitions to render
|
||||
// UI and handle load/save
|
||||
render_checkbox(content, "Double battery", settings.getBool("DBLBTR"), "DBLBTR");
|
||||
|
@ -124,6 +144,7 @@ String settings_processor(const String& var) {
|
|||
render_checkbox(content, "PWM contactor control", settings.getBool("PWMCNTCTRL"), "PWMCNTCTRL");
|
||||
render_checkbox(content, "Periodic BMS reset", settings.getBool("PERBMSRESET"), "PERBMSRESET");
|
||||
render_checkbox(content, "Remote BMS reset", settings.getBool("REMBMSRESET"), "REMBMSRESET");
|
||||
render_checkbox(content, "Use CanFD as classic CAN", settings.getBool("CANFDASCAN"), "CANFDASCAN");
|
||||
|
||||
content +=
|
||||
"<div style='grid-column: span 2; text-align: center; padding-top: 10px;'><button "
|
||||
|
@ -454,19 +475,19 @@ const char* getCANInterfaceName(CAN_Interface interface) {
|
|||
case CAN_NATIVE:
|
||||
return "CAN";
|
||||
case CANFD_NATIVE:
|
||||
#ifdef USE_CANFD_INTERFACE_AS_CLASSIC_CAN
|
||||
return "CAN-FD Native (Classic CAN)";
|
||||
#else
|
||||
return "CAN-FD Native";
|
||||
#endif
|
||||
if (use_canfd_as_can) {
|
||||
return "CAN-FD Native (Classic CAN)";
|
||||
} else {
|
||||
return "CAN-FD Native";
|
||||
}
|
||||
case CAN_ADDON_MCP2515:
|
||||
return "Add-on CAN via GPIO MCP2515";
|
||||
case CANFD_ADDON_MCP2518:
|
||||
#ifdef USE_CANFD_INTERFACE_AS_CLASSIC_CAN
|
||||
return "Add-on CAN-FD via GPIO MCP2518 (Classic CAN)";
|
||||
#else
|
||||
return "Add-on CAN-FD via GPIO MCP2518";
|
||||
#endif
|
||||
if (use_canfd_as_can) {
|
||||
return "Add-on CAN-FD via GPIO MCP2518 (Classic CAN)";
|
||||
} else {
|
||||
return "Add-on CAN-FD via GPIO MCP2518";
|
||||
}
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
|
|
@ -394,7 +394,8 @@ void init_webserver() {
|
|||
bool newValue;
|
||||
};
|
||||
|
||||
const char* boolSettingNames[] = {"DBLBTR", "CNTCTRL", "CNTCTRLDBL", "PWMCNTCTRL", "PERBMSRESET", "REMBMSRESET"};
|
||||
const char* boolSettingNames[] = {"DBLBTR", "CNTCTRL", "CNTCTRLDBL", "PWMCNTCTRL",
|
||||
"PERBMSRESET", "REMBMSRESET", "CANFDASCAN"};
|
||||
|
||||
#ifdef COMMON_IMAGE
|
||||
// Handles the form POST from UI to save certain settings: battery/inverter type and double battery on/off
|
||||
|
@ -419,19 +420,10 @@ void init_webserver() {
|
|||
} else if (p->name() == "charger") {
|
||||
auto type = static_cast<ChargerType>(atoi(p->value().c_str()));
|
||||
settings.saveUInt("CHGTYPE", (int)type);
|
||||
} /*else if (p->name() == "dblbtr") {
|
||||
newDoubleBattery = p->value() == "on";
|
||||
} else if (p->name() == "contctrl") {
|
||||
settings.saveBool("CNTCTRL", p->value() == "on");
|
||||
} else if (p->name() == "contctrldbl") {
|
||||
settings.saveBool("CNTCTRLDBL", p->value() == "on");
|
||||
} else if (p->name() == "pwmcontctrl") {
|
||||
settings.saveBool("PWMCNTCTRL", p->value() == "on");
|
||||
} else if (p->name() == "PERBMSRESET") {
|
||||
settings.saveBool("PERBMSRESET", p->value() == "on");
|
||||
} else if (p->name() == "REMBMSRESET") {
|
||||
settings.saveBool("REMBMSRESET", p->value() == "on");
|
||||
}*/
|
||||
} else if (p->name() == "EQSTOP") {
|
||||
auto type = static_cast<STOP_BUTTON_BEHAVIOR>(atoi(p->value().c_str()));
|
||||
settings.saveUInt("EQSTOP", (int)type);
|
||||
}
|
||||
|
||||
for (auto& boolSetting : boolSettings) {
|
||||
if (p->name() == boolSetting.name) {
|
||||
|
@ -929,19 +921,8 @@ String get_firmware_info_processor(const String& var) {
|
|||
if (var == "X") {
|
||||
String content = "";
|
||||
static JsonDocument doc;
|
||||
#ifdef HW_LILYGO
|
||||
doc["hardware"] = "LilyGo T-CAN485";
|
||||
#endif // HW_LILYGO
|
||||
#ifdef HW_STARK
|
||||
doc["hardware"] = "Stark CMR Module";
|
||||
#endif // HW_STARK
|
||||
#ifdef HW_3LB
|
||||
doc["hardware"] = "3LB board";
|
||||
#endif // HW_3LB
|
||||
#ifdef HW_DEVKIT
|
||||
doc["hardware"] = "ESP32 DevKit V1";
|
||||
#endif // HW_DEVKIT
|
||||
|
||||
doc["hardware"] = esp32hal->name();
|
||||
doc["firmware"] = String(version_number);
|
||||
serializeJson(doc, content);
|
||||
return content;
|
||||
|
@ -1022,7 +1003,7 @@ String processor(const String& var) {
|
|||
// Display which components are used
|
||||
if (inverter) {
|
||||
content += "<h4 style='color: white;'>Inverter protocol: ";
|
||||
content += datalayer.system.info.inverter_protocol;
|
||||
content += inverter->name();
|
||||
content += " ";
|
||||
content += datalayer.system.info.inverter_brand;
|
||||
content += "</h4>";
|
||||
|
|
|
@ -19,17 +19,10 @@
|
|||
|
||||
/* - ERROR CHECKS BELOW, DON'T TOUCH - */
|
||||
|
||||
#if !defined(HW_CONFIGURED)
|
||||
#if !defined(HW_LILYGO) || !defined(HW_STARK) || !defined(HW_3LB) || !defined(HW_DEVKIT)
|
||||
#error You must select a target hardware in the USER_SETTINGS.h file!
|
||||
#endif
|
||||
|
||||
#ifdef USE_CANFD_INTERFACE_AS_CLASSIC_CAN
|
||||
#if !defined(CANFD_ADDON)
|
||||
// Check that user did not try to use classic CAN over FD, without FD component
|
||||
#error PLEASE ENABLE CANFD_ADDON TO USE CLASSIC CAN OVER CANFD INTERFACE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef HW_LILYGO
|
||||
#if defined(PERIODIC_BMS_RESET) || defined(REMOTE_BMS_RESET)
|
||||
#if defined(CAN_ADDON) || defined(CANFD_ADDON) || defined(CHADEMO_BATTERY)
|
||||
|
|
|
@ -169,8 +169,3 @@ void AforeCanInverter::transmit_can(unsigned long currentMillis) {
|
|||
time_to_send_info = false;
|
||||
}
|
||||
}
|
||||
|
||||
void AforeCanInverter::setup(void) { // Performs one time setup at startup over CAN bus
|
||||
strncpy(datalayer.system.info.inverter_protocol, Name, 63);
|
||||
datalayer.system.info.inverter_protocol[63] = '\0';
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
class AforeCanInverter : public CanInverterProtocol {
|
||||
public:
|
||||
void setup();
|
||||
const char* name() override { return Name; }
|
||||
void transmit_can(unsigned long currentMillis);
|
||||
void map_can_frame_to_variable(CAN_frame rx_frame);
|
||||
void update_values();
|
||||
|
|
|
@ -169,8 +169,3 @@ void BydCanInverter::send_initial_data() {
|
|||
transmit_can_frame(&BYD_3D0_2, can_config.inverter);
|
||||
transmit_can_frame(&BYD_3D0_3, can_config.inverter);
|
||||
}
|
||||
|
||||
void BydCanInverter::setup(void) { // Performs one time setup at startup over CAN bus
|
||||
strncpy(datalayer.system.info.inverter_protocol, Name, 63);
|
||||
datalayer.system.info.inverter_protocol[63] = '\0';
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
class BydCanInverter : public CanInverterProtocol {
|
||||
public:
|
||||
void setup();
|
||||
const char* name() override { return Name; }
|
||||
void transmit_can(unsigned long currentMillis);
|
||||
void map_can_frame_to_variable(CAN_frame rx_frame);
|
||||
void update_values();
|
||||
|
|
|
@ -145,17 +145,21 @@ void BydModbusInverter::verify_inverter_modbus() {
|
|||
}
|
||||
}
|
||||
|
||||
void BydModbusInverter::setup(void) { // Performs one time setup at startup over CAN bus
|
||||
strncpy(datalayer.system.info.inverter_protocol, Name, 63);
|
||||
datalayer.system.info.inverter_protocol[63] = '\0';
|
||||
|
||||
bool BydModbusInverter::setup(void) { // Performs one time setup at startup over CAN bus
|
||||
// Init Static data to the RTU Modbus
|
||||
handle_static_data();
|
||||
|
||||
// Init Serial2 connected to the RTU Modbus
|
||||
RTUutils::prepareHardwareSerial(Serial2);
|
||||
|
||||
Serial2.begin(9600, SERIAL_8N1, RS485_RX_PIN, RS485_TX_PIN);
|
||||
auto rx_pin = esp32hal->RS485_RX_PIN();
|
||||
auto tx_pin = esp32hal->RS485_TX_PIN();
|
||||
|
||||
if (!esp32hal->alloc_pins(Name, rx_pin, tx_pin)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Serial2.begin(9600, SERIAL_8N1, rx_pin, tx_pin);
|
||||
// Register served function code worker for server
|
||||
MBserver.registerWorker(MBTCP_ID, READ_HOLD_REGISTER, &FC03);
|
||||
MBserver.registerWorker(MBTCP_ID, WRITE_HOLD_REGISTER, &FC06);
|
||||
|
@ -163,5 +167,7 @@ void BydModbusInverter::setup(void) { // Performs one time setup at startup ove
|
|||
MBserver.registerWorker(MBTCP_ID, R_W_MULT_REGISTERS, &FC23);
|
||||
|
||||
// Start ModbusRTU background task
|
||||
MBserver.begin(Serial2, MODBUS_CORE);
|
||||
MBserver.begin(Serial2, esp32hal->MODBUS_CORE());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -10,7 +10,8 @@
|
|||
|
||||
class BydModbusInverter : public ModbusInverterProtocol {
|
||||
public:
|
||||
void setup();
|
||||
const char* name() override { return Name; }
|
||||
bool setup() override;
|
||||
void update_values();
|
||||
static constexpr char* Name = "BYD 11kWh HVM battery over Modbus RTU";
|
||||
|
||||
|
|
|
@ -365,8 +365,3 @@ void FerroampCanInverter::send_system_data() { //System equipment information
|
|||
transmit_can_frame(&PYLON_4291, can_config.inverter);
|
||||
#endif
|
||||
}
|
||||
|
||||
void FerroampCanInverter::setup(void) { // Performs one time setup at startup over CAN bus
|
||||
strncpy(datalayer.system.info.inverter_protocol, Name, 63);
|
||||
datalayer.system.info.inverter_protocol[63] = '\0';
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
class FerroampCanInverter : public CanInverterProtocol {
|
||||
public:
|
||||
void setup();
|
||||
const char* name() override { return Name; }
|
||||
void update_values();
|
||||
void transmit_can(unsigned long currentMillis);
|
||||
void map_can_frame_to_variable(CAN_frame rx_frame);
|
||||
|
|
|
@ -561,7 +561,3 @@ void FoxessCanInverter::map_can_frame_to_variable(CAN_frame rx_frame) {
|
|||
}
|
||||
}
|
||||
}
|
||||
void FoxessCanInverter::setup(void) { // Performs one time setup at startup over CAN bus
|
||||
strncpy(datalayer.system.info.inverter_protocol, Name, 63);
|
||||
datalayer.system.info.inverter_protocol[63] = '\0';
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
class FoxessCanInverter : public CanInverterProtocol {
|
||||
public:
|
||||
void setup();
|
||||
const char* name() override { return Name; }
|
||||
void update_values();
|
||||
void transmit_can(unsigned long currentMillis);
|
||||
void map_can_frame_to_variable(CAN_frame rx_frame);
|
||||
|
|
|
@ -449,8 +449,3 @@ void GrowattHvInverter::transmit_can(unsigned long currentMillis) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GrowattHvInverter::setup(void) { // Performs one time setup at startup over CAN bus
|
||||
strncpy(datalayer.system.info.inverter_protocol, Name, 63);
|
||||
datalayer.system.info.inverter_protocol[63] = '\0';
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
class GrowattHvInverter : public CanInverterProtocol {
|
||||
public:
|
||||
void setup();
|
||||
const char* name() override { return Name; }
|
||||
void update_values();
|
||||
void transmit_can(unsigned long currentMillis);
|
||||
void map_can_frame_to_variable(CAN_frame rx_frame);
|
||||
|
|
|
@ -202,8 +202,3 @@ void GrowattLvInverter::map_can_frame_to_variable(CAN_frame rx_frame) {
|
|||
void GrowattLvInverter::transmit_can(unsigned long currentMillis) {
|
||||
// No periodic sending for this battery type. Data is sent when inverter requests it
|
||||
}
|
||||
|
||||
void GrowattLvInverter::setup(void) { // Performs one time setup at startup over CAN bus
|
||||
strncpy(datalayer.system.info.inverter_protocol, Name, 63);
|
||||
datalayer.system.info.inverter_protocol[63] = '\0';
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
class GrowattLvInverter : public CanInverterProtocol {
|
||||
public:
|
||||
void setup();
|
||||
const char* name() override { return Name; }
|
||||
void update_values();
|
||||
void transmit_can(unsigned long currentMillis);
|
||||
void map_can_frame_to_variable(CAN_frame rx_frame);
|
||||
|
|
|
@ -81,9 +81,9 @@ extern const char* name_for_inverter_type(InverterProtocolType type) {
|
|||
#error "Compile time SELECTED_INVERTER_CLASS should not be defined with COMMON_IMAGE"
|
||||
#endif
|
||||
|
||||
void setup_inverter() {
|
||||
bool setup_inverter() {
|
||||
if (inverter) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (user_selected_inverter_protocol) {
|
||||
|
@ -160,6 +160,7 @@ void setup_inverter() {
|
|||
break;
|
||||
|
||||
case InverterProtocolType::None:
|
||||
return true;
|
||||
case InverterProtocolType::Highest:
|
||||
default:
|
||||
inverter = nullptr; // Or handle as error
|
||||
|
@ -167,23 +168,29 @@ void setup_inverter() {
|
|||
}
|
||||
|
||||
if (inverter) {
|
||||
inverter->setup();
|
||||
return inverter->setup();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#else
|
||||
void setup_inverter() {
|
||||
bool setup_inverter() {
|
||||
if (inverter) {
|
||||
// The inverter is setup only once.
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef SELECTED_INVERTER_CLASS
|
||||
inverter = new SELECTED_INVERTER_CLASS();
|
||||
|
||||
if (inverter) {
|
||||
inverter->setup();
|
||||
return inverter->setup();
|
||||
}
|
||||
|
||||
return false;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -31,6 +31,6 @@ extern InverterProtocol* inverter;
|
|||
#include "SUNGROW-CAN.h"
|
||||
|
||||
// Call to initialize the build-time selected inverter. Safe to call even though inverter was not selected.
|
||||
void setup_inverter();
|
||||
bool setup_inverter();
|
||||
|
||||
#endif
|
||||
|
|
|
@ -34,7 +34,8 @@ enum class InverterInterfaceType { Can, Rs485, Modbus };
|
|||
// The abstract base class for all inverter protocols
|
||||
class InverterProtocol {
|
||||
public:
|
||||
virtual void setup() = 0;
|
||||
virtual const char* name() = 0;
|
||||
virtual bool setup() {}
|
||||
virtual const char* interface_name() = 0;
|
||||
virtual InverterInterfaceType interface_type() = 0;
|
||||
|
||||
|
|
|
@ -301,11 +301,18 @@ void KostalInverterProtocol::receive() // Runs as fast as possible to handle th
|
|||
}
|
||||
}
|
||||
|
||||
void KostalInverterProtocol::setup(void) { // Performs one time setup at startup
|
||||
bool KostalInverterProtocol::setup(void) { // Performs one time setup at startup
|
||||
datalayer.system.status.inverter_allows_contactor_closing = false;
|
||||
dbg_message("inverter_allows_contactor_closing -> false");
|
||||
strncpy(datalayer.system.info.inverter_protocol, Name, 63);
|
||||
datalayer.system.info.inverter_protocol[63] = '\0';
|
||||
|
||||
Serial2.begin(baud_rate(), SERIAL_8N1, RS485_RX_PIN, RS485_TX_PIN);
|
||||
auto rx_pin = esp32hal->RS485_RX_PIN();
|
||||
auto tx_pin = esp32hal->RS485_TX_PIN();
|
||||
|
||||
if (!esp32hal->alloc_pins(Name, rx_pin, tx_pin)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Serial2.begin(baud_rate(), SERIAL_8N1, rx_pin, tx_pin);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -19,7 +19,8 @@
|
|||
|
||||
class KostalInverterProtocol : public Rs485InverterProtocol {
|
||||
public:
|
||||
void setup();
|
||||
const char* name() override { return Name; }
|
||||
bool setup() override;
|
||||
void receive();
|
||||
void update_values();
|
||||
static constexpr char* Name = "BYD battery via Kostal RS485";
|
||||
|
|
|
@ -352,8 +352,3 @@ void PylonInverter::send_system_data() { //System equipment information
|
|||
transmit_can_frame(&PYLON_4291, can_config.inverter);
|
||||
#endif
|
||||
}
|
||||
|
||||
void PylonInverter::setup(void) { // Performs one time setup at startup over CAN bus
|
||||
strncpy(datalayer.system.info.inverter_protocol, Name, 63);
|
||||
datalayer.system.info.inverter_protocol[63] = '\0';
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
class PylonInverter : public CanInverterProtocol {
|
||||
public:
|
||||
void setup();
|
||||
const char* name() override { return Name; }
|
||||
void update_values();
|
||||
void transmit_can(unsigned long currentMillis);
|
||||
void map_can_frame_to_variable(CAN_frame rx_frame);
|
||||
|
|
|
@ -144,8 +144,3 @@ void PylonLvInverter::transmit_can(unsigned long currentMillis) {
|
|||
transmit_can_frame(&PYLON_35E, can_config.inverter);
|
||||
}
|
||||
}
|
||||
|
||||
void PylonLvInverter::setup(void) { // Performs one time setup at startup over CAN bus
|
||||
strncpy(datalayer.system.info.inverter_protocol, Name, 63);
|
||||
datalayer.system.info.inverter_protocol[63] = '\0';
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
class PylonLvInverter : public CanInverterProtocol {
|
||||
public:
|
||||
void setup();
|
||||
const char* name() override { return Name; }
|
||||
void update_values();
|
||||
void transmit_can(unsigned long currentMillis);
|
||||
void map_can_frame_to_variable(CAN_frame rx_frame);
|
||||
|
|
|
@ -225,8 +225,3 @@ void SchneiderInverter::transmit_can(unsigned long currentMillis) {
|
|||
transmit_can_frame(&SE_333, can_config.inverter);
|
||||
}
|
||||
}
|
||||
|
||||
void SchneiderInverter::setup(void) { // Performs one time setup
|
||||
strncpy(datalayer.system.info.inverter_protocol, Name, 63);
|
||||
datalayer.system.info.inverter_protocol[63] = '\0';
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
class SchneiderInverter : public CanInverterProtocol {
|
||||
public:
|
||||
void setup();
|
||||
const char* name() override { return Name; }
|
||||
void update_values();
|
||||
void transmit_can(unsigned long currentMillis);
|
||||
void map_can_frame_to_variable(CAN_frame rx_frame);
|
||||
|
|
|
@ -69,16 +69,7 @@ void SmaBydHInverter::
|
|||
SMA_158.data.u8[2] = 0x6A;
|
||||
}
|
||||
|
||||
#ifdef INVERTER_CONTACTOR_ENABLE_LED_PIN
|
||||
// Inverter allows contactor closing
|
||||
if (datalayer.system.status.inverter_allows_contactor_closing) {
|
||||
digitalWrite(INVERTER_CONTACTOR_ENABLE_LED_PIN,
|
||||
HIGH); // Turn on LED to indicate that SMA inverter allows contactor closing
|
||||
} else {
|
||||
digitalWrite(INVERTER_CONTACTOR_ENABLE_LED_PIN,
|
||||
LOW); // Turn off LED to indicate that SMA inverter does not allow contactor closing
|
||||
}
|
||||
#endif // INVERTER_CONTACTOR_ENABLE_LED_PIN
|
||||
control_contactor_led();
|
||||
|
||||
// Check if Enable line is working. If we go too long without any input, raise an event
|
||||
if (!datalayer.system.status.inverter_allows_contactor_closing) {
|
||||
|
@ -258,14 +249,3 @@ void SmaBydHInverter::transmit_can_init() {
|
|||
transmit_can_frame(&SMA_518, can_config.inverter);
|
||||
transmit_can_frame(&SMA_4D8, can_config.inverter);
|
||||
}
|
||||
|
||||
void SmaBydHInverter::setup(void) { // Performs one time setup at startup over CAN bus
|
||||
strncpy(datalayer.system.info.inverter_protocol, Name, 63);
|
||||
datalayer.system.info.inverter_protocol[63] = '\0';
|
||||
datalayer.system.status.inverter_allows_contactor_closing = false; // The inverter needs to allow first
|
||||
pinMode(INVERTER_CONTACTOR_ENABLE_PIN, INPUT);
|
||||
#ifdef INVERTER_CONTACTOR_ENABLE_LED_PIN
|
||||
pinMode(INVERTER_CONTACTOR_ENABLE_LED_PIN, OUTPUT);
|
||||
digitalWrite(INVERTER_CONTACTOR_ENABLE_LED_PIN, LOW); // Turn LED off, until inverter allows contactor closing
|
||||
#endif // INVERTER_CONTACTOR_ENABLE_LED_PIN
|
||||
}
|
||||
|
|
|
@ -2,23 +2,24 @@
|
|||
#define SMA_BYD_H_CAN_H
|
||||
#include "../include.h"
|
||||
|
||||
#include "CanInverterProtocol.h"
|
||||
#include "SmaInverterBase.h"
|
||||
#include "src/devboard/hal/hal.h"
|
||||
|
||||
#ifdef SMA_BYD_H_CAN
|
||||
#define SELECTED_INVERTER_CLASS SmaBydHInverter
|
||||
#endif
|
||||
|
||||
class SmaBydHInverter : public CanInverterProtocol {
|
||||
class SmaBydHInverter : public SmaInverterBase {
|
||||
public:
|
||||
void setup();
|
||||
SmaBydHInverter();
|
||||
|
||||
const char* name() override { return Name; }
|
||||
void update_values();
|
||||
void transmit_can(unsigned long currentMillis);
|
||||
void map_can_frame_to_variable(CAN_frame rx_frame);
|
||||
static constexpr char* Name = "BYD over SMA CAN";
|
||||
|
||||
virtual bool controls_contactor() { return true; }
|
||||
virtual bool allows_contactor_closing() { return digitalRead(INVERTER_CONTACTOR_ENABLE_PIN) == 1; }
|
||||
|
||||
private:
|
||||
static const int READY_STATE = 0x03;
|
||||
|
|
|
@ -68,16 +68,7 @@ void SmaBydHvsInverter::
|
|||
SMA_158.data.u8[2] = 0x6A;
|
||||
}
|
||||
|
||||
#ifdef INVERTER_CONTACTOR_ENABLE_LED_PIN
|
||||
// Inverter allows contactor closing
|
||||
if (datalayer.system.status.inverter_allows_contactor_closing) {
|
||||
digitalWrite(INVERTER_CONTACTOR_ENABLE_LED_PIN,
|
||||
HIGH); // Turn on LED to indicate that SMA inverter allows contactor closing
|
||||
} else {
|
||||
digitalWrite(INVERTER_CONTACTOR_ENABLE_LED_PIN,
|
||||
LOW); // Turn off LED to indicate that SMA inverter does not allow contactor closing
|
||||
}
|
||||
#endif // INVERTER_CONTACTOR_ENABLE_LED_PIN
|
||||
control_contactor_led();
|
||||
|
||||
// Check if Enable line is working. If we go too long without any input, raise an event
|
||||
if (!datalayer.system.status.inverter_allows_contactor_closing) {
|
||||
|
@ -276,14 +267,3 @@ void SmaBydHvsInverter::transmit_can(unsigned long currentMillis) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SmaBydHvsInverter::setup(void) { // Performs one time setup at startup over CAN bus
|
||||
strncpy(datalayer.system.info.inverter_protocol, Name, 63);
|
||||
datalayer.system.info.inverter_protocol[63] = '\0';
|
||||
datalayer.system.status.inverter_allows_contactor_closing = false; // The inverter needs to allow first
|
||||
pinMode(INVERTER_CONTACTOR_ENABLE_PIN, INPUT);
|
||||
#ifdef INVERTER_CONTACTOR_ENABLE_LED_PIN
|
||||
pinMode(INVERTER_CONTACTOR_ENABLE_LED_PIN, OUTPUT);
|
||||
digitalWrite(INVERTER_CONTACTOR_ENABLE_LED_PIN, LOW); // Turn LED off, until inverter allows contactor closing
|
||||
#endif // INVERTER_CONTACTOR_ENABLE_LED_PIN
|
||||
}
|
||||
|
|
|
@ -2,23 +2,22 @@
|
|||
#define SMA_BYD_HVS_CAN_H
|
||||
#include "../include.h"
|
||||
|
||||
#include "CanInverterProtocol.h"
|
||||
#include "SmaInverterBase.h"
|
||||
#include "src/devboard/hal/hal.h"
|
||||
|
||||
#ifdef SMA_BYD_HVS_CAN
|
||||
#define SELECTED_INVERTER_CLASS SmaBydHvsInverter
|
||||
#endif
|
||||
|
||||
class SmaBydHvsInverter : public CanInverterProtocol {
|
||||
class SmaBydHvsInverter : public SmaInverterBase {
|
||||
public:
|
||||
void setup();
|
||||
const char* name() override { return Name; }
|
||||
void update_values();
|
||||
void transmit_can(unsigned long currentMillis);
|
||||
void map_can_frame_to_variable(CAN_frame rx_frame);
|
||||
static constexpr char* Name = "BYD Battery-Box HVS over SMA CAN";
|
||||
|
||||
virtual bool controls_contactor() { return true; }
|
||||
virtual bool allows_contactor_closing() { return digitalRead(INVERTER_CONTACTOR_ENABLE_PIN) == 1; }
|
||||
|
||||
private:
|
||||
static const int READY_STATE = 0x03;
|
||||
|
|
|
@ -107,8 +107,3 @@ void SmaLvInverter::transmit_can(unsigned long currentMillis) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SmaLvInverter::setup(void) { // Performs one time setup at startup over CAN bus
|
||||
strncpy(datalayer.system.info.inverter_protocol, Name, 63);
|
||||
datalayer.system.info.inverter_protocol[63] = '\0';
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
class SmaLvInverter : public CanInverterProtocol {
|
||||
public:
|
||||
void setup();
|
||||
const char* name() override { return Name; }
|
||||
void update_values();
|
||||
void transmit_can(unsigned long currentMillis);
|
||||
void map_can_frame_to_variable(CAN_frame rx_frame);
|
||||
|
|
|
@ -65,6 +65,8 @@ void SmaTripowerInverter::
|
|||
SMA_4D8.data.u8[6] = READY_STATE;
|
||||
}
|
||||
|
||||
control_contactor_led();
|
||||
|
||||
// Check if Enable line is working. If we go too long without any input, raise an event
|
||||
if (!datalayer.system.status.inverter_allows_contactor_closing) {
|
||||
timeWithoutInverterAllowsContactorClosing++;
|
||||
|
@ -181,14 +183,3 @@ void SmaTripowerInverter::transmit_can_init() {
|
|||
pushFrame(&SMA_4D8);
|
||||
pushFrame(&SMA_518, [this]() { this->completePairing(); });
|
||||
}
|
||||
|
||||
void SmaTripowerInverter::setup(void) { // Performs one time setup at startup over CAN bus
|
||||
strncpy(datalayer.system.info.inverter_protocol, Name, 63);
|
||||
datalayer.system.info.inverter_protocol[63] = '\0';
|
||||
datalayer.system.status.inverter_allows_contactor_closing = false; // The inverter needs to allow first
|
||||
pinMode(INVERTER_CONTACTOR_ENABLE_PIN, INPUT);
|
||||
#ifdef INVERTER_CONTACTOR_ENABLE_LED_PIN
|
||||
pinMode(INVERTER_CONTACTOR_ENABLE_LED_PIN, OUTPUT);
|
||||
digitalWrite(INVERTER_CONTACTOR_ENABLE_LED_PIN, LOW); // Turn LED off, until inverter allows contactor closing
|
||||
#endif // INVERTER_CONTACTOR_ENABLE_LED_PIN
|
||||
}
|
||||
|
|
|
@ -2,23 +2,22 @@
|
|||
#define SMA_CAN_TRIPOWER_H
|
||||
#include "../include.h"
|
||||
|
||||
#include "CanInverterProtocol.h"
|
||||
#include "SmaInverterBase.h"
|
||||
#include "src/devboard/hal/hal.h"
|
||||
|
||||
#ifdef SMA_TRIPOWER_CAN
|
||||
#define SELECTED_INVERTER_CLASS SmaTripowerInverter
|
||||
#endif
|
||||
|
||||
class SmaTripowerInverter : public CanInverterProtocol {
|
||||
class SmaTripowerInverter : public SmaInverterBase {
|
||||
public:
|
||||
void setup();
|
||||
const char* name() override { return Name; }
|
||||
void update_values();
|
||||
void transmit_can(unsigned long currentMillis);
|
||||
void map_can_frame_to_variable(CAN_frame rx_frame);
|
||||
static constexpr char* Name = "SMA Tripower CAN";
|
||||
|
||||
virtual bool controls_contactor() { return true; }
|
||||
virtual bool allows_contactor_closing() { return digitalRead(INVERTER_CONTACTOR_ENABLE_PIN) == 1; }
|
||||
|
||||
private:
|
||||
const int READY_STATE = 0x03;
|
||||
|
|
|
@ -66,8 +66,3 @@ void SofarInverter::transmit_can(unsigned long currentMillis) {
|
|||
transmit_can_frame(&SOFAR_35A, can_config.inverter);
|
||||
}
|
||||
}
|
||||
|
||||
void SofarInverter::setup(void) { // Performs one time setup at startup over CAN bus
|
||||
strncpy(datalayer.system.info.inverter_protocol, Name, 63);
|
||||
datalayer.system.info.inverter_protocol[63] = '\0';
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
class SofarInverter : public CanInverterProtocol {
|
||||
public:
|
||||
void setup();
|
||||
const char* name() override { return Name; }
|
||||
void update_values();
|
||||
void transmit_can(unsigned long currentMillis);
|
||||
void map_can_frame_to_variable(CAN_frame rx_frame);
|
||||
|
|
|
@ -207,8 +207,7 @@ void SolaxInverter::map_can_frame_to_variable(CAN_frame rx_frame) {
|
|||
}
|
||||
}
|
||||
|
||||
void SolaxInverter::setup(void) { // Performs one time setup at startup
|
||||
strncpy(datalayer.system.info.inverter_protocol, Name, 63);
|
||||
datalayer.system.info.inverter_protocol[63] = '\0';
|
||||
bool SolaxInverter::setup(void) { // Performs one time setup at startup
|
||||
datalayer.system.status.inverter_allows_contactor_closing = false; // The inverter needs to allow first
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -10,7 +10,8 @@
|
|||
|
||||
class SolaxInverter : public CanInverterProtocol {
|
||||
public:
|
||||
void setup();
|
||||
const char* name() override { return Name; }
|
||||
bool setup();
|
||||
void update_values();
|
||||
void transmit_can(unsigned long currentMillis);
|
||||
void map_can_frame_to_variable(CAN_frame rx_frame);
|
||||
|
|
|
@ -352,8 +352,3 @@ void SungrowInverter::transmit_can(unsigned long currentMillis) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SungrowInverter::setup(void) { // Performs one time setup at startup over CAN bus
|
||||
strncpy(datalayer.system.info.inverter_protocol, Name, 63);
|
||||
datalayer.system.info.inverter_protocol[63] = '\0';
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
class SungrowInverter : public CanInverterProtocol {
|
||||
public:
|
||||
void setup();
|
||||
const char* name() override { return Name; }
|
||||
void update_values();
|
||||
void transmit_can(unsigned long currentMillis);
|
||||
void map_can_frame_to_variable(CAN_frame rx_frame);
|
||||
|
|
51
Software/src/inverter/SmaInverterBase.h
Normal file
51
Software/src/inverter/SmaInverterBase.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
#ifndef _SMA_INVERTER_BASE_H
|
||||
#define _SMA_INVERTER_BASE_H
|
||||
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "CanInverterProtocol.h"
|
||||
#include "src/devboard/hal/hal.h"
|
||||
|
||||
class SmaInverterBase : public CanInverterProtocol {
|
||||
public:
|
||||
SmaInverterBase() { contactorEnablePin = esp32hal->INVERTER_CONTACTOR_ENABLE_PIN(); }
|
||||
bool allows_contactor_closing() override { return digitalRead(contactorEnablePin) == 1; }
|
||||
|
||||
virtual bool setup() override {
|
||||
datalayer.system.status.inverter_allows_contactor_closing = false; // The inverter needs to allow first
|
||||
|
||||
if (!esp32hal->alloc_pins("SMA inverter", contactorEnablePin)) {
|
||||
return false;
|
||||
}
|
||||
pinMode(contactorEnablePin, INPUT);
|
||||
|
||||
contactorLedPin = esp32hal->INVERTER_CONTACTOR_ENABLE_LED_PIN();
|
||||
if (contactorLedPin != GPIO_NUM_NC) {
|
||||
if (!esp32hal->alloc_pins("SMA inverter", contactorLedPin)) {
|
||||
return false;
|
||||
}
|
||||
pinMode(contactorLedPin, OUTPUT);
|
||||
digitalWrite(contactorLedPin, LOW); // Turn LED off, until inverter allows contactor closing
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
void control_contactor_led() {
|
||||
if (contactorLedPin != GPIO_NUM_NC) {
|
||||
if (datalayer.system.status.inverter_allows_contactor_closing) {
|
||||
digitalWrite(contactorLedPin,
|
||||
HIGH); // Turn on LED to indicate that SMA inverter allows contactor closing
|
||||
} else {
|
||||
digitalWrite(contactorLedPin,
|
||||
LOW); // Turn off LED to indicate that SMA inverter does not allow contactor closing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
gpio_num_t contactorEnablePin;
|
||||
gpio_num_t contactorLedPin;
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue