diff --git a/Software/BYD-CAN.cpp b/Software/BYD-CAN.cpp index 0a780e94..1cbca503 100644 --- a/Software/BYD-CAN.cpp +++ b/Software/BYD-CAN.cpp @@ -3,13 +3,12 @@ #include "CAN_config.h" /* Do not change code below unless you are sure what you are doing */ -unsigned long previousMillis2s = 0; // will store last time a 2s CAN Message was send -unsigned long previousMillis10s = 0; // will store last time a 10s CAN Message was send -unsigned long previousMillis60s = 0; // will store last time a 60s CAN Message was send -const int interval2s = 2000; // interval (ms) at which send CAN Messages -const int interval10s = 10000; // interval (ms) at which send CAN Messages -const int interval60s = 60000; // interval (ms) at which send CAN Messages -const int rx_queue_size = 10; // Receive Queue size +static unsigned long previousMillis2s = 0; // will store last time a 2s CAN Message was send +static unsigned long previousMillis10s = 0; // will store last time a 10s CAN Message was send +static unsigned long previousMillis60s = 0; // will store last time a 60s CAN Message was send +static const int interval2s = 2000; // interval (ms) at which send CAN Messages +static const int interval10s = 10000; // interval (ms) at which send CAN Messages +static const int interval60s = 60000; // interval (ms) at which send CAN Messages //Constant startup messages const CAN_frame_t BYD_250 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x250,.data = {0x03, 0x16, 0x00, 0x66, 0x00, 0x33, 0x02, 0x09}}; @@ -74,7 +73,22 @@ void update_values_can_byd() BYD_210.data.u8[3] = (temperature_min & 0x00FF); } -void handle_can_byd() +void receive_can_byd(CAN_frame_t rx_frame) +{ + switch (rx_frame.MsgID) + { + case 0x151: //Message originating from BYD HVS compatible inverter. Reply with CAN identifier! + if(rx_frame.data.u8[0] & 0x01) + { + send_intial_data(); + } + break; + default: + break; + } +} + +void send_can_byd() { unsigned long currentMillis = millis(); // Send 2s CAN Message diff --git a/Software/BYD-CAN.h b/Software/BYD-CAN.h index a2ebad4f..b505fd83 100644 --- a/Software/BYD-CAN.h +++ b/Software/BYD-CAN.h @@ -28,7 +28,8 @@ extern uint16_t max_volt_byd_can; #define UPDATING 5 void update_values_can_byd(); -void handle_can_byd(); +void send_can_byd(); +void receive_can_byd(CAN_frame_t rx_frame); void send_intial_data(); #endif \ No newline at end of file diff --git a/Software/INVERTERS.h b/Software/INVERTERS.h new file mode 100644 index 00000000..38363172 --- /dev/null +++ b/Software/INVERTERS.h @@ -0,0 +1,12 @@ +#ifndef INVERTERS_H +#define INVERTERS_H + +#ifdef SOLAX_CAN + #include "SOLAX-CAN.h" +#endif + +#ifdef CAN_BYD + #include "BYD-CAN.h" +#endif + +#endif \ No newline at end of file diff --git a/Software/NISSAN-LEAF-BATTERY.cpp b/Software/NISSAN-LEAF-BATTERY.cpp index 6ece3cfa..4cc8419f 100644 --- a/Software/NISSAN-LEAF-BATTERY.cpp +++ b/Software/NISSAN-LEAF-BATTERY.cpp @@ -274,140 +274,121 @@ void update_values_leaf_battery() } } -void handle_can_leaf_battery() +void receive_can_leaf_battery(CAN_frame_t rx_frame) { - CAN_frame_t rx_frame; - static unsigned long currentMillis = millis(); - - // Receive next CAN frame from queue - if (xQueueReceive(CAN_cfg.rx_queue, &rx_frame, 3 * portTICK_PERIOD_MS) == pdTRUE) + switch (rx_frame.MsgID) { - if (rx_frame.FIR.B.FF == CAN_frame_std) + case 0x1DB: + if(is_message_corrupt(rx_frame)) { - //printf("New standard frame"); - switch (rx_frame.MsgID) - { - case 0x1DB: - if(is_message_corrupt(rx_frame)) - { - CANerror++; - break; //Message content malformed, abort reading data from it - } - LB_Current = (rx_frame.data.u8[0] << 3) | (rx_frame.data.u8[1] & 0xe0) >> 5; - if (LB_Current & 0x0400) - { - // negative so extend the sign bit - LB_Current |= 0xf800; - } + CANerror++; + break; //Message content malformed, abort reading data from it + } + LB_Current = (rx_frame.data.u8[0] << 3) | (rx_frame.data.u8[1] & 0xe0) >> 5; + if (LB_Current & 0x0400) + { + // negative so extend the sign bit + LB_Current |= 0xf800; + } - LB_Total_Voltage = ((rx_frame.data.u8[2] << 2) | (rx_frame.data.u8[3] & 0xc0) >> 6) / 2; - - //Collect various data from the BMS - LB_Relay_Cut_Request = ((rx_frame.data.u8[1] & 0x18) >> 3); - LB_Failsafe_Status = (rx_frame.data.u8[1] & 0x07); - LB_MainRelayOn_flag = (byte) ((rx_frame.data.u8[3] & 0x20) >> 5); - LB_Full_CHARGE_flag = (byte) ((rx_frame.data.u8[3] & 0x10) >> 4); - LB_Interlock = (byte) ((rx_frame.data.u8[3] & 0x08) >> 3); - break; - case 0x1DC: - if(is_message_corrupt(rx_frame)) - { - CANerror++; - break; //Message content malformed, abort reading data from it - } - LB_Discharge_Power_Limit = ((rx_frame.data.u8[0] << 2 | rx_frame.data.u8[1] >> 6) / 4.0); - LB_Charge_Power_Limit = (((rx_frame.data.u8[1] & 0x3F) << 2 | rx_frame.data.u8[2] >> 4) / 4.0); - LB_MAX_POWER_FOR_CHARGER = ((((rx_frame.data.u8[2] & 0x0F) << 6 | rx_frame.data.u8[3] >> 2) / 10.0) - 10); - break; - case 0x55B: - if(is_message_corrupt(rx_frame)) - { - CANerror++; - break; //Message content malformed, abort reading data from it - } - LB_TEMP = (rx_frame.data.u8[0] << 2 | rx_frame.data.u8[1] >> 6); - if (LB_TEMP != 0x3ff) //3FF is unavailable value - { - LB_SOC = LB_TEMP; - } - break; - case 0x5BC: - CANstillAlive = 12; //Indicate that we are still getting CAN messages from the BMS + LB_Total_Voltage = ((rx_frame.data.u8[2] << 2) | (rx_frame.data.u8[3] & 0xc0) >> 6) / 2; + + //Collect various data from the BMS + LB_Relay_Cut_Request = ((rx_frame.data.u8[1] & 0x18) >> 3); + LB_Failsafe_Status = (rx_frame.data.u8[1] & 0x07); + LB_MainRelayOn_flag = (byte) ((rx_frame.data.u8[3] & 0x20) >> 5); + LB_Full_CHARGE_flag = (byte) ((rx_frame.data.u8[3] & 0x10) >> 4); + LB_Interlock = (byte) ((rx_frame.data.u8[3] & 0x08) >> 3); + break; + case 0x1DC: + if(is_message_corrupt(rx_frame)) + { + CANerror++; + break; //Message content malformed, abort reading data from it + } + LB_Discharge_Power_Limit = ((rx_frame.data.u8[0] << 2 | rx_frame.data.u8[1] >> 6) / 4.0); + LB_Charge_Power_Limit = (((rx_frame.data.u8[1] & 0x3F) << 2 | rx_frame.data.u8[2] >> 4) / 4.0); + LB_MAX_POWER_FOR_CHARGER = ((((rx_frame.data.u8[2] & 0x0F) << 6 | rx_frame.data.u8[3] >> 2) / 10.0) - 10); + break; + case 0x55B: + if(is_message_corrupt(rx_frame)) + { + CANerror++; + break; //Message content malformed, abort reading data from it + } + LB_TEMP = (rx_frame.data.u8[0] << 2 | rx_frame.data.u8[1] >> 6); + if (LB_TEMP != 0x3ff) //3FF is unavailable value + { + LB_SOC = LB_TEMP; + } + break; + case 0x5BC: + CANstillAlive = 12; //Indicate that we are still getting CAN messages from the BMS - LB_MAX = ((rx_frame.data.u8[5] & 0x10) >> 4); - if (LB_MAX) - { - LB_Max_GIDS = (rx_frame.data.u8[0] << 2) | ((rx_frame.data.u8[1] & 0xC0) >> 6); - //Max gids active, do nothing - //Only the 30/40/62kWh packs have this mux - } - else - { - //Normal current GIDS value is transmitted - LB_GIDS = (rx_frame.data.u8[0] << 2) | ((rx_frame.data.u8[1] & 0xC0) >> 6); - LB_Wh_Remaining = (LB_GIDS * WH_PER_GID); - } - - LB_TEMP = (rx_frame.data.u8[4] >> 1); - if (LB_TEMP != 0) - { - LB_StateOfHealth = LB_TEMP; //Collect state of health from battery - } - break; - 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 ((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); - } - 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); - } - } - if(LEAF_Battery_Type == ZE1_BATTERY) - { //note different mux location in first frame - if ((rx_frame.data.u8[0] & 0x0F) == 1) - { - LB_HistData_Temperature_MAX = ((rx_frame.data.u8[2] / 2) - 40); - } - if ((rx_frame.data.u8[0] & 0x0F) == 3) - { - LB_HistData_Temperature_MIN = ((rx_frame.data.u8[2] / 2) - 40); - } - } - break; - case 0x59E: - //AZE0 2013-2017 or ZE1 2018-2023 battery detected - //Only detect as AZE0 if not already set as ZE1 - if(LEAF_Battery_Type != ZE1_BATTERY) - { - LEAF_Battery_Type = AZE0_BATTERY; - } - break; - case 0x1ED: - case 0x1C2: - //ZE1 2018-2023 battery detected! - LEAF_Battery_Type = ZE1_BATTERY; - break; - #ifdef CAN_BYD - case 0x151: //Message originating from BYD HVS compatible inverter. Send CAN identifier! - if(rx_frame.data.u8[0] & 0x01) - { - send_intial_data(); - } - break; - #endif - default: - break; - } + LB_MAX = ((rx_frame.data.u8[5] & 0x10) >> 4); + if (LB_MAX) + { + LB_Max_GIDS = (rx_frame.data.u8[0] << 2) | ((rx_frame.data.u8[1] & 0xC0) >> 6); + //Max gids active, do nothing + //Only the 30/40/62kWh packs have this mux } else { - //printf("New extended frame"); + //Normal current GIDS value is transmitted + LB_GIDS = (rx_frame.data.u8[0] << 2) | ((rx_frame.data.u8[1] & 0xC0) >> 6); + LB_Wh_Remaining = (LB_GIDS * WH_PER_GID); } - } + + LB_TEMP = (rx_frame.data.u8[4] >> 1); + if (LB_TEMP != 0) + { + LB_StateOfHealth = LB_TEMP; //Collect state of health from battery + } + break; + 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 ((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); + } + 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); + } + } + if(LEAF_Battery_Type == ZE1_BATTERY) + { //note different mux location in first frame + if ((rx_frame.data.u8[0] & 0x0F) == 1) + { + LB_HistData_Temperature_MAX = ((rx_frame.data.u8[2] / 2) - 40); + } + if ((rx_frame.data.u8[0] & 0x0F) == 3) + { + LB_HistData_Temperature_MIN = ((rx_frame.data.u8[2] / 2) - 40); + } + } + break; + case 0x59E: + //AZE0 2013-2017 or ZE1 2018-2023 battery detected + //Only detect as AZE0 if not already set as ZE1 + if(LEAF_Battery_Type != ZE1_BATTERY) + { + LEAF_Battery_Type = AZE0_BATTERY; + } + break; + case 0x1ED: + case 0x1C2: + //ZE1 2018-2023 battery detected! + LEAF_Battery_Type = ZE1_BATTERY; + break; + default: + break; + } +} +void send_can_leaf_battery() +{ + static unsigned long currentMillis = millis(); // Send 100ms CAN Message if (currentMillis - previousMillis100 >= interval100) { diff --git a/Software/NISSAN-LEAF-BATTERY.h b/Software/NISSAN-LEAF-BATTERY.h index 355743cc..eac5d81e 100644 --- a/Software/NISSAN-LEAF-BATTERY.h +++ b/Software/NISSAN-LEAF-BATTERY.h @@ -35,7 +35,8 @@ extern uint16_t CANerror; #define UPDATING 5 void update_values_leaf_battery(); -void handle_can_leaf_battery(); +void receive_can_leaf_battery(CAN_frame_t rx_frame); +void send_can_leaf_battery(); uint16_t convert2unsignedint16(uint16_t signed_value); bool is_message_corrupt(CAN_frame_t rx_frame); diff --git a/Software/RENAULT-ZOE-BATTERY.cpp b/Software/RENAULT-ZOE-BATTERY.cpp index 538280c1..4737bef8 100644 --- a/Software/RENAULT-ZOE-BATTERY.cpp +++ b/Software/RENAULT-ZOE-BATTERY.cpp @@ -120,54 +120,35 @@ void update_values_zoe_battery() } } -void handle_can_zoe_battery() +void receive_can_zoe_battery(CAN_frame_t rx_frame) { - CAN_frame_t rx_frame; - static unsigned long currentMillis = millis(); - - // Receive next CAN frame from queue - if (xQueueReceive(CAN_cfg.rx_queue, &rx_frame, 3 * portTICK_PERIOD_MS) == pdTRUE) + switch (rx_frame.MsgID) { - if (rx_frame.FIR.B.FF == CAN_frame_std) - { - //printf("New standard frame"); - switch (rx_frame.MsgID) - { - case 0x155: //BMS1 - CANstillAlive = 12; //Indicate that we are still getting CAN messages from the BMS - //LB_Max_Charge_Amps = - //LB_Current = (((rx_frame.data.u8[1] & 0xF8) << 5) | (rx_frame.data.u8[2])); - LB_SOC = ((rx_frame.data.u8[4] << 8) | (rx_frame.data.u8[5])); - break; - case 0x424: //BMS2 - LB_Charge_Power_Limit = (rx_frame.data.u8[2]); - LB_Discharge_Power_Limit = (rx_frame.data.u8[3]); - LB_SOH = (rx_frame.data.u8[5]); - LB_MIN_TEMPERATURE = ((rx_frame.data.u8[4] & 0x7F) - 40); - LB_MAX_TEMPERATURE = ((rx_frame.data.u8[7] & 0x7F) - 40); - break; - case 0x425: //BMS3 (could also be 445?) - //LB_kWh_Remaining = - //LB_Cell_Max_Voltage = - //LB_Cell_Min_Voltage = - break; - #ifdef CAN_BYD - case 0x151: //Message originating from BYD HVS compatible inverter. Send CAN identifier! - if(rx_frame.data.u8[0] & 0x01) - { - send_intial_data(); - } - #endif - break; - default: - break; - } - } - else - { - //printf("New extended frame"); - } + case 0x155: //BMS1 + CANstillAlive = 12; //Indicate that we are still getting CAN messages from the BMS + //LB_Max_Charge_Amps = + //LB_Current = (((rx_frame.data.u8[1] & 0xF8) << 5) | (rx_frame.data.u8[2])); + LB_SOC = ((rx_frame.data.u8[4] << 8) | (rx_frame.data.u8[5])); + break; + case 0x424: //BMS2 + LB_Charge_Power_Limit = (rx_frame.data.u8[2]); + LB_Discharge_Power_Limit = (rx_frame.data.u8[3]); + LB_SOH = (rx_frame.data.u8[5]); + LB_MIN_TEMPERATURE = ((rx_frame.data.u8[4] & 0x7F) - 40); + LB_MAX_TEMPERATURE = ((rx_frame.data.u8[7] & 0x7F) - 40); + break; + case 0x425: //BMS3 (could also be 445?) + //LB_kWh_Remaining = + //LB_Cell_Max_Voltage = + //LB_Cell_Min_Voltage = + break; + default: + break; } +} +void send_can_zoe_battery() +{ + static unsigned long currentMillis = millis(); // Send 100ms CAN Message if (currentMillis - previousMillis100 >= interval100) { diff --git a/Software/RENAULT-ZOE-BATTERY.h b/Software/RENAULT-ZOE-BATTERY.h index 9ff2eccc..b743d231 100644 --- a/Software/RENAULT-ZOE-BATTERY.h +++ b/Software/RENAULT-ZOE-BATTERY.h @@ -34,7 +34,8 @@ extern uint16_t CANerror; #define UPDATING 5 void update_values_zoe_battery(); -void handle_can_zoe_battery(); +void receive_can_zoe_battery(CAN_frame_t rx_frame); +void send_can_zoe_battery(); uint16_t convert2unsignedint16(uint16_t signed_value); #endif \ No newline at end of file diff --git a/Software/SOLAX-CAN.cpp b/Software/SOLAX-CAN.cpp new file mode 100644 index 00000000..0d6342e5 --- /dev/null +++ b/Software/SOLAX-CAN.cpp @@ -0,0 +1,49 @@ +#include "SOLAX-CAN.h" +#include "ESP32CAN.h" +#include "CAN_config.h" + +/* Do not change code below unless you are sure what you are doing */ +static unsigned long previousMillis2s = 0; // will store last time a 2s CAN Message was send +static unsigned long previousMillis10s = 0; // will store last time a 10s CAN Message was send +static unsigned long previousMillis60s = 0; // will store last time a 60s CAN Message was send +static const int interval2s = 2000; // interval (ms) at which send CAN Messages +static const int interval10s = 10000; // interval (ms) at which send CAN Messages +static const int interval60s = 60000; // interval (ms) at which send CAN Messages + +CAN_frame_t SOLAX_1872 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_ext,}},.MsgID = 0x1872,.data = {0x03, 0x16, 0x00, 0x66, 0x00, 0x33, 0x02, 0x09}}; + +void update_values_can_solax() +{ //This function maps all the values fetched from battery CAN to the correct CAN messages + +} + +void send_can_solax() +{ + unsigned long currentMillis = millis(); + // Send 2s CAN Message + if (currentMillis - previousMillis2s >= interval2s) + { + previousMillis2s = currentMillis; + + } + // Send 10s CAN Message + if (currentMillis - previousMillis10s >= interval10s) + { + previousMillis10s = currentMillis; + + //Serial.println("CAN 10s done"); + } + //Send 60s message + if (currentMillis - previousMillis60s >= interval60s) + { + previousMillis60s = currentMillis; + + //ESP32Can.CANWriteFrame(&BYD_190); + //Serial.println("CAN 60s done"); + } +} + +void receive_can_solax(CAN_frame_t rx_frame) +{ + Serial.println("Inverter sending CAN message"); +} \ No newline at end of file diff --git a/Software/SOLAX-CAN.h b/Software/SOLAX-CAN.h new file mode 100644 index 00000000..6a91c7f0 --- /dev/null +++ b/Software/SOLAX-CAN.h @@ -0,0 +1,33 @@ +#ifndef SOLAX_CAN_H +#define SOLAX_CAN_H +#include +#include "ESP32CAN.h" + +extern uint16_t SOC; +extern uint16_t StateOfHealth; +extern uint16_t battery_voltage; +extern uint16_t battery_current; +extern uint16_t capacity_Wh; +extern uint16_t remaining_capacity_Wh; +extern uint16_t max_target_discharge_power; +extern uint16_t max_target_charge_power; +extern uint16_t bms_status; +extern uint16_t bms_char_dis_status; +extern uint16_t stat_batt_power; +extern uint16_t temperature_min; +extern uint16_t temperature_max; +extern uint16_t CANerror; +extern uint16_t min_volt_byd_can; +extern uint16_t max_volt_byd_can; +// Definitions for BMS status +#define STANDBY 0 +#define INACTIVE 1 +#define DARKSTART 2 +#define ACTIVE 3 +#define FAULT 4 +#define UPDATING 5 + +void update_values_can_byd(); +void send_can_solax(); +void receive_can_solax(CAN_frame_t rx_frame); +#endif \ No newline at end of file diff --git a/Software/Software.ino b/Software/Software.ino index a344cd38..1dee6f69 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -1,11 +1,12 @@ /* Select battery used */ -//#define BATTERY_TYPE_LEAF // See NISSAN-LEAF-BATTERY.h for more LEAF battery settings +#define BATTERY_TYPE_LEAF // See NISSAN-LEAF-BATTERY.h for more LEAF battery settings //#define TESLA_MODEL_3_BATTERY // See TESLA-MODEL-3-BATTERY.h for more Tesla battery settings -#define RENAULT_ZOE_BATTERY // See RENAULT-ZOE-BATTERY.h for more Zoe battery settings +//#define RENAULT_ZOE_BATTERY // See RENAULT-ZOE-BATTERY.h for more Zoe battery settings /* Select inverter communication protocol. See Wiki for which to use with your inverter: https://github.com/dalathegreat/BYD-Battery-Emulator-For-Gen24/wiki */ -#define MODBUS_BYD //Enable this line to emulate a "BYD 11kWh HVM battery" over Modbus RTU +//#define MODBUS_BYD //Enable this line to emulate a "BYD 11kWh HVM battery" over Modbus RTU //#define CAN_BYD //Enable this line to emulate a "BYD Battery-Box Premium HVS" over CAN Bus +#define SOLAX_CAN //Enable this line to emulate a "SolaX Triple Power LFP" over CAN bus /* Do not change any code below this line unless you are sure what you are doing */ /* Only change battery specific settings and limits in their respective .h files */ @@ -13,14 +14,14 @@ #include #include "HardwareSerial.h" #include "config.h" -#include "logging.h" +#include "Logging.h" #include "mbServerFCs.h" #include "ModbusServerRTU.h" #include "ESP32CAN.h" #include "CAN_config.h" #include "Adafruit_NeoPixel.h" #include "BATTERIES.h" -#include "BYD-CAN.h" +#include "INVERTERS.h" //CAN parameters #define MAX_CAN_FAILURES 5000 //Amount of malformed CAN messages to allow before raising a warning CAN_device_t CAN_cfg; // CAN Config @@ -48,6 +49,8 @@ const uint16_t max_voltage = ABSOLUTE_MAX_VOLTAGE; //if higher charging is not p const uint16_t min_voltage = ABSOLUTE_MIN_VOLTAGE; //if lower Gen24 disables battery uint16_t min_volt_byd_can = min_voltage; uint16_t max_volt_byd_can = max_voltage; +uint16_t min_volt_solax_can = min_voltage; +uint16_t max_volt_solax_can = max_voltage; uint16_t battery_voltage = 3700; uint16_t battery_current = 0; uint16_t SOC = 5000; //SOC 0-100.00% //Updates later on from CAN @@ -175,18 +178,52 @@ void loop() } void handle_can() -{ //Depending on which parts are used, handle their respective CAN routines +{ //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 + CAN_frame_t rx_frame; + if (xQueueReceive(CAN_cfg.rx_queue, &rx_frame, 3 * portTICK_PERIOD_MS) == pdTRUE) + { + if (rx_frame.FIR.B.FF == CAN_frame_std) + { + //printf("New standard frame"); + #ifdef BATTERY_TYPE_LEAF + receive_can_leaf_battery(rx_frame); + #endif + #ifdef TESLA_MODEL_3_BATTERY + receive_can_tesla_model_3_battery(rx_frame); + #endif + #ifdef RENAULT_ZOE_BATTERY + receive_can_zoe_battery(rx_frame); + #endif + #ifdef CAN_BYD + receive_can_byd(rx_frame); + #endif + } + else + { + //printf("New extended frame"); + #ifdef SOLAX_CAN + receive_can_solax(rx_frame); + #endif + } + } + //When we are done checking if a CAN message has arrived, we can focus on sending CAN messages + //Inverter sending #ifdef CAN_BYD - handle_can_byd(); + send_can_byd(); #endif + #ifdef SOLAX_CAN + send_can_solax(); + #endif + //Battery sending #ifdef BATTERY_TYPE_LEAF - handle_can_leaf_battery(); + send_can_leaf_battery(); #endif #ifdef TESLA_MODEL_3_BATTERY - handle_can_tesla_model_3_battery(); + send_can_tesla_model_3_battery(); #endif #ifdef RENAULT_ZOE_BATTERY - handle_can_zoe_battery(); + send_can_zoe_battery(); #endif } diff --git a/Software/TESLA-MODEL-3-BATTERY.cpp b/Software/TESLA-MODEL-3-BATTERY.cpp index ef60e186..c91ceef6 100644 --- a/Software/TESLA-MODEL-3-BATTERY.cpp +++ b/Software/TESLA-MODEL-3-BATTERY.cpp @@ -177,127 +177,108 @@ void update_values_tesla_model_3_battery() } } -void handle_can_tesla_model_3_battery() +void receive_can_tesla_model_3_battery(CAN_frame_t rx_frame) { - CAN_frame_t rx_frame; - static unsigned long currentMillis = millis(); static int mux = 0; static int temp = 0; - // Receive next CAN frame from queue - if (xQueueReceive(CAN_cfg.rx_queue, &rx_frame, 3 * portTICK_PERIOD_MS) == pdTRUE) - { - if (rx_frame.FIR.B.FF == CAN_frame_std) - { - stillAliveCAN = 6; //We got CAN-messages flowing in! - //printf("New standard frame"); - switch (rx_frame.MsgID) - { - case 0x352: - //SOC - 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 - 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) - 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); + switch (rx_frame.MsgID) + { + case 0x352: + //SOC + 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 + 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) + 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); - if(nominal_full_pack_energy > 0) - { //Avoid division by 0 - calculated_soc_float = ((float)expected_energy_remaining / nominal_full_pack_energy) * 10000; - calculated_soc = calculated_soc_float; - } - else - { - calculated_soc = 0; - } - break; - case 0x20A: - //Contactor state - packContNegativeState = (rx_frame.data.u8[0] & 0x07); - packContPositiveState = (rx_frame.data.u8[0] & 0x38) >> 3; - contactor = (rx_frame.data.u8[1] & 0x0F); - packContactorSetState = (rx_frame.data.u8[1] & 0x0F); - packCtrsClosingAllowed = (rx_frame.data.u8[4] & 0x08) >> 3; - pyroTestInProgress = (rx_frame.data.u8[4] & 0x20) >> 5; - hvil_status = (rx_frame.data.u8[5] & 0x0F); - break; - case 0x252: - //Limits - 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??? - 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? - break; - case 0x132: - //battery amps/volts - 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) - if (amps > 32768) - { - amps = - (65535 - amps); - } - amps = amps * 0.1; - 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 - if(battery_charge_time_remaining == 4095) - { - battery_charge_time_remaining = 0; - } - - break; - case 0x3D2: - // total charge/discharge kwh - break; - case 0x332: - //min/max hist values - mux = rx_frame.data.u8[0]; - mux = mux & 0x03; - - if(mux == 1) //Cell voltages - { - //todo handle cell voltages - //not required by the Gen24, but nice stats located here! - } - if(mux == 0)//Temperature sensors - { - temp = rx_frame.data.u8[2]; - max_temp = (temp * 0.5) - 40; //in celcius, Example 24 - - temp = rx_frame.data.u8[3]; - min_temp = (temp * 0.5) - 40; //in celcius , Example 24 - } - break; - case 0x2d2: - //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 - 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_discharge_current = (((rx_frame.data.u8[7] & 0x3F) << 8) | rx_frame.data.u8[6]) * 0.128; //Example 430? * 0.128 = 55.4? - break; - case 0x2b4: - low_voltage = (((rx_frame.data.u8[1] & 0x03) << 8) | rx_frame.data.u8[0]) * 0.0390625; - high_voltage = (((rx_frame.data.u8[2] << 6) | ((rx_frame.data.u8[1] & 0xFC) >> 2))) * 0.146484; - output_current = (((rx_frame.data.u8[4] & 0x0F) << 8) | rx_frame.data.u8[3]) / 100; - break; - #ifdef CAN_BYD - case 0x151: //Message originating from BYD HVS compatible inverter. Send CAN identifier! - if(rx_frame.data.u8[0] & 0x01) - { - send_intial_data(); - } + if(nominal_full_pack_energy > 0) + { //Avoid division by 0 + calculated_soc_float = ((float)expected_energy_remaining / nominal_full_pack_energy) * 10000; + calculated_soc = calculated_soc_float; + } + else + { + calculated_soc = 0; + } break; - #endif - default: - break; - } - } - else - { - //printf("New extended frame"); - } - } + case 0x20A: + //Contactor state + packContNegativeState = (rx_frame.data.u8[0] & 0x07); + packContPositiveState = (rx_frame.data.u8[0] & 0x38) >> 3; + contactor = (rx_frame.data.u8[1] & 0x0F); + packContactorSetState = (rx_frame.data.u8[1] & 0x0F); + packCtrsClosingAllowed = (rx_frame.data.u8[4] & 0x08) >> 3; + pyroTestInProgress = (rx_frame.data.u8[4] & 0x20) >> 5; + hvil_status = (rx_frame.data.u8[5] & 0x0F); + break; + case 0x252: + //Limits + 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??? + 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? + break; + case 0x132: + //battery amps/volts + 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) + if (amps > 32768) + { + amps = - (65535 - amps); + } + amps = amps * 0.1; + 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 + if(battery_charge_time_remaining == 4095) + { + battery_charge_time_remaining = 0; + } + + break; + case 0x3D2: + // total charge/discharge kwh + break; + case 0x332: + //min/max hist values + mux = rx_frame.data.u8[0]; + mux = mux & 0x03; + + if(mux == 1) //Cell voltages + { + //todo handle cell voltages + //not required by the Gen24, but nice stats located here! + } + if(mux == 0)//Temperature sensors + { + temp = rx_frame.data.u8[2]; + max_temp = (temp * 0.5) - 40; //in celcius, Example 24 + + temp = rx_frame.data.u8[3]; + min_temp = (temp * 0.5) - 40; //in celcius , Example 24 + } + break; + case 0x2d2: + //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 + 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_discharge_current = (((rx_frame.data.u8[7] & 0x3F) << 8) | rx_frame.data.u8[6]) * 0.128; //Example 430? * 0.128 = 55.4? + break; + case 0x2b4: + low_voltage = (((rx_frame.data.u8[1] & 0x03) << 8) | rx_frame.data.u8[0]) * 0.0390625; + high_voltage = (((rx_frame.data.u8[2] << 6) | ((rx_frame.data.u8[1] & 0xFC) >> 2))) * 0.146484; + output_current = (((rx_frame.data.u8[4] & 0x0F) << 8) | rx_frame.data.u8[3]) / 100; + break; + default: + break; + } +} +void send_can_tesla_model_3_battery() +{ + static unsigned long currentMillis = millis(); // Send 100ms CAN Message if (currentMillis - previousMillis100 >= interval100) { diff --git a/Software/TESLA-MODEL-3-BATTERY.h b/Software/TESLA-MODEL-3-BATTERY.h index 8afd42e7..35185a66 100644 --- a/Software/TESLA-MODEL-3-BATTERY.h +++ b/Software/TESLA-MODEL-3-BATTERY.h @@ -1,6 +1,7 @@ #ifndef TESLA_MODEL_3_BATTERY_H #define TESLA_MODEL_3_BATTERY_H #include +#include "ESP32CAN.h" /* User definable settings for the Tesla Model 3 battery */ #define BATTERY_WH_MAX 60000 //Battery size in Wh (Maximum value Fronius accepts is 60000 [60kWh] you can use larger 65/75/90 batteries but do set value over 60000! @@ -34,7 +35,8 @@ extern uint16_t CANerror; #define UPDATING 5 void update_values_tesla_model_3_battery(); -void handle_can_tesla_model_3_battery(); +void receive_can_tesla_model_3_battery(CAN_frame_t rx_frame); +void send_can_tesla_model_3_battery(); uint16_t convert2unsignedint16(uint16_t signed_value); #endif \ No newline at end of file