mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-04 02:09:30 +02:00
run clang-format for all files except libraries in this repository
This commit is contained in:
parent
cb63316cd9
commit
6b6bf57804
38 changed files with 3438 additions and 2905 deletions
|
@ -3,26 +3,26 @@
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "HardwareSerial.h"
|
#include "HardwareSerial.h"
|
||||||
|
#include "USER_SETTINGS.h"
|
||||||
|
#include "src/battery/BATTERIES.h"
|
||||||
|
#include "src/devboard/can/ESP32CAN.h"
|
||||||
|
#include "src/devboard/config.h"
|
||||||
|
#include "src/devboard/modbus/mbServerFCs.h"
|
||||||
|
#include "src/inverter/INVERTERS.h"
|
||||||
|
#include "src/lib/ThomasBarth-ESP32-CAN-Driver/CAN_config.h"
|
||||||
#include "src/lib/adafruit-Adafruit_NeoPixel/Adafruit_NeoPixel.h"
|
#include "src/lib/adafruit-Adafruit_NeoPixel/Adafruit_NeoPixel.h"
|
||||||
#include "src/lib/eModbus-eModbus/Logging.h"
|
#include "src/lib/eModbus-eModbus/Logging.h"
|
||||||
#include "src/lib/eModbus-eModbus/ModbusServerRTU.h"
|
#include "src/lib/eModbus-eModbus/ModbusServerRTU.h"
|
||||||
#include "src/lib/ThomasBarth-ESP32-CAN-Driver/CAN_config.h"
|
|
||||||
#include "USER_SETTINGS.h"
|
|
||||||
#include "src/devboard/config.h"
|
|
||||||
#include "src/devboard/modbus/mbServerFCs.h"
|
|
||||||
#include "src/devboard/can/ESP32CAN.h"
|
|
||||||
#include "src/battery/BATTERIES.h"
|
|
||||||
#include "src/inverter/INVERTERS.h"
|
|
||||||
|
|
||||||
//CAN parameters
|
//CAN parameters
|
||||||
CAN_device_t CAN_cfg; // CAN Config
|
CAN_device_t CAN_cfg; // CAN Config
|
||||||
const int rx_queue_size = 10; // Receive Queue size
|
const int rx_queue_size = 10; // Receive Queue size
|
||||||
|
|
||||||
#ifdef DUAL_CAN
|
#ifdef DUAL_CAN
|
||||||
#include "src/lib/pierremolinaro-acan2515/ACAN2515.h"
|
#include "src/lib/pierremolinaro-acan2515/ACAN2515.h"
|
||||||
static const uint32_t QUARTZ_FREQUENCY = 8UL * 1000UL * 1000UL ; // 8 MHz
|
static const uint32_t QUARTZ_FREQUENCY = 8UL * 1000UL * 1000UL; // 8 MHz
|
||||||
ACAN2515 can(MCP2515_CS, SPI, MCP2515_INT);
|
ACAN2515 can(MCP2515_CS, SPI, MCP2515_INT);
|
||||||
static ACAN2515_Buffer16 gBuffer;
|
static ACAN2515_Buffer16 gBuffer;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//Interval settings
|
//Interval settings
|
||||||
|
@ -73,7 +73,8 @@ uint16_t StateOfHealth = 9900; //SOH 0-100.00% //Updates later on from CAN
|
||||||
uint16_t capacity_Wh = BATTERY_WH_MAX; //Updates later on from CAN
|
uint16_t capacity_Wh = BATTERY_WH_MAX; //Updates later on from CAN
|
||||||
uint16_t remaining_capacity_Wh = BATTERY_WH_MAX; //Updates later on from CAN
|
uint16_t remaining_capacity_Wh = BATTERY_WH_MAX; //Updates later on from CAN
|
||||||
uint16_t max_target_discharge_power = 0; //0W (0W > restricts to no discharge) //Updates later on from CAN
|
uint16_t max_target_discharge_power = 0; //0W (0W > restricts to no discharge) //Updates later on from CAN
|
||||||
uint16_t max_target_charge_power = 4312; //4.3kW (during charge), both 307&308 can be set (>0) at the same time //Updates later on from CAN. Max value is 30000W
|
uint16_t max_target_charge_power =
|
||||||
|
4312; //4.3kW (during charge), both 307&308 can be set (>0) at the same time //Updates later on from CAN. Max value is 30000W
|
||||||
uint16_t temperature_max = 50; //reads from battery later
|
uint16_t temperature_max = 50; //reads from battery later
|
||||||
uint16_t temperature_min = 60; //reads from battery later
|
uint16_t temperature_min = 60; //reads from battery later
|
||||||
uint16_t bms_char_dis_status; //0 idle, 1 discharging, 2, charging
|
uint16_t bms_char_dis_status; //0 idle, 1 discharging, 2, charging
|
||||||
|
@ -95,15 +96,7 @@ const uint8_t maxBrightness = 100;
|
||||||
uint8_t LEDcolor = GREEN;
|
uint8_t LEDcolor = GREEN;
|
||||||
|
|
||||||
//Contactor parameters
|
//Contactor parameters
|
||||||
enum State {
|
enum State { DISCONNECTED, PRECHARGE, NEGATIVE, POSITIVE, PRECHARGE_OFF, COMPLETED, SHUTDOWN_REQUESTED };
|
||||||
DISCONNECTED,
|
|
||||||
PRECHARGE,
|
|
||||||
NEGATIVE,
|
|
||||||
POSITIVE,
|
|
||||||
PRECHARGE_OFF,
|
|
||||||
COMPLETED,
|
|
||||||
SHUTDOWN_REQUESTED
|
|
||||||
};
|
|
||||||
State contactorStatus = DISCONNECTED;
|
State contactorStatus = DISCONNECTED;
|
||||||
|
|
||||||
#define MAX_ALLOWED_FAULT_TICKS 500
|
#define MAX_ALLOWED_FAULT_TICKS 500
|
||||||
|
@ -122,13 +115,10 @@ uint8_t batteryAllowsContactorClosing = 0;
|
||||||
uint8_t inverterAllowsContactorClosing = 1;
|
uint8_t inverterAllowsContactorClosing = 1;
|
||||||
|
|
||||||
// Setup() - initialization happens here
|
// Setup() - initialization happens here
|
||||||
void setup()
|
void setup() {
|
||||||
{
|
|
||||||
// Init Serial monitor
|
// Init Serial monitor
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
while (!Serial)
|
while (!Serial) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
Serial.println("__ OK __");
|
Serial.println("__ OK __");
|
||||||
|
|
||||||
//CAN pins
|
//CAN pins
|
||||||
|
@ -142,34 +132,33 @@ void setup()
|
||||||
ESP32Can.CANInit();
|
ESP32Can.CANInit();
|
||||||
Serial.println(CAN_cfg.speed);
|
Serial.println(CAN_cfg.speed);
|
||||||
|
|
||||||
#ifdef DUAL_CAN
|
#ifdef DUAL_CAN
|
||||||
Serial.println("Dual CAN Bus (ESP32+MCP2515) selected");
|
Serial.println("Dual CAN Bus (ESP32+MCP2515) selected");
|
||||||
gBuffer.initWithSize(25);
|
gBuffer.initWithSize(25);
|
||||||
SPI.begin(MCP2515_SCK, MCP2515_MISO, MCP2515_MOSI);
|
SPI.begin(MCP2515_SCK, MCP2515_MISO, MCP2515_MOSI);
|
||||||
Serial.println ("Configure ACAN2515") ;
|
Serial.println("Configure ACAN2515");
|
||||||
ACAN2515Settings settings (QUARTZ_FREQUENCY, 500UL * 1000UL) ; // CAN bit rate 500 kb/s
|
ACAN2515Settings settings(QUARTZ_FREQUENCY, 500UL * 1000UL); // CAN bit rate 500 kb/s
|
||||||
settings.mRequestedMode = ACAN2515Settings::NormalMode ; // Select loopback mode
|
settings.mRequestedMode = ACAN2515Settings::NormalMode; // Select loopback mode
|
||||||
can.begin (settings, [] { can.isr (); });
|
can.begin(settings, [] { can.isr(); });
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//Init contactor pins
|
//Init contactor pins
|
||||||
#ifdef CONTACTOR_CONTROL
|
#ifdef CONTACTOR_CONTROL
|
||||||
pinMode(POSITIVE_CONTACTOR_PIN, OUTPUT);
|
pinMode(POSITIVE_CONTACTOR_PIN, OUTPUT);
|
||||||
digitalWrite(POSITIVE_CONTACTOR_PIN, LOW);
|
digitalWrite(POSITIVE_CONTACTOR_PIN, LOW);
|
||||||
pinMode(NEGATIVE_CONTACTOR_PIN, OUTPUT);
|
pinMode(NEGATIVE_CONTACTOR_PIN, OUTPUT);
|
||||||
digitalWrite(NEGATIVE_CONTACTOR_PIN, LOW);
|
digitalWrite(NEGATIVE_CONTACTOR_PIN, LOW);
|
||||||
#ifdef PWM_CONTACTOR_CONTROL
|
#ifdef PWM_CONTACTOR_CONTROL
|
||||||
ledcSetup(POSITIVE_PWM_Ch, PWM_Freq, PWM_Res); // Setup PWM Channel Frequency and Resolution
|
ledcSetup(POSITIVE_PWM_Ch, PWM_Freq, PWM_Res); // Setup PWM Channel Frequency and Resolution
|
||||||
ledcSetup(NEGATIVE_PWM_Ch, PWM_Freq, PWM_Res); // Setup PWM Channel Frequency and Resolution
|
ledcSetup(NEGATIVE_PWM_Ch, PWM_Freq, PWM_Res); // Setup PWM Channel Frequency and Resolution
|
||||||
ledcAttachPin(POSITIVE_CONTACTOR_PIN, POSITIVE_PWM_Ch); // Attach Positive Contactor Pin to Hardware PWM Channel
|
ledcAttachPin(POSITIVE_CONTACTOR_PIN, POSITIVE_PWM_Ch); // Attach Positive Contactor Pin to Hardware PWM Channel
|
||||||
ledcAttachPin(NEGATIVE_CONTACTOR_PIN, NEGATIVE_PWM_Ch); // Attach Positive Contactor Pin to Hardware PWM Channel
|
ledcAttachPin(NEGATIVE_CONTACTOR_PIN, NEGATIVE_PWM_Ch); // Attach Positive Contactor Pin to Hardware PWM Channel
|
||||||
ledcWrite(POSITIVE_PWM_Ch, 0); // Set Positive PWM to 0%
|
ledcWrite(POSITIVE_PWM_Ch, 0); // Set Positive PWM to 0%
|
||||||
ledcWrite(NEGATIVE_PWM_Ch, 0); // Set Negative PWM to 0%
|
ledcWrite(NEGATIVE_PWM_Ch, 0); // Set Negative PWM to 0%
|
||||||
#endif
|
#endif
|
||||||
pinMode(PRECHARGE_PIN, OUTPUT);
|
pinMode(PRECHARGE_PIN, OUTPUT);
|
||||||
digitalWrite(PRECHARGE_PIN, LOW);
|
digitalWrite(PRECHARGE_PIN, LOW);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
//Set up Modbus RTU Server
|
//Set up Modbus RTU Server
|
||||||
pinMode(RS485_EN_PIN, OUTPUT);
|
pinMode(RS485_EN_PIN, OUTPUT);
|
||||||
|
@ -179,11 +168,11 @@ void setup()
|
||||||
pinMode(PIN_5V_EN, OUTPUT);
|
pinMode(PIN_5V_EN, OUTPUT);
|
||||||
digitalWrite(PIN_5V_EN, HIGH);
|
digitalWrite(PIN_5V_EN, HIGH);
|
||||||
|
|
||||||
#ifdef BYD_MODBUS
|
#ifdef BYD_MODBUS
|
||||||
// Init Static data to the RTU Modbus
|
// Init Static data to the RTU Modbus
|
||||||
handle_static_data_modbus_byd();
|
handle_static_data_modbus_byd();
|
||||||
#endif
|
#endif
|
||||||
#if defined(BYD_MODBUS) || defined(LUNA2000_MODBUS)
|
#if defined(BYD_MODBUS) || defined(LUNA2000_MODBUS)
|
||||||
// Init Serial2 connected to the RTU Modbus
|
// Init Serial2 connected to the RTU Modbus
|
||||||
RTUutils::prepareHardwareSerial(Serial2);
|
RTUutils::prepareHardwareSerial(Serial2);
|
||||||
Serial2.begin(9600, SERIAL_8N1, RS485_RX_PIN, RS485_TX_PIN);
|
Serial2.begin(9600, SERIAL_8N1, RS485_RX_PIN, RS485_TX_PIN);
|
||||||
|
@ -194,75 +183,75 @@ void setup()
|
||||||
MBserver.registerWorker(MBTCP_ID, R_W_MULT_REGISTERS, &FC23);
|
MBserver.registerWorker(MBTCP_ID, R_W_MULT_REGISTERS, &FC23);
|
||||||
// Start ModbusRTU background task
|
// Start ModbusRTU background task
|
||||||
MBserver.begin(Serial2);
|
MBserver.begin(Serial2);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Init LED control
|
// Init LED control
|
||||||
pixels.begin();
|
pixels.begin();
|
||||||
|
|
||||||
//Inform user what Inverter is used
|
//Inform user what Inverter is used
|
||||||
#ifdef BYD_CAN
|
#ifdef BYD_CAN
|
||||||
Serial.println("BYD CAN protocol selected");
|
Serial.println("BYD CAN protocol selected");
|
||||||
#endif
|
#endif
|
||||||
#ifdef BYD_MODBUS
|
#ifdef BYD_MODBUS
|
||||||
Serial.println("BYD Modbus RTU protocol selected");
|
Serial.println("BYD Modbus RTU protocol selected");
|
||||||
#endif
|
#endif
|
||||||
#ifdef LUNA2000_MODBUS
|
#ifdef LUNA2000_MODBUS
|
||||||
Serial.println("Luna2000 Modbus RTU protocol selected");
|
Serial.println("Luna2000 Modbus RTU protocol selected");
|
||||||
#endif
|
#endif
|
||||||
#ifdef PYLON_CAN
|
#ifdef PYLON_CAN
|
||||||
Serial.println("PYLON CAN protocol selected")
|
Serial
|
||||||
#endif
|
.println("PYLON CAN protocol selected")
|
||||||
#ifdef SMA_CAN
|
#endif
|
||||||
|
#ifdef SMA_CAN
|
||||||
Serial.println("SMA CAN protocol selected");
|
Serial.println("SMA CAN protocol selected");
|
||||||
#endif
|
#endif
|
||||||
#ifdef SOFAR_CAN
|
#ifdef SOFAR_CAN
|
||||||
Serial.println("SOFAR CAN protocol selected");
|
Serial.println("SOFAR CAN protocol selected");
|
||||||
#endif
|
#endif
|
||||||
#ifdef SOLAX_CAN
|
#ifdef SOLAX_CAN
|
||||||
inverterAllowsContactorClosing = 0; //The inverter needs to allow first on this protocol
|
inverterAllowsContactorClosing = 0; //The inverter needs to allow first on this protocol
|
||||||
intervalInverterTask = 800; //This protocol also requires the values to be updated faster
|
intervalInverterTask = 800; //This protocol also requires the values to be updated faster
|
||||||
Serial.println("SOLAX CAN protocol selected");
|
Serial.println("SOLAX CAN protocol selected");
|
||||||
#endif
|
#endif
|
||||||
//Inform user what battery is used
|
//Inform user what battery is used
|
||||||
#ifdef BMW_I3_BATTERY
|
#ifdef BMW_I3_BATTERY
|
||||||
Serial.println("BMW i3 battery selected");
|
Serial.println("BMW i3 battery selected");
|
||||||
#endif
|
#endif
|
||||||
#ifdef CHADEMO_BATTERY
|
#ifdef CHADEMO_BATTERY
|
||||||
Serial.println("Chademo battery selected");
|
Serial.println("Chademo battery selected");
|
||||||
#endif
|
#endif
|
||||||
#ifdef IMIEV_CZERO_ION_BATTERY
|
#ifdef IMIEV_CZERO_ION_BATTERY
|
||||||
Serial.println("Mitsubishi i-MiEV / Citroen C-Zero / Peugeot Ion battery selected");
|
Serial.println("Mitsubishi i-MiEV / Citroen C-Zero / Peugeot Ion battery selected");
|
||||||
#endif
|
#endif
|
||||||
#ifdef KIA_HYUNDAI_64_BATTERY
|
#ifdef KIA_HYUNDAI_64_BATTERY
|
||||||
Serial.println("Kia Niro / Hyundai Kona 64kWh battery selected");
|
Serial.println("Kia Niro / Hyundai Kona 64kWh battery selected");
|
||||||
#endif
|
#endif
|
||||||
#ifdef NISSAN_LEAF_BATTERY
|
#ifdef NISSAN_LEAF_BATTERY
|
||||||
Serial.println("Nissan LEAF battery selected");
|
Serial.println("Nissan LEAF battery selected");
|
||||||
#endif
|
#endif
|
||||||
#ifdef RENAULT_ZOE_BATTERY
|
#ifdef RENAULT_ZOE_BATTERY
|
||||||
Serial.println("Renault Zoe / Kangoo battery selected");
|
Serial.println("Renault Zoe / Kangoo battery selected");
|
||||||
#endif
|
#endif
|
||||||
#ifdef TESLA_MODEL_3_BATTERY
|
#ifdef TESLA_MODEL_3_BATTERY
|
||||||
Serial.println("Tesla Model 3 battery selected");
|
Serial.println("Tesla Model 3 battery selected");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// perform main program functions
|
// perform main program functions
|
||||||
void loop()
|
void loop() {
|
||||||
{
|
|
||||||
handle_can(); //runs as fast as possible, handle CAN routines
|
handle_can(); //runs as fast as possible, handle CAN routines
|
||||||
|
|
||||||
#ifdef DUAL_CAN
|
#ifdef DUAL_CAN
|
||||||
handle_can2();
|
handle_can2();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (millis() - previousMillis10ms >= interval10) //every 10ms
|
if (millis() - previousMillis10ms >= interval10) //every 10ms
|
||||||
{
|
{
|
||||||
previousMillis10ms = millis();
|
previousMillis10ms = millis();
|
||||||
handle_LED_state(); //Set the LED color according to state
|
handle_LED_state(); //Set the LED color according to state
|
||||||
#ifdef CONTACTOR_CONTROL
|
#ifdef CONTACTOR_CONTROL
|
||||||
handle_contactors(); //Take care of startup precharge/contactor closing
|
handle_contactors(); //Take care of startup precharge/contactor closing
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (millis() - previousMillisInverter >= intervalInverterTask) //every 5s
|
if (millis() - previousMillisInverter >= intervalInverterTask) //every 5s
|
||||||
|
@ -272,201 +261,186 @@ void loop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void handle_can()
|
void handle_can() { //This section checks if we have a complete CAN message incoming
|
||||||
{ //This section checks if we have a complete CAN message incoming
|
|
||||||
//Depending on which battery/inverter is selected, we forward this to their respective CAN routines
|
//Depending on which battery/inverter is selected, we forward this to their respective CAN routines
|
||||||
CAN_frame_t rx_frame;
|
CAN_frame_t rx_frame;
|
||||||
if (xQueueReceive(CAN_cfg.rx_queue, &rx_frame, 3 * portTICK_PERIOD_MS) == pdTRUE)
|
if (xQueueReceive(CAN_cfg.rx_queue, &rx_frame, 3 * portTICK_PERIOD_MS) == pdTRUE) {
|
||||||
{
|
if (rx_frame.FIR.B.FF == CAN_frame_std) {
|
||||||
if (rx_frame.FIR.B.FF == CAN_frame_std)
|
//printf("New standard frame");
|
||||||
{
|
// battery
|
||||||
//printf("New standard frame");
|
#ifdef BMW_I3_BATTERY
|
||||||
// battery
|
|
||||||
#ifdef BMW_I3_BATTERY
|
|
||||||
receive_can_i3_battery(rx_frame);
|
receive_can_i3_battery(rx_frame);
|
||||||
#endif
|
#endif
|
||||||
#ifdef CHADEMO_BATTERY
|
#ifdef CHADEMO_BATTERY
|
||||||
receive_can_chademo(rx_frame);
|
receive_can_chademo(rx_frame);
|
||||||
#endif
|
#endif
|
||||||
#ifdef IMIEV_CZERO_ION_BATTERY
|
#ifdef IMIEV_CZERO_ION_BATTERY
|
||||||
receive_can_imiev_battery(rx_frame);
|
receive_can_imiev_battery(rx_frame);
|
||||||
#endif
|
#endif
|
||||||
#ifdef KIA_HYUNDAI_64_BATTERY
|
#ifdef KIA_HYUNDAI_64_BATTERY
|
||||||
receive_can_kiaHyundai_64_battery(rx_frame);
|
receive_can_kiaHyundai_64_battery(rx_frame);
|
||||||
#endif
|
#endif
|
||||||
#ifdef NISSAN_LEAF_BATTERY
|
#ifdef NISSAN_LEAF_BATTERY
|
||||||
receive_can_leaf_battery(rx_frame);
|
receive_can_leaf_battery(rx_frame);
|
||||||
#endif
|
#endif
|
||||||
#ifdef RENAULT_ZOE_BATTERY
|
#ifdef RENAULT_ZOE_BATTERY
|
||||||
receive_can_zoe_battery(rx_frame);
|
receive_can_zoe_battery(rx_frame);
|
||||||
#endif
|
#endif
|
||||||
#ifdef TESLA_MODEL_3_BATTERY
|
#ifdef TESLA_MODEL_3_BATTERY
|
||||||
receive_can_tesla_model_3_battery(rx_frame);
|
receive_can_tesla_model_3_battery(rx_frame);
|
||||||
#endif
|
#endif
|
||||||
// inverter
|
// inverter
|
||||||
#ifdef BYD_CAN
|
#ifdef BYD_CAN
|
||||||
receive_can_byd(rx_frame);
|
receive_can_byd(rx_frame);
|
||||||
#endif
|
#endif
|
||||||
#ifdef SMA_CAN
|
#ifdef SMA_CAN
|
||||||
receive_can_sma(rx_frame);
|
receive_can_sma(rx_frame);
|
||||||
#endif
|
#endif
|
||||||
}
|
} else {
|
||||||
else
|
//printf("New extended frame");
|
||||||
{
|
#ifdef PYLON_CAN
|
||||||
//printf("New extended frame");
|
|
||||||
#ifdef PYLON_CAN
|
|
||||||
receive_can_pylon(rx_frame);
|
receive_can_pylon(rx_frame);
|
||||||
#endif
|
#endif
|
||||||
#ifdef SOFAR_CAN
|
#ifdef SOFAR_CAN
|
||||||
receive_can_sofar(rx_frame);
|
receive_can_sofar(rx_frame);
|
||||||
#endif
|
#endif
|
||||||
#ifdef SOLAX_CAN
|
#ifdef SOLAX_CAN
|
||||||
receive_can_solax(rx_frame);
|
receive_can_solax(rx_frame);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//When we are done checking if a CAN message has arrived, we can focus on sending CAN messages
|
//When we are done checking if a CAN message has arrived, we can focus on sending CAN messages
|
||||||
//Inverter sending
|
//Inverter sending
|
||||||
#ifdef BYD_CAN
|
#ifdef BYD_CAN
|
||||||
send_can_byd();
|
send_can_byd();
|
||||||
#endif
|
#endif
|
||||||
#ifdef SMA_CAN
|
#ifdef SMA_CAN
|
||||||
send_can_sma();
|
send_can_sma();
|
||||||
#endif
|
#endif
|
||||||
#ifdef SOFAR_CAN
|
#ifdef SOFAR_CAN
|
||||||
send_can_sofar();
|
send_can_sofar();
|
||||||
#endif
|
#endif
|
||||||
//Battery sending
|
//Battery sending
|
||||||
#ifdef BMW_I3_BATTERY
|
#ifdef BMW_I3_BATTERY
|
||||||
send_can_i3_battery();
|
send_can_i3_battery();
|
||||||
#endif
|
#endif
|
||||||
#ifdef CHADEMO_BATTERY
|
#ifdef CHADEMO_BATTERY
|
||||||
send_can_chademo_battery();
|
send_can_chademo_battery();
|
||||||
#endif
|
#endif
|
||||||
#ifdef IMIEV_CZERO_ION_BATTERY
|
#ifdef IMIEV_CZERO_ION_BATTERY
|
||||||
send_can_imiev_battery();
|
send_can_imiev_battery();
|
||||||
#endif
|
#endif
|
||||||
#ifdef KIA_HYUNDAI_64_BATTERY
|
#ifdef KIA_HYUNDAI_64_BATTERY
|
||||||
send_can_kiaHyundai_64_battery();
|
send_can_kiaHyundai_64_battery();
|
||||||
#endif
|
#endif
|
||||||
#ifdef NISSAN_LEAF_BATTERY
|
#ifdef NISSAN_LEAF_BATTERY
|
||||||
send_can_leaf_battery();
|
send_can_leaf_battery();
|
||||||
#endif
|
#endif
|
||||||
#ifdef RENAULT_ZOE_BATTERY
|
#ifdef RENAULT_ZOE_BATTERY
|
||||||
send_can_zoe_battery();
|
send_can_zoe_battery();
|
||||||
#endif
|
#endif
|
||||||
#ifdef TESLA_MODEL_3_BATTERY
|
#ifdef TESLA_MODEL_3_BATTERY
|
||||||
send_can_tesla_model_3_battery();
|
send_can_tesla_model_3_battery();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DUAL_CAN
|
#ifdef DUAL_CAN
|
||||||
void handle_can2()
|
void handle_can2() { //This function is similar to handle_can, but just takes care of inverters in the 2nd bus.
|
||||||
{ //This function is similar to handle_can, but just takes care of inverters in the 2nd bus.
|
|
||||||
//Depending on which inverter is selected, we forward this to their respective CAN routines
|
//Depending on which inverter is selected, we forward this to their respective CAN routines
|
||||||
CAN_frame_t rx_frame2; //Struct with ESP32Can library format, compatible with the rest of the program
|
CAN_frame_t rx_frame2; //Struct with ESP32Can library format, compatible with the rest of the program
|
||||||
CANMessage MCP2515Frame; //Struct with ACAN2515 library format, needed to use thw MCP2515 library
|
CANMessage MCP2515Frame; //Struct with ACAN2515 library format, needed to use thw MCP2515 library
|
||||||
|
|
||||||
if ( can.available() )
|
if (can.available()) {
|
||||||
{
|
|
||||||
can.receive(MCP2515Frame);
|
can.receive(MCP2515Frame);
|
||||||
|
|
||||||
rx_frame2.MsgID = MCP2515Frame.id;
|
rx_frame2.MsgID = MCP2515Frame.id;
|
||||||
rx_frame2.FIR.B.FF = MCP2515Frame.ext ? CAN_frame_ext : CAN_frame_std;
|
rx_frame2.FIR.B.FF = MCP2515Frame.ext ? CAN_frame_ext : CAN_frame_std;
|
||||||
rx_frame2.FIR.B.RTR = MCP2515Frame.rtr ? CAN_RTR : CAN_no_RTR;
|
rx_frame2.FIR.B.RTR = MCP2515Frame.rtr ? CAN_RTR : CAN_no_RTR;
|
||||||
rx_frame2.FIR.B.DLC = MCP2515Frame.len;
|
rx_frame2.FIR.B.DLC = MCP2515Frame.len;
|
||||||
for (uint8_t i=0 ; i<MCP2515Frame.len ; i++) {
|
for (uint8_t i = 0; i < MCP2515Frame.len; i++) {
|
||||||
rx_frame2.data.u8[i] = MCP2515Frame.data[i] ;
|
rx_frame2.data.u8[i] = MCP2515Frame.data[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rx_frame2.FIR.B.FF == CAN_frame_std)
|
if (rx_frame2.FIR.B.FF == CAN_frame_std) {
|
||||||
{
|
//Serial.println("New standard frame");
|
||||||
//Serial.println("New standard frame");
|
#ifdef BYD_CAN
|
||||||
#ifdef BYD_CAN
|
|
||||||
receive_can_byd(rx_frame2);
|
receive_can_byd(rx_frame2);
|
||||||
#endif
|
#endif
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
//Serial.println("New extended frame");
|
//Serial.println("New extended frame");
|
||||||
#ifdef PYLON_CAN
|
#ifdef PYLON_CAN
|
||||||
receive_can_pylon(rx_frame2);
|
receive_can_pylon(rx_frame2);
|
||||||
#endif
|
#endif
|
||||||
#ifdef SOLAX_CAN
|
#ifdef SOLAX_CAN
|
||||||
receive_can_solax(rx_frame2);
|
receive_can_solax(rx_frame2);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//When we are done checking if a CAN message has arrived, we can focus on sending CAN messages
|
//When we are done checking if a CAN message has arrived, we can focus on sending CAN messages
|
||||||
//Inverter sending
|
//Inverter sending
|
||||||
#ifdef BYD_CAN
|
#ifdef BYD_CAN
|
||||||
send_can_byd();
|
send_can_byd();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void handle_inverter()
|
void handle_inverter() {
|
||||||
{
|
|
||||||
// battery
|
// battery
|
||||||
#ifdef BMW_I3_BATTERY
|
#ifdef BMW_I3_BATTERY
|
||||||
update_values_i3_battery(); //Map the values to the correct registers
|
update_values_i3_battery(); //Map the values to the correct registers
|
||||||
#endif
|
#endif
|
||||||
#ifdef CHADEMO_BATTERY
|
#ifdef CHADEMO_BATTERY
|
||||||
update_values_can_chademo();
|
update_values_can_chademo();
|
||||||
#endif
|
#endif
|
||||||
#ifdef IMIEV_CZERO_ION_BATTERY
|
#ifdef IMIEV_CZERO_ION_BATTERY
|
||||||
update_values_imiev_battery(); //Map the values to the correct registers
|
update_values_imiev_battery(); //Map the values to the correct registers
|
||||||
#endif
|
#endif
|
||||||
#ifdef KIA_HYUNDAI_64_BATTERY
|
#ifdef KIA_HYUNDAI_64_BATTERY
|
||||||
update_values_kiaHyundai_64_battery(); //Map the values to the correct registers
|
update_values_kiaHyundai_64_battery(); //Map the values to the correct registers
|
||||||
#endif
|
#endif
|
||||||
#ifdef NISSAN_LEAF_BATTERY
|
#ifdef NISSAN_LEAF_BATTERY
|
||||||
update_values_leaf_battery(); //Map the values to the correct registers
|
update_values_leaf_battery(); //Map the values to the correct registers
|
||||||
#endif
|
#endif
|
||||||
#ifdef RENAULT_ZOE_BATTERY
|
#ifdef RENAULT_ZOE_BATTERY
|
||||||
update_values_zoe_battery(); //Map the values to the correct registers
|
update_values_zoe_battery(); //Map the values to the correct registers
|
||||||
#endif
|
#endif
|
||||||
#ifdef TESLA_MODEL_3_BATTERY
|
#ifdef TESLA_MODEL_3_BATTERY
|
||||||
update_values_tesla_model_3_battery(); //Map the values to the correct registers
|
update_values_tesla_model_3_battery(); //Map the values to the correct registers
|
||||||
#endif
|
#endif
|
||||||
// inverter
|
// inverter
|
||||||
#ifdef BYD_CAN
|
#ifdef BYD_CAN
|
||||||
update_values_can_byd();
|
update_values_can_byd();
|
||||||
#endif
|
#endif
|
||||||
#ifdef BYD_MODBUS
|
#ifdef BYD_MODBUS
|
||||||
update_modbus_registers_byd();
|
update_modbus_registers_byd();
|
||||||
#endif
|
#endif
|
||||||
#ifdef LUNA2000_MODBUS
|
#ifdef LUNA2000_MODBUS
|
||||||
update_modbus_registers_luna2000();
|
update_modbus_registers_luna2000();
|
||||||
#endif
|
#endif
|
||||||
#ifdef PYLON_CAN
|
#ifdef PYLON_CAN
|
||||||
update_values_can_pylon();
|
update_values_can_pylon();
|
||||||
#endif
|
#endif
|
||||||
#ifdef SMA_CAN
|
#ifdef SMA_CAN
|
||||||
update_values_can_sma();
|
update_values_can_sma();
|
||||||
#endif
|
#endif
|
||||||
#ifdef SOLAX_CAN
|
#ifdef SOLAX_CAN
|
||||||
update_values_can_solax();
|
update_values_can_solax();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONTACTOR_CONTROL
|
#ifdef CONTACTOR_CONTROL
|
||||||
void handle_contactors()
|
void handle_contactors() {
|
||||||
{
|
|
||||||
//First check if we have any active errors, incase we do, turn off the battery
|
//First check if we have any active errors, incase we do, turn off the battery
|
||||||
if(bms_status == FAULT){
|
if (bms_status == FAULT) {
|
||||||
timeSpentInFaultedMode++;
|
timeSpentInFaultedMode++;
|
||||||
}
|
} else {
|
||||||
else{
|
|
||||||
timeSpentInFaultedMode = 0;
|
timeSpentInFaultedMode = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(timeSpentInFaultedMode > MAX_ALLOWED_FAULT_TICKS)
|
if (timeSpentInFaultedMode > MAX_ALLOWED_FAULT_TICKS) {
|
||||||
{
|
|
||||||
contactorStatus = SHUTDOWN_REQUESTED;
|
contactorStatus = SHUTDOWN_REQUESTED;
|
||||||
}
|
}
|
||||||
if(contactorStatus == SHUTDOWN_REQUESTED)
|
if (contactorStatus == SHUTDOWN_REQUESTED) {
|
||||||
{
|
|
||||||
digitalWrite(PRECHARGE_PIN, LOW);
|
digitalWrite(PRECHARGE_PIN, LOW);
|
||||||
digitalWrite(NEGATIVE_CONTACTOR_PIN, LOW);
|
digitalWrite(NEGATIVE_CONTACTOR_PIN, LOW);
|
||||||
digitalWrite(POSITIVE_CONTACTOR_PIN, LOW);
|
digitalWrite(POSITIVE_CONTACTOR_PIN, LOW);
|
||||||
|
@ -474,24 +448,22 @@ void handle_contactors()
|
||||||
}
|
}
|
||||||
|
|
||||||
//After that, check if we are OK to start turning on the battery
|
//After that, check if we are OK to start turning on the battery
|
||||||
if(contactorStatus == DISCONNECTED)
|
if (contactorStatus == DISCONNECTED) {
|
||||||
{
|
|
||||||
digitalWrite(PRECHARGE_PIN, LOW);
|
digitalWrite(PRECHARGE_PIN, LOW);
|
||||||
#ifdef PWM_CONTACTOR_CONTROL
|
#ifdef PWM_CONTACTOR_CONTROL
|
||||||
ledcWrite(POSITIVE_PWM_Ch, 0);
|
ledcWrite(POSITIVE_PWM_Ch, 0);
|
||||||
ledcWrite(NEGATIVE_PWM_Ch, 0);
|
ledcWrite(NEGATIVE_PWM_Ch, 0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if(batteryAllowsContactorClosing && inverterAllowsContactorClosing)
|
if (batteryAllowsContactorClosing && inverterAllowsContactorClosing) {
|
||||||
{
|
|
||||||
contactorStatus = PRECHARGE;
|
contactorStatus = PRECHARGE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Incase the inverter requests contactors to open, set the state accordingly
|
//Incase the inverter requests contactors to open, set the state accordingly
|
||||||
if(contactorStatus == COMPLETED)
|
if (contactorStatus == COMPLETED) {
|
||||||
{
|
if (!inverterAllowsContactorClosing)
|
||||||
if (!inverterAllowsContactorClosing) contactorStatus = DISCONNECTED;
|
contactorStatus = DISCONNECTED;
|
||||||
//Skip running the state machine below if it has already completed
|
//Skip running the state machine below if it has already completed
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -508,9 +480,9 @@ void handle_contactors()
|
||||||
case NEGATIVE:
|
case NEGATIVE:
|
||||||
if (currentTime - prechargeStartTime >= PRECHARGE_TIME_MS) {
|
if (currentTime - prechargeStartTime >= PRECHARGE_TIME_MS) {
|
||||||
digitalWrite(NEGATIVE_CONTACTOR_PIN, HIGH);
|
digitalWrite(NEGATIVE_CONTACTOR_PIN, HIGH);
|
||||||
#ifdef PWM_CONTACTOR_CONTROL
|
#ifdef PWM_CONTACTOR_CONTROL
|
||||||
ledcWrite(NEGATIVE_PWM_Ch, 1023);
|
ledcWrite(NEGATIVE_PWM_Ch, 1023);
|
||||||
#endif
|
#endif
|
||||||
negativeStartTime = currentTime;
|
negativeStartTime = currentTime;
|
||||||
contactorStatus = POSITIVE;
|
contactorStatus = POSITIVE;
|
||||||
}
|
}
|
||||||
|
@ -519,9 +491,9 @@ void handle_contactors()
|
||||||
case POSITIVE:
|
case POSITIVE:
|
||||||
if (currentTime - negativeStartTime >= NEGATIVE_CONTACTOR_TIME_MS) {
|
if (currentTime - negativeStartTime >= NEGATIVE_CONTACTOR_TIME_MS) {
|
||||||
digitalWrite(POSITIVE_CONTACTOR_PIN, HIGH);
|
digitalWrite(POSITIVE_CONTACTOR_PIN, HIGH);
|
||||||
#ifdef PWM_CONTACTOR_CONTROL
|
#ifdef PWM_CONTACTOR_CONTROL
|
||||||
ledcWrite(POSITIVE_PWM_Ch, 1023);
|
ledcWrite(POSITIVE_PWM_Ch, 1023);
|
||||||
#endif
|
#endif
|
||||||
contactorStatus = PRECHARGE_OFF;
|
contactorStatus = PRECHARGE_OFF;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -529,10 +501,10 @@ void handle_contactors()
|
||||||
case PRECHARGE_OFF:
|
case PRECHARGE_OFF:
|
||||||
if (currentTime - negativeStartTime >= POSITIVE_CONTACTOR_TIME_MS) {
|
if (currentTime - negativeStartTime >= POSITIVE_CONTACTOR_TIME_MS) {
|
||||||
digitalWrite(PRECHARGE_PIN, LOW);
|
digitalWrite(PRECHARGE_PIN, LOW);
|
||||||
#ifdef PWM_CONTACTOR_CONTROL
|
#ifdef PWM_CONTACTOR_CONTROL
|
||||||
ledcWrite(NEGATIVE_PWM_Ch, PWM_Hold_Duty);
|
ledcWrite(NEGATIVE_PWM_Ch, PWM_Hold_Duty);
|
||||||
ledcWrite(POSITIVE_PWM_Ch, PWM_Hold_Duty);
|
ledcWrite(POSITIVE_PWM_Ch, PWM_Hold_Duty);
|
||||||
#endif
|
#endif
|
||||||
contactorStatus = COMPLETED;
|
contactorStatus = COMPLETED;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -542,23 +514,18 @@ void handle_contactors()
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void handle_LED_state()
|
void handle_LED_state() {
|
||||||
{
|
|
||||||
// Determine how bright the LED should be
|
// Determine how bright the LED should be
|
||||||
if (rampUp && brightness < maxBrightness){
|
if (rampUp && brightness < maxBrightness) {
|
||||||
brightness++;
|
brightness++;
|
||||||
}
|
} else if (rampUp && brightness == maxBrightness) {
|
||||||
else if (rampUp && brightness == maxBrightness){
|
|
||||||
rampUp = false;
|
rampUp = false;
|
||||||
}
|
} else if (!rampUp && brightness > 0) {
|
||||||
else if (!rampUp && brightness > 0){
|
|
||||||
brightness--;
|
brightness--;
|
||||||
}
|
} else if (!rampUp && brightness == 0) {
|
||||||
else if (!rampUp && brightness == 0){
|
|
||||||
rampUp = true;
|
rampUp = true;
|
||||||
}
|
}
|
||||||
switch (LEDcolor)
|
switch (LEDcolor) {
|
||||||
{
|
|
||||||
case GREEN:
|
case GREEN:
|
||||||
pixels.setPixelColor(0, pixels.Color(0, brightness, 0)); // Green pulsing LED
|
pixels.setPixelColor(0, pixels.Color(0, brightness, 0)); // Green pulsing LED
|
||||||
break;
|
break;
|
||||||
|
@ -576,8 +543,7 @@ void handle_LED_state()
|
||||||
}
|
}
|
||||||
|
|
||||||
//BMS in fault state overrides everything
|
//BMS in fault state overrides everything
|
||||||
if(bms_status == FAULT)
|
if (bms_status == FAULT) {
|
||||||
{
|
|
||||||
pixels.setPixelColor(0, pixels.Color(255, 0, 0)); // Red LED full brightness
|
pixels.setPixelColor(0, pixels.Color(255, 0, 0)); // Red LED full brightness
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,11 +24,16 @@
|
||||||
//#define SOLAX_CAN //Enable this line to emulate a "SolaX Triple Power LFP" over CAN bus
|
//#define SOLAX_CAN //Enable this line to emulate a "SolaX Triple Power LFP" over CAN bus
|
||||||
|
|
||||||
/* Battery settings */
|
/* Battery settings */
|
||||||
#define BATTERY_WH_MAX 30000 //Battery size in Wh (Maximum value for most inverters is 60000 [60kWh], you can use larger batteries but do not set value over 60000!
|
#define BATTERY_WH_MAX \
|
||||||
#define MAXPERCENTAGE 800 //80.0% , Max percentage the battery will charge to (App will show 100% once this value is reached)
|
30000 //Battery size in Wh (Maximum value for most inverters is 60000 [60kWh], you can use larger batteries but do not set value over 60000!
|
||||||
#define MINPERCENTAGE 200 //20.0% , Min percentage the battery will discharge to (App will show 0% once this value is reached)
|
#define MAXPERCENTAGE \
|
||||||
#define MAXCHARGEAMP 300 //30.0A , BYD CAN specific setting, Max charge speed in Amp (Some inverters needs to be artificially limited)
|
800 //80.0% , Max percentage the battery will charge to (App will show 100% once this value is reached)
|
||||||
#define MAXDISCHARGEAMP 300 //30.0A , BYD CAN specific setting, Max discharge speed in Amp (Some inverters needs to be artificially limited)
|
#define MINPERCENTAGE \
|
||||||
|
200 //20.0% , Min percentage the battery will discharge to (App will show 0% once this value is reached)
|
||||||
|
#define MAXCHARGEAMP \
|
||||||
|
300 //30.0A , BYD CAN specific setting, Max charge speed in Amp (Some inverters needs to be artificially limited)
|
||||||
|
#define MAXDISCHARGEAMP \
|
||||||
|
300 //30.0A , BYD CAN specific setting, Max discharge speed in Amp (Some inverters needs to be artificially limited)
|
||||||
//define INTERLOCK_REQUIRED //Nissan LEAF specific setting, if enabled requires both high voltage conenctors to be seated before starting
|
//define INTERLOCK_REQUIRED //Nissan LEAF specific setting, if enabled requires both high voltage conenctors to be seated before starting
|
||||||
|
|
||||||
/* Other options */
|
/* Other options */
|
||||||
|
|
|
@ -2,31 +2,31 @@
|
||||||
#define BATTERIES_H
|
#define BATTERIES_H
|
||||||
|
|
||||||
#ifdef BMW_I3_BATTERY
|
#ifdef BMW_I3_BATTERY
|
||||||
#include "BMW-I3-BATTERY.h" //See this file for more i3 battery settings
|
#include "BMW-I3-BATTERY.h" //See this file for more i3 battery settings
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CHADEMO_BATTERY
|
#ifdef CHADEMO_BATTERY
|
||||||
#include "CHADEMO-BATTERY.h" //See this file for more Chademo settings
|
#include "CHADEMO-BATTERY.h" //See this file for more Chademo settings
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef IMIEV_CZERO_ION_BATTERY
|
#ifdef IMIEV_CZERO_ION_BATTERY
|
||||||
#include "IMIEV-CZERO-ION-BATTERY.h" //See this file for more triplet battery settings
|
#include "IMIEV-CZERO-ION-BATTERY.h" //See this file for more triplet battery settings
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef KIA_HYUNDAI_64_BATTERY
|
#ifdef KIA_HYUNDAI_64_BATTERY
|
||||||
#include "KIA-HYUNDAI-64-BATTERY.h" //See this file for more 64kWh battery settings
|
#include "KIA-HYUNDAI-64-BATTERY.h" //See this file for more 64kWh battery settings
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef NISSAN_LEAF_BATTERY
|
#ifdef NISSAN_LEAF_BATTERY
|
||||||
#include "NISSAN-LEAF-BATTERY.h" //See this file for more LEAF battery settings
|
#include "NISSAN-LEAF-BATTERY.h" //See this file for more LEAF battery settings
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef RENAULT_ZOE_BATTERY
|
#ifdef RENAULT_ZOE_BATTERY
|
||||||
#include "RENAULT-ZOE-BATTERY.h" //See this file for more Zoe battery settings
|
#include "RENAULT-ZOE-BATTERY.h" //See this file for more Zoe battery settings
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef TESLA_MODEL_3_BATTERY
|
#ifdef TESLA_MODEL_3_BATTERY
|
||||||
#include "TESLA-MODEL-3-BATTERY.h" //See this file for more Tesla battery settings
|
#include "TESLA-MODEL-3-BATTERY.h" //See this file for more Tesla battery settings
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -16,13 +16,28 @@ static uint8_t CANstillAlive = 12; //counter for checking if CAN is still alive
|
||||||
#define LB_MAX_SOC 1000 //BMS never goes over this value. We use this info to rescale SOC% sent to Inverter
|
#define LB_MAX_SOC 1000 //BMS never goes over this value. We use this info to rescale SOC% sent to Inverter
|
||||||
#define LB_MIN_SOC 0 //BMS never goes below this value. We use this info to rescale SOC% sent to Inverter
|
#define LB_MIN_SOC 0 //BMS never goes below this value. We use this info to rescale SOC% sent to Inverter
|
||||||
|
|
||||||
CAN_frame_t BMW_10B = {.FIR = {.B = {.DLC = 3,.FF = CAN_frame_std,}},.MsgID = 0x10B,.data = {0xCD, 0x01, 0xFC}};
|
CAN_frame_t BMW_10B = {.FIR = {.B =
|
||||||
CAN_frame_t BMW_512 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x512,.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12}}; //0x512 Network management edme VCU
|
{
|
||||||
|
.DLC = 3,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x10B,
|
||||||
|
.data = {0xCD, 0x01, 0xFC}};
|
||||||
|
CAN_frame_t BMW_512 = {
|
||||||
|
.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x512,
|
||||||
|
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12}}; //0x512 Network management edme VCU
|
||||||
//CAN_frame_t BMW_12F //Might be needed, Wake up ,VCU 100ms
|
//CAN_frame_t BMW_12F //Might be needed, Wake up ,VCU 100ms
|
||||||
//These CAN messages need to be sent towards the battery to keep it alive
|
//These CAN messages need to be sent towards the battery to keep it alive
|
||||||
|
|
||||||
static const uint8_t BMW_10B_0[15] = {0xCD,0x19,0x94,0x6D,0xE0,0x34,0x78,0xDB,0x97,0x43,0x0F,0xF6,0xBA,0x6E,0x81};
|
static const uint8_t BMW_10B_0[15] = {0xCD, 0x19, 0x94, 0x6D, 0xE0, 0x34, 0x78, 0xDB,
|
||||||
static const uint8_t BMW_10B_1[15] = {0x01,0x02,0x33,0x34,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x00};
|
0x97, 0x43, 0x0F, 0xF6, 0xBA, 0x6E, 0x81};
|
||||||
|
static const uint8_t BMW_10B_1[15] = {0x01, 0x02, 0x33, 0x34, 0x05, 0x06, 0x07, 0x08,
|
||||||
|
0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x00};
|
||||||
static uint8_t BMW_10B_counter = 0;
|
static uint8_t BMW_10B_counter = 0;
|
||||||
|
|
||||||
static int16_t Battery_Current = 0;
|
static int16_t Battery_Current = 0;
|
||||||
|
@ -38,19 +53,17 @@ static uint16_t Battery_Status = 0;
|
||||||
static uint16_t DC_link = 0;
|
static uint16_t DC_link = 0;
|
||||||
static int16_t Battery_Power = 0;
|
static int16_t Battery_Power = 0;
|
||||||
|
|
||||||
void update_values_i3_battery()
|
void update_values_i3_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
||||||
{ //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
|
||||||
bms_status = ACTIVE; //Startout in active mode
|
bms_status = ACTIVE; //Startout in active mode
|
||||||
|
|
||||||
//Calculate the SOC% value to send to inverter
|
//Calculate the SOC% value to send to inverter
|
||||||
Calculated_SOC = (Display_SOC * 10); //Increase decimal amount
|
Calculated_SOC = (Display_SOC * 10); //Increase decimal amount
|
||||||
Calculated_SOC = LB_MIN_SOC + (LB_MAX_SOC - LB_MIN_SOC) * (Calculated_SOC - MINPERCENTAGE) / (MAXPERCENTAGE - MINPERCENTAGE);
|
Calculated_SOC =
|
||||||
if (Calculated_SOC < 0)
|
LB_MIN_SOC + (LB_MAX_SOC - LB_MIN_SOC) * (Calculated_SOC - MINPERCENTAGE) / (MAXPERCENTAGE - MINPERCENTAGE);
|
||||||
{ //We are in the real SOC% range of 0-20%, always set SOC sent to inverter as 0%
|
if (Calculated_SOC < 0) { //We are in the real SOC% range of 0-20%, always set SOC sent to inverter as 0%
|
||||||
Calculated_SOC = 0;
|
Calculated_SOC = 0;
|
||||||
}
|
}
|
||||||
if (Calculated_SOC > 1000)
|
if (Calculated_SOC > 1000) { //We are in the real SOC% range of 80-100%, always set SOC sent to inverter as 100%
|
||||||
{ //We are in the real SOC% range of 80-100%, always set SOC sent to inverter as 100%
|
|
||||||
Calculated_SOC = 1000;
|
Calculated_SOC = 1000;
|
||||||
}
|
}
|
||||||
SOC = (Calculated_SOC * 10); //increase LB_SOC range from 0-100.0 -> 100.00
|
SOC = (Calculated_SOC * 10); //increase LB_SOC range from 0-100.0 -> 100.00
|
||||||
|
@ -63,25 +76,22 @@ void update_values_i3_battery()
|
||||||
|
|
||||||
remaining_capacity_Wh = (Battery_Capacity_kWh * 1000);
|
remaining_capacity_Wh = (Battery_Capacity_kWh * 1000);
|
||||||
|
|
||||||
if(SOC > 9900) //If Soc is over 99%, stop charging
|
if (SOC > 9900) //If Soc is over 99%, stop charging
|
||||||
{
|
{
|
||||||
max_target_charge_power = 0;
|
max_target_charge_power = 0;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
max_target_charge_power = 5000; //Hardcoded value for testing. TODO, read real value from battery when discovered
|
max_target_charge_power = 5000; //Hardcoded value for testing. TODO, read real value from battery when discovered
|
||||||
}
|
}
|
||||||
|
|
||||||
if(SOC < 500) //If Soc is under 5%, stop dicharging
|
if (SOC < 500) //If Soc is under 5%, stop dicharging
|
||||||
{
|
{
|
||||||
max_target_discharge_power = 0;
|
max_target_discharge_power = 0;
|
||||||
}
|
} else {
|
||||||
else
|
max_target_discharge_power =
|
||||||
{
|
5000; //Hardcoded value for testing. TODO, read real value from battery when discovered
|
||||||
max_target_discharge_power = 5000; //Hardcoded value for testing. TODO, read real value from battery when discovered
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Battery_Power = (Battery_Current * (Battery_Volts/10));
|
Battery_Power = (Battery_Current * (Battery_Volts / 10));
|
||||||
|
|
||||||
stat_batt_power = Battery_Power; //TODO, is mapping OK?
|
stat_batt_power = Battery_Power; //TODO, is mapping OK?
|
||||||
|
|
||||||
|
@ -90,17 +100,14 @@ void update_values_i3_battery()
|
||||||
temperature_max; //hardcoded to 6*C in startup, TODO, find from battery CAN later
|
temperature_max; //hardcoded to 6*C in startup, TODO, find from battery CAN later
|
||||||
|
|
||||||
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
|
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
|
||||||
if(!CANstillAlive)
|
if (!CANstillAlive) {
|
||||||
{
|
|
||||||
bms_status = FAULT;
|
bms_status = FAULT;
|
||||||
Serial.println("No CAN communication detected for 60s. Shutting down battery control.");
|
Serial.println("No CAN communication detected for 60s. Shutting down battery control.");
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
CANstillAlive--;
|
CANstillAlive--;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_VIA_USB
|
#ifdef DEBUG_VIA_USB
|
||||||
Serial.print("SOC% battery: ");
|
Serial.print("SOC% battery: ");
|
||||||
Serial.print(Display_SOC);
|
Serial.print(Display_SOC);
|
||||||
Serial.print(" SOC% sent to inverter: ");
|
Serial.print(" SOC% sent to inverter: ");
|
||||||
|
@ -113,14 +120,12 @@ void update_values_i3_battery()
|
||||||
Serial.print(max_target_charge_power);
|
Serial.print(max_target_charge_power);
|
||||||
Serial.print(" Max discharge power: ");
|
Serial.print(" Max discharge power: ");
|
||||||
Serial.print(max_target_discharge_power);
|
Serial.print(max_target_discharge_power);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void receive_can_i3_battery(CAN_frame_t rx_frame)
|
void receive_can_i3_battery(CAN_frame_t rx_frame) {
|
||||||
{
|
|
||||||
CANstillAlive = 12;
|
CANstillAlive = 12;
|
||||||
switch (rx_frame.MsgID)
|
switch (rx_frame.MsgID) {
|
||||||
{
|
|
||||||
case 0x431: //Battery capacity [200ms]
|
case 0x431: //Battery capacity [200ms]
|
||||||
Battery_Capacity_kWh = (((rx_frame.data.u8[1] & 0x0F) << 8 | rx_frame.data.u8[5])) / 50;
|
Battery_Capacity_kWh = (((rx_frame.data.u8[1] & 0x0F) << 8 | rx_frame.data.u8[5])) / 50;
|
||||||
break;
|
break;
|
||||||
|
@ -164,26 +169,22 @@ void receive_can_i3_battery(CAN_frame_t rx_frame)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void send_can_i3_battery()
|
void send_can_i3_battery() {
|
||||||
{
|
|
||||||
unsigned long currentMillis = millis();
|
unsigned long currentMillis = millis();
|
||||||
// Send 600ms CAN Message
|
// Send 600ms CAN Message
|
||||||
if (currentMillis - previousMillis600 >= interval600)
|
if (currentMillis - previousMillis600 >= interval600) {
|
||||||
{
|
|
||||||
previousMillis600 = currentMillis;
|
previousMillis600 = currentMillis;
|
||||||
|
|
||||||
ESP32Can.CANWriteFrame(&BMW_512);
|
ESP32Can.CANWriteFrame(&BMW_512);
|
||||||
}
|
}
|
||||||
//Send 20ms message
|
//Send 20ms message
|
||||||
if (currentMillis - previousMillis20 >= interval20)
|
if (currentMillis - previousMillis20 >= interval20) {
|
||||||
{
|
|
||||||
previousMillis20 = currentMillis;
|
previousMillis20 = currentMillis;
|
||||||
|
|
||||||
BMW_10B.data.u8[0] = BMW_10B_0[BMW_10B_counter];
|
BMW_10B.data.u8[0] = BMW_10B_0[BMW_10B_counter];
|
||||||
BMW_10B.data.u8[1] = BMW_10B_1[BMW_10B_counter];
|
BMW_10B.data.u8[1] = BMW_10B_1[BMW_10B_counter];
|
||||||
BMW_10B_counter++;
|
BMW_10B_counter++;
|
||||||
if(BMW_10B_counter > 14)
|
if (BMW_10B_counter > 14) {
|
||||||
{
|
|
||||||
BMW_10B_counter = 0;
|
BMW_10B_counter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
#ifndef BMW_I3_BATTERY_H
|
#ifndef BMW_I3_BATTERY_H
|
||||||
#define BMW_I3_BATTERY_H
|
#define BMW_I3_BATTERY_H
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "../devboard/can/ESP32CAN.h"
|
|
||||||
#include "../../USER_SETTINGS.h"
|
#include "../../USER_SETTINGS.h"
|
||||||
|
#include "../devboard/can/ESP32CAN.h"
|
||||||
|
|
||||||
#define ABSOLUTE_MAX_VOLTAGE 4040 // 404.4V,if battery voltage goes over this, charging is not possible (goes into forced discharge)
|
#define ABSOLUTE_MAX_VOLTAGE \
|
||||||
|
4040 // 404.4V,if battery voltage goes over this, charging is not possible (goes into forced discharge)
|
||||||
#define ABSOLUTE_MIN_VOLTAGE 3100 // 310.0V if battery voltage goes under this, discharging further is disabled
|
#define ABSOLUTE_MIN_VOLTAGE 3100 // 310.0V if battery voltage goes under this, discharging further is disabled
|
||||||
|
|
||||||
// These parameters need to be mapped for the inverter
|
// These parameters need to be mapped for the inverter
|
||||||
|
|
|
@ -9,12 +9,42 @@ const int rx_queue_size = 10; // Receive Queue size
|
||||||
static uint8_t CANstillAlive = 12; //counter for checking if CAN is still alive
|
static uint8_t CANstillAlive = 12; //counter for checking if CAN is still alive
|
||||||
static uint8_t errorCode = 0; //stores if we have an error code active from battery control logic
|
static uint8_t errorCode = 0; //stores if we have an error code active from battery control logic
|
||||||
|
|
||||||
CAN_frame_t CHADEMO_108 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x108,.data = {0x01, 0xF4, 0x01, 0x0F, 0xB3, 0x01, 0x00, 0x00}};
|
CAN_frame_t CHADEMO_108 = {.FIR = {.B =
|
||||||
CAN_frame_t CHADEMO_109 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x109,.data = {0x02, 0x00, 0x00, 0x00, 0x01, 0x20, 0xFF, 0xFF}};
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x108,
|
||||||
|
.data = {0x01, 0xF4, 0x01, 0x0F, 0xB3, 0x01, 0x00, 0x00}};
|
||||||
|
CAN_frame_t CHADEMO_109 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x109,
|
||||||
|
.data = {0x02, 0x00, 0x00, 0x00, 0x01, 0x20, 0xFF, 0xFF}};
|
||||||
//For chademo v2.0 only
|
//For chademo v2.0 only
|
||||||
CAN_frame_t CHADEMO_118 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x118,.data = {0x10, 0x64, 0x00, 0xB0, 0x00, 0x1E, 0x00, 0x8F}};
|
CAN_frame_t CHADEMO_118 = {.FIR = {.B =
|
||||||
CAN_frame_t CHADEMO_208 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x208,.data = {0xFF, 0xF4, 0x01, 0xF0, 0x00, 0x00, 0xFA, 0x00}};
|
{
|
||||||
CAN_frame_t CHADEMO_209 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x209,.data = {0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x118,
|
||||||
|
.data = {0x10, 0x64, 0x00, 0xB0, 0x00, 0x1E, 0x00, 0x8F}};
|
||||||
|
CAN_frame_t CHADEMO_208 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x208,
|
||||||
|
.data = {0xFF, 0xF4, 0x01, 0xF0, 0x00, 0x00, 0xFA, 0x00}};
|
||||||
|
CAN_frame_t CHADEMO_209 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x209,
|
||||||
|
.data = {0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
|
||||||
//H100
|
//H100
|
||||||
uint8_t MinimumChargeCurrent = 0;
|
uint8_t MinimumChargeCurrent = 0;
|
||||||
|
@ -58,43 +88,37 @@ uint8_t DynamicControlStatus = 0;
|
||||||
uint8_t HighCurrentControlStatus = 0;
|
uint8_t HighCurrentControlStatus = 0;
|
||||||
uint8_t HighVoltageControlStatus = 0;
|
uint8_t HighVoltageControlStatus = 0;
|
||||||
|
|
||||||
|
void update_values_chademo_battery() { //This function maps all the values fetched via CAN to the correct parameters used for the inverter
|
||||||
void update_values_chademo_battery()
|
|
||||||
{ //This function maps all the values fetched via CAN to the correct parameters used for the inverter
|
|
||||||
bms_status = ACTIVE; //Startout in active mode
|
bms_status = ACTIVE; //Startout in active mode
|
||||||
|
|
||||||
SOC = ChargingRate;
|
SOC = ChargingRate;
|
||||||
|
|
||||||
max_target_discharge_power = (MaximumDischargeCurrent*MaximumBatteryVoltage); //In Watts, Convert A to P
|
max_target_discharge_power = (MaximumDischargeCurrent * MaximumBatteryVoltage); //In Watts, Convert A to P
|
||||||
|
|
||||||
battery_voltage = TargetBatteryVoltage; //Todo, scaling?
|
battery_voltage = TargetBatteryVoltage; //Todo, scaling?
|
||||||
|
|
||||||
capacity_Wh = ((RatedBatteryCapacity/0.11)*1000); //(Added in CHAdeMO v1.0.1), maybe handle hardcoded on lower protocol version?
|
capacity_Wh = ((RatedBatteryCapacity / 0.11) *
|
||||||
|
1000); //(Added in CHAdeMO v1.0.1), maybe handle hardcoded on lower protocol version?
|
||||||
|
|
||||||
remaining_capacity_Wh = (SOC/100)*capacity_Wh;
|
remaining_capacity_Wh = (SOC / 100) * capacity_Wh;
|
||||||
|
|
||||||
/* Check if the Vehicle is still sending CAN messages. If we go 60s without messages we raise an error*/
|
/* Check if the Vehicle is still sending CAN messages. If we go 60s without messages we raise an error*/
|
||||||
if(!CANstillAlive)
|
if (!CANstillAlive) {
|
||||||
{
|
|
||||||
bms_status = FAULT;
|
bms_status = FAULT;
|
||||||
errorCode = 7;
|
errorCode = 7;
|
||||||
Serial.println("No CAN communication detected for 60s. Shutting down battery control.");
|
Serial.println("No CAN communication detected for 60s. Shutting down battery control.");
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
CANstillAlive--;
|
CANstillAlive--;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_VIA_USB
|
#ifdef DEBUG_VIA_USB
|
||||||
if(errorCode > 0)
|
if (errorCode > 0) {
|
||||||
{
|
|
||||||
Serial.print("ERROR CODE ACTIVE IN SYSTEM. NUMBER: ");
|
Serial.print("ERROR CODE ACTIVE IN SYSTEM. NUMBER: ");
|
||||||
Serial.println(errorCode);
|
Serial.println(errorCode);
|
||||||
}
|
}
|
||||||
Serial.print("BMS Status (3=OK): ");
|
Serial.print("BMS Status (3=OK): ");
|
||||||
Serial.println(bms_status);
|
Serial.println(bms_status);
|
||||||
switch (bms_char_dis_status)
|
switch (bms_char_dis_status) {
|
||||||
{
|
|
||||||
case 0:
|
case 0:
|
||||||
Serial.println("Battery Idle");
|
Serial.println("Battery Idle");
|
||||||
break;
|
break;
|
||||||
|
@ -119,15 +143,13 @@ void update_values_chademo_battery()
|
||||||
Serial.println(temperature_min);
|
Serial.println(temperature_min);
|
||||||
Serial.print("Temperature Max: ");
|
Serial.print("Temperature Max: ");
|
||||||
Serial.println(temperature_max);
|
Serial.println(temperature_max);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void receive_can_chademo_battery(CAN_frame_t rx_frame)
|
void receive_can_chademo_battery(CAN_frame_t rx_frame) {
|
||||||
{
|
|
||||||
CANstillAlive == 12; //We are getting CAN messages from the vehicle, inform the watchdog
|
CANstillAlive == 12; //We are getting CAN messages from the vehicle, inform the watchdog
|
||||||
|
|
||||||
switch (rx_frame.MsgID)
|
switch (rx_frame.MsgID) {
|
||||||
{
|
|
||||||
case 0x100:
|
case 0x100:
|
||||||
MinimumChargeCurrent = rx_frame.data.u8[0];
|
MinimumChargeCurrent = rx_frame.data.u8[0];
|
||||||
MinumumBatteryVoltage = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]);
|
MinumumBatteryVoltage = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]);
|
||||||
|
@ -169,7 +191,8 @@ void receive_can_chademo_battery(CAN_frame_t rx_frame)
|
||||||
break;
|
break;
|
||||||
case 0x700:
|
case 0x700:
|
||||||
AutomakerCode = rx_frame.data.u8[0];
|
AutomakerCode = rx_frame.data.u8[0];
|
||||||
OptionalContent = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2]); //Actually more bytes, but not needed for our purpose
|
OptionalContent =
|
||||||
|
((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2]); //Actually more bytes, but not needed for our purpose
|
||||||
break;
|
break;
|
||||||
case 0x110: //Only present on Chademo v2.0
|
case 0x110: //Only present on Chademo v2.0
|
||||||
DynamicControlStatus = (rx_frame.data.u8[0] & 0x01);
|
DynamicControlStatus = (rx_frame.data.u8[0] & 0x01);
|
||||||
|
@ -179,12 +202,10 @@ void receive_can_chademo_battery(CAN_frame_t rx_frame)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void send_can_chademo_battery()
|
void send_can_chademo_battery() {
|
||||||
{
|
|
||||||
unsigned long currentMillis = millis();
|
unsigned long currentMillis = millis();
|
||||||
// Send 100ms CAN Message
|
// Send 100ms CAN Message
|
||||||
if (currentMillis - previousMillis100 >= interval100)
|
if (currentMillis - previousMillis100 >= interval100) {
|
||||||
{
|
|
||||||
previousMillis100 = currentMillis;
|
previousMillis100 = currentMillis;
|
||||||
|
|
||||||
ESP32Can.CANWriteFrame(&CHADEMO_108);
|
ESP32Can.CANWriteFrame(&CHADEMO_108);
|
||||||
|
@ -192,10 +213,8 @@ void send_can_chademo_battery()
|
||||||
ESP32Can.CANWriteFrame(&CHADEMO_208);
|
ESP32Can.CANWriteFrame(&CHADEMO_208);
|
||||||
ESP32Can.CANWriteFrame(&CHADEMO_209);
|
ESP32Can.CANWriteFrame(&CHADEMO_209);
|
||||||
|
|
||||||
if(ControlProtocolNumberEV >= 0x03)
|
if (ControlProtocolNumberEV >= 0x03) { //Only send the following on Chademo 2.0 vehicles?
|
||||||
{ //Only send the following on Chademo 2.0 vehicles?
|
|
||||||
ESP32Can.CANWriteFrame(&CHADEMO_118);
|
ESP32Can.CANWriteFrame(&CHADEMO_118);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
#ifndef CHADEMO_BATTERY_H
|
#ifndef CHADEMO_BATTERY_H
|
||||||
#define CHADEMO_BATTERY_H
|
#define CHADEMO_BATTERY_H
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "../devboard/can/ESP32CAN.h"
|
|
||||||
#include "../../USER_SETTINGS.h"
|
#include "../../USER_SETTINGS.h"
|
||||||
|
#include "../devboard/can/ESP32CAN.h"
|
||||||
|
|
||||||
#define ABSOLUTE_MAX_VOLTAGE 4040 // 404.4V,if battery voltage goes over this, charging is not possible (goes into forced discharge)
|
#define ABSOLUTE_MAX_VOLTAGE \
|
||||||
|
4040 // 404.4V,if battery voltage goes over this, charging is not possible (goes into forced discharge)
|
||||||
#define ABSOLUTE_MIN_VOLTAGE 3100 // 310.0V if battery voltage goes under this, discharging further is disabled
|
#define ABSOLUTE_MIN_VOLTAGE 3100 // 310.0V if battery voltage goes under this, discharging further is disabled
|
||||||
|
|
||||||
// These parameters need to be mapped
|
// These parameters need to be mapped
|
||||||
|
|
|
@ -39,33 +39,29 @@ static double min_volt_cel = 3.70;
|
||||||
static double max_temp_cel = 20.00;
|
static double max_temp_cel = 20.00;
|
||||||
static double min_temp_cel = 19.00;
|
static double min_temp_cel = 19.00;
|
||||||
|
|
||||||
|
void update_values_imiev_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
||||||
void update_values_imiev_battery()
|
|
||||||
{ //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
|
||||||
bms_status = ACTIVE; //Startout in active mode
|
bms_status = ACTIVE; //Startout in active mode
|
||||||
|
|
||||||
SOC = (uint16_t)(BMU_SOC * 100); //increase BMU_SOC range from 0-100 -> 100.00
|
SOC = (uint16_t)(BMU_SOC * 100); //increase BMU_SOC range from 0-100 -> 100.00
|
||||||
|
|
||||||
battery_voltage = (uint16_t)(BMU_PackVoltage * 10); // Multiply by 10 and cast to uint16_t
|
battery_voltage = (uint16_t)(BMU_PackVoltage * 10); // Multiply by 10 and cast to uint16_t
|
||||||
|
|
||||||
battery_current = (BMU_Current*10); //Todo, scaling?
|
battery_current = (BMU_Current * 10); //Todo, scaling?
|
||||||
|
|
||||||
capacity_Wh = BATTERY_WH_MAX; //Hardcoded to header value
|
capacity_Wh = BATTERY_WH_MAX; //Hardcoded to header value
|
||||||
|
|
||||||
remaining_capacity_Wh = (uint16_t)((SOC/10000)*capacity_Wh);
|
remaining_capacity_Wh = (uint16_t)((SOC / 10000) * capacity_Wh);
|
||||||
|
|
||||||
//We do not know if the max charge power is sent by the battery. So we estimate the value based on SOC%
|
//We do not know if the max charge power is sent by the battery. So we estimate the value based on SOC%
|
||||||
if(SOC == 10000){ //100.00%
|
if (SOC == 10000) { //100.00%
|
||||||
max_target_charge_power = 0; //When battery is 100% full, set allowed charge W to 0
|
max_target_charge_power = 0; //When battery is 100% full, set allowed charge W to 0
|
||||||
}
|
} else {
|
||||||
else{
|
|
||||||
max_target_charge_power = 10000; //Otherwise we can push 10kW into the pack!
|
max_target_charge_power = 10000; //Otherwise we can push 10kW into the pack!
|
||||||
}
|
}
|
||||||
|
|
||||||
if(SOC < 200){ //2.00%
|
if (SOC < 200) { //2.00%
|
||||||
max_target_discharge_power = 0; //When battery is empty (below 2%), set allowed discharge W to 0
|
max_target_discharge_power = 0; //When battery is empty (below 2%), set allowed discharge W to 0
|
||||||
}
|
} else {
|
||||||
else{
|
|
||||||
max_target_discharge_power = 10000; //Otherwise we can discharge 10kW from the pack!
|
max_target_discharge_power = 10000; //Otherwise we can discharge 10kW from the pack!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,36 +91,33 @@ void update_values_imiev_battery()
|
||||||
min_temp_cel = cell_temperatures[0]; // Initialize min with the first element of the array
|
min_temp_cel = cell_temperatures[0]; // Initialize min with the first element of the array
|
||||||
for (int i = 1; i < m; i++) {
|
for (int i = 1; i < m; i++) {
|
||||||
if (cell_temperatures[i] < min_temp_cel) {
|
if (cell_temperatures[i] < min_temp_cel) {
|
||||||
if(min_temp_cel != -50.00){ //-50.00 means this sensor not connected
|
if (min_temp_cel != -50.00) { //-50.00 means this sensor not connected
|
||||||
min_temp_cel = cell_temperatures[i]; // Update max if we find a smaller element
|
min_temp_cel = cell_temperatures[i]; // Update max if we find a smaller element
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cell_max_voltage = (uint16_t)(max_volt_cel*1000);
|
cell_max_voltage = (uint16_t)(max_volt_cel * 1000);
|
||||||
|
|
||||||
cell_min_voltage = (uint16_t)(min_volt_cel*1000);
|
cell_min_voltage = (uint16_t)(min_volt_cel * 1000);
|
||||||
|
|
||||||
temperature_min = (uint16_t)(min_temp_cel*1000);
|
temperature_min = (uint16_t)(min_temp_cel * 1000);
|
||||||
|
|
||||||
temperature_max = (uint16_t)(max_temp_cel*1000);
|
temperature_max = (uint16_t)(max_temp_cel * 1000);
|
||||||
|
|
||||||
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
|
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
|
||||||
if(!CANstillAlive)
|
if (!CANstillAlive) {
|
||||||
{
|
|
||||||
bms_status = FAULT;
|
bms_status = FAULT;
|
||||||
Serial.println("No CAN communication detected for 60s. Shutting down battery control.");
|
Serial.println("No CAN communication detected for 60s. Shutting down battery control.");
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
CANstillAlive--;
|
CANstillAlive--;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!BMU_Detected){
|
if (!BMU_Detected) {
|
||||||
Serial.println("BMU not detected, check wiring!");
|
Serial.println("BMU not detected, check wiring!");
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_VIA_USB
|
#ifdef DEBUG_VIA_USB
|
||||||
|
|
||||||
Serial.println("Battery Values");
|
Serial.println("Battery Values");
|
||||||
Serial.print("BMU SOC: ");
|
Serial.print("BMU SOC: ");
|
||||||
|
@ -166,17 +159,16 @@ void update_values_imiev_battery()
|
||||||
Serial.print(" Cell mV min ");
|
Serial.print(" Cell mV min ");
|
||||||
Serial.print(cell_min_voltage);
|
Serial.print(cell_min_voltage);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void receive_can_imiev_battery(CAN_frame_t rx_frame)
|
void receive_can_imiev_battery(CAN_frame_t rx_frame) {
|
||||||
{
|
CANstillAlive =
|
||||||
CANstillAlive = 12; //Todo, move this inside a known message ID to prevent CAN inverter from keeping battery alive detection going
|
12; //Todo, move this inside a known message ID to prevent CAN inverter from keeping battery alive detection going
|
||||||
switch (rx_frame.MsgID)
|
switch (rx_frame.MsgID) {
|
||||||
{
|
|
||||||
case 0x374: //BMU message, 10ms - SOC
|
case 0x374: //BMU message, 10ms - SOC
|
||||||
temp_value = ((rx_frame.data.u8[1] - 10) / 2);
|
temp_value = ((rx_frame.data.u8[1] - 10) / 2);
|
||||||
if(temp_value >= 0 && temp_value <= 101){
|
if (temp_value >= 0 && temp_value <= 101) {
|
||||||
BMU_SOC = temp_value;
|
BMU_SOC = temp_value;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -204,24 +196,19 @@ CANstillAlive = 12; //Todo, move this inside a known message ID to prevent CAN i
|
||||||
|
|
||||||
voltage_index = ((cmu_id - 1) * 8 + (2 * pid_index));
|
voltage_index = ((cmu_id - 1) * 8 + (2 * pid_index));
|
||||||
temp_index = ((cmu_id - 1) * 6 + (2 * pid_index));
|
temp_index = ((cmu_id - 1) * 6 + (2 * pid_index));
|
||||||
if (cmu_id >= 7)
|
if (cmu_id >= 7) {
|
||||||
{
|
|
||||||
voltage_index -= 4;
|
voltage_index -= 4;
|
||||||
temp_index -= 3;
|
temp_index -= 3;
|
||||||
}
|
}
|
||||||
cell_voltages[voltage_index] = voltage1;
|
cell_voltages[voltage_index] = voltage1;
|
||||||
cell_voltages[voltage_index + 1] = voltage2;
|
cell_voltages[voltage_index + 1] = voltage2;
|
||||||
|
|
||||||
if (pid_index == 0)
|
if (pid_index == 0) {
|
||||||
{
|
|
||||||
cell_temperatures[temp_index] = temp2;
|
cell_temperatures[temp_index] = temp2;
|
||||||
cell_temperatures[temp_index + 1] = temp3;
|
cell_temperatures[temp_index + 1] = temp3;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
cell_temperatures[temp_index] = temp1;
|
cell_temperatures[temp_index] = temp1;
|
||||||
if (cmu_id != 6 && cmu_id != 12)
|
if (cmu_id != 6 && cmu_id != 12) {
|
||||||
{
|
|
||||||
cell_temperatures[temp_index + 1] = temp2;
|
cell_temperatures[temp_index + 1] = temp2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -231,12 +218,10 @@ CANstillAlive = 12; //Todo, move this inside a known message ID to prevent CAN i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_can_imiev_battery()
|
void send_can_imiev_battery() {
|
||||||
{
|
|
||||||
unsigned long currentMillis = millis();
|
unsigned long currentMillis = millis();
|
||||||
// Send 100ms CAN Message
|
// Send 100ms CAN Message
|
||||||
if (currentMillis - previousMillis100 >= interval100)
|
if (currentMillis - previousMillis100 >= interval100) {
|
||||||
{
|
|
||||||
previousMillis100 = currentMillis;
|
previousMillis100 = currentMillis;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
#ifndef IMIEV_CZERO_ION_BATTERY_H
|
#ifndef IMIEV_CZERO_ION_BATTERY_H
|
||||||
#define IMIEV_CZERO_ION_BATTERY_H
|
#define IMIEV_CZERO_ION_BATTERY_H
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "../devboard/can/ESP32CAN.h"
|
|
||||||
#include "../../USER_SETTINGS.h"
|
#include "../../USER_SETTINGS.h"
|
||||||
|
#include "../devboard/can/ESP32CAN.h"
|
||||||
|
|
||||||
#define ABSOLUTE_MAX_VOLTAGE 4040 // 404.4V,if battery voltage goes over this, charging is not possible (goes into forced discharge)
|
#define ABSOLUTE_MAX_VOLTAGE \
|
||||||
|
4040 // 404.4V,if battery voltage goes over this, charging is not possible (goes into forced discharge)
|
||||||
#define ABSOLUTE_MIN_VOLTAGE 3100 // 310.0V if battery voltage goes under this, discharging further is disabled
|
#define ABSOLUTE_MIN_VOLTAGE 3100 // 310.0V if battery voltage goes under this, discharging further is disabled
|
||||||
|
|
||||||
// These parameters need to be mapped for the Gen24
|
// These parameters need to be mapped for the Gen24
|
||||||
|
|
|
@ -16,8 +16,7 @@ static int SOC_1 = 0;
|
||||||
static int SOC_2 = 0;
|
static int SOC_2 = 0;
|
||||||
static int SOC_3 = 0;
|
static int SOC_3 = 0;
|
||||||
|
|
||||||
void update_values_kiaHyundai_64_battery()
|
void update_values_kiaHyundai_64_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
||||||
{ //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
|
||||||
bms_status = ACTIVE; //Startout in active mode
|
bms_status = ACTIVE; //Startout in active mode
|
||||||
|
|
||||||
SOC;
|
SOC;
|
||||||
|
@ -41,31 +40,26 @@ void update_values_kiaHyundai_64_battery()
|
||||||
temperature_max;
|
temperature_max;
|
||||||
|
|
||||||
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
|
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
|
||||||
if(!CANstillAlive)
|
if (!CANstillAlive) {
|
||||||
{
|
|
||||||
bms_status = FAULT;
|
bms_status = FAULT;
|
||||||
Serial.println("No CAN communication detected for 60s. Shutting down battery control.");
|
Serial.println("No CAN communication detected for 60s. Shutting down battery control.");
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
CANstillAlive--;
|
CANstillAlive--;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_VIA_USB
|
#ifdef DEBUG_VIA_USB
|
||||||
Serial.print("SOC% candidate 1: ");
|
Serial.print("SOC% candidate 1: ");
|
||||||
Serial.println(SOC_1);
|
Serial.println(SOC_1);
|
||||||
Serial.print("SOC% candidate 2: ");
|
Serial.print("SOC% candidate 2: ");
|
||||||
Serial.println(SOC_2);
|
Serial.println(SOC_2);
|
||||||
Serial.print("SOC% candidate 3: ");
|
Serial.print("SOC% candidate 3: ");
|
||||||
Serial.println(SOC_3);
|
Serial.println(SOC_3);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void receive_can_kiaHyundai_64_battery(CAN_frame_t rx_frame)
|
void receive_can_kiaHyundai_64_battery(CAN_frame_t rx_frame) {
|
||||||
{
|
|
||||||
CANstillAlive = 12;
|
CANstillAlive = 12;
|
||||||
switch (rx_frame.MsgID)
|
switch (rx_frame.MsgID) {
|
||||||
{
|
|
||||||
case 0x3F6:
|
case 0x3F6:
|
||||||
break;
|
break;
|
||||||
case 0x491:
|
case 0x491:
|
||||||
|
@ -117,18 +111,14 @@ void receive_can_kiaHyundai_64_battery(CAN_frame_t rx_frame)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void send_can_kiaHyundai_64_battery()
|
void send_can_kiaHyundai_64_battery() {
|
||||||
{
|
|
||||||
unsigned long currentMillis = millis();
|
unsigned long currentMillis = millis();
|
||||||
// Send 100ms CAN Message
|
// Send 100ms CAN Message
|
||||||
if (currentMillis - previousMillis100 >= interval100)
|
if (currentMillis - previousMillis100 >= interval100) {
|
||||||
{
|
|
||||||
previousMillis100 = currentMillis;
|
previousMillis100 = currentMillis;
|
||||||
|
|
||||||
}
|
}
|
||||||
//Send 10ms message
|
//Send 10ms message
|
||||||
if (currentMillis - previousMillis10 >= interval10)
|
if (currentMillis - previousMillis10 >= interval10) {
|
||||||
{
|
|
||||||
previousMillis10 = currentMillis;
|
previousMillis10 = currentMillis;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
#ifndef KIA_HYUNDAI_64_BATTERY_H
|
#ifndef KIA_HYUNDAI_64_BATTERY_H
|
||||||
#define KIA_HYUNDAI_64_BATTERY_H
|
#define KIA_HYUNDAI_64_BATTERY_H
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "../devboard/can/ESP32CAN.h"
|
|
||||||
#include "../../USER_SETTINGS.h"
|
#include "../../USER_SETTINGS.h"
|
||||||
|
#include "../devboard/can/ESP32CAN.h"
|
||||||
|
|
||||||
#define ABSOLUTE_MAX_VOLTAGE 4040 // 404.4V,if battery voltage goes over this, charging is not possible (goes into forced discharge)
|
#define ABSOLUTE_MAX_VOLTAGE \
|
||||||
|
4040 // 404.4V,if battery voltage goes over this, charging is not possible (goes into forced discharge)
|
||||||
#define ABSOLUTE_MIN_VOLTAGE 3100 // 310.0V if battery voltage goes under this, discharging further is disabled
|
#define ABSOLUTE_MIN_VOLTAGE 3100 // 310.0V if battery voltage goes under this, discharging further is disabled
|
||||||
|
|
||||||
// These parameters need to be mapped for the Gen24
|
// These parameters need to be mapped for the Gen24
|
||||||
|
|
|
@ -17,14 +17,50 @@ static uint8_t mprun10r = 0; //counter 0-20 for 0x1F2 message
|
||||||
static byte mprun10 = 0; //counter 0-3
|
static byte mprun10 = 0; //counter 0-3
|
||||||
static byte mprun100 = 0; //counter 0-3
|
static byte mprun100 = 0; //counter 0-3
|
||||||
|
|
||||||
CAN_frame_t LEAF_1F2 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x1F2,.data = {0x10, 0x64, 0x00, 0xB0, 0x00, 0x1E, 0x00, 0x8F}};
|
CAN_frame_t LEAF_1F2 = {.FIR = {.B =
|
||||||
CAN_frame_t LEAF_50B = {.FIR = {.B = {.DLC = 7,.FF = CAN_frame_std,}},.MsgID = 0x50B,.data = {0x00, 0x00, 0x06, 0xC0, 0x00, 0x00, 0x00}};
|
{
|
||||||
CAN_frame_t LEAF_50C = {.FIR = {.B = {.DLC = 6,.FF = CAN_frame_std,}},.MsgID = 0x50C,.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
.DLC = 8,
|
||||||
CAN_frame_t LEAF_1D4 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x1D4,.data = {0x6E, 0x6E, 0x00, 0x04, 0x07, 0x46, 0xE0, 0x44}};
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x1F2,
|
||||||
|
.data = {0x10, 0x64, 0x00, 0xB0, 0x00, 0x1E, 0x00, 0x8F}};
|
||||||
|
CAN_frame_t LEAF_50B = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 7,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x50B,
|
||||||
|
.data = {0x00, 0x00, 0x06, 0xC0, 0x00, 0x00, 0x00}};
|
||||||
|
CAN_frame_t LEAF_50C = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 6,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x50C,
|
||||||
|
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
CAN_frame_t LEAF_1D4 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x1D4,
|
||||||
|
.data = {0x6E, 0x6E, 0x00, 0x04, 0x07, 0x46, 0xE0, 0x44}};
|
||||||
//These CAN messages need to be sent towards the battery to keep it alive
|
//These CAN messages need to be sent towards the battery to keep it alive
|
||||||
|
|
||||||
CAN_frame_t LEAF_GROUP_REQUEST = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x79B,.data = {2, 0x21, 1, 0, 0, 0, 0, 0}};
|
CAN_frame_t LEAF_GROUP_REQUEST = {.FIR = {.B =
|
||||||
const CAN_frame_t LEAF_NEXT_LINE_REQUEST = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x79B,.data = {0x30, 1, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}};
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x79B,
|
||||||
|
.data = {2, 0x21, 1, 0, 0, 0, 0, 0}};
|
||||||
|
const CAN_frame_t LEAF_NEXT_LINE_REQUEST = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x79B,
|
||||||
|
.data = {0x30, 1, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}};
|
||||||
// The Li-ion battery controller only accepts a multi-message query. In fact, the LBC transmits many
|
// The Li-ion battery controller only accepts a multi-message query. In fact, the LBC transmits many
|
||||||
// groups: the first one contains lots of High Voltage battery data as SOC, currents, and voltage; the second
|
// groups: the first one contains lots of High Voltage battery data as SOC, currents, and voltage; the second
|
||||||
// replies with all the battery’s cells voltages in millivolt, the third and the fifth one are still unknown, the
|
// replies with all the battery’s cells voltages in millivolt, the third and the fifth one are still unknown, the
|
||||||
|
@ -32,14 +68,19 @@ const CAN_frame_t LEAF_NEXT_LINE_REQUEST = {.FIR = {.B = {.DLC = 8,.FF = CAN_fra
|
||||||
// There are also two more groups: group 61, which replies with lots of CAN messages (up to 48); here we
|
// There are also two more groups: group 61, which replies with lots of CAN messages (up to 48); here we
|
||||||
// found the SOH value, and group 84 that replies with the HV battery production serial.
|
// found the SOH value, and group 84 that replies with the HV battery production serial.
|
||||||
|
|
||||||
static uint8_t crctable[256] = {0,133,143,10,155,30,20,145,179,54,60,185,40,173,167,34,227,102,108,233,120,253,247,114,80,213,223,90,203,78,68,193,67,
|
static uint8_t crctable[256] = {
|
||||||
198,204,73,216,93,87,210,240,117,127,250,107,238,228,97,160,37,47,170,59,190,180,49,19,150,156,25,136,13,7,130,134,3,9,
|
0, 133, 143, 10, 155, 30, 20, 145, 179, 54, 60, 185, 40, 173, 167, 34, 227, 102, 108, 233, 120, 253,
|
||||||
140,29,152,146,23,53,176,186,63,174,43,33,164,101,224,234,111,254,123,113,244,214,83,89,220,77,200,194,71,197,64,74,207,
|
247, 114, 80, 213, 223, 90, 203, 78, 68, 193, 67, 198, 204, 73, 216, 93, 87, 210, 240, 117, 127, 250,
|
||||||
94,219,209,84,118,243,249,124,237,104,98,231,38,163,169,44,189,56,50,183,149,16,26,159,14,139,129,4,137,12,6,131,18,151,
|
107, 238, 228, 97, 160, 37, 47, 170, 59, 190, 180, 49, 19, 150, 156, 25, 136, 13, 7, 130, 134, 3,
|
||||||
157,24,58,191,181,48,161,36,46,171,106,239,229,96,241,116,126,251,217,92,86,211,66,199,205,72,202,79,69,192,81,212,222,
|
9, 140, 29, 152, 146, 23, 53, 176, 186, 63, 174, 43, 33, 164, 101, 224, 234, 111, 254, 123, 113, 244,
|
||||||
91,121,252,246,115,226,103,109,232,41,172,166,35,178,55,61,184,154,31,21,144,1,132,142,11,15,138,128,5,148,17,27,158,188,
|
214, 83, 89, 220, 77, 200, 194, 71, 197, 64, 74, 207, 94, 219, 209, 84, 118, 243, 249, 124, 237, 104,
|
||||||
57,51,182,39,162,168,45,236,105,99,230,119,242,248,125,95,218,208,85,196,65,75,206,76,201,195,70,215,82,88,221,255,122,
|
98, 231, 38, 163, 169, 44, 189, 56, 50, 183, 149, 16, 26, 159, 14, 139, 129, 4, 137, 12, 6, 131,
|
||||||
112,245,100,225,235,110,175,42,32,165,52,177,187,62,28,153,147,22,135,2,8,141};
|
18, 151, 157, 24, 58, 191, 181, 48, 161, 36, 46, 171, 106, 239, 229, 96, 241, 116, 126, 251, 217, 92,
|
||||||
|
86, 211, 66, 199, 205, 72, 202, 79, 69, 192, 81, 212, 222, 91, 121, 252, 246, 115, 226, 103, 109, 232,
|
||||||
|
41, 172, 166, 35, 178, 55, 61, 184, 154, 31, 21, 144, 1, 132, 142, 11, 15, 138, 128, 5, 148, 17,
|
||||||
|
27, 158, 188, 57, 51, 182, 39, 162, 168, 45, 236, 105, 99, 230, 119, 242, 248, 125, 95, 218, 208, 85,
|
||||||
|
196, 65, 75, 206, 76, 201, 195, 70, 215, 82, 88, 221, 255, 122, 112, 245, 100, 225, 235, 110, 175, 42,
|
||||||
|
32, 165, 52, 177, 187, 62, 28, 153, 147, 22, 135, 2, 8, 141};
|
||||||
|
|
||||||
//Nissan LEAF battery parameters from constantly sent CAN
|
//Nissan LEAF battery parameters from constantly sent CAN
|
||||||
#define ZE0_BATTERY 0
|
#define ZE0_BATTERY 0
|
||||||
|
@ -78,7 +119,8 @@ static uint8_t LB_Failsafe_Status = 0; //LB_STATUS = 000b = normal start Request
|
||||||
//101b = Caution Lamp Request & Main Relay OFF Request
|
//101b = Caution Lamp Request & Main Relay OFF Request
|
||||||
//110b = Caution Lamp Request & Charging Mode Stop Request
|
//110b = Caution Lamp Request & Charging Mode Stop Request
|
||||||
//111b = Caution Lamp Request & Main Relay OFF Request
|
//111b = Caution Lamp Request & Main Relay OFF Request
|
||||||
static byte LB_Interlock = 1; //Contains info on if HV leads are seated (Note, to use this both HV connectors need to be inserted)
|
static byte LB_Interlock =
|
||||||
|
1; //Contains info on if HV leads are seated (Note, to use this both HV connectors need to be inserted)
|
||||||
static byte LB_Full_CHARGE_flag = 0; //LB_FCHGEND , Goes to 1 if battery is fully charged
|
static byte LB_Full_CHARGE_flag = 0; //LB_FCHGEND , Goes to 1 if battery is fully charged
|
||||||
static byte LB_MainRelayOn_flag = 0; //No-Permission=0, Main Relay On Permission=1
|
static byte LB_MainRelayOn_flag = 0; //No-Permission=0, Main Relay On Permission=1
|
||||||
static byte LB_Capacity_Empty = 0; //LB_EMPTY, , Goes to 1 if battery is empty
|
static byte LB_Capacity_Empty = 0; //LB_EMPTY, , Goes to 1 if battery is empty
|
||||||
|
@ -95,8 +137,10 @@ static uint16_t min_max_voltage[2]; //contains cell min[0] and max[1] values in
|
||||||
static uint16_t cell_deviation_mV = 0; //contains the deviation between highest and lowest cell in mV
|
static uint16_t cell_deviation_mV = 0; //contains the deviation between highest and lowest cell in mV
|
||||||
static uint16_t HX = 0; //Internal resistance
|
static uint16_t HX = 0; //Internal resistance
|
||||||
static uint16_t insulation = 0; //Insulation resistance
|
static uint16_t insulation = 0; //Insulation resistance
|
||||||
static int32_t Battery_current_1 = 0; //High Voltage battery current; it’s positive if discharged, negative when charging
|
static int32_t Battery_current_1 =
|
||||||
static int32_t Battery_current_2 = 0; //High Voltage battery current; it’s positive if discharged, negative when charging (unclear why two values exist)
|
0; //High Voltage battery current; it’s positive if discharged, negative when charging
|
||||||
|
static int32_t Battery_current_2 =
|
||||||
|
0; //High Voltage battery current; it’s positive if discharged, negative when charging (unclear why two values exist)
|
||||||
static uint16_t temp_raw_1 = 0;
|
static uint16_t temp_raw_1 = 0;
|
||||||
static uint8_t temp_raw_2_highnibble = 0;
|
static uint8_t temp_raw_2_highnibble = 0;
|
||||||
static uint16_t temp_raw_2 = 0;
|
static uint16_t temp_raw_2 = 0;
|
||||||
|
@ -107,14 +151,13 @@ static uint16_t temp_raw_min = 0;
|
||||||
static int16_t temp_polled_max = 0;
|
static int16_t temp_polled_max = 0;
|
||||||
static int16_t temp_polled_min = 0;
|
static int16_t temp_polled_min = 0;
|
||||||
|
|
||||||
void print_with_units(char *header, int value, char *units) {
|
void print_with_units(char* header, int value, char* units) {
|
||||||
Serial.print(header);
|
Serial.print(header);
|
||||||
Serial.print(value);
|
Serial.print(value);
|
||||||
Serial.print(units);
|
Serial.print(units);
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_values_leaf_battery()
|
void update_values_leaf_battery() { /* This function maps all the values fetched via CAN to the correct parameters used for modbus */
|
||||||
{ /* This function maps all the values fetched via CAN to the correct parameters used for modbus */
|
|
||||||
bms_status = ACTIVE; //Startout in active mode
|
bms_status = ACTIVE; //Startout in active mode
|
||||||
|
|
||||||
/* Start with mapping all values */
|
/* Start with mapping all values */
|
||||||
|
@ -123,48 +166,47 @@ void update_values_leaf_battery()
|
||||||
|
|
||||||
//Calculate the SOC% value to send to Fronius
|
//Calculate the SOC% value to send to Fronius
|
||||||
CalculatedSOC = LB_SOC;
|
CalculatedSOC = LB_SOC;
|
||||||
CalculatedSOC = LB_MIN_SOC + (LB_MAX_SOC - LB_MIN_SOC) * (CalculatedSOC - MINPERCENTAGE) / (MAXPERCENTAGE - MINPERCENTAGE);
|
CalculatedSOC =
|
||||||
if (CalculatedSOC < 0){ //We are in the real SOC% range of 0-20%, always set SOC sent to Fronius as 0%
|
LB_MIN_SOC + (LB_MAX_SOC - LB_MIN_SOC) * (CalculatedSOC - MINPERCENTAGE) / (MAXPERCENTAGE - MINPERCENTAGE);
|
||||||
|
if (CalculatedSOC < 0) { //We are in the real SOC% range of 0-20%, always set SOC sent to Fronius as 0%
|
||||||
CalculatedSOC = 0;
|
CalculatedSOC = 0;
|
||||||
}
|
}
|
||||||
if (CalculatedSOC > 1000){ //We are in the real SOC% range of 80-100%, always set SOC sent to Fronius as 100%
|
if (CalculatedSOC > 1000) { //We are in the real SOC% range of 80-100%, always set SOC sent to Fronius as 100%
|
||||||
CalculatedSOC = 1000;
|
CalculatedSOC = 1000;
|
||||||
}
|
}
|
||||||
SOC = (CalculatedSOC * 10); //increase CalculatedSOC range from 0-100.0 -> 100.00
|
SOC = (CalculatedSOC * 10); //increase CalculatedSOC range from 0-100.0 -> 100.00
|
||||||
|
|
||||||
battery_voltage = (LB_Total_Voltage*10); //One more decimal needed
|
battery_voltage = (LB_Total_Voltage * 10); //One more decimal needed
|
||||||
|
|
||||||
battery_current = convert2unsignedint16(LB_Current*10); //One more decimal needed, sign if needed
|
battery_current = convert2unsignedint16(LB_Current * 10); //One more decimal needed, sign if needed
|
||||||
|
|
||||||
capacity_Wh = (LB_Max_GIDS * WH_PER_GID);
|
capacity_Wh = (LB_Max_GIDS * WH_PER_GID);
|
||||||
|
|
||||||
remaining_capacity_Wh = LB_Wh_Remaining;
|
remaining_capacity_Wh = LB_Wh_Remaining;
|
||||||
|
|
||||||
LB_Power = LB_Total_Voltage * LB_Current;//P = U * I
|
LB_Power = LB_Total_Voltage * LB_Current; //P = U * I
|
||||||
stat_batt_power = convert2unsignedint16(LB_Power); //add sign if needed
|
stat_batt_power = convert2unsignedint16(LB_Power); //add sign if needed
|
||||||
|
|
||||||
//Update temperature readings. Method depends on which generation LEAF battery is used
|
//Update temperature readings. Method depends on which generation LEAF battery is used
|
||||||
if(LEAF_Battery_Type == ZE0_BATTERY){
|
if (LEAF_Battery_Type == ZE0_BATTERY) {
|
||||||
//Since we only have average value, send the minimum as -1.0 degrees below average
|
//Since we only have average value, send the minimum as -1.0 degrees below average
|
||||||
temperature_min = convert2unsignedint16((LB_AverageTemperature * 10)-10); //add sign if negative and increase range
|
temperature_min =
|
||||||
|
convert2unsignedint16((LB_AverageTemperature * 10) - 10); //add sign if negative and increase range
|
||||||
temperature_max = convert2unsignedint16((LB_AverageTemperature * 10));
|
temperature_max = convert2unsignedint16((LB_AverageTemperature * 10));
|
||||||
}
|
} else if (LEAF_Battery_Type == AZE0_BATTERY) {
|
||||||
else if(LEAF_Battery_Type == AZE0_BATTERY){
|
|
||||||
//Use the value sent constantly via CAN in 5C0 (only available on AZE0)
|
//Use the value sent constantly via CAN in 5C0 (only available on AZE0)
|
||||||
temperature_min = convert2unsignedint16((LB_HistData_Temperature_MIN * 10)); //add sign if negative and increase range
|
temperature_min =
|
||||||
|
convert2unsignedint16((LB_HistData_Temperature_MIN * 10)); //add sign if negative and increase range
|
||||||
temperature_max = convert2unsignedint16((LB_HistData_Temperature_MAX * 10));
|
temperature_max = convert2unsignedint16((LB_HistData_Temperature_MAX * 10));
|
||||||
}
|
} else { // ZE1 (TODO: Once the muxed value in 5C0 becomes known, switch to using that instead of this complicated polled value)
|
||||||
else
|
if (temp_raw_min != 0) //We have a polled value available
|
||||||
{ // ZE1 (TODO: Once the muxed value in 5C0 becomes known, switch to using that instead of this complicated polled value)
|
|
||||||
if(temp_raw_min != 0) //We have a polled value available
|
|
||||||
{
|
{
|
||||||
temp_polled_min = ((Temp_fromRAW_to_F(temp_raw_min) - 320 ) * 5) / 9; //Convert from F to C
|
temp_polled_min = ((Temp_fromRAW_to_F(temp_raw_min) - 320) * 5) / 9; //Convert from F to C
|
||||||
temp_polled_max = ((Temp_fromRAW_to_F(temp_raw_max) - 320 ) * 5) / 9; //Convert from F to C
|
temp_polled_max = ((Temp_fromRAW_to_F(temp_raw_max) - 320) * 5) / 9; //Convert from F to C
|
||||||
if(temp_polled_min < temp_polled_max){ //Catch any edge cases from Temp_fromRAW_to_F function
|
if (temp_polled_min < temp_polled_max) { //Catch any edge cases from Temp_fromRAW_to_F function
|
||||||
temperature_min = convert2unsignedint16((temp_polled_min));
|
temperature_min = convert2unsignedint16((temp_polled_min));
|
||||||
temperature_max = convert2unsignedint16((temp_polled_max));
|
temperature_max = convert2unsignedint16((temp_polled_max));
|
||||||
}
|
} else {
|
||||||
else{
|
|
||||||
temperature_min = convert2unsignedint16((temp_polled_max));
|
temperature_min = convert2unsignedint16((temp_polled_max));
|
||||||
temperature_max = convert2unsignedint16((temp_polled_min));
|
temperature_max = convert2unsignedint16((temp_polled_min));
|
||||||
}
|
}
|
||||||
|
@ -172,42 +214,38 @@ void update_values_leaf_battery()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define power able to be discharged from battery
|
// Define power able to be discharged from battery
|
||||||
if(LB_Discharge_Power_Limit > 30) { //if >30kW can be pulled from battery
|
if (LB_Discharge_Power_Limit > 30) { //if >30kW can be pulled from battery
|
||||||
max_target_discharge_power = 30000; //cap value so we don't go over the Fronius limits
|
max_target_discharge_power = 30000; //cap value so we don't go over the Fronius limits
|
||||||
}
|
} else {
|
||||||
else{
|
|
||||||
max_target_discharge_power = (LB_Discharge_Power_Limit * 1000); //kW to W
|
max_target_discharge_power = (LB_Discharge_Power_Limit * 1000); //kW to W
|
||||||
}
|
}
|
||||||
if(SOC == 0){ //Scaled SOC% value is 0.00%, we should not discharge battery further
|
if (SOC == 0) { //Scaled SOC% value is 0.00%, we should not discharge battery further
|
||||||
max_target_discharge_power = 0;
|
max_target_discharge_power = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define power able to be put into the battery
|
// Define power able to be put into the battery
|
||||||
if(LB_Charge_Power_Limit > 30){ //if >30kW can be put into the battery
|
if (LB_Charge_Power_Limit > 30) { //if >30kW can be put into the battery
|
||||||
max_target_charge_power = 30000; //cap value so we don't go over the Fronius limits
|
max_target_charge_power = 30000; //cap value so we don't go over the Fronius limits
|
||||||
}
|
} else {
|
||||||
else{
|
|
||||||
max_target_charge_power = (LB_Charge_Power_Limit * 1000); //kW to W
|
max_target_charge_power = (LB_Charge_Power_Limit * 1000); //kW to W
|
||||||
}
|
}
|
||||||
if(SOC == 10000) //Scaled SOC% value is 100.00%
|
if (SOC == 10000) //Scaled SOC% value is 100.00%
|
||||||
{
|
{
|
||||||
max_target_charge_power = 0; //No need to charge further, set max power to 0
|
max_target_charge_power = 0; //No need to charge further, set max power to 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/*Extra safety functions below*/
|
/*Extra safety functions below*/
|
||||||
if(LB_GIDS < 10) //800Wh left in battery
|
if (LB_GIDS < 10) //800Wh left in battery
|
||||||
{ //Battery is running abnormally low, some discharge logic might have failed. Zero it all out.
|
{ //Battery is running abnormally low, some discharge logic might have failed. Zero it all out.
|
||||||
SOC = 0;
|
SOC = 0;
|
||||||
max_target_discharge_power = 0;
|
max_target_discharge_power = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(LB_Full_CHARGE_flag)
|
if (LB_Full_CHARGE_flag) { //Battery reports that it is fully charged stop all further charging incase it hasn't already
|
||||||
{ //Battery reports that it is fully charged stop all further charging incase it hasn't already
|
|
||||||
max_target_charge_power = 0;
|
max_target_charge_power = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(LB_Relay_Cut_Request)
|
if (LB_Relay_Cut_Request) { //LB_FAIL, BMS requesting shutdown and contactors to be opened
|
||||||
{ //LB_FAIL, BMS requesting shutdown and contactors to be opened
|
|
||||||
Serial.println("Battery requesting immediate shutdown and contactors to be opened!");
|
Serial.println("Battery requesting immediate shutdown and contactors to be opened!");
|
||||||
//Note, this is sometimes triggered during the night while idle, and the BMS recovers after a while. Removed latching from this scenario
|
//Note, this is sometimes triggered during the night while idle, and the BMS recovers after a while. Removed latching from this scenario
|
||||||
errorCode = 1;
|
errorCode = 1;
|
||||||
|
@ -215,54 +253,53 @@ void update_values_leaf_battery()
|
||||||
max_target_charge_power = 0;
|
max_target_charge_power = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(LB_Failsafe_Status > 0) // 0 is normal, start charging/discharging
|
if (LB_Failsafe_Status > 0) // 0 is normal, start charging/discharging
|
||||||
{
|
{
|
||||||
switch(LB_Failsafe_Status)
|
switch (LB_Failsafe_Status) {
|
||||||
{
|
case (1):
|
||||||
case(1):
|
|
||||||
//Normal Stop Request
|
//Normal Stop Request
|
||||||
//This means that battery is fully discharged and it's OK to stop the session. For stationary storage we don't disconnect contactors, so we do nothing here.
|
//This means that battery is fully discharged and it's OK to stop the session. For stationary storage we don't disconnect contactors, so we do nothing here.
|
||||||
break;
|
break;
|
||||||
case(2):
|
case (2):
|
||||||
//Charging Mode Stop Request
|
//Charging Mode Stop Request
|
||||||
//This means that battery is fully charged and it's OK to stop the session. For stationary storage we don't disconnect contactors, so we do nothing here.
|
//This means that battery is fully charged and it's OK to stop the session. For stationary storage we don't disconnect contactors, so we do nothing here.
|
||||||
break;
|
break;
|
||||||
case(3):
|
case (3):
|
||||||
//Charging Mode Stop Request & Normal Stop Request
|
//Charging Mode Stop Request & Normal Stop Request
|
||||||
//Normal stop request. For stationary storage we don't disconnect contactors, so we ignore this.
|
//Normal stop request. For stationary storage we don't disconnect contactors, so we ignore this.
|
||||||
break;
|
break;
|
||||||
case(4):
|
case (4):
|
||||||
//Caution Lamp Request
|
//Caution Lamp Request
|
||||||
Serial.println("ERROR: Battery raised caution indicator. Inspect battery status!");
|
Serial.println("ERROR: Battery raised caution indicator. Inspect battery status!");
|
||||||
break;
|
break;
|
||||||
case(5):
|
case (5):
|
||||||
//Caution Lamp Request & Normal Stop Request
|
//Caution Lamp Request & Normal Stop Request
|
||||||
bms_status = FAULT;
|
bms_status = FAULT;
|
||||||
errorCode = 2;
|
errorCode = 2;
|
||||||
Serial.println("ERROR: Battery raised caution indicator AND requested discharge stop. Inspect battery status!");
|
Serial.println("ERROR: Battery raised caution indicator AND requested discharge stop. Inspect battery status!");
|
||||||
break;
|
break;
|
||||||
case(6):
|
case (6):
|
||||||
//Caution Lamp Request & Charging Mode Stop Request
|
//Caution Lamp Request & Charging Mode Stop Request
|
||||||
bms_status = FAULT;
|
bms_status = FAULT;
|
||||||
errorCode = 3;
|
errorCode = 3;
|
||||||
Serial.println("ERROR: Battery raised caution indicator AND requested charge stop. Inspect battery status!");
|
Serial.println("ERROR: Battery raised caution indicator AND requested charge stop. Inspect battery status!");
|
||||||
break;
|
break;
|
||||||
case(7):
|
case (7):
|
||||||
//Caution Lamp Request & Charging Mode Stop Request & Normal Stop Request
|
//Caution Lamp Request & Charging Mode Stop Request & Normal Stop Request
|
||||||
bms_status = FAULT;
|
bms_status = FAULT;
|
||||||
errorCode = 4;
|
errorCode = 4;
|
||||||
Serial.println("ERROR: Battery raised caution indicator AND requested charge/discharge stop. Inspect battery status!");
|
Serial.println(
|
||||||
|
"ERROR: Battery raised caution indicator AND requested charge/discharge stop. Inspect battery status!");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(LB_StateOfHealth < 25)
|
if (LB_StateOfHealth < 25) { //Battery is extremely degraded, not fit for secondlifestorage. Zero it all out.
|
||||||
{ //Battery is extremely degraded, not fit for secondlifestorage. Zero it all out.
|
if (LB_StateOfHealth != 0) { //Extra check to see that we actually have a SOH Value available
|
||||||
if(LB_StateOfHealth != 0)
|
Serial.println(
|
||||||
{ //Extra check to see that we actually have a SOH Value available
|
"ERROR: State of health critically low. Battery internal resistance too high to continue. Recycle battery.");
|
||||||
Serial.println("ERROR: State of health critically low. Battery internal resistance too high to continue. Recycle battery.");
|
|
||||||
bms_status = FAULT;
|
bms_status = FAULT;
|
||||||
errorCode = 5;
|
errorCode = 5;
|
||||||
max_target_discharge_power = 0;
|
max_target_discharge_power = 0;
|
||||||
|
@ -270,60 +307,57 @@ void update_values_leaf_battery()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef INTERLOCK_REQUIRED
|
#ifdef INTERLOCK_REQUIRED
|
||||||
if(!LB_Interlock)
|
if (!LB_Interlock) {
|
||||||
{
|
Serial.println(
|
||||||
Serial.println("ERROR: Battery interlock loop broken. Check that high voltage connectors are seated. Battery will be disabled!");
|
"ERROR: Battery interlock loop broken. Check that high voltage connectors are seated. Battery will be "
|
||||||
|
"disabled!");
|
||||||
bms_status = FAULT;
|
bms_status = FAULT;
|
||||||
errorCode = 6;
|
errorCode = 6;
|
||||||
SOC = 0;
|
SOC = 0;
|
||||||
max_target_discharge_power = 0;
|
max_target_discharge_power = 0;
|
||||||
max_target_charge_power = 0;
|
max_target_charge_power = 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
|
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
|
||||||
if(!CANstillAlive)
|
if (!CANstillAlive) {
|
||||||
{
|
|
||||||
bms_status = FAULT;
|
bms_status = FAULT;
|
||||||
errorCode = 7;
|
errorCode = 7;
|
||||||
Serial.println("ERROR: No CAN communication detected for 60s. Shutting down battery control.");
|
Serial.println("ERROR: No CAN communication detected for 60s. Shutting down battery control.");
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
CANstillAlive--;
|
CANstillAlive--;
|
||||||
}
|
}
|
||||||
if(CANerror > MAX_CAN_FAILURES) //Also check if we have recieved too many malformed CAN messages. If so, signal via LED
|
if (CANerror >
|
||||||
|
MAX_CAN_FAILURES) //Also check if we have recieved too many malformed CAN messages. If so, signal via LED
|
||||||
{
|
{
|
||||||
errorCode = 10;
|
errorCode = 10;
|
||||||
LEDcolor = YELLOW;
|
LEDcolor = YELLOW;
|
||||||
Serial.println("ERROR: High amount of corrupted CAN messages detected. Check CAN wire shielding!");
|
Serial.println("ERROR: High amount of corrupted CAN messages detected. Check CAN wire shielding!");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*Finally print out values to serial if configured to do so*/
|
/*Finally print out values to serial if configured to do so*/
|
||||||
#ifdef DEBUG_VIA_USB
|
#ifdef DEBUG_VIA_USB
|
||||||
if(errorCode > 0)
|
if (errorCode > 0) {
|
||||||
{
|
|
||||||
Serial.print("ERROR CODE ACTIVE IN SYSTEM. NUMBER: ");
|
Serial.print("ERROR CODE ACTIVE IN SYSTEM. NUMBER: ");
|
||||||
Serial.println(errorCode);
|
Serial.println(errorCode);
|
||||||
}
|
}
|
||||||
Serial.println("Values going to inverter");
|
Serial.println("Values going to inverter");
|
||||||
print_with_units("SOH%: ", (StateOfHealth*0.01), "% ");
|
print_with_units("SOH%: ", (StateOfHealth * 0.01), "% ");
|
||||||
print_with_units(", SOC% scaled: ", (SOC*0.01), "% ");
|
print_with_units(", SOC% scaled: ", (SOC * 0.01), "% ");
|
||||||
print_with_units(", Voltage: ", LB_Total_Voltage, "V ");
|
print_with_units(", Voltage: ", LB_Total_Voltage, "V ");
|
||||||
print_with_units(", Max discharge power: ", max_target_discharge_power, "W ");
|
print_with_units(", Max discharge power: ", max_target_discharge_power, "W ");
|
||||||
print_with_units(", Max charge power: ", max_target_charge_power, "W ");
|
print_with_units(", Max charge power: ", max_target_charge_power, "W ");
|
||||||
print_with_units(", Max temp: ", (temperature_max*0.1), "°C ");
|
print_with_units(", Max temp: ", (temperature_max * 0.1), "°C ");
|
||||||
print_with_units(", Min temp: ", (temperature_min*0.1), "°C ");
|
print_with_units(", Min temp: ", (temperature_min * 0.1), "°C ");
|
||||||
Serial.println("");
|
Serial.println("");
|
||||||
Serial.print("BMS Status: ");
|
Serial.print("BMS Status: ");
|
||||||
if(bms_status == 3){
|
if (bms_status == 3) {
|
||||||
Serial.print("Active, ");
|
Serial.print("Active, ");
|
||||||
}
|
} else {
|
||||||
else{
|
|
||||||
Serial.print("FAULT, ");
|
Serial.print("FAULT, ");
|
||||||
}
|
}
|
||||||
switch (bms_char_dis_status){
|
switch (bms_char_dis_status) {
|
||||||
case 0:
|
case 0:
|
||||||
Serial.print("Idle");
|
Serial.print("Idle");
|
||||||
break;
|
break;
|
||||||
|
@ -339,27 +373,25 @@ void update_values_leaf_battery()
|
||||||
print_with_units(", Power: ", LB_Power, "W ");
|
print_with_units(", Power: ", LB_Power, "W ");
|
||||||
Serial.println("");
|
Serial.println("");
|
||||||
Serial.println("Values from battery");
|
Serial.println("Values from battery");
|
||||||
print_with_units("Real SOC%: ", (LB_SOC*0.1), "% ");
|
print_with_units("Real SOC%: ", (LB_SOC * 0.1), "% ");
|
||||||
print_with_units(", GIDS: ", LB_GIDS, " (x77Wh) ");
|
print_with_units(", GIDS: ", LB_GIDS, " (x77Wh) ");
|
||||||
print_with_units(", Battery gen: ", LEAF_Battery_Type, " ");
|
print_with_units(", Battery gen: ", LEAF_Battery_Type, " ");
|
||||||
print_with_units(", Max cell voltage: ", min_max_voltage[1], "mV ");
|
print_with_units(", Max cell voltage: ", min_max_voltage[1], "mV ");
|
||||||
print_with_units(", Min cell voltage: ", min_max_voltage[0], "mV ");
|
print_with_units(", Min cell voltage: ", min_max_voltage[0], "mV ");
|
||||||
print_with_units(", Cell deviation: ", cell_deviation_mV, "mV ");
|
print_with_units(", Cell deviation: ", cell_deviation_mV, "mV ");
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void receive_can_leaf_battery(CAN_frame_t rx_frame)
|
void receive_can_leaf_battery(CAN_frame_t rx_frame) {
|
||||||
{
|
switch (rx_frame.MsgID) {
|
||||||
switch (rx_frame.MsgID)
|
|
||||||
{
|
|
||||||
case 0x1DB:
|
case 0x1DB:
|
||||||
if(is_message_corrupt(rx_frame)){
|
if (is_message_corrupt(rx_frame)) {
|
||||||
CANerror++;
|
CANerror++;
|
||||||
break; //Message content malformed, abort reading data from it
|
break; //Message content malformed, abort reading data from it
|
||||||
}
|
}
|
||||||
LB_Current = (rx_frame.data.u8[0] << 3) | (rx_frame.data.u8[1] & 0xe0) >> 5;
|
LB_Current = (rx_frame.data.u8[0] << 3) | (rx_frame.data.u8[1] & 0xe0) >> 5;
|
||||||
if (LB_Current & 0x0400){
|
if (LB_Current & 0x0400) {
|
||||||
// negative so extend the sign bit
|
// negative so extend the sign bit
|
||||||
LB_Current |= 0xf800;
|
LB_Current |= 0xf800;
|
||||||
}
|
}
|
||||||
|
@ -371,18 +403,17 @@ void receive_can_leaf_battery(CAN_frame_t rx_frame)
|
||||||
//Collect various data from the BMS
|
//Collect various data from the BMS
|
||||||
LB_Relay_Cut_Request = ((rx_frame.data.u8[1] & 0x18) >> 3);
|
LB_Relay_Cut_Request = ((rx_frame.data.u8[1] & 0x18) >> 3);
|
||||||
LB_Failsafe_Status = (rx_frame.data.u8[1] & 0x07);
|
LB_Failsafe_Status = (rx_frame.data.u8[1] & 0x07);
|
||||||
LB_MainRelayOn_flag = (byte) ((rx_frame.data.u8[3] & 0x20) >> 5);
|
LB_MainRelayOn_flag = (byte)((rx_frame.data.u8[3] & 0x20) >> 5);
|
||||||
if(LB_MainRelayOn_flag){
|
if (LB_MainRelayOn_flag) {
|
||||||
batteryAllowsContactorClosing = 1;
|
batteryAllowsContactorClosing = 1;
|
||||||
}
|
} else {
|
||||||
else{
|
|
||||||
batteryAllowsContactorClosing = 0;
|
batteryAllowsContactorClosing = 0;
|
||||||
}
|
}
|
||||||
LB_Full_CHARGE_flag = (byte) ((rx_frame.data.u8[3] & 0x10) >> 4);
|
LB_Full_CHARGE_flag = (byte)((rx_frame.data.u8[3] & 0x10) >> 4);
|
||||||
LB_Interlock = (byte) ((rx_frame.data.u8[3] & 0x08) >> 3);
|
LB_Interlock = (byte)((rx_frame.data.u8[3] & 0x08) >> 3);
|
||||||
break;
|
break;
|
||||||
case 0x1DC:
|
case 0x1DC:
|
||||||
if(is_message_corrupt(rx_frame)){
|
if (is_message_corrupt(rx_frame)) {
|
||||||
CANerror++;
|
CANerror++;
|
||||||
break; //Message content malformed, abort reading data from it
|
break; //Message content malformed, abort reading data from it
|
||||||
}
|
}
|
||||||
|
@ -391,7 +422,7 @@ void receive_can_leaf_battery(CAN_frame_t rx_frame)
|
||||||
LB_MAX_POWER_FOR_CHARGER = ((((rx_frame.data.u8[2] & 0x0F) << 6 | rx_frame.data.u8[3] >> 2) / 10.0) - 10);
|
LB_MAX_POWER_FOR_CHARGER = ((((rx_frame.data.u8[2] & 0x0F) << 6 | rx_frame.data.u8[3] >> 2) / 10.0) - 10);
|
||||||
break;
|
break;
|
||||||
case 0x55B:
|
case 0x55B:
|
||||||
if(is_message_corrupt(rx_frame)){
|
if (is_message_corrupt(rx_frame)) {
|
||||||
CANerror++;
|
CANerror++;
|
||||||
break; //Message content malformed, abort reading data from it
|
break; //Message content malformed, abort reading data from it
|
||||||
}
|
}
|
||||||
|
@ -404,31 +435,32 @@ void receive_can_leaf_battery(CAN_frame_t rx_frame)
|
||||||
CANstillAlive = 12; //Indicate that we are still getting CAN messages from the BMS
|
CANstillAlive = 12; //Indicate that we are still getting CAN messages from the BMS
|
||||||
|
|
||||||
LB_MAX = ((rx_frame.data.u8[5] & 0x10) >> 4);
|
LB_MAX = ((rx_frame.data.u8[5] & 0x10) >> 4);
|
||||||
if (LB_MAX){
|
if (LB_MAX) {
|
||||||
LB_Max_GIDS = (rx_frame.data.u8[0] << 2) | ((rx_frame.data.u8[1] & 0xC0) >> 6);
|
LB_Max_GIDS = (rx_frame.data.u8[0] << 2) | ((rx_frame.data.u8[1] & 0xC0) >> 6);
|
||||||
//Max gids active, do nothing
|
//Max gids active, do nothing
|
||||||
//Only the 30/40/62kWh packs have this mux
|
//Only the 30/40/62kWh packs have this mux
|
||||||
}
|
} else { //Normal current GIDS value is transmitted
|
||||||
else{ //Normal current GIDS value is transmitted
|
|
||||||
LB_GIDS = (rx_frame.data.u8[0] << 2) | ((rx_frame.data.u8[1] & 0xC0) >> 6);
|
LB_GIDS = (rx_frame.data.u8[0] << 2) | ((rx_frame.data.u8[1] & 0xC0) >> 6);
|
||||||
LB_Wh_Remaining = (LB_GIDS * WH_PER_GID);
|
LB_Wh_Remaining = (LB_GIDS * WH_PER_GID);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(LEAF_Battery_Type == ZE0_BATTERY){
|
if (LEAF_Battery_Type == ZE0_BATTERY) {
|
||||||
LB_AverageTemperature = (rx_frame.data.u8[3] - 40); //In celcius, -40 to +55
|
LB_AverageTemperature = (rx_frame.data.u8[3] - 40); //In celcius, -40 to +55
|
||||||
}
|
}
|
||||||
|
|
||||||
LB_TEMP = (rx_frame.data.u8[4] >> 1);
|
LB_TEMP = (rx_frame.data.u8[4] >> 1);
|
||||||
if (LB_TEMP != 0){
|
if (LB_TEMP != 0) {
|
||||||
LB_StateOfHealth = LB_TEMP; //Collect state of health from battery
|
LB_StateOfHealth = LB_TEMP; //Collect state of health from battery
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x5C0: //This method only works for 2013-2017 AZE0 LEAF packs, the mux is different on other generations
|
case 0x5C0: //This method only works for 2013-2017 AZE0 LEAF packs, the mux is different on other generations
|
||||||
if(LEAF_Battery_Type == AZE0_BATTERY){
|
if (LEAF_Battery_Type == AZE0_BATTERY) {
|
||||||
if ((rx_frame.data.u8[0]>>6) == 1){ // Battery MAX temperature. Effectively has only 7-bit precision, as the bottom bit is always 0.
|
if ((rx_frame.data.u8[0] >> 6) ==
|
||||||
|
1) { // Battery MAX temperature. Effectively has only 7-bit precision, as the bottom bit is always 0.
|
||||||
LB_HistData_Temperature_MAX = ((rx_frame.data.u8[2] / 2) - 40);
|
LB_HistData_Temperature_MAX = ((rx_frame.data.u8[2] / 2) - 40);
|
||||||
}
|
}
|
||||||
if ((rx_frame.data.u8[0]>>6) == 3){ // Battery MIN temperature. Effectively has only 7-bit precision, as the bottom bit is always 0.
|
if ((rx_frame.data.u8[0] >> 6) ==
|
||||||
|
3) { // Battery MIN temperature. Effectively has only 7-bit precision, as the bottom bit is always 0.
|
||||||
LB_HistData_Temperature_MIN = ((rx_frame.data.u8[2] / 2) - 40);
|
LB_HistData_Temperature_MIN = ((rx_frame.data.u8[2] / 2) - 40);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -436,7 +468,7 @@ void receive_can_leaf_battery(CAN_frame_t rx_frame)
|
||||||
case 0x59E:
|
case 0x59E:
|
||||||
//AZE0 2013-2017 or ZE1 2018-2023 battery detected
|
//AZE0 2013-2017 or ZE1 2018-2023 battery detected
|
||||||
//Only detect as AZE0 if not already set as ZE1
|
//Only detect as AZE0 if not already set as ZE1
|
||||||
if(LEAF_Battery_Type != ZE1_BATTERY){
|
if (LEAF_Battery_Type != ZE1_BATTERY) {
|
||||||
LEAF_Battery_Type = AZE0_BATTERY;
|
LEAF_Battery_Type = AZE0_BATTERY;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -458,59 +490,60 @@ void receive_can_leaf_battery(CAN_frame_t rx_frame)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(stop_battery_query){ //Leafspy is active, stop our own polling
|
if (stop_battery_query) { //Leafspy is active, stop our own polling
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP32Can.CANWriteFrame(&LEAF_NEXT_LINE_REQUEST); //Request the next frame for the group
|
ESP32Can.CANWriteFrame(&LEAF_NEXT_LINE_REQUEST); //Request the next frame for the group
|
||||||
|
|
||||||
if(group_7bb == 1) //High precision SOC, Current, voltages etc.
|
if (group_7bb == 1) //High precision SOC, Current, voltages etc.
|
||||||
{
|
{
|
||||||
if(rx_frame.data.u8[0] == 0x10){ //First frame
|
if (rx_frame.data.u8[0] == 0x10) { //First frame
|
||||||
Battery_current_1 = (rx_frame.data.u8[4] << 24) | (rx_frame.data.u8[5] << 16 | ((rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]));
|
Battery_current_1 = (rx_frame.data.u8[4] << 24) |
|
||||||
if(Battery_current_1 & 0x8000000 == 0x8000000){
|
(rx_frame.data.u8[5] << 16 | ((rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]));
|
||||||
Battery_current_1 = (( Battery_current_1 | -0x100000000 ) / 1024);
|
if (Battery_current_1 & 0x8000000 == 0x8000000) {
|
||||||
}
|
Battery_current_1 = ((Battery_current_1 | -0x100000000) / 1024);
|
||||||
else{
|
} else {
|
||||||
Battery_current_1 = (Battery_current_1 / 1024);
|
Battery_current_1 = (Battery_current_1 / 1024);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(rx_frame.data.u8[0] == 0x21){ //Second frame
|
if (rx_frame.data.u8[0] == 0x21) { //Second frame
|
||||||
Battery_current_2 = (rx_frame.data.u8[3] << 24) | (rx_frame.data.u8[4] << 16 | ((rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6]));
|
Battery_current_2 = (rx_frame.data.u8[3] << 24) |
|
||||||
if(Battery_current_2 & 0x8000000 == 0x8000000){
|
(rx_frame.data.u8[4] << 16 | ((rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6]));
|
||||||
Battery_current_2 = (( Battery_current_2 | -0x100000000 ) / 1024);
|
if (Battery_current_2 & 0x8000000 == 0x8000000) {
|
||||||
}
|
Battery_current_2 = ((Battery_current_2 | -0x100000000) / 1024);
|
||||||
else{
|
} else {
|
||||||
Battery_current_2 = (Battery_current_2 / 1024);
|
Battery_current_2 = (Battery_current_2 / 1024);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(rx_frame.data.u8[0] == 0x23){ // Fourth frame
|
if (rx_frame.data.u8[0] == 0x23) { // Fourth frame
|
||||||
insulation = (uint16_t) ((rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6]);
|
insulation = (uint16_t)((rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(rx_frame.data.u8[0] == 0x24){ // Fifth frame
|
if (rx_frame.data.u8[0] == 0x24) { // Fifth frame
|
||||||
HX = (uint16_t) ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 102.4;
|
HX = (uint16_t)((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 102.4;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
if (group_7bb == 2) //Cell Voltages
|
||||||
|
|
||||||
if(group_7bb == 2) //Cell Voltages
|
|
||||||
{
|
{
|
||||||
if(rx_frame.data.u8[0] == 0x10){ //first frame is anomalous
|
if (rx_frame.data.u8[0] == 0x10) { //first frame is anomalous
|
||||||
battery_request_idx = 0;
|
battery_request_idx = 0;
|
||||||
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
|
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
|
||||||
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7];
|
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(rx_frame.data.u8[6] == 0xFF && rx_frame.data.u8[0] == 0x2C){ //Last frame
|
if (rx_frame.data.u8[6] == 0xFF && rx_frame.data.u8[0] == 0x2C) { //Last frame
|
||||||
//Last frame does not contain any cell data, calculate the result
|
//Last frame does not contain any cell data, calculate the result
|
||||||
min_max_voltage[0] = 9999;
|
min_max_voltage[0] = 9999;
|
||||||
min_max_voltage[1] = 0;
|
min_max_voltage[1] = 0;
|
||||||
for(cellcounter = 0; cellcounter < 96; cellcounter++){
|
for (cellcounter = 0; cellcounter < 96; cellcounter++) {
|
||||||
if(min_max_voltage[0] > cell_voltages[cellcounter]) min_max_voltage[0] = cell_voltages[cellcounter];
|
if (min_max_voltage[0] > cell_voltages[cellcounter])
|
||||||
if(min_max_voltage[1] < cell_voltages[cellcounter]) min_max_voltage[1] = cell_voltages[cellcounter];
|
min_max_voltage[0] = cell_voltages[cellcounter];
|
||||||
|
if (min_max_voltage[1] < cell_voltages[cellcounter])
|
||||||
|
min_max_voltage[1] = cell_voltages[cellcounter];
|
||||||
}
|
}
|
||||||
|
|
||||||
cell_deviation_mV = (min_max_voltage[1] - min_max_voltage[0]);
|
cell_deviation_mV = (min_max_voltage[1] - min_max_voltage[0]);
|
||||||
|
@ -518,17 +551,17 @@ void receive_can_leaf_battery(CAN_frame_t rx_frame)
|
||||||
cell_max_voltage = min_max_voltage[1];
|
cell_max_voltage = min_max_voltage[1];
|
||||||
cell_min_voltage = min_max_voltage[0];
|
cell_min_voltage = min_max_voltage[0];
|
||||||
|
|
||||||
if(cell_deviation_mV > MAX_CELL_DEVIATION){
|
if (cell_deviation_mV > MAX_CELL_DEVIATION) {
|
||||||
LEDcolor = YELLOW;
|
LEDcolor = YELLOW;
|
||||||
Serial.println("HIGH CELL DEVIATION!!! Inspect battery!");
|
Serial.println("HIGH CELL DEVIATION!!! Inspect battery!");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(min_max_voltage[1] >= MAX_CELL_VOLTAGE){
|
if (min_max_voltage[1] >= MAX_CELL_VOLTAGE) {
|
||||||
bms_status = FAULT;
|
bms_status = FAULT;
|
||||||
errorCode = 8;
|
errorCode = 8;
|
||||||
Serial.println("CELL OVERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!");
|
Serial.println("CELL OVERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!");
|
||||||
}
|
}
|
||||||
if(min_max_voltage[0] <= MIN_CELL_VOLTAGE){
|
if (min_max_voltage[0] <= MIN_CELL_VOLTAGE) {
|
||||||
bms_status = FAULT;
|
bms_status = FAULT;
|
||||||
errorCode = 9;
|
errorCode = 9;
|
||||||
Serial.println("CELL UNDERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!");
|
Serial.println("CELL UNDERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!");
|
||||||
|
@ -536,7 +569,7 @@ void receive_can_leaf_battery(CAN_frame_t rx_frame)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if((rx_frame.data.u8[0] % 2) == 0){ //even frames
|
if ((rx_frame.data.u8[0] % 2) == 0) { //even frames
|
||||||
cell_voltages[battery_request_idx++] |= rx_frame.data.u8[1];
|
cell_voltages[battery_request_idx++] |= rx_frame.data.u8[1];
|
||||||
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3];
|
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3];
|
||||||
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
|
cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
|
||||||
|
@ -549,7 +582,7 @@ void receive_can_leaf_battery(CAN_frame_t rx_frame)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(group_7bb == 4) { //Temperatures
|
if (group_7bb == 4) { //Temperatures
|
||||||
if (rx_frame.data.u8[0] == 0x10) { //First message
|
if (rx_frame.data.u8[0] == 0x10) { //First message
|
||||||
temp_raw_1 = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
|
temp_raw_1 = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
|
||||||
temp_raw_2_highnibble = rx_frame.data.u8[7];
|
temp_raw_2_highnibble = rx_frame.data.u8[7];
|
||||||
|
@ -562,45 +595,44 @@ void receive_can_leaf_battery(CAN_frame_t rx_frame)
|
||||||
if (rx_frame.data.u8[0] == 0x22) { //Third message
|
if (rx_frame.data.u8[0] == 0x22) { //Third message
|
||||||
//All values read, let's figure out the min/max!
|
//All values read, let's figure out the min/max!
|
||||||
|
|
||||||
if(temp_raw_3 == 65535){ //We are on a 2013+ pack that only has three temp sensors.
|
if (temp_raw_3 == 65535) { //We are on a 2013+ pack that only has three temp sensors.
|
||||||
//Start with finding max value
|
//Start with finding max value
|
||||||
temp_raw_max = temp_raw_1;
|
temp_raw_max = temp_raw_1;
|
||||||
if(temp_raw_2 > temp_raw_max){
|
if (temp_raw_2 > temp_raw_max) {
|
||||||
temp_raw_max = temp_raw_2;
|
temp_raw_max = temp_raw_2;
|
||||||
}
|
}
|
||||||
if(temp_raw_4 > temp_raw_max){
|
if (temp_raw_4 > temp_raw_max) {
|
||||||
temp_raw_max = temp_raw_4;
|
temp_raw_max = temp_raw_4;
|
||||||
}
|
}
|
||||||
//Then find min
|
//Then find min
|
||||||
temp_raw_min = temp_raw_1;
|
temp_raw_min = temp_raw_1;
|
||||||
if(temp_raw_2 < temp_raw_min){
|
if (temp_raw_2 < temp_raw_min) {
|
||||||
temp_raw_min = temp_raw_2;
|
temp_raw_min = temp_raw_2;
|
||||||
}
|
}
|
||||||
if(temp_raw_4 < temp_raw_min){
|
if (temp_raw_4 < temp_raw_min) {
|
||||||
temp_raw_min = temp_raw_4;
|
temp_raw_min = temp_raw_4;
|
||||||
}
|
}
|
||||||
}
|
} else { //All 4 temp sensors available on 2011-2012
|
||||||
else{ //All 4 temp sensors available on 2011-2012
|
|
||||||
//Start with finding max value
|
//Start with finding max value
|
||||||
temp_raw_max = temp_raw_1;
|
temp_raw_max = temp_raw_1;
|
||||||
if(temp_raw_2 > temp_raw_max){
|
if (temp_raw_2 > temp_raw_max) {
|
||||||
temp_raw_max = temp_raw_2;
|
temp_raw_max = temp_raw_2;
|
||||||
}
|
}
|
||||||
if(temp_raw_3 > temp_raw_max){
|
if (temp_raw_3 > temp_raw_max) {
|
||||||
temp_raw_max = temp_raw_3;
|
temp_raw_max = temp_raw_3;
|
||||||
}
|
}
|
||||||
if(temp_raw_4 > temp_raw_max){
|
if (temp_raw_4 > temp_raw_max) {
|
||||||
temp_raw_max = temp_raw_4;
|
temp_raw_max = temp_raw_4;
|
||||||
}
|
}
|
||||||
//Then find min
|
//Then find min
|
||||||
temp_raw_min = temp_raw_1;
|
temp_raw_min = temp_raw_1;
|
||||||
if(temp_raw_2 < temp_raw_min){
|
if (temp_raw_2 < temp_raw_min) {
|
||||||
temp_raw_min = temp_raw_2;
|
temp_raw_min = temp_raw_2;
|
||||||
}
|
}
|
||||||
if(temp_raw_3 < temp_raw_min){
|
if (temp_raw_3 < temp_raw_min) {
|
||||||
temp_raw_min = temp_raw_2;
|
temp_raw_min = temp_raw_2;
|
||||||
}
|
}
|
||||||
if(temp_raw_4 < temp_raw_min){
|
if (temp_raw_4 < temp_raw_min) {
|
||||||
temp_raw_min = temp_raw_4;
|
temp_raw_min = temp_raw_4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -612,37 +644,33 @@ void receive_can_leaf_battery(CAN_frame_t rx_frame)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void send_can_leaf_battery()
|
void send_can_leaf_battery() {
|
||||||
{
|
|
||||||
unsigned long currentMillis = millis();
|
unsigned long currentMillis = millis();
|
||||||
// Send 100ms CAN Message
|
// Send 100ms CAN Message
|
||||||
if (currentMillis - previousMillis100 >= interval100)
|
if (currentMillis - previousMillis100 >= interval100) {
|
||||||
{
|
|
||||||
previousMillis100 = currentMillis;
|
previousMillis100 = currentMillis;
|
||||||
|
|
||||||
ESP32Can.CANWriteFrame(&LEAF_50B); //Always send 50B as a static message (Contains HCM_WakeUpSleepCommand == 11b == WakeUp, and CANMASK = 1)
|
ESP32Can.CANWriteFrame(
|
||||||
|
&LEAF_50B); //Always send 50B as a static message (Contains HCM_WakeUpSleepCommand == 11b == WakeUp, and CANMASK = 1)
|
||||||
|
|
||||||
mprun100++;
|
mprun100++;
|
||||||
if (mprun100 > 3){
|
if (mprun100 > 3) {
|
||||||
mprun100 = 0;
|
mprun100 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mprun100 == 0){
|
if (mprun100 == 0) {
|
||||||
LEAF_50C.data.u8[3] = 0x00;
|
LEAF_50C.data.u8[3] = 0x00;
|
||||||
LEAF_50C.data.u8[4] = 0x5D;
|
LEAF_50C.data.u8[4] = 0x5D;
|
||||||
LEAF_50C.data.u8[5] = 0xC8;
|
LEAF_50C.data.u8[5] = 0xC8;
|
||||||
}
|
} else if (mprun100 == 1) {
|
||||||
else if(mprun100 == 1){
|
|
||||||
LEAF_50C.data.u8[3] = 0x01;
|
LEAF_50C.data.u8[3] = 0x01;
|
||||||
LEAF_50C.data.u8[4] = 0xB2;
|
LEAF_50C.data.u8[4] = 0xB2;
|
||||||
LEAF_50C.data.u8[5] = 0x31;
|
LEAF_50C.data.u8[5] = 0x31;
|
||||||
}
|
} else if (mprun100 == 2) {
|
||||||
else if(mprun100 == 2){
|
|
||||||
LEAF_50C.data.u8[3] = 0x02;
|
LEAF_50C.data.u8[3] = 0x02;
|
||||||
LEAF_50C.data.u8[4] = 0x5D;
|
LEAF_50C.data.u8[4] = 0x5D;
|
||||||
LEAF_50C.data.u8[5] = 0x63;
|
LEAF_50C.data.u8[5] = 0x63;
|
||||||
}
|
} else if (mprun100 == 3) {
|
||||||
else if(mprun100 == 3){
|
|
||||||
LEAF_50C.data.u8[3] = 0x03;
|
LEAF_50C.data.u8[3] = 0x03;
|
||||||
LEAF_50C.data.u8[4] = 0xB2;
|
LEAF_50C.data.u8[4] = 0xB2;
|
||||||
LEAF_50C.data.u8[5] = 0x9A;
|
LEAF_50C.data.u8[5] = 0x9A;
|
||||||
|
@ -650,131 +678,126 @@ void send_can_leaf_battery()
|
||||||
ESP32Can.CANWriteFrame(&LEAF_50C);
|
ESP32Can.CANWriteFrame(&LEAF_50C);
|
||||||
}
|
}
|
||||||
//Send 10ms message
|
//Send 10ms message
|
||||||
if (currentMillis - previousMillis10 >= interval10)
|
if (currentMillis - previousMillis10 >= interval10) {
|
||||||
{
|
|
||||||
previousMillis10 = currentMillis;
|
previousMillis10 = currentMillis;
|
||||||
|
|
||||||
if(mprun10 == 0){
|
if (mprun10 == 0) {
|
||||||
LEAF_1D4.data.u8[4] = 0x07;
|
LEAF_1D4.data.u8[4] = 0x07;
|
||||||
LEAF_1D4.data.u8[7] = 0x12;
|
LEAF_1D4.data.u8[7] = 0x12;
|
||||||
}
|
} else if (mprun10 == 1) {
|
||||||
else if(mprun10 == 1){
|
|
||||||
LEAF_1D4.data.u8[4] = 0x47;
|
LEAF_1D4.data.u8[4] = 0x47;
|
||||||
LEAF_1D4.data.u8[7] = 0xD5;
|
LEAF_1D4.data.u8[7] = 0xD5;
|
||||||
}
|
} else if (mprun10 == 2) {
|
||||||
else if(mprun10 == 2){
|
|
||||||
LEAF_1D4.data.u8[4] = 0x87;
|
LEAF_1D4.data.u8[4] = 0x87;
|
||||||
LEAF_1D4.data.u8[7] = 0x19;
|
LEAF_1D4.data.u8[7] = 0x19;
|
||||||
}
|
} else if (mprun10 == 3) {
|
||||||
else if(mprun10 == 3){
|
|
||||||
LEAF_1D4.data.u8[4] = 0xC7;
|
LEAF_1D4.data.u8[4] = 0xC7;
|
||||||
LEAF_1D4.data.u8[7] = 0xDE;
|
LEAF_1D4.data.u8[7] = 0xDE;
|
||||||
}
|
}
|
||||||
ESP32Can.CANWriteFrame(&LEAF_1D4);
|
ESP32Can.CANWriteFrame(&LEAF_1D4);
|
||||||
|
|
||||||
mprun10++;
|
mprun10++;
|
||||||
if (mprun10 > 3){
|
if (mprun10 > 3) {
|
||||||
mprun10 = 0;
|
mprun10 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(mprun10r)
|
switch (mprun10r) {
|
||||||
{
|
case (0):
|
||||||
case(0):
|
|
||||||
LEAF_1F2.data.u8[3] = 0xB0;
|
LEAF_1F2.data.u8[3] = 0xB0;
|
||||||
LEAF_1F2.data.u8[6] = 0x00;
|
LEAF_1F2.data.u8[6] = 0x00;
|
||||||
LEAF_1F2.data.u8[7] = 0x8F;
|
LEAF_1F2.data.u8[7] = 0x8F;
|
||||||
break;
|
break;
|
||||||
case(1):
|
case (1):
|
||||||
LEAF_1F2.data.u8[3] = 0xB0;
|
LEAF_1F2.data.u8[3] = 0xB0;
|
||||||
LEAF_1F2.data.u8[6] = 0x01;
|
LEAF_1F2.data.u8[6] = 0x01;
|
||||||
LEAF_1F2.data.u8[7] = 0x80;
|
LEAF_1F2.data.u8[7] = 0x80;
|
||||||
break;
|
break;
|
||||||
case(2):
|
case (2):
|
||||||
LEAF_1F2.data.u8[3] = 0xB0;
|
LEAF_1F2.data.u8[3] = 0xB0;
|
||||||
LEAF_1F2.data.u8[6] = 0x02;
|
LEAF_1F2.data.u8[6] = 0x02;
|
||||||
LEAF_1F2.data.u8[7] = 0x81;
|
LEAF_1F2.data.u8[7] = 0x81;
|
||||||
break;
|
break;
|
||||||
case(3):
|
case (3):
|
||||||
LEAF_1F2.data.u8[3] = 0xB0;
|
LEAF_1F2.data.u8[3] = 0xB0;
|
||||||
LEAF_1F2.data.u8[6] = 0x03;
|
LEAF_1F2.data.u8[6] = 0x03;
|
||||||
LEAF_1F2.data.u8[7] = 0x82;
|
LEAF_1F2.data.u8[7] = 0x82;
|
||||||
break;
|
break;
|
||||||
case(4):
|
case (4):
|
||||||
LEAF_1F2.data.u8[3] = 0xB0;
|
LEAF_1F2.data.u8[3] = 0xB0;
|
||||||
LEAF_1F2.data.u8[6] = 0x00;
|
LEAF_1F2.data.u8[6] = 0x00;
|
||||||
LEAF_1F2.data.u8[7] = 0x8F;
|
LEAF_1F2.data.u8[7] = 0x8F;
|
||||||
break;
|
break;
|
||||||
case(5): // Set 2
|
case (5): // Set 2
|
||||||
LEAF_1F2.data.u8[3] = 0xB4;
|
LEAF_1F2.data.u8[3] = 0xB4;
|
||||||
LEAF_1F2.data.u8[6] = 0x01;
|
LEAF_1F2.data.u8[6] = 0x01;
|
||||||
LEAF_1F2.data.u8[7] = 0x84;
|
LEAF_1F2.data.u8[7] = 0x84;
|
||||||
break;
|
break;
|
||||||
case(6):
|
case (6):
|
||||||
LEAF_1F2.data.u8[3] = 0xB4;
|
LEAF_1F2.data.u8[3] = 0xB4;
|
||||||
LEAF_1F2.data.u8[6] = 0x02;
|
LEAF_1F2.data.u8[6] = 0x02;
|
||||||
LEAF_1F2.data.u8[7] = 0x85;
|
LEAF_1F2.data.u8[7] = 0x85;
|
||||||
break;
|
break;
|
||||||
case(7):
|
case (7):
|
||||||
LEAF_1F2.data.u8[3] = 0xB4;
|
LEAF_1F2.data.u8[3] = 0xB4;
|
||||||
LEAF_1F2.data.u8[6] = 0x03;
|
LEAF_1F2.data.u8[6] = 0x03;
|
||||||
LEAF_1F2.data.u8[7] = 0x86;
|
LEAF_1F2.data.u8[7] = 0x86;
|
||||||
break;
|
break;
|
||||||
case(8):
|
case (8):
|
||||||
LEAF_1F2.data.u8[3] = 0xB4;
|
LEAF_1F2.data.u8[3] = 0xB4;
|
||||||
LEAF_1F2.data.u8[6] = 0x00;
|
LEAF_1F2.data.u8[6] = 0x00;
|
||||||
LEAF_1F2.data.u8[7] = 0x83;
|
LEAF_1F2.data.u8[7] = 0x83;
|
||||||
break;
|
break;
|
||||||
case(9):
|
case (9):
|
||||||
LEAF_1F2.data.u8[3] = 0xB4;
|
LEAF_1F2.data.u8[3] = 0xB4;
|
||||||
LEAF_1F2.data.u8[6] = 0x01;
|
LEAF_1F2.data.u8[6] = 0x01;
|
||||||
LEAF_1F2.data.u8[7] = 0x84;
|
LEAF_1F2.data.u8[7] = 0x84;
|
||||||
break;
|
break;
|
||||||
case(10): // Set 3
|
case (10): // Set 3
|
||||||
LEAF_1F2.data.u8[3] = 0xB0;
|
LEAF_1F2.data.u8[3] = 0xB0;
|
||||||
LEAF_1F2.data.u8[6] = 0x02;
|
LEAF_1F2.data.u8[6] = 0x02;
|
||||||
LEAF_1F2.data.u8[7] = 0x81;
|
LEAF_1F2.data.u8[7] = 0x81;
|
||||||
break;
|
break;
|
||||||
case(11):
|
case (11):
|
||||||
LEAF_1F2.data.u8[3] = 0xB0;
|
LEAF_1F2.data.u8[3] = 0xB0;
|
||||||
LEAF_1F2.data.u8[6] = 0x03;
|
LEAF_1F2.data.u8[6] = 0x03;
|
||||||
LEAF_1F2.data.u8[7] = 0x82;
|
LEAF_1F2.data.u8[7] = 0x82;
|
||||||
break;
|
break;
|
||||||
case(12):
|
case (12):
|
||||||
LEAF_1F2.data.u8[3] = 0xB0;
|
LEAF_1F2.data.u8[3] = 0xB0;
|
||||||
LEAF_1F2.data.u8[6] = 0x00;
|
LEAF_1F2.data.u8[6] = 0x00;
|
||||||
LEAF_1F2.data.u8[7] = 0x8F;
|
LEAF_1F2.data.u8[7] = 0x8F;
|
||||||
break;
|
break;
|
||||||
case(13):
|
case (13):
|
||||||
LEAF_1F2.data.u8[3] = 0xB0;
|
LEAF_1F2.data.u8[3] = 0xB0;
|
||||||
LEAF_1F2.data.u8[6] = 0x01;
|
LEAF_1F2.data.u8[6] = 0x01;
|
||||||
LEAF_1F2.data.u8[7] = 0x80;
|
LEAF_1F2.data.u8[7] = 0x80;
|
||||||
break;
|
break;
|
||||||
case(14):
|
case (14):
|
||||||
LEAF_1F2.data.u8[3] = 0xB0;
|
LEAF_1F2.data.u8[3] = 0xB0;
|
||||||
LEAF_1F2.data.u8[6] = 0x02;
|
LEAF_1F2.data.u8[6] = 0x02;
|
||||||
LEAF_1F2.data.u8[7] = 0x81;
|
LEAF_1F2.data.u8[7] = 0x81;
|
||||||
break;
|
break;
|
||||||
case(15): // Set 4
|
case (15): // Set 4
|
||||||
LEAF_1F2.data.u8[3] = 0xB4;
|
LEAF_1F2.data.u8[3] = 0xB4;
|
||||||
LEAF_1F2.data.u8[6] = 0x03;
|
LEAF_1F2.data.u8[6] = 0x03;
|
||||||
LEAF_1F2.data.u8[7] = 0x86;
|
LEAF_1F2.data.u8[7] = 0x86;
|
||||||
break;
|
break;
|
||||||
case(16):
|
case (16):
|
||||||
LEAF_1F2.data.u8[3] = 0xB4;
|
LEAF_1F2.data.u8[3] = 0xB4;
|
||||||
LEAF_1F2.data.u8[6] = 0x00;
|
LEAF_1F2.data.u8[6] = 0x00;
|
||||||
LEAF_1F2.data.u8[7] = 0x83;
|
LEAF_1F2.data.u8[7] = 0x83;
|
||||||
break;
|
break;
|
||||||
case(17):
|
case (17):
|
||||||
LEAF_1F2.data.u8[3] = 0xB4;
|
LEAF_1F2.data.u8[3] = 0xB4;
|
||||||
LEAF_1F2.data.u8[6] = 0x01;
|
LEAF_1F2.data.u8[6] = 0x01;
|
||||||
LEAF_1F2.data.u8[7] = 0x84;
|
LEAF_1F2.data.u8[7] = 0x84;
|
||||||
break;
|
break;
|
||||||
case(18):
|
case (18):
|
||||||
LEAF_1F2.data.u8[3] = 0xB4;
|
LEAF_1F2.data.u8[3] = 0xB4;
|
||||||
LEAF_1F2.data.u8[6] = 0x02;
|
LEAF_1F2.data.u8[6] = 0x02;
|
||||||
LEAF_1F2.data.u8[7] = 0x85;
|
LEAF_1F2.data.u8[7] = 0x85;
|
||||||
break;
|
break;
|
||||||
case(19):
|
case (19):
|
||||||
LEAF_1F2.data.u8[3] = 0xB4;
|
LEAF_1F2.data.u8[3] = 0xB4;
|
||||||
LEAF_1F2.data.u8[6] = 0x03;
|
LEAF_1F2.data.u8[6] = 0x03;
|
||||||
LEAF_1F2.data.u8[7] = 0x86;
|
LEAF_1F2.data.u8[7] = 0x86;
|
||||||
|
@ -786,61 +809,52 @@ void send_can_leaf_battery()
|
||||||
ESP32Can.CANWriteFrame(&LEAF_1F2); //Contains (CHG_STA_RQ == 1 == Normal Charge)
|
ESP32Can.CANWriteFrame(&LEAF_1F2); //Contains (CHG_STA_RQ == 1 == Normal Charge)
|
||||||
|
|
||||||
mprun10r++;
|
mprun10r++;
|
||||||
if(mprun10r > 19){ // 0x1F2 patter repeats after 20 messages,
|
if (mprun10r > 19) { // 0x1F2 patter repeats after 20 messages,
|
||||||
mprun10r = 0;
|
mprun10r = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//Send 10s CAN messages
|
//Send 10s CAN messages
|
||||||
if (currentMillis - previousMillis10s >= interval10s)
|
if (currentMillis - previousMillis10s >= interval10s) {
|
||||||
{
|
|
||||||
previousMillis10s = currentMillis;
|
previousMillis10s = currentMillis;
|
||||||
|
|
||||||
//Every 10s, ask diagnostic data from the battery. Don't ask if someone is already polling on the bus (Leafspy?)
|
//Every 10s, ask diagnostic data from the battery. Don't ask if someone is already polling on the bus (Leafspy?)
|
||||||
if(!stop_battery_query){
|
if (!stop_battery_query) {
|
||||||
if (group == 1){ // Cycle between group 1, 2, and 4 using bit manipulation
|
if (group == 1) { // Cycle between group 1, 2, and 4 using bit manipulation
|
||||||
group = 2;
|
group = 2;
|
||||||
}
|
} else if (group == 2) {
|
||||||
else if (group == 2){
|
|
||||||
group = 4;
|
group = 4;
|
||||||
}
|
} else if (group == 4) {
|
||||||
else if (group == 4){
|
|
||||||
group = 1;
|
group = 1;
|
||||||
}
|
}
|
||||||
LEAF_GROUP_REQUEST.data.u8[2] = group;
|
LEAF_GROUP_REQUEST.data.u8[2] = group;
|
||||||
ESP32Can.CANWriteFrame(&LEAF_GROUP_REQUEST);
|
ESP32Can.CANWriteFrame(&LEAF_GROUP_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(hold_off_with_polling_10seconds > 0){
|
if (hold_off_with_polling_10seconds > 0) {
|
||||||
hold_off_with_polling_10seconds--;
|
hold_off_with_polling_10seconds--;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
stop_battery_query = 0;
|
stop_battery_query = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t convert2unsignedint16(int16_t signed_value)
|
uint16_t convert2unsignedint16(int16_t signed_value) {
|
||||||
{
|
if (signed_value < 0) {
|
||||||
if(signed_value < 0){
|
return (65535 + signed_value);
|
||||||
return(65535 + signed_value);
|
} else {
|
||||||
}
|
|
||||||
else{
|
|
||||||
return (uint16_t)signed_value;
|
return (uint16_t)signed_value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_message_corrupt(CAN_frame_t rx_frame)
|
bool is_message_corrupt(CAN_frame_t rx_frame) {
|
||||||
{
|
|
||||||
uint8_t crc = 0;
|
uint8_t crc = 0;
|
||||||
for (uint8_t j = 0; j < 7; j++){
|
for (uint8_t j = 0; j < 7; j++) {
|
||||||
crc = crctable[(crc ^ static_cast<uint8_t>(rx_frame.data.u8[j])) % 256];
|
crc = crctable[(crc ^ static_cast<uint8_t>(rx_frame.data.u8[j])) % 256];
|
||||||
}
|
}
|
||||||
return crc != rx_frame.data.u8[7];
|
return crc != rx_frame.data.u8[7];
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t Temp_fromRAW_to_F(uint16_t temperature)
|
uint16_t Temp_fromRAW_to_F(uint16_t temperature) { //This function feels horrible, but apparently works well
|
||||||
{ //This function feels horrible, but apparently works well
|
|
||||||
if (temperature == 1021) {
|
if (temperature == 1021) {
|
||||||
return 10;
|
return 10;
|
||||||
} else if (temperature >= 589) {
|
} else if (temperature >= 589) {
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
#ifndef NISSAN_LEAF_BATTERY_H
|
#ifndef NISSAN_LEAF_BATTERY_H
|
||||||
#define NISSAN_LEAF_BATTERY_H
|
#define NISSAN_LEAF_BATTERY_H
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "../devboard/can/ESP32CAN.h"
|
|
||||||
#include "../../USER_SETTINGS.h"
|
#include "../../USER_SETTINGS.h"
|
||||||
|
#include "../devboard/can/ESP32CAN.h"
|
||||||
|
|
||||||
#define ABSOLUTE_MAX_VOLTAGE 4040 // 404.4V,if battery voltage goes over this, charging is not possible (goes into forced discharge)
|
#define ABSOLUTE_MAX_VOLTAGE \
|
||||||
|
4040 // 404.4V,if battery voltage goes over this, charging is not possible (goes into forced discharge)
|
||||||
#define ABSOLUTE_MIN_VOLTAGE 3100 // 310.0V if battery voltage goes under this, discharging further is disabled
|
#define ABSOLUTE_MIN_VOLTAGE 3100 // 310.0V if battery voltage goes under this, discharging further is disabled
|
||||||
|
|
||||||
// These parameters need to be mapped for the inverter
|
// These parameters need to be mapped for the inverter
|
||||||
|
|
|
@ -17,8 +17,13 @@ static uint32_t LB_Discharge_Power_Limit_Watts = 0;
|
||||||
static uint16_t LB_Charge_Power_Limit = 0;
|
static uint16_t LB_Charge_Power_Limit = 0;
|
||||||
static uint32_t LB_Charge_Power_Limit_Watts = 0;
|
static uint32_t LB_Charge_Power_Limit_Watts = 0;
|
||||||
|
|
||||||
|
CAN_frame_t ZOE_423 = {.FIR = {.B =
|
||||||
CAN_frame_t ZOE_423 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x423,.data = {0x33, 0x00, 0xFF, 0xFF, 0x00, 0xE0, 0x00, 0x00}};
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x423,
|
||||||
|
.data = {0x33, 0x00, 0xFF, 0xFF, 0x00, 0xE0, 0x00, 0x00}};
|
||||||
|
|
||||||
static unsigned long previousMillis10 = 0; // will store last time a 10ms CAN Message was sent
|
static unsigned long previousMillis10 = 0; // will store last time a 10ms CAN Message was sent
|
||||||
static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was sent
|
static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was sent
|
||||||
|
@ -26,20 +31,17 @@ static const int interval10 = 10; // interval (ms) at which send CAN Messages
|
||||||
static const int interval100 = 100; // interval (ms) at which send CAN Messages
|
static const int interval100 = 100; // interval (ms) at which send CAN Messages
|
||||||
static int BMSPollCounter = 0;
|
static int BMSPollCounter = 0;
|
||||||
|
|
||||||
void update_values_zoe_battery()
|
void update_values_zoe_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
||||||
{ //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
|
||||||
bms_status = ACTIVE; //Startout in active mode
|
bms_status = ACTIVE; //Startout in active mode
|
||||||
|
|
||||||
StateOfHealth = (LB_SOH * 100); //Increase range from 99% -> 99.00%
|
StateOfHealth = (LB_SOH * 100); //Increase range from 99% -> 99.00%
|
||||||
|
|
||||||
//Calculate the SOC% value to send to Fronius
|
//Calculate the SOC% value to send to Fronius
|
||||||
LB_SOC = LB_MIN_SOC + (LB_MAX_SOC - LB_MIN_SOC) * (LB_SOC - MINPERCENTAGE) / (MAXPERCENTAGE - MINPERCENTAGE);
|
LB_SOC = LB_MIN_SOC + (LB_MAX_SOC - LB_MIN_SOC) * (LB_SOC - MINPERCENTAGE) / (MAXPERCENTAGE - MINPERCENTAGE);
|
||||||
if (LB_SOC < 0)
|
if (LB_SOC < 0) { //We are in the real SOC% range of 0-20%, always set SOC sent to Fronius as 0%
|
||||||
{ //We are in the real SOC% range of 0-20%, always set SOC sent to Fronius as 0%
|
|
||||||
LB_SOC = 0;
|
LB_SOC = 0;
|
||||||
}
|
}
|
||||||
if (LB_SOC > 1000)
|
if (LB_SOC > 1000) { //We are in the real SOC% range of 80-100%, always set SOC sent to Fronius as 100%
|
||||||
{ //We are in the real SOC% range of 80-100%, always set SOC sent to Fronius as 100%
|
|
||||||
LB_SOC = 1000;
|
LB_SOC = 1000;
|
||||||
}
|
}
|
||||||
SOC = (LB_SOC * 10); //increase LB_SOC range from 0-100.0 -> 100.00
|
SOC = (LB_SOC * 10); //increase LB_SOC range from 0-100.0 -> 100.00
|
||||||
|
@ -51,46 +53,38 @@ void update_values_zoe_battery()
|
||||||
|
|
||||||
LB_Discharge_Power_Limit_Watts = (LB_Discharge_Power_Limit * 500); //Convert value fetched from battery to watts
|
LB_Discharge_Power_Limit_Watts = (LB_Discharge_Power_Limit * 500); //Convert value fetched from battery to watts
|
||||||
/* Define power able to be discharged from battery */
|
/* Define power able to be discharged from battery */
|
||||||
if(LB_Discharge_Power_Limit_Watts > 30000) //if >30kW can be pulled from battery
|
if (LB_Discharge_Power_Limit_Watts > 30000) //if >30kW can be pulled from battery
|
||||||
{
|
{
|
||||||
max_target_discharge_power = 30000; //cap value so we don't go over the Fronius limits
|
max_target_discharge_power = 30000; //cap value so we don't go over the Fronius limits
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
max_target_discharge_power = LB_Discharge_Power_Limit_Watts;
|
max_target_discharge_power = LB_Discharge_Power_Limit_Watts;
|
||||||
}
|
}
|
||||||
if(SOC == 0) //Scaled SOC% value is 0.00%, we should not discharge battery further
|
if (SOC == 0) //Scaled SOC% value is 0.00%, we should not discharge battery further
|
||||||
{
|
{
|
||||||
max_target_discharge_power = 0;
|
max_target_discharge_power = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
LB_Charge_Power_Limit_Watts = (LB_Charge_Power_Limit * 500); //Convert value fetched from battery to watts
|
LB_Charge_Power_Limit_Watts = (LB_Charge_Power_Limit * 500); //Convert value fetched from battery to watts
|
||||||
/* Define power able to be put into the battery */
|
/* Define power able to be put into the battery */
|
||||||
if(LB_Charge_Power_Limit_Watts > 30000) //if >30kW can be put into the battery
|
if (LB_Charge_Power_Limit_Watts > 30000) //if >30kW can be put into the battery
|
||||||
{
|
{
|
||||||
max_target_charge_power = 30000; //cap value so we don't go over the Fronius limits
|
max_target_charge_power = 30000; //cap value so we don't go over the Fronius limits
|
||||||
}
|
}
|
||||||
if(LB_Charge_Power_Limit_Watts < 0)
|
if (LB_Charge_Power_Limit_Watts < 0) {
|
||||||
{
|
|
||||||
max_target_charge_power = 0; //cap calue so we dont do under the Fronius limits
|
max_target_charge_power = 0; //cap calue so we dont do under the Fronius limits
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
max_target_charge_power = LB_Charge_Power_Limit_Watts;
|
max_target_charge_power = LB_Charge_Power_Limit_Watts;
|
||||||
}
|
}
|
||||||
if(SOC == 10000) //Scaled SOC% value is 100.00%
|
if (SOC == 10000) //Scaled SOC% value is 100.00%
|
||||||
{
|
{
|
||||||
max_target_charge_power = 0; //No need to charge further, set max power to 0
|
max_target_charge_power = 0; //No need to charge further, set max power to 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
|
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
|
||||||
if(!CANstillAlive)
|
if (!CANstillAlive) {
|
||||||
{
|
|
||||||
bms_status = FAULT;
|
bms_status = FAULT;
|
||||||
Serial.println("No CAN communication detected for 60s. Shutting down battery control.");
|
Serial.println("No CAN communication detected for 60s. Shutting down battery control.");
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
CANstillAlive--;
|
CANstillAlive--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +92,7 @@ void update_values_zoe_battery()
|
||||||
temperature_min;
|
temperature_min;
|
||||||
temperature_max;
|
temperature_max;
|
||||||
|
|
||||||
#ifdef DEBUG_VIA_USB
|
#ifdef DEBUG_VIA_USB
|
||||||
Serial.print("BMS Status (3=OK): ");
|
Serial.print("BMS Status (3=OK): ");
|
||||||
Serial.println(bms_status);
|
Serial.println(bms_status);
|
||||||
Serial.print("Max discharge power: ");
|
Serial.print("Max discharge power: ");
|
||||||
|
@ -117,13 +111,11 @@ void update_values_zoe_battery()
|
||||||
Serial.println(temperature_min);
|
Serial.println(temperature_min);
|
||||||
Serial.print("Temperature Max: ");
|
Serial.print("Temperature Max: ");
|
||||||
Serial.println(temperature_max);
|
Serial.println(temperature_max);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void receive_can_zoe_battery(CAN_frame_t rx_frame)
|
void receive_can_zoe_battery(CAN_frame_t rx_frame) {
|
||||||
{
|
switch (rx_frame.MsgID) {
|
||||||
switch (rx_frame.MsgID)
|
|
||||||
{
|
|
||||||
case 0x155: //BMS1
|
case 0x155: //BMS1
|
||||||
CANstillAlive = 12; //Indicate that we are still getting CAN messages from the BMS
|
CANstillAlive = 12; //Indicate that we are still getting CAN messages from the BMS
|
||||||
//LB_Max_Charge_Amps =
|
//LB_Max_Charge_Amps =
|
||||||
|
@ -146,12 +138,10 @@ void receive_can_zoe_battery(CAN_frame_t rx_frame)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void send_can_zoe_battery()
|
void send_can_zoe_battery() {
|
||||||
{
|
|
||||||
unsigned long currentMillis = millis();
|
unsigned long currentMillis = millis();
|
||||||
// Send 100ms CAN Message
|
// Send 100ms CAN Message
|
||||||
if (currentMillis - previousMillis100 >= interval100)
|
if (currentMillis - previousMillis100 >= interval100) {
|
||||||
{
|
|
||||||
previousMillis100 = currentMillis;
|
previousMillis100 = currentMillis;
|
||||||
BMSPollCounter++; //GKOE
|
BMSPollCounter++; //GKOE
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
#ifndef RENAULT_ZOE_BATTERY_H
|
#ifndef RENAULT_ZOE_BATTERY_H
|
||||||
#define RENAULT_ZOE_BATTERY_H
|
#define RENAULT_ZOE_BATTERY_H
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "../devboard/can/ESP32CAN.h"
|
|
||||||
#include "../../USER_SETTINGS.h"
|
#include "../../USER_SETTINGS.h"
|
||||||
|
#include "../devboard/can/ESP32CAN.h"
|
||||||
|
|
||||||
#define ABSOLUTE_MAX_VOLTAGE 4040 // 404.4V,if battery voltage goes over this, charging is not possible (goes into forced discharge)
|
#define ABSOLUTE_MAX_VOLTAGE \
|
||||||
|
4040 // 404.4V,if battery voltage goes over this, charging is not possible (goes into forced discharge)
|
||||||
#define ABSOLUTE_MIN_VOLTAGE 3100 // 310.0V if battery voltage goes under this, discharging further is disabled
|
#define ABSOLUTE_MIN_VOLTAGE 3100 // 310.0V if battery voltage goes under this, discharging further is disabled
|
||||||
|
|
||||||
// These parameters need to be mapped for the Gen24
|
// These parameters need to be mapped for the Gen24
|
||||||
|
|
|
@ -9,8 +9,22 @@ static unsigned long previousMillis30 = 0; // will store last time a 30ms CAN Me
|
||||||
static const int interval30 = 30; // interval (ms) at which send CAN Messages
|
static const int interval30 = 30; // interval (ms) at which send CAN Messages
|
||||||
static uint8_t stillAliveCAN = 6; //counter for checking if CAN is still alive
|
static uint8_t stillAliveCAN = 6; //counter for checking if CAN is still alive
|
||||||
|
|
||||||
CAN_frame_t TESLA_221_1 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x221,.data = {0x41, 0x11, 0x01, 0x00, 0x00, 0x00, 0x20, 0x96}}; //Contactor frame 221 - close contactors
|
CAN_frame_t TESLA_221_1 = {
|
||||||
CAN_frame_t TESLA_221_2 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x221,.data = {0x61, 0x15, 0x01, 0x00, 0x00, 0x00, 0x20, 0xBA}}; //Contactor Frame 221 - hv_up_for_drive
|
.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x221,
|
||||||
|
.data = {0x41, 0x11, 0x01, 0x00, 0x00, 0x00, 0x20, 0x96}}; //Contactor frame 221 - close contactors
|
||||||
|
CAN_frame_t TESLA_221_2 = {
|
||||||
|
.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x221,
|
||||||
|
.data = {0x61, 0x15, 0x01, 0x00, 0x00, 0x00, 0x20, 0xBA}}; //Contactor Frame 221 - hv_up_for_drive
|
||||||
|
|
||||||
static uint32_t temporaryvariable = 0;
|
static uint32_t temporaryvariable = 0;
|
||||||
static uint32_t total_discharge = 0;
|
static uint32_t total_discharge = 0;
|
||||||
|
@ -67,11 +81,13 @@ static uint8_t OverDchgCurrentFault = 0; //Warns if the pack discharge is above
|
||||||
static uint8_t OverChargeCurrentFault = 0; //Warns if the pack discharge current is above max charge current limit
|
static uint8_t OverChargeCurrentFault = 0; //Warns if the pack discharge current is above max charge current limit
|
||||||
static uint8_t OverCurrentFault = 0; //Warns if the pack current (discharge or charge) is above max current limit.
|
static uint8_t OverCurrentFault = 0; //Warns if the pack current (discharge or charge) is above max current limit.
|
||||||
static uint8_t OverTemperatureFault = 0; //A pack module temperature is above maximum temperature limit
|
static uint8_t OverTemperatureFault = 0; //A pack module temperature is above maximum temperature limit
|
||||||
static uint8_t OverVoltageFault= 0; //A brick voltage is above maximum voltage limit
|
static uint8_t OverVoltageFault = 0; //A brick voltage is above maximum voltage limit
|
||||||
static uint8_t UnderVoltageFault = 0; //A brick voltage is below minimum voltage limit
|
static uint8_t UnderVoltageFault = 0; //A brick voltage is below minimum voltage limit
|
||||||
static uint8_t PrimaryBmbMiaFault = 0; //Warns if the voltage and temperature readings from primary BMB chain are mia
|
static uint8_t PrimaryBmbMiaFault = 0; //Warns if the voltage and temperature readings from primary BMB chain are mia
|
||||||
static uint8_t SecondaryBmbMiaFault = 0; //Warns if the voltage and temperature readings from secondary BMB chain are mia
|
static uint8_t SecondaryBmbMiaFault =
|
||||||
static uint8_t BmbMismatchFault = 0; //Warns if the primary and secondary BMB chain readings don't match with each other
|
0; //Warns if the voltage and temperature readings from secondary BMB chain are mia
|
||||||
|
static uint8_t BmbMismatchFault =
|
||||||
|
0; //Warns if the primary and secondary BMB chain readings don't match with each other
|
||||||
static uint8_t BmsHviMiaFault = 0; //Warns if the BMS node is mia on HVS or HVI CAN
|
static uint8_t BmsHviMiaFault = 0; //Warns if the BMS node is mia on HVS or HVI CAN
|
||||||
static uint8_t CpMiaFault = 0; //Warns if the CP node is mia on HVS CAN
|
static uint8_t CpMiaFault = 0; //Warns if the CP node is mia on HVS CAN
|
||||||
static uint8_t PcsMiaFault = 0; //The PCS node is mia on HVS CAN
|
static uint8_t PcsMiaFault = 0; //The PCS node is mia on HVS CAN
|
||||||
|
@ -86,7 +102,8 @@ static uint8_t Supply12vFault = 0; //Warns if the low voltage (12V) battery is b
|
||||||
static uint8_t VerSupplyFault = 0; //Warns if the Energy reserve voltage supply is below minimum voltage threshold
|
static uint8_t VerSupplyFault = 0; //Warns if the Energy reserve voltage supply is below minimum voltage threshold
|
||||||
static uint8_t HvilFault = 0; //Warn if a High Voltage Inter Lock fault is detected
|
static uint8_t HvilFault = 0; //Warn if a High Voltage Inter Lock fault is detected
|
||||||
static uint8_t BmsHvsMiaFault = 0; //Warns if the BMS node is mia on HVS or HVI CAN
|
static uint8_t BmsHvsMiaFault = 0; //Warns if the BMS node is mia on HVS or HVI CAN
|
||||||
static uint8_t PackVoltMismatchFault = 0; //Warns if the pack voltage doesn't match approximately with sum of brick voltages
|
static uint8_t PackVoltMismatchFault =
|
||||||
|
0; //Warns if the pack voltage doesn't match approximately with sum of brick voltages
|
||||||
static uint8_t EnsMiaFault = 0; //Warns if the ENS line is not connected to HVC
|
static uint8_t EnsMiaFault = 0; //Warns if the ENS line is not connected to HVC
|
||||||
static uint8_t PackPosCtrArcFault = 0; //Warns if the HVP detectes series arc at pack contactor
|
static uint8_t PackPosCtrArcFault = 0; //Warns if the HVP detectes series arc at pack contactor
|
||||||
static uint8_t packNegCtrArcFault = 0; //Warns if the HVP detectes series arc at FC contactor
|
static uint8_t packNegCtrArcFault = 0; //Warns if the HVP detectes series arc at FC contactor
|
||||||
|
@ -110,10 +127,28 @@ static uint8_t logUploadRequest = 0;
|
||||||
static uint8_t packCtrCloseFailed = 0;
|
static uint8_t packCtrCloseFailed = 0;
|
||||||
static uint8_t fcCtrCloseFailed = 0;
|
static uint8_t fcCtrCloseFailed = 0;
|
||||||
static uint8_t shuntThermistorMia = 0;
|
static uint8_t shuntThermistorMia = 0;
|
||||||
static const char* contactorText[] = {"UNKNOWN(0)","OPEN","CLOSING","BLOCKED","OPENING","CLOSED","UNKNOWN(6)","WELDED","POS_CL","NEG_CL","UNKNOWN(10)","UNKNOWN(11)","UNKNOWN(12)"};
|
static const char* contactorText[] = {"UNKNOWN(0)", "OPEN", "CLOSING", "BLOCKED", "OPENING",
|
||||||
static const char* contactorState[] = {"SNA","OPEN","PRECHARGE","BLOCKED","PULLED_IN","OPENING","ECONOMIZED","WELDED","UNKNOWN(8)","UNKNOWN(9)","UNKNOWN(10)","UNKNOWN(11)"};
|
"CLOSED", "UNKNOWN(6)", "WELDED", "POS_CL", "NEG_CL",
|
||||||
static const char* hvilStatusState[] = {"NOT OK","STATUS_OK","CURRENT_SOURCE_FAULT","INTERNAL_OPEN_FAULT","VEHICLE_OPEN_FAULT","PENTHOUSE_LID_OPEN_FAULT","UNKNOWN_LOCATION_OPEN_FAULT","VEHICLE_NODE_FAULT","NO_12V_SUPPLY","VEHICLE_OR_PENTHOUSE_LID_OPENFAULT","UNKNOWN(10)","UNKNOWN(11)","UNKNOWN(12)","UNKNOWN(13)","UNKNOWN(14)","UNKNOWN(15)"};
|
"UNKNOWN(10)", "UNKNOWN(11)", "UNKNOWN(12)"};
|
||||||
|
static const char* contactorState[] = {"SNA", "OPEN", "PRECHARGE", "BLOCKED",
|
||||||
|
"PULLED_IN", "OPENING", "ECONOMIZED", "WELDED",
|
||||||
|
"UNKNOWN(8)", "UNKNOWN(9)", "UNKNOWN(10)", "UNKNOWN(11)"};
|
||||||
|
static const char* hvilStatusState[] = {"NOT OK",
|
||||||
|
"STATUS_OK",
|
||||||
|
"CURRENT_SOURCE_FAULT",
|
||||||
|
"INTERNAL_OPEN_FAULT",
|
||||||
|
"VEHICLE_OPEN_FAULT",
|
||||||
|
"PENTHOUSE_LID_OPEN_FAULT",
|
||||||
|
"UNKNOWN_LOCATION_OPEN_FAULT",
|
||||||
|
"VEHICLE_NODE_FAULT",
|
||||||
|
"NO_12V_SUPPLY",
|
||||||
|
"VEHICLE_OR_PENTHOUSE_LID_OPENFAULT",
|
||||||
|
"UNKNOWN(10)",
|
||||||
|
"UNKNOWN(11)",
|
||||||
|
"UNKNOWN(12)",
|
||||||
|
"UNKNOWN(13)",
|
||||||
|
"UNKNOWN(14)",
|
||||||
|
"UNKNOWN(15)"};
|
||||||
|
|
||||||
#define MAX_SOC 1000 //BMS never goes over this value. We use this info to rescale SOC% sent to inverter
|
#define MAX_SOC 1000 //BMS never goes over this value. We use this info to rescale SOC% sent to inverter
|
||||||
#define MIN_SOC 0 //BMS never goes below this value. We use this info to rescale SOC% sent to inverter
|
#define MIN_SOC 0 //BMS never goes below this value. We use this info to rescale SOC% sent to inverter
|
||||||
|
@ -125,31 +160,27 @@ static const char* hvilStatusState[] = {"NOT OK","STATUS_OK","CURRENT_SOURCE_FAU
|
||||||
#define MIN_CELL_VOLTAGE_LFP 2800 //Battery is put into emergency stop if one cell goes over this value
|
#define MIN_CELL_VOLTAGE_LFP 2800 //Battery is put into emergency stop if one cell goes over this value
|
||||||
#define MAX_CELL_DEVIATION_LFP 150 //LED turns yellow on the board if mv delta exceeds this value
|
#define MAX_CELL_DEVIATION_LFP 150 //LED turns yellow on the board if mv delta exceeds this value
|
||||||
|
|
||||||
void update_values_tesla_model_3_battery()
|
void update_values_tesla_model_3_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
||||||
{ //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
|
||||||
//After values are mapped, we perform some safety checks, and do some serial printouts
|
//After values are mapped, we perform some safety checks, and do some serial printouts
|
||||||
StateOfHealth = 9900; //Hardcoded to 99%SOH
|
StateOfHealth = 9900; //Hardcoded to 99%SOH
|
||||||
|
|
||||||
//Calculate the SOC% value to send to inverter
|
//Calculate the SOC% value to send to inverter
|
||||||
soc_calculated = soc_vi;
|
soc_calculated = soc_vi;
|
||||||
soc_calculated = MIN_SOC + (MAX_SOC - MIN_SOC) * (soc_calculated - MINPERCENTAGE) / (MAXPERCENTAGE - MINPERCENTAGE);
|
soc_calculated = MIN_SOC + (MAX_SOC - MIN_SOC) * (soc_calculated - MINPERCENTAGE) / (MAXPERCENTAGE - MINPERCENTAGE);
|
||||||
if (soc_calculated < 0)
|
if (soc_calculated < 0) { //We are in the real SOC% range of 0-20%, always set SOC sent to Inverter as 0%
|
||||||
{ //We are in the real SOC% range of 0-20%, always set SOC sent to Inverter as 0%
|
|
||||||
soc_calculated = 0;
|
soc_calculated = 0;
|
||||||
}
|
}
|
||||||
if (soc_calculated > 1000)
|
if (soc_calculated > 1000) { //We are in the real SOC% range of 80-100%, always set SOC sent to Inverter as 100%
|
||||||
{ //We are in the real SOC% range of 80-100%, always set SOC sent to Inverter as 100%
|
|
||||||
soc_calculated = 1000;
|
soc_calculated = 1000;
|
||||||
}
|
}
|
||||||
SOC = (soc_calculated * 10); //increase SOC range from 0-100.0 -> 100.00
|
SOC = (soc_calculated * 10); //increase SOC range from 0-100.0 -> 100.00
|
||||||
|
|
||||||
battery_voltage = (volts*10); //One more decimal needed (370 -> 3700)
|
battery_voltage = (volts * 10); //One more decimal needed (370 -> 3700)
|
||||||
|
|
||||||
battery_current = amps; //TODO, this needs verifying if scaling is right
|
battery_current = amps; //TODO, this needs verifying if scaling is right
|
||||||
|
|
||||||
capacity_Wh = (nominal_full_pack_energy * 100); //Scale up 75.2kWh -> 75200Wh
|
capacity_Wh = (nominal_full_pack_energy * 100); //Scale up 75.2kWh -> 75200Wh
|
||||||
if(capacity_Wh > 60000)
|
if (capacity_Wh > 60000) {
|
||||||
{
|
|
||||||
capacity_Wh = 60000;
|
capacity_Wh = 60000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,18 +188,16 @@ void update_values_tesla_model_3_battery()
|
||||||
|
|
||||||
//Calculate the allowed discharge power, cap it if it gets too large
|
//Calculate the allowed discharge power, cap it if it gets too large
|
||||||
temporaryvariable = (max_discharge_current * volts);
|
temporaryvariable = (max_discharge_current * volts);
|
||||||
if(temporaryvariable > 60000){
|
if (temporaryvariable > 60000) {
|
||||||
max_target_discharge_power = 60000;
|
max_target_discharge_power = 60000;
|
||||||
}
|
} else {
|
||||||
else{
|
|
||||||
max_target_discharge_power = temporaryvariable;
|
max_target_discharge_power = temporaryvariable;
|
||||||
}
|
}
|
||||||
|
|
||||||
//The allowed charge power behaves strangely. We instead estimate this value
|
//The allowed charge power behaves strangely. We instead estimate this value
|
||||||
if(SOC == 10000){
|
if (SOC == 10000) {
|
||||||
max_target_charge_power = 0; //When battery is 100% full, set allowed charge W to 0
|
max_target_charge_power = 0; //When battery is 100% full, set allowed charge W to 0
|
||||||
}
|
} else {
|
||||||
else{
|
|
||||||
max_target_charge_power = 15000; //Otherwise we can push 15kW into the pack!
|
max_target_charge_power = 15000; //Otherwise we can push 15kW into the pack!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,17 +218,14 @@ void update_values_tesla_model_3_battery()
|
||||||
bms_status = ACTIVE; //Startout in active mode before checking if we have any faults
|
bms_status = ACTIVE; //Startout in active mode before checking if we have any faults
|
||||||
|
|
||||||
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
|
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
|
||||||
if(!stillAliveCAN)
|
if (!stillAliveCAN) {
|
||||||
{
|
|
||||||
bms_status = FAULT;
|
bms_status = FAULT;
|
||||||
Serial.println("ERROR: No CAN communication detected for 60s. Shutting down battery control.");
|
Serial.println("ERROR: No CAN communication detected for 60s. Shutting down battery control.");
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
stillAliveCAN--;
|
stillAliveCAN--;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hvil_status == 3){ //INTERNAL_OPEN_FAULT - Someone disconnected a high voltage cable while battery was in use
|
if (hvil_status == 3) { //INTERNAL_OPEN_FAULT - Someone disconnected a high voltage cable while battery was in use
|
||||||
bms_status = FAULT;
|
bms_status = FAULT;
|
||||||
Serial.println("ERROR: High voltage cable removed while battery running. Opening contactors!");
|
Serial.println("ERROR: High voltage cable removed while battery running. Opening contactors!");
|
||||||
}
|
}
|
||||||
|
@ -207,48 +233,46 @@ void update_values_tesla_model_3_battery()
|
||||||
cell_deviation_mV = (cell_max_v - cell_min_v);
|
cell_deviation_mV = (cell_max_v - cell_min_v);
|
||||||
|
|
||||||
//Determine which chemistry battery pack is using (crude method, TODO, replace with real CAN data later)
|
//Determine which chemistry battery pack is using (crude method, TODO, replace with real CAN data later)
|
||||||
if(soc_vi > 900){ //When SOC% is over 90.0%, we can use max cell voltage to estimate what chemistry is used
|
if (soc_vi > 900) { //When SOC% is over 90.0%, we can use max cell voltage to estimate what chemistry is used
|
||||||
if(cell_max_v < 3450){
|
if (cell_max_v < 3450) {
|
||||||
LFP_Chemistry = 1;
|
LFP_Chemistry = 1;
|
||||||
}
|
}
|
||||||
if(cell_max_v > 3700){
|
if (cell_max_v > 3700) {
|
||||||
LFP_Chemistry = 0;
|
LFP_Chemistry = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(LFP_Chemistry){ //LFP limits used for voltage safeties
|
if (LFP_Chemistry) { //LFP limits used for voltage safeties
|
||||||
if(cell_max_v >= MAX_CELL_VOLTAGE_LFP){
|
if (cell_max_v >= MAX_CELL_VOLTAGE_LFP) {
|
||||||
bms_status = FAULT;
|
bms_status = FAULT;
|
||||||
Serial.println("ERROR: CELL OVERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!");
|
Serial.println("ERROR: CELL OVERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!");
|
||||||
}
|
}
|
||||||
if(cell_min_v <= MIN_CELL_VOLTAGE_LFP){
|
if (cell_min_v <= MIN_CELL_VOLTAGE_LFP) {
|
||||||
bms_status = FAULT;
|
bms_status = FAULT;
|
||||||
Serial.println("ERROR: CELL UNDERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!");
|
Serial.println("ERROR: CELL UNDERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!");
|
||||||
}
|
}
|
||||||
if(cell_deviation_mV > MAX_CELL_DEVIATION_LFP){
|
if (cell_deviation_mV > MAX_CELL_DEVIATION_LFP) {
|
||||||
|
LEDcolor = YELLOW;
|
||||||
|
Serial.println("ERROR: HIGH CELL DEVIATION!!! Inspect battery!");
|
||||||
|
}
|
||||||
|
} else { //NCA/NCM limits used
|
||||||
|
if (cell_max_v >= MAX_CELL_VOLTAGE_NCA_NCM) {
|
||||||
|
bms_status = FAULT;
|
||||||
|
Serial.println("ERROR: CELL OVERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!");
|
||||||
|
}
|
||||||
|
if (cell_min_v <= MIN_CELL_VOLTAGE_NCA_NCM) {
|
||||||
|
bms_status = FAULT;
|
||||||
|
Serial.println("ERROR: CELL UNDERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!");
|
||||||
|
}
|
||||||
|
if (cell_deviation_mV > MAX_CELL_DEVIATION_NCA_NCM) {
|
||||||
LEDcolor = YELLOW;
|
LEDcolor = YELLOW;
|
||||||
Serial.println("ERROR: HIGH CELL DEVIATION!!! Inspect battery!");
|
Serial.println("ERROR: HIGH CELL DEVIATION!!! Inspect battery!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else{ //NCA/NCM limits used
|
|
||||||
if(cell_max_v >= MAX_CELL_VOLTAGE_NCA_NCM){
|
|
||||||
bms_status = FAULT;
|
|
||||||
Serial.println("ERROR: CELL OVERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!");
|
|
||||||
}
|
|
||||||
if(cell_min_v <= MIN_CELL_VOLTAGE_NCA_NCM){
|
|
||||||
bms_status = FAULT;
|
|
||||||
Serial.println("ERROR: CELL UNDERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!");
|
|
||||||
}
|
|
||||||
if(cell_deviation_mV > MAX_CELL_DEVIATION_NCA_NCM){
|
|
||||||
LEDcolor = YELLOW;
|
|
||||||
Serial.println("ERROR: HIGH CELL DEVIATION!!! Inspect battery!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Safeties verified. Perform USB serial printout if configured to do so */
|
/* Safeties verified. Perform USB serial printout if configured to do so */
|
||||||
|
|
||||||
#ifdef DEBUG_VIA_USB
|
#ifdef DEBUG_VIA_USB
|
||||||
|
|
||||||
printFaultCodesIfActive();
|
printFaultCodesIfActive();
|
||||||
|
|
||||||
|
@ -277,7 +301,7 @@ void update_values_tesla_model_3_battery()
|
||||||
Serial.print(", ");
|
Serial.print(", ");
|
||||||
print_int_with_units("Charge limit battery: ", regenerative_limit, "kW");
|
print_int_with_units("Charge limit battery: ", regenerative_limit, "kW");
|
||||||
Serial.print(", Fully charged?: ");
|
Serial.print(", Fully charged?: ");
|
||||||
if(full_charge_complete)
|
if (full_charge_complete)
|
||||||
Serial.print("YES, ");
|
Serial.print("YES, ");
|
||||||
else
|
else
|
||||||
Serial.print("NO, ");
|
Serial.print("NO, ");
|
||||||
|
@ -318,24 +342,27 @@ void update_values_tesla_model_3_battery()
|
||||||
Serial.print(", ");
|
Serial.print(", ");
|
||||||
print_int_with_units(" Min temperature: ", temperature_min, "C");
|
print_int_with_units(" Min temperature: ", temperature_min, "C");
|
||||||
Serial.println("");
|
Serial.println("");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void receive_can_tesla_model_3_battery(CAN_frame_t rx_frame)
|
void receive_can_tesla_model_3_battery(CAN_frame_t rx_frame) {
|
||||||
{
|
|
||||||
static int mux = 0;
|
static int mux = 0;
|
||||||
static int temp = 0;
|
static int temp = 0;
|
||||||
|
|
||||||
switch (rx_frame.MsgID)
|
switch (rx_frame.MsgID) {
|
||||||
{
|
|
||||||
case 0x352:
|
case 0x352:
|
||||||
//SOC
|
//SOC
|
||||||
nominal_full_pack_energy = (((rx_frame.data.u8[1] & 0x0F) << 8) | (rx_frame.data.u8[0])); //Example 752 (75.2kWh)
|
nominal_full_pack_energy = (((rx_frame.data.u8[1] & 0x0F) << 8) | (rx_frame.data.u8[0])); //Example 752 (75.2kWh)
|
||||||
nominal_energy_remaining = (((rx_frame.data.u8[2] & 0x3F) << 5) | ((rx_frame.data.u8[1] & 0xF8) >> 3)) * 0.1; //Example 1247 * 0.1 = 124.7kWh
|
nominal_energy_remaining = (((rx_frame.data.u8[2] & 0x3F) << 5) | ((rx_frame.data.u8[1] & 0xF8) >> 3)) *
|
||||||
expected_energy_remaining = (((rx_frame.data.u8[4] & 0x01) << 10) | (rx_frame.data.u8[3] << 2) | ((rx_frame.data.u8[2] & 0xC0) >> 6)); //Example 622 (62.2kWh)
|
0.1; //Example 1247 * 0.1 = 124.7kWh
|
||||||
ideal_energy_remaining = (((rx_frame.data.u8[5] & 0x0F) << 7) | ((rx_frame.data.u8[4] & 0xFE) >> 1)) * 0.1; //Example 311 * 0.1 = 31.1kWh
|
expected_energy_remaining = (((rx_frame.data.u8[4] & 0x01) << 10) | (rx_frame.data.u8[3] << 2) |
|
||||||
energy_to_charge_complete = (((rx_frame.data.u8[6] & 0x7F) << 4) | ((rx_frame.data.u8[5] & 0xF0) >> 4)) * 0.1; //Example 147 * 0.1 = 14.7kWh
|
((rx_frame.data.u8[2] & 0xC0) >> 6)); //Example 622 (62.2kWh)
|
||||||
energy_buffer = (((rx_frame.data.u8[7] & 0x7F) << 1) | ((rx_frame.data.u8[6] & 0x80) >> 7)) * 0.1; //Example 1 * 0.1 = 0
|
ideal_energy_remaining = (((rx_frame.data.u8[5] & 0x0F) << 7) | ((rx_frame.data.u8[4] & 0xFE) >> 1)) *
|
||||||
|
0.1; //Example 311 * 0.1 = 31.1kWh
|
||||||
|
energy_to_charge_complete = (((rx_frame.data.u8[6] & 0x7F) << 4) | ((rx_frame.data.u8[5] & 0xF0) >> 4)) *
|
||||||
|
0.1; //Example 147 * 0.1 = 14.7kWh
|
||||||
|
energy_buffer =
|
||||||
|
(((rx_frame.data.u8[7] & 0x7F) << 1) | ((rx_frame.data.u8[6] & 0x80) >> 7)) * 0.1; //Example 1 * 0.1 = 0
|
||||||
full_charge_complete = ((rx_frame.data.u8[7] & 0x80) >> 7);
|
full_charge_complete = ((rx_frame.data.u8[7] & 0x80) >> 7);
|
||||||
break;
|
break;
|
||||||
case 0x20A:
|
case 0x20A:
|
||||||
|
@ -353,46 +380,50 @@ void receive_can_tesla_model_3_battery(CAN_frame_t rx_frame)
|
||||||
regenerative_limit = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]) * 0.01; //Example 4715 * 0.01 = 47.15kW
|
regenerative_limit = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]) * 0.01; //Example 4715 * 0.01 = 47.15kW
|
||||||
discharge_limit = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]) * 0.013; //Example 2009 * 0.013 = 26.117???
|
discharge_limit = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]) * 0.013; //Example 2009 * 0.013 = 26.117???
|
||||||
max_heat_park = (((rx_frame.data.u8[5] & 0x03) << 8) | rx_frame.data.u8[4]) * 0.01; //Example 500 * 0.01 = 5kW
|
max_heat_park = (((rx_frame.data.u8[5] & 0x03) << 8) | rx_frame.data.u8[4]) * 0.01; //Example 500 * 0.01 = 5kW
|
||||||
hvac_max_power = (((rx_frame.data.u8[7] << 6) | ((rx_frame.data.u8[6] & 0xFC) >> 2))) * 0.02; //Example 1000 * 0.02 = 20kW?
|
hvac_max_power =
|
||||||
|
(((rx_frame.data.u8[7] << 6) | ((rx_frame.data.u8[6] & 0xFC) >> 2))) * 0.02; //Example 1000 * 0.02 = 20kW?
|
||||||
break;
|
break;
|
||||||
case 0x132:
|
case 0x132:
|
||||||
//battery amps/volts
|
//battery amps/volts
|
||||||
volts = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]) * 0.01; //Example 37030mv * 0.01 = 370V
|
volts = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]) * 0.01; //Example 37030mv * 0.01 = 370V
|
||||||
amps = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]); //Example 65492 (-4.3A) OR 225 (22.5A)
|
amps = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]); //Example 65492 (-4.3A) OR 225 (22.5A)
|
||||||
if (amps > 32768)
|
if (amps > 32768) {
|
||||||
{
|
amps = -(65535 - amps);
|
||||||
amps = - (65535 - amps);
|
|
||||||
}
|
}
|
||||||
amps = amps * 0.1;
|
amps = amps * 0.1;
|
||||||
raw_amps = ((rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]) * -0.05; //Example 10425 * -0.05 = ?
|
raw_amps = ((rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]) * -0.05; //Example 10425 * -0.05 = ?
|
||||||
battery_charge_time_remaining = (((rx_frame.data.u8[7] & 0x0F) << 8) | rx_frame.data.u8[6]) * 0.1; //Example 228 * 0.1 = 22.8min
|
battery_charge_time_remaining =
|
||||||
if(battery_charge_time_remaining == 4095)
|
(((rx_frame.data.u8[7] & 0x0F) << 8) | rx_frame.data.u8[6]) * 0.1; //Example 228 * 0.1 = 22.8min
|
||||||
{
|
if (battery_charge_time_remaining == 4095) {
|
||||||
battery_charge_time_remaining = 0;
|
battery_charge_time_remaining = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case 0x3D2:
|
case 0x3D2:
|
||||||
// total charge/discharge kwh
|
// total charge/discharge kwh
|
||||||
total_discharge = ((rx_frame.data.u8[3] << 24) | (rx_frame.data.u8[2] << 16) | (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]) * 0.001;
|
total_discharge = ((rx_frame.data.u8[3] << 24) | (rx_frame.data.u8[2] << 16) | (rx_frame.data.u8[1] << 8) |
|
||||||
total_charge = ((rx_frame.data.u8[7] << 24) | (rx_frame.data.u8[6] << 16) | (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]) * 0.001;
|
rx_frame.data.u8[0]) *
|
||||||
|
0.001;
|
||||||
|
total_charge = ((rx_frame.data.u8[7] << 24) | (rx_frame.data.u8[6] << 16) | (rx_frame.data.u8[5] << 8) |
|
||||||
|
rx_frame.data.u8[4]) *
|
||||||
|
0.001;
|
||||||
break;
|
break;
|
||||||
case 0x332:
|
case 0x332:
|
||||||
//min/max hist values
|
//min/max hist values
|
||||||
mux = (rx_frame.data.u8[0] & 0x03);
|
mux = (rx_frame.data.u8[0] & 0x03);
|
||||||
|
|
||||||
if(mux == 1) //Cell voltages
|
if (mux == 1) //Cell voltages
|
||||||
{
|
{
|
||||||
temp = ((rx_frame.data.u8[1] << 6) | (rx_frame.data.u8[0] >> 2));
|
temp = ((rx_frame.data.u8[1] << 6) | (rx_frame.data.u8[0] >> 2));
|
||||||
temp = (temp & 0xFFF);
|
temp = (temp & 0xFFF);
|
||||||
cell_max_v = temp*2;
|
cell_max_v = temp * 2;
|
||||||
temp = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]);
|
temp = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]);
|
||||||
temp = (temp & 0xFFF);
|
temp = (temp & 0xFFF);
|
||||||
cell_min_v = temp*2;
|
cell_min_v = temp * 2;
|
||||||
max_vno = 1 + (rx_frame.data.u8[4] & 0x7F); //This cell has highest voltage
|
max_vno = 1 + (rx_frame.data.u8[4] & 0x7F); //This cell has highest voltage
|
||||||
min_vno = 1 + (rx_frame.data.u8[5] & 0x7F); //This cell has lowest voltage
|
min_vno = 1 + (rx_frame.data.u8[5] & 0x7F); //This cell has lowest voltage
|
||||||
}
|
}
|
||||||
if(mux == 0)//Temperature sensors
|
if (mux == 0) //Temperature sensors
|
||||||
{
|
{
|
||||||
temp = rx_frame.data.u8[2];
|
temp = rx_frame.data.u8[2];
|
||||||
max_temp = (temp * 0.5) - 40; //in celcius, Example 24
|
max_temp = (temp * 0.5) - 40; //in celcius, Example 24
|
||||||
|
@ -405,8 +436,10 @@ void receive_can_tesla_model_3_battery(CAN_frame_t rx_frame)
|
||||||
//Min / max limits
|
//Min / max limits
|
||||||
min_voltage = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]) * 0.01 * 2; //Example 24148mv * 0.01 = 241.48 V
|
min_voltage = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]) * 0.01 * 2; //Example 24148mv * 0.01 = 241.48 V
|
||||||
max_voltage = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]) * 0.01 * 2; //Example 40282mv * 0.01 = 402.82 V
|
max_voltage = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]) * 0.01 * 2; //Example 40282mv * 0.01 = 402.82 V
|
||||||
max_charge_current = (((rx_frame.data.u8[5] & 0x3F) << 8) | rx_frame.data.u8[4]) * 0.1; //Example 1301? * 0.1 = 130.1?
|
max_charge_current =
|
||||||
max_discharge_current = (((rx_frame.data.u8[7] & 0x3F) << 8) | rx_frame.data.u8[6]) * 0.128; //Example 430? * 0.128 = 55.4?
|
(((rx_frame.data.u8[5] & 0x3F) << 8) | rx_frame.data.u8[4]) * 0.1; //Example 1301? * 0.1 = 130.1?
|
||||||
|
max_discharge_current =
|
||||||
|
(((rx_frame.data.u8[7] & 0x3F) << 8) | rx_frame.data.u8[6]) * 0.128; //Example 430? * 0.128 = 55.4?
|
||||||
break;
|
break;
|
||||||
case 0x2b4:
|
case 0x2b4:
|
||||||
low_voltage = (((rx_frame.data.u8[1] & 0x03) << 8) | rx_frame.data.u8[0]) * 0.0390625;
|
low_voltage = (((rx_frame.data.u8[1] & 0x03) << 8) | rx_frame.data.u8[0]) * 0.0390625;
|
||||||
|
@ -426,7 +459,7 @@ void receive_can_tesla_model_3_battery(CAN_frame_t rx_frame)
|
||||||
SwAssertion = ((rx_frame.data.u8[0] & 0x04) >> 2);
|
SwAssertion = ((rx_frame.data.u8[0] & 0x04) >> 2);
|
||||||
CrashEvent = ((rx_frame.data.u8[0] & 0x08) >> 3);
|
CrashEvent = ((rx_frame.data.u8[0] & 0x08) >> 3);
|
||||||
OverDchgCurrentFault = ((rx_frame.data.u8[0] & 0x10) >> 4);
|
OverDchgCurrentFault = ((rx_frame.data.u8[0] & 0x10) >> 4);
|
||||||
OverChargeCurrentFault =((rx_frame.data.u8[0] & 0x20) >> 5);
|
OverChargeCurrentFault = ((rx_frame.data.u8[0] & 0x20) >> 5);
|
||||||
OverCurrentFault = ((rx_frame.data.u8[0] & 0x40) >> 6);
|
OverCurrentFault = ((rx_frame.data.u8[0] & 0x40) >> 6);
|
||||||
OverTemperatureFault = ((rx_frame.data.u8[1] & 0x80) >> 7);
|
OverTemperatureFault = ((rx_frame.data.u8[1] & 0x80) >> 7);
|
||||||
OverVoltageFault = (rx_frame.data.u8[1] & 0x01);
|
OverVoltageFault = (rx_frame.data.u8[1] & 0x01);
|
||||||
|
@ -463,7 +496,7 @@ void receive_can_tesla_model_3_battery(CAN_frame_t rx_frame)
|
||||||
FcContactorFellOpen = ((rx_frame.data.u8[4] & 0x80) >> 7);
|
FcContactorFellOpen = ((rx_frame.data.u8[4] & 0x80) >> 7);
|
||||||
packCtrCloseBlocked = (rx_frame.data.u8[5] & 0x01);
|
packCtrCloseBlocked = (rx_frame.data.u8[5] & 0x01);
|
||||||
fcCtrCloseBlocked = ((rx_frame.data.u8[5] & 0x02) >> 1);
|
fcCtrCloseBlocked = ((rx_frame.data.u8[5] & 0x02) >> 1);
|
||||||
packContactorForceOpen =((rx_frame.data.u8[5] & 0x04) >> 2);
|
packContactorForceOpen = ((rx_frame.data.u8[5] & 0x04) >> 2);
|
||||||
fcContactorForceOpen = ((rx_frame.data.u8[5] & 0x08) >> 3);
|
fcContactorForceOpen = ((rx_frame.data.u8[5] & 0x08) >> 3);
|
||||||
dcLinkOverVoltage = ((rx_frame.data.u8[5] & 0x10) >> 4);
|
dcLinkOverVoltage = ((rx_frame.data.u8[5] & 0x10) >> 4);
|
||||||
shuntOverTemperature = ((rx_frame.data.u8[5] & 0x20) >> 5);
|
shuntOverTemperature = ((rx_frame.data.u8[5] & 0x20) >> 5);
|
||||||
|
@ -477,66 +510,60 @@ void receive_can_tesla_model_3_battery(CAN_frame_t rx_frame)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void send_can_tesla_model_3_battery()
|
void send_can_tesla_model_3_battery() {
|
||||||
{
|
/*From bielec: My fist 221 message, to close the contactors is 0x41, 0x11, 0x01, 0x00, 0x00, 0x00, 0x20, 0x96 and then,
|
||||||
/*From bielec: My fist 221 message, to close the contactors is 0x41, 0x11, 0x01, 0x00, 0x00, 0x00, 0x20, 0x96 and then,
|
|
||||||
to cause "hv_up_for_drive" I send an additional 221 message 0x61, 0x15, 0x01, 0x00, 0x00, 0x00, 0x20, 0xBA so
|
to cause "hv_up_for_drive" I send an additional 221 message 0x61, 0x15, 0x01, 0x00, 0x00, 0x00, 0x20, 0xBA so
|
||||||
two 221 messages are being continuously transmitted. When I want to shut down, I stop the second message and only send
|
two 221 messages are being continuously transmitted. When I want to shut down, I stop the second message and only send
|
||||||
the first, for a few cycles, then stop all messages which causes the contactor to open. */
|
the first, for a few cycles, then stop all messages which causes the contactor to open. */
|
||||||
|
|
||||||
unsigned long currentMillis = millis();
|
unsigned long currentMillis = millis();
|
||||||
//Send 30ms message
|
//Send 30ms message
|
||||||
if (currentMillis - previousMillis30 >= interval30)
|
if (currentMillis - previousMillis30 >= interval30) {
|
||||||
{
|
|
||||||
previousMillis30 = currentMillis;
|
previousMillis30 = currentMillis;
|
||||||
|
|
||||||
if(bms_status == ACTIVE){
|
if (bms_status == ACTIVE) {
|
||||||
send221still = 10;
|
send221still = 10;
|
||||||
ESP32Can.CANWriteFrame(&TESLA_221_1);
|
ESP32Can.CANWriteFrame(&TESLA_221_1);
|
||||||
ESP32Can.CANWriteFrame(&TESLA_221_2);
|
ESP32Can.CANWriteFrame(&TESLA_221_2);
|
||||||
}
|
} else { //bms_status == FAULT
|
||||||
else{ //bms_status == FAULT
|
if (send221still > 0) {
|
||||||
if(send221still > 0){
|
|
||||||
ESP32Can.CANWriteFrame(&TESLA_221_1);
|
ESP32Can.CANWriteFrame(&TESLA_221_1);
|
||||||
send221still--;
|
send221still--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
uint16_t convert2unsignedInt16(int16_t signed_value)
|
uint16_t convert2unsignedInt16(int16_t signed_value) {
|
||||||
{
|
if (signed_value < 0) {
|
||||||
if(signed_value < 0){
|
return (65535 + signed_value);
|
||||||
return(65535 + signed_value);
|
} else {
|
||||||
}
|
|
||||||
else{
|
|
||||||
return (uint16_t)signed_value;
|
return (uint16_t)signed_value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_int_with_units(char *header, int value, char *units) {
|
void print_int_with_units(char* header, int value, char* units) {
|
||||||
Serial.print(header);
|
Serial.print(header);
|
||||||
Serial.print(value);
|
Serial.print(value);
|
||||||
Serial.print(units);
|
Serial.print(units);
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_SOC(char *header, int SOC) {
|
void print_SOC(char* header, int SOC) {
|
||||||
Serial.print(header);
|
Serial.print(header);
|
||||||
Serial.print(SOC / 100);
|
Serial.print(SOC / 100);
|
||||||
Serial.print(".");
|
Serial.print(".");
|
||||||
int hundredth = SOC % 100;
|
int hundredth = SOC % 100;
|
||||||
if(hundredth < 10)
|
if (hundredth < 10)
|
||||||
Serial.print(0);
|
Serial.print(0);
|
||||||
Serial.print(hundredth);
|
Serial.print(hundredth);
|
||||||
Serial.println("%");
|
Serial.println("%");
|
||||||
}
|
}
|
||||||
|
|
||||||
void printFaultCodesIfActive(){
|
void printFaultCodesIfActive() {
|
||||||
if (packCtrsClosingAllowed == 0)
|
if (packCtrsClosingAllowed == 0) {
|
||||||
{
|
Serial.println(
|
||||||
Serial.println("ERROR: Check high voltage connectors and interlock circuit! Closing contactor not allowed! Values: ");
|
"ERROR: Check high voltage connectors and interlock circuit! Closing contactor not allowed! Values: ");
|
||||||
}
|
}
|
||||||
if (pyroTestInProgress == 1)
|
if (pyroTestInProgress == 1) {
|
||||||
{
|
|
||||||
Serial.println("ERROR: Please wait for Pyro Connection check to finish, HV cables successfully seated!");
|
Serial.println("ERROR: Please wait for Pyro Connection check to finish, HV cables successfully seated!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -545,7 +572,8 @@ void printFaultCodesIfActive(){
|
||||||
printDebugIfActive(PowerLossReset, "ERROR: The processor has experienced a reset due to power loss");
|
printDebugIfActive(PowerLossReset, "ERROR: The processor has experienced a reset due to power loss");
|
||||||
printDebugIfActive(SwAssertion, "ERROR: An internal software assertion has failed");
|
printDebugIfActive(SwAssertion, "ERROR: An internal software assertion has failed");
|
||||||
printDebugIfActive(CrashEvent, "ERROR: crash signal is detected by HVP");
|
printDebugIfActive(CrashEvent, "ERROR: crash signal is detected by HVP");
|
||||||
printDebugIfActive(OverDchgCurrentFault, "ERROR: Pack discharge current is above the safe max discharge current limit!");
|
printDebugIfActive(OverDchgCurrentFault,
|
||||||
|
"ERROR: Pack discharge current is above the safe max discharge current limit!");
|
||||||
printDebugIfActive(OverChargeCurrentFault, "ERROR: Pack charge current is above the safe max charge current limit!");
|
printDebugIfActive(OverChargeCurrentFault, "ERROR: Pack charge current is above the safe max charge current limit!");
|
||||||
printDebugIfActive(OverCurrentFault, "ERROR: Pack current (discharge or charge) is above max current limit!");
|
printDebugIfActive(OverCurrentFault, "ERROR: Pack current (discharge or charge) is above max current limit!");
|
||||||
printDebugIfActive(OverTemperatureFault, "ERROR: A pack module temperature is above the max temperature limit!");
|
printDebugIfActive(OverTemperatureFault, "ERROR: A pack module temperature is above the max temperature limit!");
|
||||||
|
@ -568,7 +596,8 @@ void printFaultCodesIfActive(){
|
||||||
printDebugIfActive(VerSupplyFault, "ERROR: Energy reserve voltage supply is below minimum voltage threshold");
|
printDebugIfActive(VerSupplyFault, "ERROR: Energy reserve voltage supply is below minimum voltage threshold");
|
||||||
printDebugIfActive(HvilFault, "ERROR: High Voltage Inter Lock fault is detected");
|
printDebugIfActive(HvilFault, "ERROR: High Voltage Inter Lock fault is detected");
|
||||||
printDebugIfActive(BmsHvsMiaFault, "ERROR: BMS node is mia on HVS or HVI CAN");
|
printDebugIfActive(BmsHvsMiaFault, "ERROR: BMS node is mia on HVS or HVI CAN");
|
||||||
printDebugIfActive(PackVoltMismatchFault, "ERROR: Pack voltage doesn't match approximately with sum of brick voltages");
|
printDebugIfActive(PackVoltMismatchFault,
|
||||||
|
"ERROR: Pack voltage doesn't match approximately with sum of brick voltages");
|
||||||
//printDebugIfActive(EnsMiaFault, "ERROR: ENS line is not connected to HVC"); //Uncommented due to not affecting usage
|
//printDebugIfActive(EnsMiaFault, "ERROR: ENS line is not connected to HVC"); //Uncommented due to not affecting usage
|
||||||
printDebugIfActive(PackPosCtrArcFault, "ERROR: HVP detectes series arc at pack contactor");
|
printDebugIfActive(PackPosCtrArcFault, "ERROR: HVP detectes series arc at pack contactor");
|
||||||
printDebugIfActive(packNegCtrArcFault, "ERROR: HVP detectes series arc at FC contactor");
|
printDebugIfActive(packNegCtrArcFault, "ERROR: HVP detectes series arc at FC contactor");
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
#ifndef TESLA_MODEL_3_BATTERY_H
|
#ifndef TESLA_MODEL_3_BATTERY_H
|
||||||
#define TESLA_MODEL_3_BATTERY_H
|
#define TESLA_MODEL_3_BATTERY_H
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "../devboard/can/ESP32CAN.h"
|
|
||||||
#include "../../USER_SETTINGS.h"
|
#include "../../USER_SETTINGS.h"
|
||||||
|
#include "../devboard/can/ESP32CAN.h"
|
||||||
|
|
||||||
#define ABSOLUTE_MAX_VOLTAGE 4030 // 403.0V,if battery voltage goes over this, charging is not possible (goes into forced discharge)
|
#define ABSOLUTE_MAX_VOLTAGE \
|
||||||
|
4030 // 403.0V,if battery voltage goes over this, charging is not possible (goes into forced discharge)
|
||||||
#define ABSOLUTE_MIN_VOLTAGE 2450 // 245.0V if battery voltage goes under this, discharging further is disabled
|
#define ABSOLUTE_MIN_VOLTAGE 2450 // 245.0V if battery voltage goes under this, discharging further is disabled
|
||||||
|
|
||||||
// These parameters need to be mapped for the Inverter
|
// These parameters need to be mapped for the Inverter
|
||||||
|
@ -42,8 +43,8 @@ void receive_can_tesla_model_3_battery(CAN_frame_t rx_frame);
|
||||||
void send_can_tesla_model_3_battery();
|
void send_can_tesla_model_3_battery();
|
||||||
void printFaultCodesIfActive();
|
void printFaultCodesIfActive();
|
||||||
void printDebugIfActive(uint8_t symbol, const char* message);
|
void printDebugIfActive(uint8_t symbol, const char* message);
|
||||||
void print_int_with_units(char *header, int value, char *units);
|
void print_int_with_units(char* header, int value, char* units);
|
||||||
void print_SOC(char *header, int SOC);
|
void print_SOC(char* header, int SOC);
|
||||||
uint16_t convert2unsignedInt16(int16_t signed_value);
|
uint16_t convert2unsignedInt16(int16_t signed_value);
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -1,37 +1,32 @@
|
||||||
#include "ESP32CAN.h"
|
#include "ESP32CAN.h"
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
int ESP32CAN::CANInit()
|
int ESP32CAN::CANInit() {
|
||||||
{
|
|
||||||
return CAN_init();
|
return CAN_init();
|
||||||
}
|
}
|
||||||
int ESP32CAN::CANWriteFrame(const CAN_frame_t* p_frame)
|
int ESP32CAN::CANWriteFrame(const CAN_frame_t* p_frame) {
|
||||||
{
|
|
||||||
static unsigned long start_time;
|
static unsigned long start_time;
|
||||||
int result = -1;
|
int result = -1;
|
||||||
if(tx_ok){
|
if (tx_ok) {
|
||||||
result = CAN_write_frame(p_frame);
|
result = CAN_write_frame(p_frame);
|
||||||
tx_ok = (result == 0) ? true : false;
|
tx_ok = (result == 0) ? true : false;
|
||||||
if(tx_ok == false){
|
if (tx_ok == false) {
|
||||||
Serial.println("CAN failure! Check wires");
|
Serial.println("CAN failure! Check wires");
|
||||||
LEDcolor = 3;
|
LEDcolor = 3;
|
||||||
start_time = millis();
|
start_time = millis();
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else{
|
if ((millis() - start_time) >= 2000) {
|
||||||
if((millis() - start_time) >= 2000){
|
|
||||||
tx_ok = true;
|
tx_ok = true;
|
||||||
LEDcolor = 0;
|
LEDcolor = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
int ESP32CAN::CANStop()
|
int ESP32CAN::CANStop() {
|
||||||
{
|
|
||||||
return CAN_stop();
|
return CAN_stop();
|
||||||
}
|
}
|
||||||
int ESP32CAN::CANConfigFilter(const CAN_filter_t* p_filter)
|
int ESP32CAN::CANConfigFilter(const CAN_filter_t* p_filter) {
|
||||||
{
|
|
||||||
return CAN_config_filter(p_filter);
|
return CAN_config_filter(p_filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,18 @@
|
||||||
#ifndef ESP32CAN_H
|
#ifndef ESP32CAN_H
|
||||||
#define ESP32CAN_H
|
#define ESP32CAN_H
|
||||||
|
|
||||||
#include "../../lib/ThomasBarth-ESP32-CAN-Driver/CAN_config.h"
|
|
||||||
#include "../../lib/ThomasBarth-ESP32-CAN-Driver/CAN.h"
|
#include "../../lib/ThomasBarth-ESP32-CAN-Driver/CAN.h"
|
||||||
|
#include "../../lib/ThomasBarth-ESP32-CAN-Driver/CAN_config.h"
|
||||||
extern uint8_t LEDcolor;
|
extern uint8_t LEDcolor;
|
||||||
|
|
||||||
class ESP32CAN
|
class ESP32CAN {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
bool tx_ok = true;
|
bool tx_ok = true;
|
||||||
int CANInit();
|
int CANInit();
|
||||||
int CANConfigFilter(const CAN_filter_t* p_filter);
|
int CANConfigFilter(const CAN_filter_t* p_filter);
|
||||||
int CANWriteFrame(const CAN_frame_t* p_frame);
|
int CANWriteFrame(const CAN_frame_t* p_frame);
|
||||||
int CANStop();
|
int CANStop();
|
||||||
void CANSetCfg(CAN_device_t *can_cfg);
|
void CANSetCfg(CAN_device_t* can_cfg);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern ESP32CAN ESP32Can;
|
extern ESP32CAN ESP32Can;
|
||||||
|
|
|
@ -13,17 +13,17 @@
|
||||||
#define RS485_SE_PIN 19 // 22 /SHDN
|
#define RS485_SE_PIN 19 // 22 /SHDN
|
||||||
|
|
||||||
#ifdef DUAL_CAN
|
#ifdef DUAL_CAN
|
||||||
#define MCP2515_SCK 12 // SCK input of MCP2515
|
#define MCP2515_SCK 12 // SCK input of MCP2515
|
||||||
#define MCP2515_MOSI 5 // SDI 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_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_CS 18 // CS input of MCP2515
|
||||||
#define MCP2515_INT 35 // INT output of MCP2515 | | Pin 35 is input only, without pullup/down resistors
|
#define MCP2515_INT 35 // INT output of MCP2515 | | Pin 35 is input only, without pullup/down resistors
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONTACTOR_CONTROL
|
#ifdef CONTACTOR_CONTROL
|
||||||
#define POSITIVE_CONTACTOR_PIN 32
|
#define POSITIVE_CONTACTOR_PIN 32
|
||||||
#define NEGATIVE_CONTACTOR_PIN 33
|
#define NEGATIVE_CONTACTOR_PIN 33
|
||||||
#define PRECHARGE_PIN 25
|
#define PRECHARGE_PIN 25
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define SD_MISO_PIN 2
|
#define SD_MISO_PIN 2
|
||||||
|
|
|
@ -56,20 +56,19 @@ static uint32_t t1l_ticks = 0;
|
||||||
|
|
||||||
bool rmt_reserved_channels[ADAFRUIT_RMT_CHANNEL_MAX];
|
bool rmt_reserved_channels[ADAFRUIT_RMT_CHANNEL_MAX];
|
||||||
|
|
||||||
static void IRAM_ATTR ws2812_rmt_adapter(const void *src, rmt_item32_t *dest, size_t src_size,
|
static void IRAM_ATTR ws2812_rmt_adapter(const void* src, rmt_item32_t* dest, size_t src_size, size_t wanted_num,
|
||||||
size_t wanted_num, size_t *translated_size, size_t *item_num)
|
size_t* translated_size, size_t* item_num) {
|
||||||
{
|
|
||||||
if (src == NULL || dest == NULL) {
|
if (src == NULL || dest == NULL) {
|
||||||
*translated_size = 0;
|
*translated_size = 0;
|
||||||
*item_num = 0;
|
*item_num = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const rmt_item32_t bit0 = {{{ t0h_ticks, 1, t0l_ticks, 0 }}}; //Logical 0
|
const rmt_item32_t bit0 = {{{t0h_ticks, 1, t0l_ticks, 0}}}; //Logical 0
|
||||||
const rmt_item32_t bit1 = {{{ t1h_ticks, 1, t1l_ticks, 0 }}}; //Logical 1
|
const rmt_item32_t bit1 = {{{t1h_ticks, 1, t1l_ticks, 0}}}; //Logical 1
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
size_t num = 0;
|
size_t num = 0;
|
||||||
uint8_t *psrc = (uint8_t *)src;
|
uint8_t* psrc = (uint8_t*)src;
|
||||||
rmt_item32_t *pdest = dest;
|
rmt_item32_t* pdest = dest;
|
||||||
while (size < src_size && num < wanted_num) {
|
while (size < src_size && num < wanted_num) {
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
// MSB first
|
// MSB first
|
||||||
|
@ -88,7 +87,7 @@ static void IRAM_ATTR ws2812_rmt_adapter(const void *src, rmt_item32_t *dest, si
|
||||||
*item_num = num;
|
*item_num = num;
|
||||||
}
|
}
|
||||||
|
|
||||||
void espShow(uint8_t pin, uint8_t *pixels, uint32_t numBytes, boolean is800KHz) {
|
void espShow(uint8_t pin, uint8_t* pixels, uint32_t numBytes, boolean is800KHz) {
|
||||||
// Reserve channel
|
// Reserve channel
|
||||||
rmt_channel_t channel = ADAFRUIT_RMT_CHANNEL_MAX;
|
rmt_channel_t channel = ADAFRUIT_RMT_CHANNEL_MAX;
|
||||||
for (size_t i = 0; i < ADAFRUIT_RMT_CHANNEL_MAX; i++) {
|
for (size_t i = 0; i < ADAFRUIT_RMT_CHANNEL_MAX; i++) {
|
||||||
|
@ -108,8 +107,7 @@ void espShow(uint8_t pin, uint8_t *pixels, uint32_t numBytes, boolean is800KHz)
|
||||||
config.clk_div = 2;
|
config.clk_div = 2;
|
||||||
#else
|
#else
|
||||||
// Match default TX config from ESP-IDF version 3.4
|
// Match default TX config from ESP-IDF version 3.4
|
||||||
rmt_config_t config = {
|
rmt_config_t config = {.rmt_mode = RMT_MODE_TX,
|
||||||
.rmt_mode = RMT_MODE_TX,
|
|
||||||
.channel = channel,
|
.channel = channel,
|
||||||
.gpio_num = pin,
|
.gpio_num = pin,
|
||||||
.clk_div = 2,
|
.clk_div = 2,
|
||||||
|
@ -122,8 +120,7 @@ void espShow(uint8_t pin, uint8_t *pixels, uint32_t numBytes, boolean is800KHz)
|
||||||
.carrier_en = false,
|
.carrier_en = false,
|
||||||
.loop_en = false,
|
.loop_en = false,
|
||||||
.idle_output_en = true,
|
.idle_output_en = true,
|
||||||
}
|
}};
|
||||||
};
|
|
||||||
#endif
|
#endif
|
||||||
rmt_config(&config);
|
rmt_config(&config);
|
||||||
rmt_driver_install(config.channel, 0, 0);
|
rmt_driver_install(config.channel, 0, 0);
|
||||||
|
|
|
@ -6,8 +6,7 @@
|
||||||
extern uint16_t mbPV[MBPV_MAX];
|
extern uint16_t mbPV[MBPV_MAX];
|
||||||
|
|
||||||
// Server function to handle FC 0x03
|
// Server function to handle FC 0x03
|
||||||
ModbusMessage FC03(ModbusMessage request)
|
ModbusMessage FC03(ModbusMessage request) {
|
||||||
{
|
|
||||||
//Serial.println(request);
|
//Serial.println(request);
|
||||||
ModbusMessage response; // The Modbus message we are going to give back
|
ModbusMessage response; // The Modbus message we are going to give back
|
||||||
uint16_t addr = 0; // Start address
|
uint16_t addr = 0; // Start address
|
||||||
|
@ -19,8 +18,7 @@ ModbusMessage FC03(ModbusMessage request)
|
||||||
LOG_D("FC03 received: read %d words @ %d\r\n", words, addr);
|
LOG_D("FC03 received: read %d words @ %d\r\n", words, addr);
|
||||||
|
|
||||||
// Address overflow?
|
// Address overflow?
|
||||||
if ((addr + words) > MBPV_MAX)
|
if ((addr + words) > MBPV_MAX) {
|
||||||
{
|
|
||||||
// Yes - send respective error response
|
// Yes - send respective error response
|
||||||
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_ADDRESS);
|
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_ADDRESS);
|
||||||
LOG_D("ERROR - ILLEGAL DATA ADDRESS\r\n");
|
LOG_D("ERROR - ILLEGAL DATA ADDRESS\r\n");
|
||||||
|
@ -30,8 +28,7 @@ ModbusMessage FC03(ModbusMessage request)
|
||||||
// Set up response
|
// Set up response
|
||||||
response.add(request.getServerID(), request.getFunctionCode(), (uint8_t)(words * 2));
|
response.add(request.getServerID(), request.getFunctionCode(), (uint8_t)(words * 2));
|
||||||
sprintf(debugString, "Read : ");
|
sprintf(debugString, "Read : ");
|
||||||
for (uint8_t i = 0; i < words; ++i)
|
for (uint8_t i = 0; i < words; ++i) {
|
||||||
{
|
|
||||||
// send increasing data values
|
// send increasing data values
|
||||||
response.add((uint16_t)(mbPV[addr + i]));
|
response.add((uint16_t)(mbPV[addr + i]));
|
||||||
sprintf(debugString + strlen(debugString), "%i ", mbPV[addr + i]);
|
sprintf(debugString + strlen(debugString), "%i ", mbPV[addr + i]);
|
||||||
|
@ -42,8 +39,7 @@ ModbusMessage FC03(ModbusMessage request)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server function to handle FC 0x06
|
// Server function to handle FC 0x06
|
||||||
ModbusMessage FC06(ModbusMessage request)
|
ModbusMessage FC06(ModbusMessage request) {
|
||||||
{
|
|
||||||
//Serial.println(request);
|
//Serial.println(request);
|
||||||
ModbusMessage response; // The Modbus message we are going to give back
|
ModbusMessage response; // The Modbus message we are going to give back
|
||||||
uint16_t addr = 0; // Start address
|
uint16_t addr = 0; // Start address
|
||||||
|
@ -54,8 +50,7 @@ ModbusMessage FC06(ModbusMessage request)
|
||||||
LOG_D("FC06 received: write 1 word @ %d\r\n", addr);
|
LOG_D("FC06 received: write 1 word @ %d\r\n", addr);
|
||||||
|
|
||||||
// Address overflow?
|
// Address overflow?
|
||||||
if ((addr) > MBPV_MAX)
|
if ((addr) > MBPV_MAX) {
|
||||||
{
|
|
||||||
// Yes - send respective error response
|
// Yes - send respective error response
|
||||||
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_ADDRESS);
|
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_ADDRESS);
|
||||||
LOG_D("ERROR - ILLEGAL DATA ADDRESS\r\n");
|
LOG_D("ERROR - ILLEGAL DATA ADDRESS\r\n");
|
||||||
|
@ -72,8 +67,7 @@ ModbusMessage FC06(ModbusMessage request)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server function to handle FC 0x10 (FC16)
|
// Server function to handle FC 0x10 (FC16)
|
||||||
ModbusMessage FC16(ModbusMessage request)
|
ModbusMessage FC16(ModbusMessage request) {
|
||||||
{
|
|
||||||
//Serial.println(request);
|
//Serial.println(request);
|
||||||
ModbusMessage response; // The Modbus message we are going to give back
|
ModbusMessage response; // The Modbus message we are going to give back
|
||||||
uint16_t addr = 0; // Start address
|
uint16_t addr = 0; // Start address
|
||||||
|
@ -96,8 +90,7 @@ ModbusMessage FC16(ModbusMessage request)
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
// Address overflow?
|
// Address overflow?
|
||||||
if ((addr + words) > MBPV_MAX)
|
if ((addr + words) > MBPV_MAX) {
|
||||||
{
|
|
||||||
// Yes - send respective error response
|
// Yes - send respective error response
|
||||||
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_ADDRESS);
|
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_ADDRESS);
|
||||||
LOG_D("ERROR - ILLEGAL DATA ADDRESS\r\n");
|
LOG_D("ERROR - ILLEGAL DATA ADDRESS\r\n");
|
||||||
|
@ -106,8 +99,7 @@ ModbusMessage FC16(ModbusMessage request)
|
||||||
|
|
||||||
// Do the writes
|
// Do the writes
|
||||||
sprintf(debugString, "Write : ");
|
sprintf(debugString, "Write : ");
|
||||||
for (uint8_t i = 0; i < words; ++i)
|
for (uint8_t i = 0; i < words; ++i) {
|
||||||
{
|
|
||||||
request.get(7 + (i * 2), val); //data starts at byte 6 in request packet
|
request.get(7 + (i * 2), val); //data starts at byte 6 in request packet
|
||||||
mbPV[addr + i] = val;
|
mbPV[addr + i] = val;
|
||||||
sprintf(debugString + strlen(debugString), "%i ", mbPV[addr + i]);
|
sprintf(debugString + strlen(debugString), "%i ", mbPV[addr + i]);
|
||||||
|
@ -120,8 +112,7 @@ ModbusMessage FC16(ModbusMessage request)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server function to handle FC 0x17 (FC23)
|
// Server function to handle FC 0x17 (FC23)
|
||||||
ModbusMessage FC23(ModbusMessage request)
|
ModbusMessage FC23(ModbusMessage request) {
|
||||||
{
|
|
||||||
//Serial.println(request);
|
//Serial.println(request);
|
||||||
ModbusMessage response; // The Modbus message we are going to give back
|
ModbusMessage response; // The Modbus message we are going to give back
|
||||||
uint16_t read_addr = 0; // Start address for read
|
uint16_t read_addr = 0; // Start address for read
|
||||||
|
@ -150,8 +141,8 @@ ModbusMessage FC23(ModbusMessage request)
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
// Address overflow?
|
// Address overflow?
|
||||||
if (((write_addr + write_words) > MBPV_MAX) || ((read_addr + read_words) > MBPV_MAX))
|
if (((write_addr + write_words) > MBPV_MAX) ||
|
||||||
{ // Yes - send respective error response
|
((read_addr + read_words) > MBPV_MAX)) { // Yes - send respective error response
|
||||||
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_ADDRESS);
|
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_ADDRESS);
|
||||||
LOG_D("ERROR - ILLEGAL DATA ADDRESS\r\n");
|
LOG_D("ERROR - ILLEGAL DATA ADDRESS\r\n");
|
||||||
return response;
|
return response;
|
||||||
|
@ -160,8 +151,7 @@ ModbusMessage FC23(ModbusMessage request)
|
||||||
//WRITE SECTION - write is done before read for FC23
|
//WRITE SECTION - write is done before read for FC23
|
||||||
// Do the writes
|
// Do the writes
|
||||||
sprintf(debugString, "Write: ");
|
sprintf(debugString, "Write: ");
|
||||||
for (uint8_t i = 0; i < write_words; ++i)
|
for (uint8_t i = 0; i < write_words; ++i) {
|
||||||
{
|
|
||||||
request.get(11 + (i * 2), write_val); //data starts at byte 6 in request packet
|
request.get(11 + (i * 2), write_val); //data starts at byte 6 in request packet
|
||||||
mbPV[write_addr + i] = write_val;
|
mbPV[write_addr + i] = write_val;
|
||||||
sprintf(debugString + strlen(debugString), "%i ", mbPV[write_addr + i]);
|
sprintf(debugString + strlen(debugString), "%i ", mbPV[write_addr + i]);
|
||||||
|
@ -172,8 +162,7 @@ ModbusMessage FC23(ModbusMessage request)
|
||||||
// Set up response
|
// Set up response
|
||||||
sprintf(debugString, "Read: ");
|
sprintf(debugString, "Read: ");
|
||||||
response.add(request.getServerID(), request.getFunctionCode(), (uint8_t)(read_words * 2));
|
response.add(request.getServerID(), request.getFunctionCode(), (uint8_t)(read_words * 2));
|
||||||
for (uint8_t i = 0; i < read_words; ++i)
|
for (uint8_t i = 0; i < read_words; ++i) {
|
||||||
{
|
|
||||||
// send increasing data values
|
// send increasing data values
|
||||||
response.add((uint16_t)(mbPV[read_addr + i]));
|
response.add((uint16_t)(mbPV[read_addr + i]));
|
||||||
sprintf(debugString + strlen(debugString), "%i ", mbPV[read_addr + i]);
|
sprintf(debugString + strlen(debugString), "%i ", mbPV[read_addr + i]);
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
#define MBTCP_ID 21 // modbus TCP server ID
|
#define MBTCP_ID 21 // modbus TCP server ID
|
||||||
#define MBPV_MAX 30000
|
#define MBPV_MAX 30000
|
||||||
|
|
||||||
|
|
||||||
ModbusMessage FC03(ModbusMessage request);
|
ModbusMessage FC03(ModbusMessage request);
|
||||||
ModbusMessage FC06(ModbusMessage request);
|
ModbusMessage FC06(ModbusMessage request);
|
||||||
ModbusMessage FC16(ModbusMessage request);
|
ModbusMessage FC16(ModbusMessage request);
|
||||||
|
|
|
@ -11,19 +11,93 @@ static const int interval10s = 10000; // interval (ms) at which send CAN Message
|
||||||
static const int interval60s = 60000; // interval (ms) at which send CAN Messages
|
static const int interval60s = 60000; // interval (ms) at which send CAN Messages
|
||||||
|
|
||||||
//Startup messages
|
//Startup messages
|
||||||
const CAN_frame_t BYD_250 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x250,.data = {0x03, 0x16, 0x00, 0x66, (uint8_t)((BATTERY_WH_MAX/100) >> 8), (uint8_t)(BATTERY_WH_MAX/100), 0x02, 0x09}}; //3.16 FW , Capacity kWh byte4&5 (example 24kWh = 240)
|
const CAN_frame_t BYD_250 = {
|
||||||
const CAN_frame_t BYD_290 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x290,.data = {0x06, 0x37, 0x10, 0xD9, 0x00, 0x00, 0x00, 0x00}};
|
.FIR = {.B =
|
||||||
const CAN_frame_t BYD_2D0 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x2D0,.data = {0x00, 0x42, 0x59, 0x44, 0x00, 0x00, 0x00, 0x00}}; //BYD
|
{
|
||||||
const CAN_frame_t BYD_3D0_0 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x3D0,.data = {0x00, 0x42, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79}}; //Battery
|
.DLC = 8,
|
||||||
const CAN_frame_t BYD_3D0_1 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x3D0,.data = {0x01, 0x2D, 0x42, 0x6F, 0x78, 0x20, 0x50, 0x72}}; //-Box Pr
|
.FF = CAN_frame_std,
|
||||||
const CAN_frame_t BYD_3D0_2 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x3D0,.data = {0x02, 0x65, 0x6D, 0x69, 0x75, 0x6D, 0x20, 0x48}}; //emium H
|
}},
|
||||||
const CAN_frame_t BYD_3D0_3 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x3D0,.data = {0x03, 0x56, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00}}; //VS
|
.MsgID = 0x250,
|
||||||
|
.data = {0x03, 0x16, 0x00, 0x66, (uint8_t)((BATTERY_WH_MAX / 100) >> 8), (uint8_t)(BATTERY_WH_MAX / 100), 0x02,
|
||||||
|
0x09}}; //3.16 FW , Capacity kWh byte4&5 (example 24kWh = 240)
|
||||||
|
const CAN_frame_t BYD_290 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x290,
|
||||||
|
.data = {0x06, 0x37, 0x10, 0xD9, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
const CAN_frame_t BYD_2D0 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x2D0,
|
||||||
|
.data = {0x00, 0x42, 0x59, 0x44, 0x00, 0x00, 0x00, 0x00}}; //BYD
|
||||||
|
const CAN_frame_t BYD_3D0_0 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x3D0,
|
||||||
|
.data = {0x00, 0x42, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79}}; //Battery
|
||||||
|
const CAN_frame_t BYD_3D0_1 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x3D0,
|
||||||
|
.data = {0x01, 0x2D, 0x42, 0x6F, 0x78, 0x20, 0x50, 0x72}}; //-Box Pr
|
||||||
|
const CAN_frame_t BYD_3D0_2 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x3D0,
|
||||||
|
.data = {0x02, 0x65, 0x6D, 0x69, 0x75, 0x6D, 0x20, 0x48}}; //emium H
|
||||||
|
const CAN_frame_t BYD_3D0_3 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x3D0,
|
||||||
|
.data = {0x03, 0x56, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00}}; //VS
|
||||||
//Actual content messages
|
//Actual content messages
|
||||||
CAN_frame_t BYD_110 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x110,.data = {0x01, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
CAN_frame_t BYD_110 = {.FIR = {.B =
|
||||||
CAN_frame_t BYD_150 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x150,.data = {0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00}};
|
{
|
||||||
CAN_frame_t BYD_190 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x190,.data = {0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
.DLC = 8,
|
||||||
CAN_frame_t BYD_1D0 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x1D0,.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x08}};
|
.FF = CAN_frame_std,
|
||||||
CAN_frame_t BYD_210 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x210,.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
}},
|
||||||
|
.MsgID = 0x110,
|
||||||
|
.data = {0x01, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
CAN_frame_t BYD_150 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x150,
|
||||||
|
.data = {0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00}};
|
||||||
|
CAN_frame_t BYD_190 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x190,
|
||||||
|
.data = {0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
CAN_frame_t BYD_1D0 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x1D0,
|
||||||
|
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x08}};
|
||||||
|
CAN_frame_t BYD_210 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x210,
|
||||||
|
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
|
||||||
static int discharge_current = 0;
|
static int discharge_current = 0;
|
||||||
static int charge_current = 0;
|
static int charge_current = 0;
|
||||||
|
@ -34,26 +108,26 @@ static int inverter_voltage = 0;
|
||||||
static int inverter_SOC = 0;
|
static int inverter_SOC = 0;
|
||||||
static long inverter_timestamp = 0;
|
static long inverter_timestamp = 0;
|
||||||
|
|
||||||
void update_values_can_byd()
|
void update_values_can_byd() { //This function maps all the values fetched from battery CAN to the correct CAN messages
|
||||||
{ //This function maps all the values fetched from battery CAN to the correct CAN messages
|
|
||||||
//Calculate values
|
//Calculate values
|
||||||
charge_current = ((max_target_charge_power*10)/max_volt_byd_can); //Charge power in W , max volt in V+1decimal (P=UI, solve for I)
|
charge_current = ((max_target_charge_power * 10) /
|
||||||
|
max_volt_byd_can); //Charge power in W , max volt in V+1decimal (P=UI, solve for I)
|
||||||
//The above calculation results in (30 000*10)/3700=81A
|
//The above calculation results in (30 000*10)/3700=81A
|
||||||
charge_current = (charge_current*10); //Value needs a decimal before getting sent to inverter (81.0A)
|
charge_current = (charge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A)
|
||||||
if(charge_current > MAXCHARGEAMP)
|
if (charge_current > MAXCHARGEAMP) {
|
||||||
{
|
|
||||||
charge_current = MAXCHARGEAMP; //Cap the value to the max allowed Amp. Some inverters cannot handle large values.
|
charge_current = MAXCHARGEAMP; //Cap the value to the max allowed Amp. Some inverters cannot handle large values.
|
||||||
}
|
}
|
||||||
|
|
||||||
discharge_current = ((max_target_discharge_power*10)/max_volt_byd_can); //Charge power in W , max volt in V+1decimal (P=UI, solve for I)
|
discharge_current = ((max_target_discharge_power * 10) /
|
||||||
|
max_volt_byd_can); //Charge power in W , max volt in V+1decimal (P=UI, solve for I)
|
||||||
//The above calculation results in (30 000*10)/3700=81A
|
//The above calculation results in (30 000*10)/3700=81A
|
||||||
discharge_current = (discharge_current*10); //Value needs a decimal before getting sent to inverter (81.0A)
|
discharge_current = (discharge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A)
|
||||||
if(discharge_current > MAXDISCHARGEAMP)
|
if (discharge_current > MAXDISCHARGEAMP) {
|
||||||
{
|
discharge_current =
|
||||||
discharge_current = MAXDISCHARGEAMP; //Cap the value to the max allowed Amp. Some inverters cannot handle large values.
|
MAXDISCHARGEAMP; //Cap the value to the max allowed Amp. Some inverters cannot handle large values.
|
||||||
}
|
}
|
||||||
|
|
||||||
temperature_average = ((temperature_max + temperature_min)/2);
|
temperature_average = ((temperature_max + temperature_min) / 2);
|
||||||
|
|
||||||
//Map values to CAN messages
|
//Map values to CAN messages
|
||||||
//Maxvoltage (eg 400.0V = 4000 , 16bits long)
|
//Maxvoltage (eg 400.0V = 4000 , 16bits long)
|
||||||
|
@ -100,13 +174,10 @@ void update_values_can_byd()
|
||||||
BYD_210.data.u8[3] = (temperature_min & 0x00FF);
|
BYD_210.data.u8[3] = (temperature_min & 0x00FF);
|
||||||
}
|
}
|
||||||
|
|
||||||
void receive_can_byd(CAN_frame_t rx_frame)
|
void receive_can_byd(CAN_frame_t rx_frame) {
|
||||||
{
|
switch (rx_frame.MsgID) {
|
||||||
switch (rx_frame.MsgID)
|
|
||||||
{
|
|
||||||
case 0x151: //Message originating from BYD HVS compatible inverter. Reply with CAN identifier!
|
case 0x151: //Message originating from BYD HVS compatible inverter. Reply with CAN identifier!
|
||||||
if(rx_frame.data.u8[0] & 0x01)
|
if (rx_frame.data.u8[0] & 0x01) {
|
||||||
{
|
|
||||||
send_intial_data();
|
send_intial_data();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -117,33 +188,30 @@ void receive_can_byd(CAN_frame_t rx_frame)
|
||||||
inverter_SOC = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]) * 0.1;
|
inverter_SOC = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]) * 0.1;
|
||||||
break;
|
break;
|
||||||
case 0x111:
|
case 0x111:
|
||||||
inverter_timestamp = ((rx_frame.data.u8[3] << 24) | (rx_frame.data.u8[2] << 16) | (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]);
|
inverter_timestamp = ((rx_frame.data.u8[3] << 24) | (rx_frame.data.u8[2] << 16) | (rx_frame.data.u8[1] << 8) |
|
||||||
|
rx_frame.data.u8[0]);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_can_byd()
|
void send_can_byd() {
|
||||||
{
|
|
||||||
unsigned long currentMillis = millis();
|
unsigned long currentMillis = millis();
|
||||||
// Send initial CAN data once on bootup
|
// Send initial CAN data once on bootup
|
||||||
if (!initialDataSent)
|
if (!initialDataSent) {
|
||||||
{
|
|
||||||
send_intial_data();
|
send_intial_data();
|
||||||
initialDataSent = 1;
|
initialDataSent = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send 2s CAN Message
|
// Send 2s CAN Message
|
||||||
if (currentMillis - previousMillis2s >= interval2s)
|
if (currentMillis - previousMillis2s >= interval2s) {
|
||||||
{
|
|
||||||
previousMillis2s = currentMillis;
|
previousMillis2s = currentMillis;
|
||||||
|
|
||||||
ESP32Can.CANWriteFrame(&BYD_110);
|
ESP32Can.CANWriteFrame(&BYD_110);
|
||||||
}
|
}
|
||||||
// Send 10s CAN Message
|
// Send 10s CAN Message
|
||||||
if (currentMillis - previousMillis10s >= interval10s)
|
if (currentMillis - previousMillis10s >= interval10s) {
|
||||||
{
|
|
||||||
previousMillis10s = currentMillis;
|
previousMillis10s = currentMillis;
|
||||||
|
|
||||||
ESP32Can.CANWriteFrame(&BYD_150);
|
ESP32Can.CANWriteFrame(&BYD_150);
|
||||||
|
@ -152,8 +220,7 @@ void send_can_byd()
|
||||||
//Serial.println("CAN 10s done");
|
//Serial.println("CAN 10s done");
|
||||||
}
|
}
|
||||||
//Send 60s message
|
//Send 60s message
|
||||||
if (currentMillis - previousMillis60s >= interval60s)
|
if (currentMillis - previousMillis60s >= interval60s) {
|
||||||
{
|
|
||||||
previousMillis60s = currentMillis;
|
previousMillis60s = currentMillis;
|
||||||
|
|
||||||
ESP32Can.CANWriteFrame(&BYD_190);
|
ESP32Can.CANWriteFrame(&BYD_190);
|
||||||
|
@ -161,8 +228,7 @@ void send_can_byd()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_intial_data()
|
void send_intial_data() {
|
||||||
{
|
|
||||||
ESP32Can.CANWriteFrame(&BYD_250);
|
ESP32Can.CANWriteFrame(&BYD_250);
|
||||||
ESP32Can.CANWriteFrame(&BYD_290);
|
ESP32Can.CANWriteFrame(&BYD_290);
|
||||||
ESP32Can.CANWriteFrame(&BYD_2D0);
|
ESP32Can.CANWriteFrame(&BYD_2D0);
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
#ifndef BYD_CAN_H
|
#ifndef BYD_CAN_H
|
||||||
#define BYD_CAN_H
|
#define BYD_CAN_H
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "../devboard/can/ESP32CAN.h"
|
|
||||||
#include "../../USER_SETTINGS.h"
|
#include "../../USER_SETTINGS.h"
|
||||||
|
#include "../devboard/can/ESP32CAN.h"
|
||||||
|
|
||||||
extern uint16_t SOC;
|
extern uint16_t SOC;
|
||||||
extern uint16_t StateOfHealth;
|
extern uint16_t StateOfHealth;
|
||||||
|
|
|
@ -8,14 +8,16 @@ void update_modbus_registers_byd() {
|
||||||
|
|
||||||
void handle_static_data_modbus_byd() {
|
void handle_static_data_modbus_byd() {
|
||||||
// Store the data into the array
|
// Store the data into the array
|
||||||
static uint16_t si_data[] = { 21321, 1 };
|
static uint16_t si_data[] = {21321, 1};
|
||||||
static uint16_t byd_data[] = { 16985, 17408, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
static uint16_t byd_data[] = {16985, 17408, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||||
static uint16_t battery_data[] = { 16985, 17440, 16993, 29812, 25970, 31021, 17007, 30752, 20594, 25965, 26997, 27936, 18518, 0, 0, 0 };
|
static uint16_t battery_data[] = {16985, 17440, 16993, 29812, 25970, 31021, 17007, 30752,
|
||||||
static uint16_t volt_data[] = { 13614, 12288, 0, 0, 0, 0, 0, 0, 13102, 12598, 0, 0, 0, 0, 0, 0 };
|
20594, 25965, 26997, 27936, 18518, 0, 0, 0};
|
||||||
static uint16_t serial_data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
static uint16_t volt_data[] = {13614, 12288, 0, 0, 0, 0, 0, 0, 13102, 12598, 0, 0, 0, 0, 0, 0};
|
||||||
static uint16_t static_data[] = { 1, 0 };
|
static uint16_t serial_data[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||||
static uint16_t* data_array_pointers[] = { si_data, byd_data, battery_data, volt_data, serial_data, static_data };
|
static uint16_t static_data[] = {1, 0};
|
||||||
static uint16_t data_sizes[] = { sizeof(si_data), sizeof(byd_data), sizeof(battery_data), sizeof(volt_data), sizeof(serial_data), sizeof(static_data) };
|
static uint16_t* data_array_pointers[] = {si_data, byd_data, battery_data, volt_data, serial_data, static_data};
|
||||||
|
static uint16_t data_sizes[] = {sizeof(si_data), sizeof(byd_data), sizeof(battery_data),
|
||||||
|
sizeof(volt_data), sizeof(serial_data), sizeof(static_data)};
|
||||||
static uint16_t i = 100;
|
static uint16_t i = 100;
|
||||||
for (uint8_t arr_idx = 0; arr_idx < sizeof(data_array_pointers) / sizeof(uint16_t*); arr_idx++) {
|
for (uint8_t arr_idx = 0; arr_idx < sizeof(data_array_pointers) / sizeof(uint16_t*); arr_idx++) {
|
||||||
uint16_t data_size = data_sizes[arr_idx];
|
uint16_t data_size = data_sizes[arr_idx];
|
||||||
|
@ -29,14 +31,20 @@ void handle_update_data_modbusp201_byd() {
|
||||||
static uint16_t system_data[13];
|
static uint16_t system_data[13];
|
||||||
system_data[0] = 0; // Id.: p201 Value.: 0 Scaled value.: 0 Comment.: Always 0
|
system_data[0] = 0; // Id.: p201 Value.: 0 Scaled value.: 0 Comment.: Always 0
|
||||||
system_data[1] = 0; // Id.: p202 Value.: 0 Scaled value.: 0 Comment.: Always 0
|
system_data[1] = 0; // Id.: p202 Value.: 0 Scaled value.: 0 Comment.: Always 0
|
||||||
system_data[2] = (capacity_Wh_startup); // Id.: p203 Value.: 32000 Scaled value.: 32kWh Comment.: Capacity rated, maximum value is 60000 (60kWh)
|
system_data[2] =
|
||||||
|
(capacity_Wh_startup); // Id.: p203 Value.: 32000 Scaled value.: 32kWh Comment.: Capacity rated, maximum value is 60000 (60kWh)
|
||||||
system_data[3] = (max_power); // Id.: p204 Value.: 32000 Scaled value.: 32kWh Comment.: Nominal capacity
|
system_data[3] = (max_power); // Id.: p204 Value.: 32000 Scaled value.: 32kWh Comment.: Nominal capacity
|
||||||
system_data[4] = (max_power); // Id.: p205 Value.: 14500 Scaled value.: 30,42kW Comment.: Max Charge/Discharge Power=10.24kW (lowest value of 204 and 205 will be enforced by Gen24)
|
system_data[4] =
|
||||||
system_data[5] = (max_volt_modbus_byd); // Id.: p206 Value.: 3667 Scaled value.: 362,7VDC Comment.: Max Voltage, if higher charging is not possible (goes into forced discharge)
|
(max_power); // Id.: p205 Value.: 14500 Scaled value.: 30,42kW Comment.: Max Charge/Discharge Power=10.24kW (lowest value of 204 and 205 will be enforced by Gen24)
|
||||||
system_data[6] = (min_volt_modbus_byd); // Id.: p207 Value.: 2776 Scaled value.: 277,6VDC Comment.: Min Voltage, if lower Gen24 disables battery
|
system_data[5] =
|
||||||
system_data[7] = 53248; // Id.: p208 Value.: 53248 Scaled value.: 53248 Comment.: Always 53248 for this BYD, Peak Charge power?
|
(max_volt_modbus_byd); // Id.: p206 Value.: 3667 Scaled value.: 362,7VDC Comment.: Max Voltage, if higher charging is not possible (goes into forced discharge)
|
||||||
|
system_data[6] =
|
||||||
|
(min_volt_modbus_byd); // Id.: p207 Value.: 2776 Scaled value.: 277,6VDC Comment.: Min Voltage, if lower Gen24 disables battery
|
||||||
|
system_data[7] =
|
||||||
|
53248; // Id.: p208 Value.: 53248 Scaled value.: 53248 Comment.: Always 53248 for this BYD, Peak Charge power?
|
||||||
system_data[8] = 10; // Id.: p209 Value.: 10 Scaled value.: 10 Comment.: Always 10
|
system_data[8] = 10; // Id.: p209 Value.: 10 Scaled value.: 10 Comment.: Always 10
|
||||||
system_data[9] = 53248; // Id.: p210 Value.: 53248 Scaled value.: 53248 Comment.: Always 53248 for this BYD, Peak Discharge power?
|
system_data[9] =
|
||||||
|
53248; // Id.: p210 Value.: 53248 Scaled value.: 53248 Comment.: Always 53248 for this BYD, Peak Discharge power?
|
||||||
system_data[10] = 10; // Id.: p211 Value.: 10 Scaled value.: 10 Comment.: Always 10
|
system_data[10] = 10; // Id.: p211 Value.: 10 Scaled value.: 10 Comment.: Always 10
|
||||||
system_data[11] = 0; // Id.: p212 Value.: 0 Scaled value.: 0 Comment.: Always 0
|
system_data[11] = 0; // Id.: p212 Value.: 0 Scaled value.: 0 Comment.: Always 0
|
||||||
system_data[12] = 0; // Id.: p213 Value.: 0 Scaled value.: 0 Comment.: Always 0
|
system_data[12] = 0; // Id.: p213 Value.: 0 Scaled value.: 0 Comment.: Always 0
|
||||||
|
@ -56,32 +64,43 @@ void handle_update_data_modbusp301_byd() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bms_status == ACTIVE) {
|
if (bms_status == ACTIVE) {
|
||||||
battery_data[8] = battery_voltage; // Id.: p309 Value.: 3161 Scaled value.: 316,1VDC Comment.: Batt Voltage outer (0 if status !=3, maybe a contactor closes when active): 173.4V
|
battery_data[8] =
|
||||||
|
battery_voltage; // Id.: p309 Value.: 3161 Scaled value.: 316,1VDC Comment.: Batt Voltage outer (0 if status !=3, maybe a contactor closes when active): 173.4V
|
||||||
} else {
|
} else {
|
||||||
battery_data[8] = 0;
|
battery_data[8] = 0;
|
||||||
}
|
}
|
||||||
battery_data[0] = bms_status; // Id.: p301 Value.: 3 Scaled value.: 3 Comment.: status(*): ACTIVE - [0..5]<>[STANDBY,INACTIVE,DARKSTART,ACTIVE,FAULT,UPDATING]
|
battery_data[0] =
|
||||||
|
bms_status; // Id.: p301 Value.: 3 Scaled value.: 3 Comment.: status(*): ACTIVE - [0..5]<>[STANDBY,INACTIVE,DARKSTART,ACTIVE,FAULT,UPDATING]
|
||||||
battery_data[1] = 0; // Id.: p302 Value.: 0 Scaled value.: 0 Comment.: always 0
|
battery_data[1] = 0; // Id.: p302 Value.: 0 Scaled value.: 0 Comment.: always 0
|
||||||
battery_data[2] = 128 + bms_char_dis_status; // Id.: p303 Value.: 130 Scaled value.: 130 Comment.: mode(*): normal
|
battery_data[2] = 128 + bms_char_dis_status; // Id.: p303 Value.: 130 Scaled value.: 130 Comment.: mode(*): normal
|
||||||
battery_data[3] = SOC; // Id.: p304 Value.: 1700 Scaled value.: 50% Comment.: SOC: (50% would equal 5000)
|
battery_data[3] = SOC; // Id.: p304 Value.: 1700 Scaled value.: 50% Comment.: SOC: (50% would equal 5000)
|
||||||
battery_data[4] = capacity_Wh; // Id.: p305 Value.: 32000 Scaled value.: 32kWh Comment.: tot cap:
|
battery_data[4] = capacity_Wh; // Id.: p305 Value.: 32000 Scaled value.: 32kWh Comment.: tot cap:
|
||||||
battery_data[5] = remaining_capacity_Wh; // Id.: p306 Value.: 13260 Scaled value.: 13,26kWh Comment.: remaining cap: 7.68kWh
|
battery_data[5] =
|
||||||
battery_data[6] = max_target_discharge_power; // Id.: p307 Value.: 25604 Scaled value.: 25,604kW Comment.: max/target discharge power: 0W (0W > restricts to no discharge)
|
remaining_capacity_Wh; // Id.: p306 Value.: 13260 Scaled value.: 13,26kWh Comment.: remaining cap: 7.68kWh
|
||||||
battery_data[7] = max_target_charge_power; // Id.: p308 Value.: 25604 Scaled value.: 25,604kW Comment.: max/target charge power: 4.3kW (during charge), both 307&308 can be set (>0) at the same time
|
battery_data[6] =
|
||||||
|
max_target_discharge_power; // Id.: p307 Value.: 25604 Scaled value.: 25,604kW Comment.: max/target discharge power: 0W (0W > restricts to no discharge)
|
||||||
|
battery_data[7] =
|
||||||
|
max_target_charge_power; // Id.: p308 Value.: 25604 Scaled value.: 25,604kW Comment.: max/target charge power: 4.3kW (during charge), both 307&308 can be set (>0) at the same time
|
||||||
//Battery_data[8] set previously in function // Id.: p309 Value.: 3161 Scaled value.: 316,1VDC Comment.: Batt Voltage outer (0 if status !=3, maybe a contactor closes when active): 173.4V
|
//Battery_data[8] set previously in function // Id.: p309 Value.: 3161 Scaled value.: 316,1VDC Comment.: Batt Voltage outer (0 if status !=3, maybe a contactor closes when active): 173.4V
|
||||||
battery_data[9] = 2000; // Id.: p310 Value.: 64121 Scaled value.: 6412,1W Comment.: Current Power to API: if>32768... -(65535-61760)=3775W
|
battery_data[9] =
|
||||||
battery_data[10] = battery_voltage; // Id.: p311 Value.: 3161 Scaled value.: 316,1VDC Comment.: Batt Voltage inner: 173.2V (LEAF voltage is in whole volts, need to add a decimal)
|
2000; // Id.: p310 Value.: 64121 Scaled value.: 6412,1W Comment.: Current Power to API: if>32768... -(65535-61760)=3775W
|
||||||
|
battery_data[10] =
|
||||||
|
battery_voltage; // Id.: p311 Value.: 3161 Scaled value.: 316,1VDC Comment.: Batt Voltage inner: 173.2V (LEAF voltage is in whole volts, need to add a decimal)
|
||||||
battery_data[11] = 2000; // Id.: p312 Value.: 64121 Scaled value.: 6412,1W Comment.: p310
|
battery_data[11] = 2000; // Id.: p312 Value.: 64121 Scaled value.: 6412,1W Comment.: p310
|
||||||
battery_data[12] = temperature_min; // Id.: p313 Value.: 75 Scaled value.: 7,5 Comment.: temp min: 7 degrees (if below 0....65535-t)
|
battery_data[12] =
|
||||||
battery_data[13] = temperature_max; // Id.: p314 Value.: 95 Scaled value.: 9,5 Comment.: temp max: 9 degrees (if below 0....65535-t)
|
temperature_min; // Id.: p313 Value.: 75 Scaled value.: 7,5 Comment.: temp min: 7 degrees (if below 0....65535-t)
|
||||||
|
battery_data[13] =
|
||||||
|
temperature_max; // Id.: p314 Value.: 95 Scaled value.: 9,5 Comment.: temp max: 9 degrees (if below 0....65535-t)
|
||||||
battery_data[14] = 0; // Id.: p315 Value.: 0 Scaled value.: 0 Comment.: always 0
|
battery_data[14] = 0; // Id.: p315 Value.: 0 Scaled value.: 0 Comment.: always 0
|
||||||
battery_data[15] = 0; // Id.: p316 Value.: 0 Scaled value.: 0 Comment.: always 0
|
battery_data[15] = 0; // Id.: p316 Value.: 0 Scaled value.: 0 Comment.: always 0
|
||||||
battery_data[16] = 16; // Id.: p317 Value.: 0 Scaled value.: 0 Comment.: counter charge hi
|
battery_data[16] = 16; // Id.: p317 Value.: 0 Scaled value.: 0 Comment.: counter charge hi
|
||||||
battery_data[17] = 22741; // Id.: p318 Value.: 0 Scaled value.: 0 Comment.: counter charge lo....65536*101+9912 = 6629048 Wh?
|
battery_data[17] =
|
||||||
|
22741; // Id.: p318 Value.: 0 Scaled value.: 0 Comment.: counter charge lo....65536*101+9912 = 6629048 Wh?
|
||||||
battery_data[18] = 0; // Id.: p319 Value.: 0 Scaled value.: 0 Comment.: always 0
|
battery_data[18] = 0; // Id.: p319 Value.: 0 Scaled value.: 0 Comment.: always 0
|
||||||
battery_data[19] = 0; // Id.: p320 Value.: 0 Scaled value.: 0 Comment.: always 0
|
battery_data[19] = 0; // Id.: p320 Value.: 0 Scaled value.: 0 Comment.: always 0
|
||||||
battery_data[20] = 13; // Id.: p321 Value.: 0 Scaled value.: 0 Comment.: counter discharge hi
|
battery_data[20] = 13; // Id.: p321 Value.: 0 Scaled value.: 0 Comment.: counter discharge hi
|
||||||
battery_data[21] = 52064; // Id.: p322 Value.: 0 Scaled value.: 0 Comment.: counter discharge lo....65536*92+7448 = 6036760 Wh?
|
battery_data[21] =
|
||||||
|
52064; // Id.: p322 Value.: 0 Scaled value.: 0 Comment.: counter discharge lo....65536*92+7448 = 6036760 Wh?
|
||||||
battery_data[22] = 230; // Id.: p323 Value.: 0 Scaled value.: 0 Comment.: device temperature (23 degrees)
|
battery_data[22] = 230; // Id.: p323 Value.: 0 Scaled value.: 0 Comment.: device temperature (23 degrees)
|
||||||
battery_data[23] = StateOfHealth; // Id.: p324 Value.: 9900 Scaled value.: 99% Comment.: SOH
|
battery_data[23] = StateOfHealth; // Id.: p324 Value.: 9900 Scaled value.: 99% Comment.: SOH
|
||||||
static uint16_t i = 300;
|
static uint16_t i = 300;
|
||||||
|
|
|
@ -2,31 +2,31 @@
|
||||||
#define INVERTERS_H
|
#define INVERTERS_H
|
||||||
|
|
||||||
#ifdef BYD_CAN
|
#ifdef BYD_CAN
|
||||||
#include "BYD-CAN.h"
|
#include "BYD-CAN.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef BYD_MODBUS
|
#ifdef BYD_MODBUS
|
||||||
#include "BYD-MODBUS.h"
|
#include "BYD-MODBUS.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef LUNA2000_MODBUS
|
#ifdef LUNA2000_MODBUS
|
||||||
#include "LUNA2000-MODBUS.h"
|
#include "LUNA2000-MODBUS.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef PYLON_CAN
|
#ifdef PYLON_CAN
|
||||||
#include "PYLON-CAN.h"
|
#include "PYLON-CAN.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SMA_CAN
|
#ifdef SMA_CAN
|
||||||
#include "SMA-CAN.h"
|
#include "SMA-CAN.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SOFAR_CAN
|
#ifdef SOFAR_CAN
|
||||||
#include "SOFAR-CAN.h"
|
#include "SOFAR-CAN.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SOLAX_CAN
|
#ifdef SOLAX_CAN
|
||||||
#include "SOLAX-CAN.h"
|
#include "SOLAX-CAN.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -1,7 +1,6 @@
|
||||||
#include "LUNA2000-MODBUS.h"
|
#include "LUNA2000-MODBUS.h"
|
||||||
|
|
||||||
void update_modbus_registers_luna2000()
|
void update_modbus_registers_luna2000() {
|
||||||
{
|
|
||||||
//Updata for ModbusRTU Server for Luna2000
|
//Updata for ModbusRTU Server for Luna2000
|
||||||
handle_update_data_modbus32051();
|
handle_update_data_modbus32051();
|
||||||
handle_update_data_modbus39500();
|
handle_update_data_modbus39500();
|
||||||
|
@ -17,7 +16,7 @@ void handle_update_data_modbus32051() {
|
||||||
system_data[4] = 616; //Goes between 616- -520 [NOTE, SIGNED VALUE]
|
system_data[4] = 616; //Goes between 616- -520 [NOTE, SIGNED VALUE]
|
||||||
system_data[5] = temperature_max; //Temperature max?
|
system_data[5] = temperature_max; //Temperature max?
|
||||||
system_data[6] = temperature_min; //Temperature min?
|
system_data[6] = temperature_min; //Temperature min?
|
||||||
system_data[7] = (SOC/100); //SOC 0-100%, no decimals
|
system_data[7] = (SOC / 100); //SOC 0-100%, no decimals
|
||||||
system_data[8] = 98; //Always 98 in logs
|
system_data[8] = 98; //Always 98 in logs
|
||||||
static uint16_t i = 2051;
|
static uint16_t i = 2051;
|
||||||
memcpy(&mbPV[i], system_data, sizeof(system_data));
|
memcpy(&mbPV[i], system_data, sizeof(system_data));
|
||||||
|
@ -34,8 +33,9 @@ void handle_update_data_modbus39500() {
|
||||||
system_data[5] = 2500; //???
|
system_data[5] = 2500; //???
|
||||||
system_data[6] = 0;
|
system_data[6] = 0;
|
||||||
system_data[7] = 2500; //???
|
system_data[7] = 2500; //???
|
||||||
system_data[8] = (SOC/100); //SOC 0-100%, no decimals
|
system_data[8] = (SOC / 100); //SOC 0-100%, no decimals
|
||||||
system_data[9] = 1; //Running status, equiv to register 37762, 0 = Offline, 1 = Standby,2 = Running, 3 = Fault, 4 = sleep mode
|
system_data[9] =
|
||||||
|
1; //Running status, equiv to register 37762, 0 = Offline, 1 = Standby,2 = Running, 3 = Fault, 4 = sleep mode
|
||||||
system_data[10] = battery_voltage; //Battery bus voltage (766.5V = 7665)
|
system_data[10] = battery_voltage; //Battery bus voltage (766.5V = 7665)
|
||||||
system_data[11] = 9; //TODO, GOES LOWER WITH LOW SOC
|
system_data[11] = 9; //TODO, GOES LOWER WITH LOW SOC
|
||||||
system_data[12] = 0;
|
system_data[12] = 0;
|
||||||
|
@ -50,7 +50,7 @@ void handle_update_data_modbus39500() {
|
||||||
system_data[21] = 0;
|
system_data[21] = 0;
|
||||||
system_data[22] = 0;
|
system_data[22] = 0;
|
||||||
system_data[23] = 0;
|
system_data[23] = 0;
|
||||||
system_data[24] = (SOC/10); //SOC 0-100.0%, 1x decimal
|
system_data[24] = (SOC / 10); //SOC 0-100.0%, 1x decimal
|
||||||
system_data[25] = 0;
|
system_data[25] = 0;
|
||||||
system_data[26] = 1; //Always 1 in logs
|
system_data[26] = 1; //Always 1 in logs
|
||||||
static uint16_t i = 9500;
|
static uint16_t i = 9500;
|
||||||
|
|
|
@ -7,35 +7,165 @@
|
||||||
|
|
||||||
/* Do not change code below unless you are sure what you are doing */
|
/* Do not change code below unless you are sure what you are doing */
|
||||||
//Actual content messages
|
//Actual content messages
|
||||||
CAN_frame_t PYLON_7310 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x7310,.data = {0x01, 0x00, 0x02, 0x01, 0x01, 0x02, 0x00, 0x00}};
|
CAN_frame_t PYLON_7310 = {.FIR = {.B =
|
||||||
CAN_frame_t PYLON_7320 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x7320,.data = {0x4B, 0x00, 0x05, 0x0F, 0x2D, 0x00, 0x56, 0x00}};
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x7310,
|
||||||
|
.data = {0x01, 0x00, 0x02, 0x01, 0x01, 0x02, 0x00, 0x00}};
|
||||||
|
CAN_frame_t PYLON_7320 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x7320,
|
||||||
|
.data = {0x4B, 0x00, 0x05, 0x0F, 0x2D, 0x00, 0x56, 0x00}};
|
||||||
|
|
||||||
CAN_frame_t PYLON_4210 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x4210,.data = {0xA5, 0x09, 0x30, 0x75, 0x9D, 0x04, 0x2E, 0x64}};
|
CAN_frame_t PYLON_4210 = {.FIR = {.B =
|
||||||
CAN_frame_t PYLON_4220 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x4220,.data = {0x8C, 0x0A, 0xE9, 0x07, 0x4A, 0x79, 0x4A, 0x79}};
|
{
|
||||||
CAN_frame_t PYLON_4230 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x4230,.data = {0xDF, 0x0C, 0xDA, 0x0C, 0x03, 0x00, 0x06, 0x00}};
|
.DLC = 8,
|
||||||
CAN_frame_t PYLON_4240 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x4240,.data = {0x7E, 0x04, 0x62, 0x04, 0x11, 0x00, 0x03, 0x00}};
|
.FF = CAN_frame_ext,
|
||||||
CAN_frame_t PYLON_4250 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x4250,.data = {0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
}},
|
||||||
CAN_frame_t PYLON_4260 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x4260,.data = {0xAC, 0xC7, 0x74, 0x27, 0x03, 0x00, 0x02, 0x00}};
|
.MsgID = 0x4210,
|
||||||
CAN_frame_t PYLON_4270 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x4270,.data = {0x7E, 0x04, 0x62, 0x04, 0x05, 0x00, 0x01, 0x00}};
|
.data = {0xA5, 0x09, 0x30, 0x75, 0x9D, 0x04, 0x2E, 0x64}};
|
||||||
CAN_frame_t PYLON_4280 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x4280,.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
CAN_frame_t PYLON_4220 = {.FIR = {.B =
|
||||||
CAN_frame_t PYLON_4290 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x4290,.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x4220,
|
||||||
|
.data = {0x8C, 0x0A, 0xE9, 0x07, 0x4A, 0x79, 0x4A, 0x79}};
|
||||||
|
CAN_frame_t PYLON_4230 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x4230,
|
||||||
|
.data = {0xDF, 0x0C, 0xDA, 0x0C, 0x03, 0x00, 0x06, 0x00}};
|
||||||
|
CAN_frame_t PYLON_4240 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x4240,
|
||||||
|
.data = {0x7E, 0x04, 0x62, 0x04, 0x11, 0x00, 0x03, 0x00}};
|
||||||
|
CAN_frame_t PYLON_4250 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x4250,
|
||||||
|
.data = {0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
CAN_frame_t PYLON_4260 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x4260,
|
||||||
|
.data = {0xAC, 0xC7, 0x74, 0x27, 0x03, 0x00, 0x02, 0x00}};
|
||||||
|
CAN_frame_t PYLON_4270 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x4270,
|
||||||
|
.data = {0x7E, 0x04, 0x62, 0x04, 0x05, 0x00, 0x01, 0x00}};
|
||||||
|
CAN_frame_t PYLON_4280 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x4280,
|
||||||
|
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
CAN_frame_t PYLON_4290 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x4290,
|
||||||
|
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
|
||||||
CAN_frame_t PYLON_7311 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x7311,.data = {0x01, 0x00, 0x02, 0x01, 0x01, 0x02, 0x00, 0x00}};
|
CAN_frame_t PYLON_7311 = {.FIR = {.B =
|
||||||
CAN_frame_t PYLON_7321 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x7321,.data = {0x4B, 0x00, 0x05, 0x0F, 0x2D, 0x00, 0x56, 0x00}};
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x7311,
|
||||||
|
.data = {0x01, 0x00, 0x02, 0x01, 0x01, 0x02, 0x00, 0x00}};
|
||||||
|
CAN_frame_t PYLON_7321 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x7321,
|
||||||
|
.data = {0x4B, 0x00, 0x05, 0x0F, 0x2D, 0x00, 0x56, 0x00}};
|
||||||
|
|
||||||
CAN_frame_t PYLON_4211 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x4211,.data = {0xA5, 0x09, 0x30, 0x75, 0x9D, 0x04, 0x2E, 0x64}};
|
CAN_frame_t PYLON_4211 = {.FIR = {.B =
|
||||||
CAN_frame_t PYLON_4221 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x4221,.data = {0x8C, 0x0A, 0xE9, 0x07, 0x4A, 0x79, 0x4A, 0x79}};
|
{
|
||||||
CAN_frame_t PYLON_4231 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x4231,.data = {0xDF, 0x0C, 0xDA, 0x0C, 0x03, 0x00, 0x06, 0x00}};
|
.DLC = 8,
|
||||||
CAN_frame_t PYLON_4241 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x4241,.data = {0x7E, 0x04, 0x62, 0x04, 0x11, 0x00, 0x03, 0x00}};
|
.FF = CAN_frame_ext,
|
||||||
CAN_frame_t PYLON_4251 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x4251,.data = {0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
}},
|
||||||
CAN_frame_t PYLON_4261 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x4261,.data = {0xAC, 0xC7, 0x74, 0x27, 0x03, 0x00, 0x02, 0x00}};
|
.MsgID = 0x4211,
|
||||||
CAN_frame_t PYLON_4271 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x4271,.data = {0x7E, 0x04, 0x62, 0x04, 0x05, 0x00, 0x01, 0x00}};
|
.data = {0xA5, 0x09, 0x30, 0x75, 0x9D, 0x04, 0x2E, 0x64}};
|
||||||
CAN_frame_t PYLON_4281 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x4281,.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
CAN_frame_t PYLON_4221 = {.FIR = {.B =
|
||||||
CAN_frame_t PYLON_4291 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x4291,.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x4221,
|
||||||
|
.data = {0x8C, 0x0A, 0xE9, 0x07, 0x4A, 0x79, 0x4A, 0x79}};
|
||||||
|
CAN_frame_t PYLON_4231 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x4231,
|
||||||
|
.data = {0xDF, 0x0C, 0xDA, 0x0C, 0x03, 0x00, 0x06, 0x00}};
|
||||||
|
CAN_frame_t PYLON_4241 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x4241,
|
||||||
|
.data = {0x7E, 0x04, 0x62, 0x04, 0x11, 0x00, 0x03, 0x00}};
|
||||||
|
CAN_frame_t PYLON_4251 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x4251,
|
||||||
|
.data = {0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
CAN_frame_t PYLON_4261 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x4261,
|
||||||
|
.data = {0xAC, 0xC7, 0x74, 0x27, 0x03, 0x00, 0x02, 0x00}};
|
||||||
|
CAN_frame_t PYLON_4271 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x4271,
|
||||||
|
.data = {0x7E, 0x04, 0x62, 0x04, 0x05, 0x00, 0x01, 0x00}};
|
||||||
|
CAN_frame_t PYLON_4281 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x4281,
|
||||||
|
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
CAN_frame_t PYLON_4291 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x4291,
|
||||||
|
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
|
||||||
|
void update_values_can_pylon() { //This function maps all the values fetched from battery CAN to the correct CAN messages
|
||||||
void update_values_can_pylon()
|
|
||||||
{ //This function maps all the values fetched from battery CAN to the correct CAN messages
|
|
||||||
//There are more mappings that could be added, but this should be enough to use as a starting point
|
//There are more mappings that could be added, but this should be enough to use as a starting point
|
||||||
// Note we map both 0 and 1 messages
|
// Note we map both 0 and 1 messages
|
||||||
|
|
||||||
|
@ -62,12 +192,12 @@ void update_values_can_pylon()
|
||||||
PYLON_4211.data.u8[3] = (battery_current & 0x00FF);
|
PYLON_4211.data.u8[3] = (battery_current & 0x00FF);
|
||||||
|
|
||||||
//SOC (100.00%)
|
//SOC (100.00%)
|
||||||
PYLON_4210.data.u8[6] = (SOC*0.01); //Remove decimals
|
PYLON_4210.data.u8[6] = (SOC * 0.01); //Remove decimals
|
||||||
PYLON_4211.data.u8[6] = (SOC*0.01); //Remove decimals
|
PYLON_4211.data.u8[6] = (SOC * 0.01); //Remove decimals
|
||||||
|
|
||||||
//StateOfHealth (100.00%)
|
//StateOfHealth (100.00%)
|
||||||
PYLON_4210.data.u8[7] = (StateOfHealth*0.01);
|
PYLON_4210.data.u8[7] = (StateOfHealth * 0.01);
|
||||||
PYLON_4211.data.u8[7] = (StateOfHealth*0.01);
|
PYLON_4211.data.u8[7] = (StateOfHealth * 0.01);
|
||||||
|
|
||||||
//Minvoltage (eg 300.0V = 3000 , 16bits long) Charge Cutoff Voltage
|
//Minvoltage (eg 300.0V = 3000 , 16bits long) Charge Cutoff Voltage
|
||||||
PYLON_4220.data.u8[0] = (min_volt_pylon_can >> 8);
|
PYLON_4220.data.u8[0] = (min_volt_pylon_can >> 8);
|
||||||
|
@ -82,8 +212,7 @@ void update_values_can_pylon()
|
||||||
PYLON_4221.data.u8[3] = (max_volt_pylon_can & 0x00FF);
|
PYLON_4221.data.u8[3] = (max_volt_pylon_can & 0x00FF);
|
||||||
|
|
||||||
//In case we run into any errors/faults, we can set charge / discharge forbidden
|
//In case we run into any errors/faults, we can set charge / discharge forbidden
|
||||||
if(bms_status == FAULT)
|
if (bms_status == FAULT) {
|
||||||
{
|
|
||||||
PYLON_4280.data.u8[0] = 0xAA;
|
PYLON_4280.data.u8[0] = 0xAA;
|
||||||
PYLON_4280.data.u8[1] = 0xAA;
|
PYLON_4280.data.u8[1] = 0xAA;
|
||||||
PYLON_4280.data.u8[2] = 0xAA;
|
PYLON_4280.data.u8[2] = 0xAA;
|
||||||
|
@ -93,20 +222,15 @@ void update_values_can_pylon()
|
||||||
PYLON_4281.data.u8[2] = 0xAA;
|
PYLON_4281.data.u8[2] = 0xAA;
|
||||||
PYLON_4281.data.u8[3] = 0xAA;
|
PYLON_4281.data.u8[3] = 0xAA;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void receive_can_pylon(CAN_frame_t rx_frame)
|
void receive_can_pylon(CAN_frame_t rx_frame) {
|
||||||
{
|
switch (rx_frame.MsgID) {
|
||||||
switch (rx_frame.MsgID)
|
|
||||||
{
|
|
||||||
case 0x4200: //Message originating from inverter. Depending on which data is required, act accordingly
|
case 0x4200: //Message originating from inverter. Depending on which data is required, act accordingly
|
||||||
if(rx_frame.data.u8[0] == 0x02)
|
if (rx_frame.data.u8[0] == 0x02) {
|
||||||
{
|
|
||||||
send_setup_info();
|
send_setup_info();
|
||||||
}
|
}
|
||||||
if(rx_frame.data.u8[0] == 0x00)
|
if (rx_frame.data.u8[0] == 0x00) {
|
||||||
{
|
|
||||||
send_system_data();
|
send_system_data();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -115,21 +239,19 @@ void receive_can_pylon(CAN_frame_t rx_frame)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_setup_info()
|
void send_setup_info() { //Ensemble information
|
||||||
{ //Ensemble information
|
#ifdef SEND_0
|
||||||
#ifdef SEND_0
|
|
||||||
ESP32Can.CANWriteFrame(&PYLON_7310);
|
ESP32Can.CANWriteFrame(&PYLON_7310);
|
||||||
ESP32Can.CANWriteFrame(&PYLON_7320);
|
ESP32Can.CANWriteFrame(&PYLON_7320);
|
||||||
#endif
|
#endif
|
||||||
#ifdef SEND_1
|
#ifdef SEND_1
|
||||||
ESP32Can.CANWriteFrame(&PYLON_7311);
|
ESP32Can.CANWriteFrame(&PYLON_7311);
|
||||||
ESP32Can.CANWriteFrame(&PYLON_7321);
|
ESP32Can.CANWriteFrame(&PYLON_7321);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_system_data()
|
void send_system_data() { //System equipment information
|
||||||
{ //System equipment information
|
#ifdef SEND_0
|
||||||
#ifdef SEND_0
|
|
||||||
ESP32Can.CANWriteFrame(&PYLON_4210);
|
ESP32Can.CANWriteFrame(&PYLON_4210);
|
||||||
ESP32Can.CANWriteFrame(&PYLON_4220);
|
ESP32Can.CANWriteFrame(&PYLON_4220);
|
||||||
ESP32Can.CANWriteFrame(&PYLON_4230);
|
ESP32Can.CANWriteFrame(&PYLON_4230);
|
||||||
|
@ -139,8 +261,8 @@ void send_system_data()
|
||||||
ESP32Can.CANWriteFrame(&PYLON_4270);
|
ESP32Can.CANWriteFrame(&PYLON_4270);
|
||||||
ESP32Can.CANWriteFrame(&PYLON_4280);
|
ESP32Can.CANWriteFrame(&PYLON_4280);
|
||||||
ESP32Can.CANWriteFrame(&PYLON_4290);
|
ESP32Can.CANWriteFrame(&PYLON_4290);
|
||||||
#endif
|
#endif
|
||||||
#ifdef SEND_1
|
#ifdef SEND_1
|
||||||
ESP32Can.CANWriteFrame(&PYLON_4211);
|
ESP32Can.CANWriteFrame(&PYLON_4211);
|
||||||
ESP32Can.CANWriteFrame(&PYLON_4221);
|
ESP32Can.CANWriteFrame(&PYLON_4221);
|
||||||
ESP32Can.CANWriteFrame(&PYLON_4231);
|
ESP32Can.CANWriteFrame(&PYLON_4231);
|
||||||
|
@ -150,5 +272,5 @@ void send_system_data()
|
||||||
ESP32Can.CANWriteFrame(&PYLON_4271);
|
ESP32Can.CANWriteFrame(&PYLON_4271);
|
||||||
ESP32Can.CANWriteFrame(&PYLON_4281);
|
ESP32Can.CANWriteFrame(&PYLON_4281);
|
||||||
ESP32Can.CANWriteFrame(&PYLON_4291);
|
ESP32Can.CANWriteFrame(&PYLON_4291);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
#ifndef PYLON_CAN_H
|
#ifndef PYLON_CAN_H
|
||||||
#define PYLON_CAN_H
|
#define PYLON_CAN_H
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "../devboard/can/ESP32CAN.h"
|
|
||||||
#include "../../USER_SETTINGS.h"
|
#include "../../USER_SETTINGS.h"
|
||||||
|
#include "../devboard/can/ESP32CAN.h"
|
||||||
|
|
||||||
extern uint16_t SOC;
|
extern uint16_t SOC;
|
||||||
extern uint16_t StateOfHealth;
|
extern uint16_t StateOfHealth;
|
||||||
|
|
|
@ -31,38 +31,114 @@ static const int interval11s = 120; // interval (ms) at which send CAN Messages
|
||||||
static const int interval12s = 122; // interval (ms) at which send CAN Messages
|
static const int interval12s = 122; // interval (ms) at which send CAN Messages
|
||||||
|
|
||||||
//Actual content messages
|
//Actual content messages
|
||||||
static const CAN_frame_t SMA_558 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x558,.data = {0x03, 0x12, 0x00, 0x04, 0x00, 0x59, 0x07, 0x07}}; //7x BYD modules, Vendor ID 7 BYD
|
static const CAN_frame_t SMA_558 = {
|
||||||
static const CAN_frame_t SMA_598 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x598,.data = {0x00, 0x00, 0x12, 0x34, 0x5A, 0xDE, 0x07, 0x4F}}; //B0-4 Serial, rest unknown
|
.FIR = {.B =
|
||||||
static const CAN_frame_t SMA_5D8 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x5D8,.data = {0x00, 0x42, 0x59, 0x44, 0x00, 0x00, 0x00, 0x00}}; //B Y D
|
{
|
||||||
static const CAN_frame_t SMA_618_1 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x618,.data = {0x00, 0x42, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79}}; //0 B A T T E R Y
|
.DLC = 8,
|
||||||
static const CAN_frame_t SMA_618_2 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x618,.data = {0x01, 0x2D, 0x42, 0x6F, 0x78, 0x20, 0x48, 0x39}}; //1 - B O X H
|
.FF = CAN_frame_std,
|
||||||
static const CAN_frame_t SMA_618_3 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x618,.data = {0x02, 0x2E, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00}}; //2 - 0
|
}},
|
||||||
CAN_frame_t SMA_358 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x358,.data = {0x0F, 0x6C, 0x06, 0x20, 0x00, 0x00, 0x00, 0x00}};
|
.MsgID = 0x558,
|
||||||
CAN_frame_t SMA_3D8 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x3D8,.data = {0x04, 0x10, 0x27, 0x10, 0x00, 0x18, 0xF9, 0x00}};
|
.data = {0x03, 0x12, 0x00, 0x04, 0x00, 0x59, 0x07, 0x07}}; //7x BYD modules, Vendor ID 7 BYD
|
||||||
CAN_frame_t SMA_458 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x458,.data = {0x00, 0x00, 0x06, 0x75, 0x00, 0x00, 0x05, 0xD6}};
|
static const CAN_frame_t SMA_598 = {
|
||||||
CAN_frame_t SMA_518 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x518,.data = {0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF}};
|
.FIR = {.B =
|
||||||
CAN_frame_t SMA_4D8 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x4D8,.data = {0x09, 0xFD, 0x00, 0x00, 0x00, 0xA8, 0x02, 0x08}};
|
{
|
||||||
CAN_frame_t SMA_158 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x158,.data = {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x6A, 0xAA, 0xAA}};
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x598,
|
||||||
|
.data = {0x00, 0x00, 0x12, 0x34, 0x5A, 0xDE, 0x07, 0x4F}}; //B0-4 Serial, rest unknown
|
||||||
|
static const CAN_frame_t SMA_5D8 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x5D8,
|
||||||
|
.data = {0x00, 0x42, 0x59, 0x44, 0x00, 0x00, 0x00, 0x00}}; //B Y D
|
||||||
|
static const CAN_frame_t SMA_618_1 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x618,
|
||||||
|
.data = {0x00, 0x42, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79}}; //0 B A T T E R Y
|
||||||
|
static const CAN_frame_t SMA_618_2 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x618,
|
||||||
|
.data = {0x01, 0x2D, 0x42, 0x6F, 0x78, 0x20, 0x48, 0x39}}; //1 - B O X H
|
||||||
|
static const CAN_frame_t SMA_618_3 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x618,
|
||||||
|
.data = {0x02, 0x2E, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00}}; //2 - 0
|
||||||
|
CAN_frame_t SMA_358 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x358,
|
||||||
|
.data = {0x0F, 0x6C, 0x06, 0x20, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
CAN_frame_t SMA_3D8 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x3D8,
|
||||||
|
.data = {0x04, 0x10, 0x27, 0x10, 0x00, 0x18, 0xF9, 0x00}};
|
||||||
|
CAN_frame_t SMA_458 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x458,
|
||||||
|
.data = {0x00, 0x00, 0x06, 0x75, 0x00, 0x00, 0x05, 0xD6}};
|
||||||
|
CAN_frame_t SMA_518 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x518,
|
||||||
|
.data = {0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF}};
|
||||||
|
CAN_frame_t SMA_4D8 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x4D8,
|
||||||
|
.data = {0x09, 0xFD, 0x00, 0x00, 0x00, 0xA8, 0x02, 0x08}};
|
||||||
|
CAN_frame_t SMA_158 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_std,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x158,
|
||||||
|
.data = {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x6A, 0xAA, 0xAA}};
|
||||||
|
|
||||||
static int discharge_current = 0;
|
static int discharge_current = 0;
|
||||||
static int charge_current = 0;
|
static int charge_current = 0;
|
||||||
static int temperature_average = 0;
|
static int temperature_average = 0;
|
||||||
static int ampere_hours_remaining = 0;
|
static int ampere_hours_remaining = 0;
|
||||||
|
|
||||||
void update_values_can_sma()
|
void update_values_can_sma() { //This function maps all the values fetched from battery CAN to the correct CAN messages
|
||||||
{ //This function maps all the values fetched from battery CAN to the correct CAN messages
|
|
||||||
//Calculate values
|
//Calculate values
|
||||||
charge_current = ((max_target_charge_power*10)/max_volt_sma_can); //Charge power in W , max volt in V+1decimal (P=UI, solve for I)
|
charge_current = ((max_target_charge_power * 10) /
|
||||||
|
max_volt_sma_can); //Charge power in W , max volt in V+1decimal (P=UI, solve for I)
|
||||||
//The above calculation results in (30 000*10)/3700=81A
|
//The above calculation results in (30 000*10)/3700=81A
|
||||||
charge_current = (charge_current*10); //Value needs a decimal before getting sent to inverter (81.0A)
|
charge_current = (charge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A)
|
||||||
|
|
||||||
discharge_current = ((max_target_discharge_power*10)/max_volt_sma_can); //Charge power in W , max volt in V+1decimal (P=UI, solve for I)
|
discharge_current = ((max_target_discharge_power * 10) /
|
||||||
|
max_volt_sma_can); //Charge power in W , max volt in V+1decimal (P=UI, solve for I)
|
||||||
//The above calculation results in (30 000*10)/3700=81A
|
//The above calculation results in (30 000*10)/3700=81A
|
||||||
discharge_current = (discharge_current*10); //Value needs a decimal before getting sent to inverter (81.0A)
|
discharge_current = (discharge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A)
|
||||||
|
|
||||||
temperature_average = ((temperature_max + temperature_min)/2);
|
temperature_average = ((temperature_max + temperature_min) / 2);
|
||||||
|
|
||||||
ampere_hours_remaining = ((remaining_capacity_Wh/battery_voltage)*100); //(WH[10000] * V+1[3600])*100 = 270 (27.0Ah)
|
ampere_hours_remaining =
|
||||||
|
((remaining_capacity_Wh / battery_voltage) * 100); //(WH[10000] * V+1[3600])*100 = 270 (27.0Ah)
|
||||||
|
|
||||||
//Map values to CAN messages
|
//Map values to CAN messages
|
||||||
//Maxvoltage (eg 400.0V = 4000 , 16bits long)
|
//Maxvoltage (eg 400.0V = 4000 , 16bits long)
|
||||||
|
@ -103,10 +179,8 @@ void update_values_can_sma()
|
||||||
//TODO, add all error bits
|
//TODO, add all error bits
|
||||||
}
|
}
|
||||||
|
|
||||||
void receive_can_sma(CAN_frame_t rx_frame)
|
void receive_can_sma(CAN_frame_t rx_frame) {
|
||||||
{
|
switch (rx_frame.MsgID) {
|
||||||
switch (rx_frame.MsgID)
|
|
||||||
{
|
|
||||||
case 0x660: //Message originating from SMA inverter
|
case 0x660: //Message originating from SMA inverter
|
||||||
break;
|
break;
|
||||||
case 0x5E0: //Message originating from SMA inverter
|
case 0x5E0: //Message originating from SMA inverter
|
||||||
|
@ -118,82 +192,68 @@ void receive_can_sma(CAN_frame_t rx_frame)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_can_sma()
|
void send_can_sma() {
|
||||||
{
|
|
||||||
unsigned long currentMillis = millis();
|
unsigned long currentMillis = millis();
|
||||||
|
|
||||||
// Send CAN Message every X ms, 1000 for testing
|
// Send CAN Message every X ms, 1000 for testing
|
||||||
if (currentMillis - previousMillis1s >= interval1s)
|
if (currentMillis - previousMillis1s >= interval1s) {
|
||||||
{
|
|
||||||
previousMillis1s = currentMillis;
|
previousMillis1s = currentMillis;
|
||||||
|
|
||||||
ESP32Can.CANWriteFrame(&SMA_558);
|
ESP32Can.CANWriteFrame(&SMA_558);
|
||||||
}
|
}
|
||||||
if (currentMillis - previousMillis2s >= interval2s)
|
if (currentMillis - previousMillis2s >= interval2s) {
|
||||||
{
|
|
||||||
previousMillis2s = currentMillis;
|
previousMillis2s = currentMillis;
|
||||||
|
|
||||||
ESP32Can.CANWriteFrame(&SMA_598);
|
ESP32Can.CANWriteFrame(&SMA_598);
|
||||||
}
|
}
|
||||||
if (currentMillis - previousMillis3s >= interval3s)
|
if (currentMillis - previousMillis3s >= interval3s) {
|
||||||
{
|
|
||||||
previousMillis3s = currentMillis;
|
previousMillis3s = currentMillis;
|
||||||
|
|
||||||
ESP32Can.CANWriteFrame(&SMA_5D8);
|
ESP32Can.CANWriteFrame(&SMA_5D8);
|
||||||
}
|
}
|
||||||
if (currentMillis - previousMillis4s >= interval4s)
|
if (currentMillis - previousMillis4s >= interval4s) {
|
||||||
{
|
|
||||||
previousMillis4s = currentMillis;
|
previousMillis4s = currentMillis;
|
||||||
|
|
||||||
ESP32Can.CANWriteFrame(&SMA_618_1);
|
ESP32Can.CANWriteFrame(&SMA_618_1);
|
||||||
}
|
}
|
||||||
if (currentMillis - previousMillis5s >= interval5s)
|
if (currentMillis - previousMillis5s >= interval5s) {
|
||||||
{
|
|
||||||
previousMillis5s = currentMillis;
|
previousMillis5s = currentMillis;
|
||||||
|
|
||||||
ESP32Can.CANWriteFrame(&SMA_618_2);
|
ESP32Can.CANWriteFrame(&SMA_618_2);
|
||||||
}
|
}
|
||||||
if (currentMillis - previousMillis6s >= interval6s)
|
if (currentMillis - previousMillis6s >= interval6s) {
|
||||||
{
|
|
||||||
previousMillis6s = currentMillis;
|
previousMillis6s = currentMillis;
|
||||||
|
|
||||||
ESP32Can.CANWriteFrame(&SMA_618_3);
|
ESP32Can.CANWriteFrame(&SMA_618_3);
|
||||||
}
|
}
|
||||||
if (currentMillis - previousMillis7s >= interval7s)
|
if (currentMillis - previousMillis7s >= interval7s) {
|
||||||
{
|
|
||||||
previousMillis7s = currentMillis;
|
previousMillis7s = currentMillis;
|
||||||
|
|
||||||
ESP32Can.CANWriteFrame(&SMA_358);
|
ESP32Can.CANWriteFrame(&SMA_358);
|
||||||
}
|
}
|
||||||
if (currentMillis - previousMillis8s >= interval8s)
|
if (currentMillis - previousMillis8s >= interval8s) {
|
||||||
{
|
|
||||||
previousMillis8s = currentMillis;
|
previousMillis8s = currentMillis;
|
||||||
|
|
||||||
ESP32Can.CANWriteFrame(&SMA_3D8);
|
ESP32Can.CANWriteFrame(&SMA_3D8);
|
||||||
}
|
}
|
||||||
if (currentMillis - previousMillis9s >= interval9s)
|
if (currentMillis - previousMillis9s >= interval9s) {
|
||||||
{
|
|
||||||
previousMillis9s = currentMillis;
|
previousMillis9s = currentMillis;
|
||||||
|
|
||||||
ESP32Can.CANWriteFrame(&SMA_458);
|
ESP32Can.CANWriteFrame(&SMA_458);
|
||||||
}
|
}
|
||||||
if (currentMillis - previousMillis10s >= interval10s)
|
if (currentMillis - previousMillis10s >= interval10s) {
|
||||||
{
|
|
||||||
previousMillis10s = currentMillis;
|
previousMillis10s = currentMillis;
|
||||||
|
|
||||||
ESP32Can.CANWriteFrame(&SMA_518);
|
ESP32Can.CANWriteFrame(&SMA_518);
|
||||||
}
|
}
|
||||||
if (currentMillis - previousMillis11s >= interval11s)
|
if (currentMillis - previousMillis11s >= interval11s) {
|
||||||
{
|
|
||||||
previousMillis11s = currentMillis;
|
previousMillis11s = currentMillis;
|
||||||
|
|
||||||
ESP32Can.CANWriteFrame(&SMA_4D8);
|
ESP32Can.CANWriteFrame(&SMA_4D8);
|
||||||
}
|
}
|
||||||
if (currentMillis - previousMillis12s >= interval12s)
|
if (currentMillis - previousMillis12s >= interval12s) {
|
||||||
{
|
|
||||||
previousMillis12s = currentMillis;
|
previousMillis12s = currentMillis;
|
||||||
|
|
||||||
ESP32Can.CANWriteFrame(&SMA_158);
|
ESP32Can.CANWriteFrame(&SMA_158);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
#ifndef SMA_CAN_H
|
#ifndef SMA_CAN_H
|
||||||
#define SMA_CAN_H
|
#define SMA_CAN_H
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "../devboard/can/ESP32CAN.h"
|
|
||||||
#include "../../USER_SETTINGS.h"
|
#include "../../USER_SETTINGS.h"
|
||||||
|
#include "../devboard/can/ESP32CAN.h"
|
||||||
|
|
||||||
extern uint16_t SOC; //SOC%, 0-100.00 (0-10000)
|
extern uint16_t SOC; //SOC%, 0-100.00 (0-10000)
|
||||||
extern uint16_t StateOfHealth; //SOH%, 0-100.00 (0-10000)
|
extern uint16_t StateOfHealth; //SOH%, 0-100.00 (0-10000)
|
||||||
|
|
|
@ -10,48 +10,275 @@ static const int interval100 = 100; // interval (ms) at which send CAN Messages
|
||||||
|
|
||||||
//Actual content messages
|
//Actual content messages
|
||||||
//Note that these are technically extended frames. If more batteries are put in parallel,the first battery sends 0x351 the next battery sends 0x1351 etc. 16 batteries in parallel supported
|
//Note that these are technically extended frames. If more batteries are put in parallel,the first battery sends 0x351 the next battery sends 0x1351 etc. 16 batteries in parallel supported
|
||||||
CAN_frame_t SOFAR_351 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x351,.data = {0xC6, 0x08, 0xFA, 0x00, 0xFA, 0x00, 0x80, 0x07}};
|
CAN_frame_t SOFAR_351 = {.FIR = {.B =
|
||||||
CAN_frame_t SOFAR_355 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x355,.data = {0x31, 0x00, 0x64, 0x00, 0xFF, 0xFF, 0xF6, 0x00}};
|
{
|
||||||
CAN_frame_t SOFAR_356 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x356,.data = {0x36, 0x08, 0x10, 0x00, 0xD0, 0x00, 0x01, 0x00}};
|
.DLC = 8,
|
||||||
CAN_frame_t SOFAR_30F = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x30F,.data = {0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
.FF = CAN_frame_ext,
|
||||||
CAN_frame_t SOFAR_359 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x359,.data = {0x00, 0x00, 0x00, 0x00, 0x04, 0x10, 0x27, 0x10}};
|
}},
|
||||||
CAN_frame_t SOFAR_35E = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x35E,.data = {0x41, 0x4D, 0x41, 0x53, 0x53, 0x00, 0x00, 0x00}};
|
.MsgID = 0x351,
|
||||||
CAN_frame_t SOFAR_35F = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x35F,.data = {0x00, 0x00, 0x24, 0x4E, 0x32, 0x00, 0x00, 0x00}};
|
.data = {0xC6, 0x08, 0xFA, 0x00, 0xFA, 0x00, 0x80, 0x07}};
|
||||||
CAN_frame_t SOFAR_35A = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x35A,.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
CAN_frame_t SOFAR_355 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x355,
|
||||||
|
.data = {0x31, 0x00, 0x64, 0x00, 0xFF, 0xFF, 0xF6, 0x00}};
|
||||||
|
CAN_frame_t SOFAR_356 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x356,
|
||||||
|
.data = {0x36, 0x08, 0x10, 0x00, 0xD0, 0x00, 0x01, 0x00}};
|
||||||
|
CAN_frame_t SOFAR_30F = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x30F,
|
||||||
|
.data = {0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
CAN_frame_t SOFAR_359 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x359,
|
||||||
|
.data = {0x00, 0x00, 0x00, 0x00, 0x04, 0x10, 0x27, 0x10}};
|
||||||
|
CAN_frame_t SOFAR_35E = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x35E,
|
||||||
|
.data = {0x41, 0x4D, 0x41, 0x53, 0x53, 0x00, 0x00, 0x00}};
|
||||||
|
CAN_frame_t SOFAR_35F = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x35F,
|
||||||
|
.data = {0x00, 0x00, 0x24, 0x4E, 0x32, 0x00, 0x00, 0x00}};
|
||||||
|
CAN_frame_t SOFAR_35A = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x35A,
|
||||||
|
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
|
||||||
CAN_frame_t SOFAR_670 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x670,.data = {0x00, 0x8A, 0x33, 0x11, 0x59, 0x1A, 0x00, 0x00}};
|
CAN_frame_t SOFAR_670 = {.FIR = {.B =
|
||||||
CAN_frame_t SOFAR_671 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x671,.data = {0x00, 0x42, 0x48, 0x55, 0x35, 0x31, 0x32, 0x30}};
|
{
|
||||||
CAN_frame_t SOFAR_672 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x672,.data = {0x00, 0x32, 0x35, 0x45, 0x50, 0x43, 0x32, 0x31}};
|
.DLC = 8,
|
||||||
CAN_frame_t SOFAR_673 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x673,.data = {0x00, 0x34, 0x32, 0x36, 0x31, 0x36, 0x32, 0x00}};
|
.FF = CAN_frame_ext,
|
||||||
CAN_frame_t SOFAR_680 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x680,.data = {0x00, 0xB7, 0x0C, 0xB3, 0x0C, 0xB4, 0x0C, 0x00}};
|
}},
|
||||||
CAN_frame_t SOFAR_681 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x681,.data = {0x00, 0xB6, 0x0C, 0xB3, 0x0C, 0xB4, 0x0C, 0x00}};
|
.MsgID = 0x670,
|
||||||
CAN_frame_t SOFAR_682 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x682,.data = {0x00, 0xB6, 0x0C, 0xB3, 0x0C, 0xB4, 0x0C, 0x00}};
|
.data = {0x00, 0x8A, 0x33, 0x11, 0x59, 0x1A, 0x00, 0x00}};
|
||||||
CAN_frame_t SOFAR_683 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x683,.data = {0x00, 0xB6, 0x0C, 0xB3, 0x0C, 0xB4, 0x0C, 0x00}};
|
CAN_frame_t SOFAR_671 = {.FIR = {.B =
|
||||||
CAN_frame_t SOFAR_684 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x684,.data = {0x00, 0xB6, 0x0C, 0xB3, 0x0C, 0xB4, 0x0C, 0x00}};
|
{
|
||||||
CAN_frame_t SOFAR_685 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x685,.data = {0x00, 0xB3, 0x0C, 0xBB, 0x0C, 0xB3, 0x0C, 0x00}};
|
.DLC = 8,
|
||||||
CAN_frame_t SOFAR_690 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x690,.data = {0x00, 0xD7, 0x00, 0xD4, 0x00, 0x00, 0x00, 0x00}};
|
.FF = CAN_frame_ext,
|
||||||
CAN_frame_t SOFAR_691 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x691,.data = {0x00, 0xD4, 0x00, 0xD1, 0x00, 0x00, 0x00, 0x00}};
|
}},
|
||||||
CAN_frame_t SOFAR_6A0 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x6A0,.data = {0x00, 0xFA, 0x00, 0xDD, 0x00, 0x00, 0x00, 0x00}};
|
.MsgID = 0x671,
|
||||||
CAN_frame_t SOFAR_6B0 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x6B0,.data = {0x00, 0xF6, 0x00, 0x06, 0x02, 0x01, 0x00, 0x00}};
|
.data = {0x00, 0x42, 0x48, 0x55, 0x35, 0x31, 0x32, 0x30}};
|
||||||
CAN_frame_t SOFAR_6C0 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x6C0,.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
CAN_frame_t SOFAR_672 = {.FIR = {.B =
|
||||||
CAN_frame_t SOFAR_770 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x770,.data = {0x00, 0x56, 0x0B, 0xF0, 0x58, 0x00, 0x00, 0x00}};
|
{
|
||||||
CAN_frame_t SOFAR_771 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x771,.data = {0x00, 0x42, 0x48, 0x55, 0x35, 0x31, 0x32, 0x30}};
|
.DLC = 8,
|
||||||
CAN_frame_t SOFAR_772 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x772,.data = {0x00, 0x32, 0x35, 0x45, 0x50, 0x43, 0x32, 0x31}};
|
.FF = CAN_frame_ext,
|
||||||
CAN_frame_t SOFAR_773 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x773,.data = {0x00, 0x34, 0x32, 0x36, 0x31, 0x36, 0x32, 0x00}};
|
}},
|
||||||
CAN_frame_t SOFAR_780 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x780,.data = {0x00, 0xEB, 0x0C, 0xED, 0x0C, 0xED, 0x0C, 0x00}};
|
.MsgID = 0x672,
|
||||||
CAN_frame_t SOFAR_781 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x781,.data = {0x00, 0xEB, 0x0C, 0xED, 0x0C, 0xED, 0x0C, 0x00}};
|
.data = {0x00, 0x32, 0x35, 0x45, 0x50, 0x43, 0x32, 0x31}};
|
||||||
CAN_frame_t SOFAR_782 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x782,.data = {0x00, 0xEB, 0x0C, 0xED, 0x0C, 0xED, 0x0C, 0x00}};
|
CAN_frame_t SOFAR_673 = {.FIR = {.B =
|
||||||
CAN_frame_t SOFAR_783 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x783,.data = {0x00, 0xEB, 0x0C, 0xED, 0x0C, 0xED, 0x0C, 0x00}};
|
{
|
||||||
CAN_frame_t SOFAR_784 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x784,.data = {0x00, 0xEB, 0x0C, 0xED, 0x0C, 0xED, 0x0C, 0x00}};
|
.DLC = 8,
|
||||||
CAN_frame_t SOFAR_785 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x785,.data = {0x00, 0xEB, 0x0C, 0xED, 0x0C, 0xED, 0x0C, 0x00}};
|
.FF = CAN_frame_ext,
|
||||||
CAN_frame_t SOFAR_790 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x790,.data = {0x00, 0xCD, 0x00, 0xCF, 0x00, 0x00, 0x00, 0x00}};
|
}},
|
||||||
CAN_frame_t SOFAR_791 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x791,.data = {0x00, 0xCD, 0x00, 0xCF, 0x00, 0x00, 0x00, 0x00}};
|
.MsgID = 0x673,
|
||||||
CAN_frame_t SOFAR_7A0 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x7A0,.data = {0x00, 0xFA, 0x00, 0xE1, 0x00, 0x00, 0x00, 0x00}};
|
.data = {0x00, 0x34, 0x32, 0x36, 0x31, 0x36, 0x32, 0x00}};
|
||||||
CAN_frame_t SOFAR_7B0 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x7B0,.data = {0x00, 0xF9, 0x00, 0x06, 0x02, 0xE9, 0x5D, 0x00}};
|
CAN_frame_t SOFAR_680 = {.FIR = {.B =
|
||||||
CAN_frame_t SOFAR_7C0 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x7C0,.data = {0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x80, 0x00}};
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x680,
|
||||||
|
.data = {0x00, 0xB7, 0x0C, 0xB3, 0x0C, 0xB4, 0x0C, 0x00}};
|
||||||
|
CAN_frame_t SOFAR_681 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x681,
|
||||||
|
.data = {0x00, 0xB6, 0x0C, 0xB3, 0x0C, 0xB4, 0x0C, 0x00}};
|
||||||
|
CAN_frame_t SOFAR_682 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x682,
|
||||||
|
.data = {0x00, 0xB6, 0x0C, 0xB3, 0x0C, 0xB4, 0x0C, 0x00}};
|
||||||
|
CAN_frame_t SOFAR_683 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x683,
|
||||||
|
.data = {0x00, 0xB6, 0x0C, 0xB3, 0x0C, 0xB4, 0x0C, 0x00}};
|
||||||
|
CAN_frame_t SOFAR_684 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x684,
|
||||||
|
.data = {0x00, 0xB6, 0x0C, 0xB3, 0x0C, 0xB4, 0x0C, 0x00}};
|
||||||
|
CAN_frame_t SOFAR_685 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x685,
|
||||||
|
.data = {0x00, 0xB3, 0x0C, 0xBB, 0x0C, 0xB3, 0x0C, 0x00}};
|
||||||
|
CAN_frame_t SOFAR_690 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x690,
|
||||||
|
.data = {0x00, 0xD7, 0x00, 0xD4, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
CAN_frame_t SOFAR_691 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x691,
|
||||||
|
.data = {0x00, 0xD4, 0x00, 0xD1, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
CAN_frame_t SOFAR_6A0 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x6A0,
|
||||||
|
.data = {0x00, 0xFA, 0x00, 0xDD, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
CAN_frame_t SOFAR_6B0 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x6B0,
|
||||||
|
.data = {0x00, 0xF6, 0x00, 0x06, 0x02, 0x01, 0x00, 0x00}};
|
||||||
|
CAN_frame_t SOFAR_6C0 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x6C0,
|
||||||
|
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
CAN_frame_t SOFAR_770 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x770,
|
||||||
|
.data = {0x00, 0x56, 0x0B, 0xF0, 0x58, 0x00, 0x00, 0x00}};
|
||||||
|
CAN_frame_t SOFAR_771 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x771,
|
||||||
|
.data = {0x00, 0x42, 0x48, 0x55, 0x35, 0x31, 0x32, 0x30}};
|
||||||
|
CAN_frame_t SOFAR_772 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x772,
|
||||||
|
.data = {0x00, 0x32, 0x35, 0x45, 0x50, 0x43, 0x32, 0x31}};
|
||||||
|
CAN_frame_t SOFAR_773 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x773,
|
||||||
|
.data = {0x00, 0x34, 0x32, 0x36, 0x31, 0x36, 0x32, 0x00}};
|
||||||
|
CAN_frame_t SOFAR_780 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x780,
|
||||||
|
.data = {0x00, 0xEB, 0x0C, 0xED, 0x0C, 0xED, 0x0C, 0x00}};
|
||||||
|
CAN_frame_t SOFAR_781 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x781,
|
||||||
|
.data = {0x00, 0xEB, 0x0C, 0xED, 0x0C, 0xED, 0x0C, 0x00}};
|
||||||
|
CAN_frame_t SOFAR_782 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x782,
|
||||||
|
.data = {0x00, 0xEB, 0x0C, 0xED, 0x0C, 0xED, 0x0C, 0x00}};
|
||||||
|
CAN_frame_t SOFAR_783 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x783,
|
||||||
|
.data = {0x00, 0xEB, 0x0C, 0xED, 0x0C, 0xED, 0x0C, 0x00}};
|
||||||
|
CAN_frame_t SOFAR_784 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x784,
|
||||||
|
.data = {0x00, 0xEB, 0x0C, 0xED, 0x0C, 0xED, 0x0C, 0x00}};
|
||||||
|
CAN_frame_t SOFAR_785 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x785,
|
||||||
|
.data = {0x00, 0xEB, 0x0C, 0xED, 0x0C, 0xED, 0x0C, 0x00}};
|
||||||
|
CAN_frame_t SOFAR_790 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x790,
|
||||||
|
.data = {0x00, 0xCD, 0x00, 0xCF, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
CAN_frame_t SOFAR_791 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x791,
|
||||||
|
.data = {0x00, 0xCD, 0x00, 0xCF, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
CAN_frame_t SOFAR_7A0 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x7A0,
|
||||||
|
.data = {0x00, 0xFA, 0x00, 0xE1, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
CAN_frame_t SOFAR_7B0 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x7B0,
|
||||||
|
.data = {0x00, 0xF9, 0x00, 0x06, 0x02, 0xE9, 0x5D, 0x00}};
|
||||||
|
CAN_frame_t SOFAR_7C0 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x7C0,
|
||||||
|
.data = {0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x80, 0x00}};
|
||||||
|
|
||||||
void update_values_can_sofar()
|
void update_values_can_sofar() { //This function maps all the values fetched from battery CAN to the correct CAN messages
|
||||||
{ //This function maps all the values fetched from battery CAN to the correct CAN messages
|
|
||||||
|
|
||||||
//Maxvoltage (eg 400.0V = 4000 , 16bits long) Charge Cutoff Voltage
|
//Maxvoltage (eg 400.0V = 4000 , 16bits long) Charge Cutoff Voltage
|
||||||
SOFAR_351.data.u8[0] = (max_volt_sofar_can >> 8);
|
SOFAR_351.data.u8[0] = (max_volt_sofar_can >> 8);
|
||||||
|
@ -65,8 +292,8 @@ void update_values_can_sofar()
|
||||||
SOFAR_351.data.u8[7] = (min_volt_sofar_can & 0x00FF);
|
SOFAR_351.data.u8[7] = (min_volt_sofar_can & 0x00FF);
|
||||||
|
|
||||||
//SOC
|
//SOC
|
||||||
SOFAR_355.data.u8[0] = (SOC/100);
|
SOFAR_355.data.u8[0] = (SOC / 100);
|
||||||
SOFAR_355.data.u8[2] = (StateOfHealth/100);
|
SOFAR_355.data.u8[2] = (StateOfHealth / 100);
|
||||||
//SOFAR_355.data.u8[6] = (AH_remaining >> 8);
|
//SOFAR_355.data.u8[6] = (AH_remaining >> 8);
|
||||||
//SOFAR_355.data.u8[7] = (AH_remaining & 0x00FF);
|
//SOFAR_355.data.u8[7] = (AH_remaining & 0x00FF);
|
||||||
|
|
||||||
|
@ -77,13 +304,10 @@ void update_values_can_sofar()
|
||||||
SOFAR_356.data.u8[3] = (battery_current & 0x00FF);
|
SOFAR_356.data.u8[3] = (battery_current & 0x00FF);
|
||||||
SOFAR_356.data.u8[2] = (temperature_max >> 8);
|
SOFAR_356.data.u8[2] = (temperature_max >> 8);
|
||||||
SOFAR_356.data.u8[3] = (temperature_max & 0x00FF);
|
SOFAR_356.data.u8[3] = (temperature_max & 0x00FF);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void receive_can_sofar(CAN_frame_t rx_frame)
|
void receive_can_sofar(CAN_frame_t rx_frame) {
|
||||||
{
|
switch (rx_frame.MsgID) { //In here we need to respond to the inverter. TODO make logic
|
||||||
switch (rx_frame.MsgID)
|
|
||||||
{ //In here we need to respond to the inverter. TODO make logic
|
|
||||||
case 0x605:
|
case 0x605:
|
||||||
//frame1_605 = rx_frame.data.u8[1];
|
//frame1_605 = rx_frame.data.u8[1];
|
||||||
//frame3_605 = rx_frame.data.u8[3];
|
//frame3_605 = rx_frame.data.u8[3];
|
||||||
|
@ -97,12 +321,10 @@ void receive_can_sofar(CAN_frame_t rx_frame)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_can_sofar()
|
void send_can_sofar() {
|
||||||
{
|
|
||||||
unsigned long currentMillis = millis();
|
unsigned long currentMillis = millis();
|
||||||
// Send 100ms CAN Message
|
// Send 100ms CAN Message
|
||||||
if (currentMillis - previousMillis100 >= interval100)
|
if (currentMillis - previousMillis100 >= interval100) {
|
||||||
{
|
|
||||||
previousMillis100 = currentMillis;
|
previousMillis100 = currentMillis;
|
||||||
//Frames actively reported by BMS
|
//Frames actively reported by BMS
|
||||||
ESP32Can.CANWriteFrame(&SOFAR_351);
|
ESP32Can.CANWriteFrame(&SOFAR_351);
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
#ifndef SOFAR_CAN_H
|
#ifndef SOFAR_CAN_H
|
||||||
#define SOFAR_CAN_H
|
#define SOFAR_CAN_H
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "../devboard/can/ESP32CAN.h"
|
|
||||||
#include "../../USER_SETTINGS.h"
|
#include "../../USER_SETTINGS.h"
|
||||||
|
#include "../devboard/can/ESP32CAN.h"
|
||||||
|
|
||||||
// These parameters need to be mapped for the inverter
|
// These parameters need to be mapped for the inverter
|
||||||
extern uint16_t SOC; //SOC%, 0-100.00 (0-10000)
|
extern uint16_t SOC; //SOC%, 0-100.00 (0-10000)
|
||||||
|
|
|
@ -10,159 +10,219 @@ static int number_of_batteries = 1;
|
||||||
|
|
||||||
//CAN message translations from this amazing repository: https://github.com/rand12345/solax_can_bus
|
//CAN message translations from this amazing repository: https://github.com/rand12345/solax_can_bus
|
||||||
|
|
||||||
CAN_frame_t SOLAX_1801 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x1801,.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
|
CAN_frame_t SOLAX_1801 = {.FIR = {.B =
|
||||||
CAN_frame_t SOLAX_1872 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x1872,.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; //BMS_Limits
|
{
|
||||||
CAN_frame_t SOLAX_1873 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x1873,.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; //BMS_PackData
|
.DLC = 8,
|
||||||
CAN_frame_t SOLAX_1874 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x1874,.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; //BMS_CellData
|
.FF = CAN_frame_ext,
|
||||||
CAN_frame_t SOLAX_1875 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x1875,.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; //BMS_Status
|
}},
|
||||||
CAN_frame_t SOLAX_1876 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x1876,.data = {0x0, 0x0, 0xE2, 0x0C, 0x0, 0x0, 0xD7, 0x0C}}; //BMS_PackTemps
|
.MsgID = 0x1801,
|
||||||
CAN_frame_t SOLAX_1877 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x1877,.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
|
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
|
||||||
CAN_frame_t SOLAX_1878 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x1878,.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; //BMS_PackStats
|
CAN_frame_t SOLAX_1872 = {.FIR = {.B =
|
||||||
CAN_frame_t SOLAX_1879 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x1879,.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
|
{
|
||||||
CAN_frame_t SOLAX_1881 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x1881,.data = {0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; // E.g.: 0 6 S B M S F A
|
.DLC = 8,
|
||||||
CAN_frame_t SOLAX_1882 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x1882,.data = {0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; // E.g.: 0 2 3 A B 0 5 2
|
.FF = CAN_frame_ext,
|
||||||
CAN_frame_t SOLAX_100A001 = {.FIR = {.B = {.DLC = 0,.FF = CAN_frame_ext,}},.MsgID = 0x100A001,.data = {}};
|
}},
|
||||||
|
.MsgID = 0x1872,
|
||||||
|
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; //BMS_Limits
|
||||||
|
CAN_frame_t SOLAX_1873 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x1873,
|
||||||
|
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; //BMS_PackData
|
||||||
|
CAN_frame_t SOLAX_1874 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x1874,
|
||||||
|
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; //BMS_CellData
|
||||||
|
CAN_frame_t SOLAX_1875 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x1875,
|
||||||
|
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; //BMS_Status
|
||||||
|
CAN_frame_t SOLAX_1876 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x1876,
|
||||||
|
.data = {0x0, 0x0, 0xE2, 0x0C, 0x0, 0x0, 0xD7, 0x0C}}; //BMS_PackTemps
|
||||||
|
CAN_frame_t SOLAX_1877 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x1877,
|
||||||
|
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
|
||||||
|
CAN_frame_t SOLAX_1878 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x1878,
|
||||||
|
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; //BMS_PackStats
|
||||||
|
CAN_frame_t SOLAX_1879 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x1879,
|
||||||
|
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
|
||||||
|
CAN_frame_t SOLAX_1881 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x1881,
|
||||||
|
.data = {0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; // E.g.: 0 6 S B M S F A
|
||||||
|
CAN_frame_t SOLAX_1882 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 8,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x1882,
|
||||||
|
.data = {0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; // E.g.: 0 2 3 A B 0 5 2
|
||||||
|
CAN_frame_t SOLAX_100A001 = {.FIR = {.B =
|
||||||
|
{
|
||||||
|
.DLC = 0,
|
||||||
|
.FF = CAN_frame_ext,
|
||||||
|
}},
|
||||||
|
.MsgID = 0x100A001,
|
||||||
|
.data = {}};
|
||||||
|
|
||||||
// __builtin_bswap64 needed to convert to ESP32 little endian format
|
// __builtin_bswap64 needed to convert to ESP32 little endian format
|
||||||
// Byte[4] defines the requested contactor state: 1 = Closed , 0 = Open
|
// Byte[4] defines the requested contactor state: 1 = Closed , 0 = Open
|
||||||
#define Contactor_Open_Payload __builtin_bswap64(0x0200010000000000)
|
#define Contactor_Open_Payload __builtin_bswap64(0x0200010000000000)
|
||||||
#define Contactor_Close_Payload __builtin_bswap64(0x0200010001000000)
|
#define Contactor_Close_Payload __builtin_bswap64(0x0200010001000000)
|
||||||
|
|
||||||
void CAN_WriteFrame(CAN_frame_t* tx_frame)
|
void CAN_WriteFrame(CAN_frame_t* tx_frame) {
|
||||||
{
|
#ifdef DUAL_CAN
|
||||||
#ifdef DUAL_CAN
|
|
||||||
CANMessage MCP2515Frame; //Struct with ACAN2515 library format, needed to use the MCP2515 library
|
CANMessage MCP2515Frame; //Struct with ACAN2515 library format, needed to use the MCP2515 library
|
||||||
MCP2515Frame.id = tx_frame->MsgID;
|
MCP2515Frame.id = tx_frame->MsgID;
|
||||||
MCP2515Frame.ext = tx_frame->FIR.B.FF;
|
MCP2515Frame.ext = tx_frame->FIR.B.FF;
|
||||||
MCP2515Frame.len = tx_frame->FIR.B.DLC;
|
MCP2515Frame.len = tx_frame->FIR.B.DLC;
|
||||||
for (uint8_t i=0 ; i<MCP2515Frame.len ; i++) {
|
for (uint8_t i = 0; i < MCP2515Frame.len; i++) {
|
||||||
MCP2515Frame.data[i] = tx_frame->data.u8[i];
|
MCP2515Frame.data[i] = tx_frame->data.u8[i];
|
||||||
}
|
}
|
||||||
can.tryToSend(MCP2515Frame);
|
can.tryToSend(MCP2515Frame);
|
||||||
#else
|
#else
|
||||||
ESP32Can.CANWriteFrame(tx_frame);
|
ESP32Can.CANWriteFrame(tx_frame);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_values_can_solax()
|
void update_values_can_solax() { //This function maps all the values fetched from battery CAN to the correct CAN messages
|
||||||
{ //This function maps all the values fetched from battery CAN to the correct CAN messages
|
|
||||||
// If not receiveing any communication from the inverter, open contactors and return to battery announce state
|
// If not receiveing any communication from the inverter, open contactors and return to battery announce state
|
||||||
if (millis() - LastFrameTime >= SolaxTimeout)
|
if (millis() - LastFrameTime >= SolaxTimeout) {
|
||||||
{
|
|
||||||
inverterAllowsContactorClosing = 0;
|
inverterAllowsContactorClosing = 0;
|
||||||
STATE = BATTERY_ANNOUNCE;
|
STATE = BATTERY_ANNOUNCE;
|
||||||
}
|
}
|
||||||
//Calculate the required values
|
//Calculate the required values
|
||||||
temperature_average = ((temperature_max + temperature_min)/2);
|
temperature_average = ((temperature_max + temperature_min) / 2);
|
||||||
|
|
||||||
//max_target_charge_power (30000W max)
|
//max_target_charge_power (30000W max)
|
||||||
if(SOC > 9999) //99.99%
|
if (SOC > 9999) //99.99%
|
||||||
{ //Additional safety incase SOC% is 100, then do not charge battery further
|
{ //Additional safety incase SOC% is 100, then do not charge battery further
|
||||||
max_charge_rate_amp = 0;
|
max_charge_rate_amp = 0;
|
||||||
}
|
} else { //We can pass on the battery charge rate (in W) to the inverter (that takes A)
|
||||||
else
|
if (max_target_charge_power >= 30000) {
|
||||||
{ //We can pass on the battery charge rate (in W) to the inverter (that takes A)
|
|
||||||
if(max_target_charge_power >= 30000)
|
|
||||||
{
|
|
||||||
max_charge_rate_amp = 75; //Incase battery can take over 30kW, cap value to 75A
|
max_charge_rate_amp = 75; //Incase battery can take over 30kW, cap value to 75A
|
||||||
}
|
} else { //Calculate the W value into A
|
||||||
else
|
max_charge_rate_amp = (max_target_charge_power / (battery_voltage * 0.1)); // P/U = I
|
||||||
{ //Calculate the W value into A
|
|
||||||
max_charge_rate_amp = (max_target_charge_power/(battery_voltage*0.1)); // P/U = I
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//max_target_discharge_power (30000W max)
|
//max_target_discharge_power (30000W max)
|
||||||
if(SOC < 100) //1.00%
|
if (SOC < 100) //1.00%
|
||||||
{ //Additional safety incase SOC% is below 1, then do not charge battery further
|
{ //Additional safety incase SOC% is below 1, then do not charge battery further
|
||||||
max_discharge_rate_amp = 0;
|
max_discharge_rate_amp = 0;
|
||||||
}
|
} else { //We can pass on the battery discharge rate to the inverter
|
||||||
else
|
if (max_target_discharge_power >= 30000) {
|
||||||
{ //We can pass on the battery discharge rate to the inverter
|
|
||||||
if(max_target_discharge_power >= 30000)
|
|
||||||
{
|
|
||||||
max_discharge_rate_amp = 75; //Incase battery can be charged with over 30kW, cap value to 75A
|
max_discharge_rate_amp = 75; //Incase battery can be charged with over 30kW, cap value to 75A
|
||||||
}
|
} else { //Calculate the W value into A
|
||||||
else
|
max_discharge_rate_amp = (max_target_discharge_power / (battery_voltage * 0.1)); // P/U = I
|
||||||
{ //Calculate the W value into A
|
|
||||||
max_discharge_rate_amp = (max_target_discharge_power/(battery_voltage*0.1)); // P/U = I
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Put the values into the CAN messages
|
//Put the values into the CAN messages
|
||||||
//BMS_Limits
|
//BMS_Limits
|
||||||
SOLAX_1872.data.u8[0] = (uint8_t) max_volt_solax_can; //Todo, scaling OK?
|
SOLAX_1872.data.u8[0] = (uint8_t)max_volt_solax_can; //Todo, scaling OK?
|
||||||
SOLAX_1872.data.u8[1] = (max_volt_solax_can >> 8);
|
SOLAX_1872.data.u8[1] = (max_volt_solax_can >> 8);
|
||||||
SOLAX_1872.data.u8[2] = (uint8_t) min_volt_solax_can; //Todo, scaling OK?
|
SOLAX_1872.data.u8[2] = (uint8_t)min_volt_solax_can; //Todo, scaling OK?
|
||||||
SOLAX_1872.data.u8[3] = (min_volt_solax_can >> 8);
|
SOLAX_1872.data.u8[3] = (min_volt_solax_can >> 8);
|
||||||
SOLAX_1872.data.u8[4] = (uint8_t) (max_charge_rate_amp*10); //Todo, scaling OK?
|
SOLAX_1872.data.u8[4] = (uint8_t)(max_charge_rate_amp * 10); //Todo, scaling OK?
|
||||||
SOLAX_1872.data.u8[5] = ((max_charge_rate_amp*10) >> 8);
|
SOLAX_1872.data.u8[5] = ((max_charge_rate_amp * 10) >> 8);
|
||||||
SOLAX_1872.data.u8[6] = (uint8_t) (max_discharge_rate_amp*10); //Todo, scaling OK?
|
SOLAX_1872.data.u8[6] = (uint8_t)(max_discharge_rate_amp * 10); //Todo, scaling OK?
|
||||||
SOLAX_1872.data.u8[7] = ((max_discharge_rate_amp*10) >> 8);
|
SOLAX_1872.data.u8[7] = ((max_discharge_rate_amp * 10) >> 8);
|
||||||
|
|
||||||
//BMS_PackData
|
//BMS_PackData
|
||||||
SOLAX_1873.data.u8[0] = (uint8_t) battery_voltage; // OK
|
SOLAX_1873.data.u8[0] = (uint8_t)battery_voltage; // OK
|
||||||
SOLAX_1873.data.u8[1] = (battery_voltage >> 8);
|
SOLAX_1873.data.u8[1] = (battery_voltage >> 8);
|
||||||
SOLAX_1873.data.u8[2] = (int8_t) battery_current; // OK, Signed (Active current in Amps x 10)
|
SOLAX_1873.data.u8[2] = (int8_t)battery_current; // OK, Signed (Active current in Amps x 10)
|
||||||
SOLAX_1873.data.u8[3] = (battery_current >> 8);
|
SOLAX_1873.data.u8[3] = (battery_current >> 8);
|
||||||
SOLAX_1873.data.u8[4] = (uint8_t) (SOC/100); //SOC (100.00%)
|
SOLAX_1873.data.u8[4] = (uint8_t)(SOC / 100); //SOC (100.00%)
|
||||||
//SOLAX_1873.data.u8[5] = //Seems like this is not required? Or shall we put SOC decimals here?
|
//SOLAX_1873.data.u8[5] = //Seems like this is not required? Or shall we put SOC decimals here?
|
||||||
SOLAX_1873.data.u8[6] = (uint8_t) (remaining_capacity_Wh/100); //Todo, scaling OK?
|
SOLAX_1873.data.u8[6] = (uint8_t)(remaining_capacity_Wh / 100); //Todo, scaling OK?
|
||||||
SOLAX_1873.data.u8[7] = ((remaining_capacity_Wh/100) >> 8);
|
SOLAX_1873.data.u8[7] = ((remaining_capacity_Wh / 100) >> 8);
|
||||||
|
|
||||||
//BMS_CellData
|
//BMS_CellData
|
||||||
SOLAX_1874.data.u8[0] = (uint8_t) temperature_max;
|
SOLAX_1874.data.u8[0] = (uint8_t)temperature_max;
|
||||||
SOLAX_1874.data.u8[1] = (temperature_max >> 8);
|
SOLAX_1874.data.u8[1] = (temperature_max >> 8);
|
||||||
SOLAX_1874.data.u8[2] = (uint8_t) temperature_min;
|
SOLAX_1874.data.u8[2] = (uint8_t)temperature_min;
|
||||||
SOLAX_1874.data.u8[3] = (temperature_min >> 8);
|
SOLAX_1874.data.u8[3] = (temperature_min >> 8);
|
||||||
SOLAX_1874.data.u8[4] = (uint8_t) (cell_max_voltage); //Todo, scaling OK? Supposed to be alarm trigger absolute cell max?
|
SOLAX_1874.data.u8[4] =
|
||||||
|
(uint8_t)(cell_max_voltage); //Todo, scaling OK? Supposed to be alarm trigger absolute cell max?
|
||||||
SOLAX_1874.data.u8[5] = (cell_max_voltage >> 8);
|
SOLAX_1874.data.u8[5] = (cell_max_voltage >> 8);
|
||||||
SOLAX_1874.data.u8[6] = (uint8_t) (cell_min_voltage); //Todo, scaling OK? Supposed to be alarm trigger absolute cell min?
|
SOLAX_1874.data.u8[6] =
|
||||||
|
(uint8_t)(cell_min_voltage); //Todo, scaling OK? Supposed to be alarm trigger absolute cell min?
|
||||||
SOLAX_1874.data.u8[7] = (cell_min_voltage >> 8);
|
SOLAX_1874.data.u8[7] = (cell_min_voltage >> 8);
|
||||||
|
|
||||||
//BMS_Status
|
//BMS_Status
|
||||||
SOLAX_1875.data.u8[0] = (uint8_t) temperature_average;
|
SOLAX_1875.data.u8[0] = (uint8_t)temperature_average;
|
||||||
SOLAX_1875.data.u8[1] = (temperature_average >> 8);
|
SOLAX_1875.data.u8[1] = (temperature_average >> 8);
|
||||||
SOLAX_1875.data.u8[2] = (uint8_t) 0; // Number of slave batteries
|
SOLAX_1875.data.u8[2] = (uint8_t)0; // Number of slave batteries
|
||||||
SOLAX_1875.data.u8[4] = (uint8_t) 0; // Contactor Status 0=off, 1=on.
|
SOLAX_1875.data.u8[4] = (uint8_t)0; // Contactor Status 0=off, 1=on.
|
||||||
|
|
||||||
//BMS_PackTemps (strange name, since it has voltages?)
|
//BMS_PackTemps (strange name, since it has voltages?)
|
||||||
SOLAX_1876.data.u8[2] = (uint8_t) cell_max_voltage; //Todo, scaling OK?
|
SOLAX_1876.data.u8[2] = (uint8_t)cell_max_voltage; //Todo, scaling OK?
|
||||||
SOLAX_1876.data.u8[3] = (cell_min_voltage >> 8);
|
SOLAX_1876.data.u8[3] = (cell_min_voltage >> 8);
|
||||||
|
|
||||||
SOLAX_1876.data.u8[6] = (uint8_t) cell_min_voltage; //Todo, scaling OK?
|
SOLAX_1876.data.u8[6] = (uint8_t)cell_min_voltage; //Todo, scaling OK?
|
||||||
SOLAX_1876.data.u8[7] = (cell_min_voltage >> 8);
|
SOLAX_1876.data.u8[7] = (cell_min_voltage >> 8);
|
||||||
|
|
||||||
//Unknown
|
//Unknown
|
||||||
SOLAX_1877.data.u8[4] = (uint8_t) 0x50; // Battery type
|
SOLAX_1877.data.u8[4] = (uint8_t)0x50; // Battery type
|
||||||
SOLAX_1877.data.u8[6] = (uint8_t) 0x22; // Firmware version?
|
SOLAX_1877.data.u8[6] = (uint8_t)0x22; // Firmware version?
|
||||||
SOLAX_1877.data.u8[7] = (uint8_t) 0x02; // The above firmware version applies to:02 = Master BMS, 10 = S1, 20 = S2, 30 = S3, 40 = S4
|
SOLAX_1877.data.u8[7] =
|
||||||
|
(uint8_t)0x02; // The above firmware version applies to:02 = Master BMS, 10 = S1, 20 = S2, 30 = S3, 40 = S4
|
||||||
|
|
||||||
//BMS_PackStats
|
//BMS_PackStats
|
||||||
SOLAX_1878.data.u8[0] = (uint8_t) (battery_voltage); //TODO, should this be max or current voltage?
|
SOLAX_1878.data.u8[0] = (uint8_t)(battery_voltage); //TODO, should this be max or current voltage?
|
||||||
SOLAX_1878.data.u8[1] = ((battery_voltage) >> 8);
|
SOLAX_1878.data.u8[1] = ((battery_voltage) >> 8);
|
||||||
|
|
||||||
SOLAX_1878.data.u8[4] = (uint8_t) capacity_Wh; //TODO, scaling OK?
|
SOLAX_1878.data.u8[4] = (uint8_t)capacity_Wh; //TODO, scaling OK?
|
||||||
SOLAX_1878.data.u8[5] = (capacity_Wh >> 8);
|
SOLAX_1878.data.u8[5] = (capacity_Wh >> 8);
|
||||||
|
|
||||||
// BMS_Answer
|
// BMS_Answer
|
||||||
SOLAX_1801.data.u8[0] = 2;
|
SOLAX_1801.data.u8[0] = 2;
|
||||||
SOLAX_1801.data.u8[2] = 1;
|
SOLAX_1801.data.u8[2] = 1;
|
||||||
SOLAX_1801.data.u8[4] = 1;
|
SOLAX_1801.data.u8[4] = 1;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void receive_can_solax(CAN_frame_t rx_frame)
|
void receive_can_solax(CAN_frame_t rx_frame) {
|
||||||
{
|
if (rx_frame.MsgID == 0x1871 && rx_frame.data.u8[0] == (0x01) ||
|
||||||
if (rx_frame.MsgID == 0x1871 && rx_frame.data.u8[0] == (0x01) || rx_frame.MsgID == 0x1871 && rx_frame.data.u8[0] == (0x02)) {
|
rx_frame.MsgID == 0x1871 && rx_frame.data.u8[0] == (0x02)) {
|
||||||
LastFrameTime = millis();
|
LastFrameTime = millis();
|
||||||
switch(STATE)
|
switch (STATE) {
|
||||||
{
|
case (BATTERY_ANNOUNCE):
|
||||||
case(BATTERY_ANNOUNCE):
|
|
||||||
Serial.println("Solax Battery State: Announce");
|
Serial.println("Solax Battery State: Announce");
|
||||||
inverterAllowsContactorClosing = 0;
|
inverterAllowsContactorClosing = 0;
|
||||||
SOLAX_1875.data.u8[4] = (0x00); // Inform Inverter: Contactor 0=off, 1=on.
|
SOLAX_1875.data.u8[4] = (0x00); // Inform Inverter: Contactor 0=off, 1=on.
|
||||||
for (int i=0; i <= number_of_batteries; i++) {
|
for (int i = 0; i <= number_of_batteries; i++) {
|
||||||
CAN_WriteFrame(&SOLAX_1872);
|
CAN_WriteFrame(&SOLAX_1872);
|
||||||
CAN_WriteFrame(&SOLAX_1873);
|
CAN_WriteFrame(&SOLAX_1873);
|
||||||
CAN_WriteFrame(&SOLAX_1874);
|
CAN_WriteFrame(&SOLAX_1874);
|
||||||
|
@ -178,7 +238,7 @@ void receive_can_solax(CAN_frame_t rx_frame)
|
||||||
STATE = WAITING_FOR_CONTACTOR;
|
STATE = WAITING_FOR_CONTACTOR;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case(WAITING_FOR_CONTACTOR):
|
case (WAITING_FOR_CONTACTOR):
|
||||||
SOLAX_1875.data.u8[4] = (0x00); // Inform Inverter: Contactor 0=off, 1=on.
|
SOLAX_1875.data.u8[4] = (0x00); // Inform Inverter: Contactor 0=off, 1=on.
|
||||||
CAN_WriteFrame(&SOLAX_1872);
|
CAN_WriteFrame(&SOLAX_1872);
|
||||||
CAN_WriteFrame(&SOLAX_1873);
|
CAN_WriteFrame(&SOLAX_1873);
|
||||||
|
@ -192,7 +252,7 @@ void receive_can_solax(CAN_frame_t rx_frame)
|
||||||
Serial.println("Solax Battery State: Contactor Closed");
|
Serial.println("Solax Battery State: Contactor Closed");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case(CONTACTOR_CLOSED):
|
case (CONTACTOR_CLOSED):
|
||||||
inverterAllowsContactorClosing = 1;
|
inverterAllowsContactorClosing = 1;
|
||||||
SOLAX_1875.data.u8[4] = (0x01); // Inform Inverter: Contactor 0=off, 1=on.
|
SOLAX_1875.data.u8[4] = (0x01); // Inform Inverter: Contactor 0=off, 1=on.
|
||||||
CAN_WriteFrame(&SOLAX_1872);
|
CAN_WriteFrame(&SOLAX_1872);
|
||||||
|
@ -218,5 +278,4 @@ void receive_can_solax(CAN_frame_t rx_frame)
|
||||||
if (rx_frame.MsgID == 0x1871 && rx_frame.data.u8[0] == (0x03)) {
|
if (rx_frame.MsgID == 0x1871 && rx_frame.data.u8[0] == (0x03)) {
|
||||||
Serial.println("1871 03-frame received from inverter");
|
Serial.println("1871 03-frame received from inverter");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
#ifndef SOLAX_CAN_H
|
#ifndef SOLAX_CAN_H
|
||||||
#define SOLAX_CAN_H
|
#define SOLAX_CAN_H
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
#include "../../USER_SETTINGS.h"
|
||||||
#include "../devboard/can/ESP32CAN.h"
|
#include "../devboard/can/ESP32CAN.h"
|
||||||
#include "../devboard/config.h"
|
#include "../devboard/config.h"
|
||||||
#include "../../USER_SETTINGS.h"
|
|
||||||
|
|
||||||
#include "../lib/pierremolinaro-acan2515/ACAN2515.h"
|
#include "../lib/pierremolinaro-acan2515/ACAN2515.h"
|
||||||
extern ACAN2515 can;
|
extern ACAN2515 can;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue