mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-04 10:19:29 +02:00
Rewrite CAN handler. Add Solax
This commit is contained in:
parent
0e3af999f0
commit
deb664d097
12 changed files with 400 additions and 307 deletions
|
@ -3,13 +3,12 @@
|
||||||
#include "CAN_config.h"
|
#include "CAN_config.h"
|
||||||
|
|
||||||
/* 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 */
|
||||||
unsigned long previousMillis2s = 0; // will store last time a 2s CAN Message was send
|
static 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
|
static 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
|
static 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
|
static const int interval2s = 2000; // interval (ms) at which send CAN Messages
|
||||||
const int interval10s = 10000; // interval (ms) at which send CAN Messages
|
static const int interval10s = 10000; // interval (ms) at which send CAN Messages
|
||||||
const int interval60s = 60000; // interval (ms) at which send CAN Messages
|
static const int interval60s = 60000; // interval (ms) at which send CAN Messages
|
||||||
const int rx_queue_size = 10; // Receive Queue size
|
|
||||||
|
|
||||||
//Constant startup 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}};
|
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);
|
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();
|
unsigned long currentMillis = millis();
|
||||||
// Send 2s CAN Message
|
// Send 2s CAN Message
|
||||||
|
|
|
@ -28,7 +28,8 @@ extern uint16_t max_volt_byd_can;
|
||||||
#define UPDATING 5
|
#define UPDATING 5
|
||||||
|
|
||||||
void update_values_can_byd();
|
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();
|
void send_intial_data();
|
||||||
|
|
||||||
#endif
|
#endif
|
12
Software/INVERTERS.h
Normal file
12
Software/INVERTERS.h
Normal file
|
@ -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
|
|
@ -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;
|
switch (rx_frame.MsgID)
|
||||||
static unsigned long currentMillis = millis();
|
|
||||||
|
|
||||||
// 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)
|
case 0x1DB:
|
||||||
|
if(is_message_corrupt(rx_frame))
|
||||||
{
|
{
|
||||||
//printf("New standard frame");
|
CANerror++;
|
||||||
switch (rx_frame.MsgID)
|
break; //Message content malformed, abort reading data from it
|
||||||
{
|
}
|
||||||
case 0x1DB:
|
LB_Current = (rx_frame.data.u8[0] << 3) | (rx_frame.data.u8[1] & 0xe0) >> 5;
|
||||||
if(is_message_corrupt(rx_frame))
|
if (LB_Current & 0x0400)
|
||||||
{
|
{
|
||||||
CANerror++;
|
// negative so extend the sign bit
|
||||||
break; //Message content malformed, abort reading data from it
|
LB_Current |= 0xf800;
|
||||||
}
|
}
|
||||||
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;
|
LB_Total_Voltage = ((rx_frame.data.u8[2] << 2) | (rx_frame.data.u8[3] & 0xc0) >> 6) / 2;
|
||||||
|
|
||||||
//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);
|
||||||
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
|
||||||
}
|
}
|
||||||
LB_Discharge_Power_Limit = ((rx_frame.data.u8[0] << 2 | rx_frame.data.u8[1] >> 6) / 4.0);
|
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_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);
|
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
|
||||||
}
|
}
|
||||||
LB_TEMP = (rx_frame.data.u8[0] << 2 | rx_frame.data.u8[1] >> 6);
|
LB_TEMP = (rx_frame.data.u8[0] << 2 | rx_frame.data.u8[1] >> 6);
|
||||||
if (LB_TEMP != 0x3ff) //3FF is unavailable value
|
if (LB_TEMP != 0x3ff) //3FF is unavailable value
|
||||||
{
|
{
|
||||||
LB_SOC = LB_TEMP;
|
LB_SOC = LB_TEMP;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x5BC:
|
case 0x5BC:
|
||||||
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
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
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
|
// Send 100ms CAN Message
|
||||||
if (currentMillis - previousMillis100 >= interval100)
|
if (currentMillis - previousMillis100 >= interval100)
|
||||||
{
|
{
|
||||||
|
|
|
@ -35,7 +35,8 @@ extern uint16_t CANerror;
|
||||||
#define UPDATING 5
|
#define UPDATING 5
|
||||||
|
|
||||||
void update_values_leaf_battery();
|
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);
|
uint16_t convert2unsignedint16(uint16_t signed_value);
|
||||||
bool is_message_corrupt(CAN_frame_t rx_frame);
|
bool is_message_corrupt(CAN_frame_t rx_frame);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
switch (rx_frame.MsgID)
|
||||||
static unsigned long currentMillis = millis();
|
|
||||||
|
|
||||||
// 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)
|
case 0x155: //BMS1
|
||||||
{
|
CANstillAlive = 12; //Indicate that we are still getting CAN messages from the BMS
|
||||||
//printf("New standard frame");
|
//LB_Max_Charge_Amps =
|
||||||
switch (rx_frame.MsgID)
|
//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]));
|
||||||
case 0x155: //BMS1
|
break;
|
||||||
CANstillAlive = 12; //Indicate that we are still getting CAN messages from the BMS
|
case 0x424: //BMS2
|
||||||
//LB_Max_Charge_Amps =
|
LB_Charge_Power_Limit = (rx_frame.data.u8[2]);
|
||||||
//LB_Current = (((rx_frame.data.u8[1] & 0xF8) << 5) | (rx_frame.data.u8[2]));
|
LB_Discharge_Power_Limit = (rx_frame.data.u8[3]);
|
||||||
LB_SOC = ((rx_frame.data.u8[4] << 8) | (rx_frame.data.u8[5]));
|
LB_SOH = (rx_frame.data.u8[5]);
|
||||||
break;
|
LB_MIN_TEMPERATURE = ((rx_frame.data.u8[4] & 0x7F) - 40);
|
||||||
case 0x424: //BMS2
|
LB_MAX_TEMPERATURE = ((rx_frame.data.u8[7] & 0x7F) - 40);
|
||||||
LB_Charge_Power_Limit = (rx_frame.data.u8[2]);
|
break;
|
||||||
LB_Discharge_Power_Limit = (rx_frame.data.u8[3]);
|
case 0x425: //BMS3 (could also be 445?)
|
||||||
LB_SOH = (rx_frame.data.u8[5]);
|
//LB_kWh_Remaining =
|
||||||
LB_MIN_TEMPERATURE = ((rx_frame.data.u8[4] & 0x7F) - 40);
|
//LB_Cell_Max_Voltage =
|
||||||
LB_MAX_TEMPERATURE = ((rx_frame.data.u8[7] & 0x7F) - 40);
|
//LB_Cell_Min_Voltage =
|
||||||
break;
|
break;
|
||||||
case 0x425: //BMS3 (could also be 445?)
|
default:
|
||||||
//LB_kWh_Remaining =
|
break;
|
||||||
//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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
void send_can_zoe_battery()
|
||||||
|
{
|
||||||
|
static unsigned long currentMillis = millis();
|
||||||
// Send 100ms CAN Message
|
// Send 100ms CAN Message
|
||||||
if (currentMillis - previousMillis100 >= interval100)
|
if (currentMillis - previousMillis100 >= interval100)
|
||||||
{
|
{
|
||||||
|
|
|
@ -34,7 +34,8 @@ extern uint16_t CANerror;
|
||||||
#define UPDATING 5
|
#define UPDATING 5
|
||||||
|
|
||||||
void update_values_zoe_battery();
|
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);
|
uint16_t convert2unsignedint16(uint16_t signed_value);
|
||||||
|
|
||||||
#endif
|
#endif
|
49
Software/SOLAX-CAN.cpp
Normal file
49
Software/SOLAX-CAN.cpp
Normal file
|
@ -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");
|
||||||
|
}
|
33
Software/SOLAX-CAN.h
Normal file
33
Software/SOLAX-CAN.h
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
#ifndef SOLAX_CAN_H
|
||||||
|
#define SOLAX_CAN_H
|
||||||
|
#include <Arduino.h>
|
||||||
|
#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
|
|
@ -1,11 +1,12 @@
|
||||||
/* Select battery used */
|
/* 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 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 */
|
/* 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 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 */
|
/* 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 */
|
/* Only change battery specific settings and limits in their respective .h files */
|
||||||
|
@ -13,14 +14,14 @@
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "HardwareSerial.h"
|
#include "HardwareSerial.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "logging.h"
|
#include "Logging.h"
|
||||||
#include "mbServerFCs.h"
|
#include "mbServerFCs.h"
|
||||||
#include "ModbusServerRTU.h"
|
#include "ModbusServerRTU.h"
|
||||||
#include "ESP32CAN.h"
|
#include "ESP32CAN.h"
|
||||||
#include "CAN_config.h"
|
#include "CAN_config.h"
|
||||||
#include "Adafruit_NeoPixel.h"
|
#include "Adafruit_NeoPixel.h"
|
||||||
#include "BATTERIES.h"
|
#include "BATTERIES.h"
|
||||||
#include "BYD-CAN.h"
|
#include "INVERTERS.h"
|
||||||
//CAN parameters
|
//CAN parameters
|
||||||
#define MAX_CAN_FAILURES 5000 //Amount of malformed CAN messages to allow before raising a warning
|
#define MAX_CAN_FAILURES 5000 //Amount of malformed CAN messages to allow before raising a warning
|
||||||
CAN_device_t CAN_cfg; // CAN Config
|
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
|
const uint16_t min_voltage = ABSOLUTE_MIN_VOLTAGE; //if lower Gen24 disables battery
|
||||||
uint16_t min_volt_byd_can = min_voltage;
|
uint16_t min_volt_byd_can = min_voltage;
|
||||||
uint16_t max_volt_byd_can = max_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_voltage = 3700;
|
||||||
uint16_t battery_current = 0;
|
uint16_t battery_current = 0;
|
||||||
uint16_t SOC = 5000; //SOC 0-100.00% //Updates later on from CAN
|
uint16_t SOC = 5000; //SOC 0-100.00% //Updates later on from CAN
|
||||||
|
@ -175,18 +178,52 @@ void loop()
|
||||||
}
|
}
|
||||||
|
|
||||||
void handle_can()
|
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
|
#ifdef CAN_BYD
|
||||||
handle_can_byd();
|
send_can_byd();
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef SOLAX_CAN
|
||||||
|
send_can_solax();
|
||||||
|
#endif
|
||||||
|
//Battery sending
|
||||||
#ifdef BATTERY_TYPE_LEAF
|
#ifdef BATTERY_TYPE_LEAF
|
||||||
handle_can_leaf_battery();
|
send_can_leaf_battery();
|
||||||
#endif
|
#endif
|
||||||
#ifdef TESLA_MODEL_3_BATTERY
|
#ifdef TESLA_MODEL_3_BATTERY
|
||||||
handle_can_tesla_model_3_battery();
|
send_can_tesla_model_3_battery();
|
||||||
#endif
|
#endif
|
||||||
#ifdef RENAULT_ZOE_BATTERY
|
#ifdef RENAULT_ZOE_BATTERY
|
||||||
handle_can_zoe_battery();
|
send_can_zoe_battery();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 mux = 0;
|
||||||
static int temp = 0;
|
static int temp = 0;
|
||||||
|
|
||||||
// Receive next CAN frame from queue
|
switch (rx_frame.MsgID)
|
||||||
if (xQueueReceive(CAN_cfg.rx_queue, &rx_frame, 3 * portTICK_PERIOD_MS) == pdTRUE)
|
{
|
||||||
{
|
case 0x352:
|
||||||
if (rx_frame.FIR.B.FF == CAN_frame_std)
|
//SOC
|
||||||
{
|
nominal_full_pack_energy = (((rx_frame.data.u8[1] & 0x0F) << 8) | (rx_frame.data.u8[0])); //Example 752 (75.2kWh)
|
||||||
stillAliveCAN = 6; //We got CAN-messages flowing in!
|
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
|
||||||
//printf("New standard frame");
|
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)
|
||||||
switch (rx_frame.MsgID)
|
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
|
||||||
case 0x352:
|
energy_buffer = (((rx_frame.data.u8[7] & 0x7F) << 1) | ((rx_frame.data.u8[6] & 0x80) >> 7)) * 0.1; //Example 1 * 0.1 = 0
|
||||||
//SOC
|
full_charge_complete = (rx_frame.data.u8[7] & 0x80);
|
||||||
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)
|
if(nominal_full_pack_energy > 0)
|
||||||
{ //Avoid division by 0
|
{ //Avoid division by 0
|
||||||
calculated_soc_float = ((float)expected_energy_remaining / nominal_full_pack_energy) * 10000;
|
calculated_soc_float = ((float)expected_energy_remaining / nominal_full_pack_energy) * 10000;
|
||||||
calculated_soc = calculated_soc_float;
|
calculated_soc = calculated_soc_float;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
calculated_soc = 0;
|
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();
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
#endif
|
case 0x20A:
|
||||||
default:
|
//Contactor state
|
||||||
break;
|
packContNegativeState = (rx_frame.data.u8[0] & 0x07);
|
||||||
}
|
packContPositiveState = (rx_frame.data.u8[0] & 0x38) >> 3;
|
||||||
}
|
contactor = (rx_frame.data.u8[1] & 0x0F);
|
||||||
else
|
packContactorSetState = (rx_frame.data.u8[1] & 0x0F);
|
||||||
{
|
packCtrsClosingAllowed = (rx_frame.data.u8[4] & 0x08) >> 3;
|
||||||
//printf("New extended frame");
|
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
|
// Send 100ms CAN Message
|
||||||
if (currentMillis - previousMillis100 >= interval100)
|
if (currentMillis - previousMillis100 >= interval100)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#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 "ESP32CAN.h"
|
||||||
|
|
||||||
/* User definable settings for the Tesla Model 3 battery */
|
/* 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!
|
#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
|
#define UPDATING 5
|
||||||
|
|
||||||
void update_values_tesla_model_3_battery();
|
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);
|
uint16_t convert2unsignedint16(uint16_t signed_value);
|
||||||
|
|
||||||
#endif
|
#endif
|
Loading…
Add table
Add a link
Reference in a new issue