Skip to content

Commit

Permalink
Merge pull request #68 from dalathegreat/SMA-CAN
Browse files Browse the repository at this point in the history
SMA BYD CAN
  • Loading branch information
dalathegreat authored Oct 16, 2023
2 parents 683f71e + e7629b9 commit 7a7156b
Show file tree
Hide file tree
Showing 7 changed files with 267 additions and 3 deletions.
4 changes: 4 additions & 0 deletions Software/INVERTERS.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
#include "BYD-CAN.h"
#endif

#ifdef SMA_CAN
#include "SMA-CAN.h"
#endif

#ifdef PYLON_CAN
#include "PYLON-CAN.h"
#endif
Expand Down
199 changes: 199 additions & 0 deletions Software/SMA-CAN.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
#include "SMA-CAN.h"
#include "ESP32CAN.h"
#include "CAN_config.h"

//TODO, change CAN sending routine once confirmed that 500ms interval is OK for this battery type

/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillis1s = 0; // will store last time a Xs CAN Message was send
static unsigned long previousMillis2s = 0; // will store last time a Xs CAN Message was send
static unsigned long previousMillis3s = 0; // will store last time a Xs CAN Message was send
static unsigned long previousMillis4s = 0; // will store last time a Xs CAN Message was send
static unsigned long previousMillis5s = 0; // will store last time a Xs CAN Message was send
static unsigned long previousMillis6s = 0; // will store last time a Xs CAN Message was send
static unsigned long previousMillis7s = 0; // will store last time a Xs CAN Message was send
static unsigned long previousMillis8s = 0; // will store last time a Xs CAN Message was send
static unsigned long previousMillis9s = 0; // will store last time a Xs CAN Message was send
static unsigned long previousMillis10s = 0; // will store last time a Xs CAN Message was send
static unsigned long previousMillis11s = 0; // will store last time a Xs CAN Message was send
static unsigned long previousMillis12s = 0; // will store last time a Xs CAN Message was send
static const int interval1s = 100; // interval (ms) at which send CAN Messages
static const int interval2s = 102; // interval (ms) at which send CAN Messages
static const int interval3s = 104; // interval (ms) at which send CAN Messages
static const int interval4s = 106; // interval (ms) at which send CAN Messages
static const int interval5s = 108; // interval (ms) at which send CAN Messages
static const int interval6s = 110; // interval (ms) at which send CAN Messages
static const int interval7s = 112; // interval (ms) at which send CAN Messages
static const int interval8s = 114; // interval (ms) at which send CAN Messages
static const int interval9s = 116; // interval (ms) at which send CAN Messages
static const int interval10s = 118; // interval (ms) at which send CAN Messages
static const int interval11s = 120; // interval (ms) at which send CAN Messages
static const int interval12s = 122; // interval (ms) at which send CAN 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_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
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 charge_current = 0;
static int temperature_average = 0;
static int ampere_hours_remaining = 0;

void update_values_can_sma()
{ //This function maps all the values fetched from battery CAN to the correct CAN messages
//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)
//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)

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
discharge_current = (discharge_current*10); //Value needs a decimal before getting sent to inverter (81.0A)

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)

//Map values to CAN messages
//Maxvoltage (eg 400.0V = 4000 , 16bits long)
SMA_358.data.u8[0] = (max_volt_sma_can >> 8);
SMA_358.data.u8[1] = (max_volt_sma_can & 0x00FF);
//Minvoltage (eg 300.0V = 3000 , 16bits long)
SMA_358.data.u8[2] = (min_volt_sma_can >> 8); //Minvoltage behaves strange on SMA, cuts out at 56% of the set value?
SMA_358.data.u8[3] = (min_volt_sma_can & 0x00FF);
//Discharge limited current, 500 = 50A, (0.1, A)
SMA_358.data.u8[4] = (discharge_current >> 8);
SMA_358.data.u8[5] = (discharge_current & 0x00FF);
//Charge limited current, 125 =12.5A (0.1, A)
SMA_358.data.u8[6] = (charge_current >> 8);
SMA_358.data.u8[7] = (charge_current & 0x00FF);

//SOC (100.00%)
SMA_3D8.data.u8[0] = (SOC >> 8);
SMA_3D8.data.u8[1] = (SOC & 0x00FF);
//StateOfHealth (100.00%)
SMA_3D8.data.u8[2] = (StateOfHealth >> 8);
SMA_3D8.data.u8[3] = (StateOfHealth & 0x00FF);
//State of charge (AH, 0.1)
SMA_3D8.data.u8[4] = (ampere_hours_remaining >> 8);
SMA_3D8.data.u8[5] = (ampere_hours_remaining & 0x00FF);

//Voltage (370.0)
SMA_4D8.data.u8[0] = (battery_voltage >> 8);
SMA_4D8.data.u8[1] = (battery_voltage & 0x00FF);
//Current (TODO, signed OK?)
SMA_4D8.data.u8[2] = (battery_current >> 8);
SMA_4D8.data.u8[3] = (battery_current & 0x00FF);
//Temperature average
SMA_4D8.data.u8[4] = (temperature_average >> 8);
SMA_4D8.data.u8[5] = (temperature_average & 0x00FF);

//Error bits
//SMA_158.data.u8[0] = //bit12 Fault high temperature, bit34Battery cellundervoltage, bit56 Battery cell overvoltage, bit78 batterysystemdefect
//TODO, add all error bits
}

void receive_can_sma(CAN_frame_t rx_frame)
{
switch (rx_frame.MsgID)
{
case 0x660: //Message originating from SMA inverter
break;
case 0x5E0: //Message originating from SMA inverter
break;
case 0x560: //Message originating from SMA inverter
break;
default:
break;
}
}

void send_can_sma()
{
unsigned long currentMillis = millis();

// Send CAN Message every X ms, 1000 for testing
if (currentMillis - previousMillis1s >= interval1s)
{
previousMillis1s = currentMillis;

ESP32Can.CANWriteFrame(&SMA_558);
}
if (currentMillis - previousMillis2s >= interval2s)
{
previousMillis2s = currentMillis;

ESP32Can.CANWriteFrame(&SMA_598);
}
if (currentMillis - previousMillis3s >= interval3s)
{
previousMillis3s = currentMillis;

ESP32Can.CANWriteFrame(&SMA_5D8);
}
if (currentMillis - previousMillis4s >= interval4s)
{
previousMillis4s = currentMillis;

ESP32Can.CANWriteFrame(&SMA_618_1);
}
if (currentMillis - previousMillis5s >= interval5s)
{
previousMillis5s = currentMillis;

ESP32Can.CANWriteFrame(&SMA_618_2);
}
if (currentMillis - previousMillis6s >= interval6s)
{
previousMillis6s = currentMillis;

ESP32Can.CANWriteFrame(&SMA_618_3);
}
if (currentMillis - previousMillis7s >= interval7s)
{
previousMillis7s = currentMillis;

ESP32Can.CANWriteFrame(&SMA_358);
}
if (currentMillis - previousMillis8s >= interval8s)
{
previousMillis8s = currentMillis;

ESP32Can.CANWriteFrame(&SMA_3D8);
}
if (currentMillis - previousMillis9s >= interval9s)
{
previousMillis9s = currentMillis;

ESP32Can.CANWriteFrame(&SMA_458);
}
if (currentMillis - previousMillis10s >= interval10s)
{
previousMillis10s = currentMillis;

ESP32Can.CANWriteFrame(&SMA_518);
}
if (currentMillis - previousMillis11s >= interval11s)
{
previousMillis11s = currentMillis;

ESP32Can.CANWriteFrame(&SMA_4D8);
}
if (currentMillis - previousMillis12s >= interval12s)
{
previousMillis12s = currentMillis;

ESP32Can.CANWriteFrame(&SMA_158);
}

}
37 changes: 37 additions & 0 deletions Software/SMA-CAN.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#ifndef SMA_CAN_H
#define SMA_CAN_H
#include <Arduino.h>
#include "ESP32CAN.h"
#include "USER_SETTINGS.h"

extern uint16_t SOC; //SOC%, 0-100.00 (0-10000)
extern uint16_t StateOfHealth; //SOH%, 0-100.00 (0-10000)
extern uint16_t battery_voltage; //V+1, 0-500.0 (0-5000)
extern uint16_t battery_current; //A+1, Goes thru convert2unsignedint16 function (5.0A = 50, -5.0A = 65485)
extern uint16_t capacity_Wh; //Wh, 0-60000
extern uint16_t remaining_capacity_Wh; //Wh, 0-60000
extern uint16_t max_target_discharge_power; //W, 0-60000
extern uint16_t max_target_charge_power; //W, 0-60000
extern uint16_t bms_status; //Enum, 0-5
extern uint16_t bms_char_dis_status; //Enum, 0-2
extern uint16_t stat_batt_power; //W, Goes thru convert2unsignedint16 function (5W = 5, -5W = 65530)
extern uint16_t temperature_min; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385)
extern uint16_t temperature_max; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385)
extern uint16_t cell_max_voltage; //mV, 0-4350
extern uint16_t cell_min_voltage; //mV, 0-4350
extern uint16_t min_volt_sma_can;
extern uint16_t max_volt_sma_can;
extern uint8_t LEDcolor; //Enum, 0-2
// 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_sma();
void send_can_sma();
void receive_can_sma(CAN_frame_t rx_frame);

#endif
14 changes: 14 additions & 0 deletions Software/Software.ino
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ uint16_t min_volt_solax_can = min_voltage;
uint16_t max_volt_solax_can = max_voltage;
uint16_t min_volt_pylon_can = min_voltage;
uint16_t max_volt_pylon_can = max_voltage;
uint16_t min_volt_sma_can = min_voltage;
uint16_t max_volt_sma_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
Expand Down Expand Up @@ -200,6 +202,9 @@ void setup()
#ifdef CAN_BYD
Serial.println("BYD CAN protocol selected");
#endif
#ifdef SMA_CAN
Serial.println("SMA CAN protocol selected");
#endif
//Inform user what battery is used
#ifdef BATTERY_TYPE_LEAF
Serial.println("Nissan LEAF battery selected");
Expand Down Expand Up @@ -275,6 +280,9 @@ void handle_can()
#endif
#ifdef CAN_BYD
receive_can_byd(rx_frame);
#endif
#ifdef SMA_CAN
receive_can_sma(rx_frame);
#endif
#ifdef CHADEMO
receive_can_chademo(rx_frame);
Expand All @@ -297,6 +305,9 @@ void handle_can()
#ifdef CAN_BYD
send_can_byd();
#endif
#ifdef SMA_CAN
send_can_sma();
#endif
//Battery sending
#ifdef BATTERY_TYPE_LEAF
send_can_leaf_battery();
Expand Down Expand Up @@ -392,6 +403,9 @@ void handle_inverter()
#ifdef CAN_BYD
update_values_can_byd();
#endif
#ifdef SMA_CAN
update_values_can_sma();
#endif
#ifdef PYLON_CAN
update_values_can_pylon();
#endif
Expand Down
13 changes: 11 additions & 2 deletions Software/TESLA-MODEL-3-BATTERY.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,10 @@ void update_values_tesla_model_3_battery()
stat_batt_power = (volts * amps); //TODO, check if scaling is OK

min_temp = (min_temp * 10);
temperature_min = convert2unsignedint16(min_temp);
temperature_min = convert2unsignedInt16(min_temp);

max_temp = (max_temp * 10);
temperature_max = convert2unsignedint16(max_temp);
temperature_max = convert2unsignedInt16(max_temp);

cell_max_voltage = cell_max_v;

Expand Down Expand Up @@ -394,3 +394,12 @@ the first, for a few cycles, then stop all messages which causes the contactor
}
}
}
uint16_t convert2unsignedInt16(int16_t signed_value)
{
if(signed_value < 0){
return(65535 + signed_value);
}
else{
return (uint16_t)signed_value;
}
}
2 changes: 1 addition & 1 deletion Software/TESLA-MODEL-3-BATTERY.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,6 @@ extern uint8_t LEDcolor;
void update_values_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(int16_t signed_value);

#endif
1 change: 1 addition & 0 deletions Software/USER_SETTINGS.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
//#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
//#define PYLON_CAN //Enable this line to emulate a "Pylontech battery" over CAN bus
//#define SMA_CAN //Enable this line to emulate a "BYD Battery-Box H 8.9kWh, 7 mod" over CAN bus

/* 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 set value over 60000!
Expand Down

0 comments on commit 7a7156b

Please sign in to comment.