-
Notifications
You must be signed in to change notification settings - Fork 159
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #631 from dalathegreat/feature/SMA-LV-CAN
New inverter protocol: SMA Low Voltage 48V via CAN
- Loading branch information
Showing
4 changed files
with
184 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
#include "../include.h" | ||
#ifdef SMA_LV_CAN | ||
#include "../datalayer/datalayer.h" | ||
#include "SMA-LV-CAN.h" | ||
|
||
/* SMA Sunny Island Low Voltage (48V) CAN protocol: | ||
CAN 2.0A | ||
500kBit/sec | ||
11-Bit Identifiers */ | ||
|
||
/* Do not change code below unless you are sure what you are doing */ | ||
static unsigned long previousMillis100ms = 0; | ||
|
||
#define VOLTAGE_OFFSET_DV 40 //Offset in deciVolt from max charge voltage and min discharge voltage | ||
#define MAX_VOLTAGE_DV 630 | ||
#define MIN_VOLTAGE_DV 41 | ||
|
||
//Actual content messages | ||
CAN_frame SMA_351 = {.FD = false, // Battery charge voltage, charge/discharge limit, min discharge voltage | ||
.ext_ID = false, | ||
.DLC = 8, | ||
.ID = 0x351, | ||
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; | ||
CAN_frame SMA_355 = {.FD = false, // SOC, SOH, HiResSOC | ||
.ext_ID = false, | ||
.DLC = 8, | ||
.ID = 0x355, | ||
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; | ||
CAN_frame SMA_356 = {.FD = false, // Battery voltage, current, temperature | ||
.ext_ID = false, | ||
.DLC = 8, | ||
.ID = 0x356, | ||
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; | ||
CAN_frame SMA_35A = {.FD = false, // Alarms & Warnings | ||
.ext_ID = false, | ||
.DLC = 8, | ||
.ID = 0x35A, | ||
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; | ||
CAN_frame SMA_35B = {.FD = false, // Events | ||
.ext_ID = false, | ||
.DLC = 8, | ||
.ID = 0x35B, | ||
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; | ||
CAN_frame SMA_35E = {.FD = false, // Manufacturer ASCII | ||
.ext_ID = false, | ||
.DLC = 8, | ||
.ID = 0x35E, | ||
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; | ||
CAN_frame SMA_35F = {.FD = false, // Battery Type, version, capacity, ID | ||
.ext_ID = false, | ||
.DLC = 8, | ||
.ID = 0x35F, | ||
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; | ||
CAN_frame SMA_00F = {.FD = false, // Emergency stop message | ||
.ext_ID = false, | ||
.DLC = 8, //Documentation unclear, should message even have any content? | ||
.ID = 0x00F, | ||
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; | ||
|
||
static int16_t temperature_average = 0; | ||
|
||
void update_values_can_inverter() { //This function maps all the values fetched from battery CAN to the correct CAN messages | ||
//Calculate values | ||
|
||
temperature_average = | ||
((datalayer.battery.status.temperature_max_dC + datalayer.battery.status.temperature_min_dC) / 2); | ||
|
||
//Map values to CAN messages | ||
//Battery charge voltage (eg 400.0V = 4000 , 16bits long) (MIN 41V, MAX 63V, default 54V) | ||
SMA_351.data.u8[0] = ((datalayer.battery.info.max_design_voltage_dV - VOLTAGE_OFFSET_DV) >> 8); | ||
SMA_351.data.u8[1] = ((datalayer.battery.info.max_design_voltage_dV - VOLTAGE_OFFSET_DV) & 0x00FF); | ||
if (datalayer.battery.info.max_design_voltage_dV > MAX_VOLTAGE_DV) { | ||
//If the battery is designed for more than 63.0V, cap the value | ||
SMA_351.data.u8[0] = (MAX_VOLTAGE_DV >> 8); | ||
SMA_351.data.u8[1] = (MAX_VOLTAGE_DV & 0x00FF); | ||
//TODO; raise event? | ||
} | ||
//Discharge limited current, 500 = 50A, (0.1, A) (MIN 0, MAX 1200) | ||
SMA_351.data.u8[2] = (datalayer.battery.status.max_discharge_current_dA >> 8); | ||
SMA_351.data.u8[3] = (datalayer.battery.status.max_discharge_current_dA & 0x00FF); | ||
//Charge limited current, 125 =12.5A (0.1, A) (MIN 0, MAX 1200) | ||
SMA_351.data.u8[4] = (datalayer.battery.status.max_charge_current_dA >> 8); | ||
SMA_351.data.u8[5] = (datalayer.battery.status.max_charge_current_dA & 0x00FF); | ||
//Discharge voltage (eg 300.0V = 3000 , 16bits long) (MIN 41V, MAX 48V, default 41V) | ||
SMA_351.data.u8[6] = ((datalayer.battery.info.min_design_voltage_dV + VOLTAGE_OFFSET_DV) >> 8); | ||
SMA_351.data.u8[7] = ((datalayer.battery.info.min_design_voltage_dV + VOLTAGE_OFFSET_DV) & 0x00FF); | ||
if (datalayer.battery.info.min_design_voltage_dV < MIN_VOLTAGE_DV) { | ||
//If the battery is designed for discharge voltage below 41.0V, cap the value | ||
SMA_351.data.u8[6] = (MIN_VOLTAGE_DV >> 8); | ||
SMA_351.data.u8[7] = (MIN_VOLTAGE_DV & 0x00FF); | ||
//TODO; raise event? | ||
} | ||
|
||
//SOC (100%) | ||
SMA_355.data.u8[0] = ((datalayer.battery.status.reported_soc / 100) >> 8); | ||
SMA_355.data.u8[1] = ((datalayer.battery.status.reported_soc / 100) & 0x00FF); | ||
//StateOfHealth (100%) | ||
SMA_355.data.u8[2] = ((datalayer.battery.status.soh_pptt / 100) >> 8); | ||
SMA_355.data.u8[3] = ((datalayer.battery.status.soh_pptt / 100) & 0x00FF); | ||
//State of charge High Precision (100.00%) | ||
SMA_355.data.u8[4] = (datalayer.battery.status.reported_soc >> 8); | ||
SMA_355.data.u8[5] = (datalayer.battery.status.reported_soc & 0x00FF); | ||
|
||
//Voltage (370.0) | ||
SMA_356.data.u8[0] = ((datalayer.battery.status.voltage_dV * 10) >> 8); | ||
SMA_356.data.u8[1] = ((datalayer.battery.status.voltage_dV * 10) & 0x00FF); | ||
//Current (S16 dA) | ||
SMA_356.data.u8[2] = (datalayer.battery.status.current_dA >> 8); | ||
SMA_356.data.u8[3] = (datalayer.battery.status.current_dA & 0x00FF); | ||
//Temperature (s16 degC) | ||
SMA_356.data.u8[4] = (temperature_average >> 8); | ||
SMA_356.data.u8[5] = (temperature_average & 0x00FF); | ||
|
||
//TODO: Map error/warnings in 0x35A | ||
} | ||
|
||
void receive_can_inverter(CAN_frame rx_frame) { | ||
switch (rx_frame.ID) { | ||
case 0x305: | ||
datalayer.system.status.CAN_inverter_still_alive = CAN_STILL_ALIVE; | ||
//Frame0-1 Battery Voltage | ||
//Frame2-3 Battery Current | ||
//Frame4-5 Battery Temperature | ||
//Frame6-7 SOC Battery | ||
break; | ||
case 0x306: | ||
datalayer.system.status.CAN_inverter_still_alive = CAN_STILL_ALIVE; | ||
//Frame0-1 SOH Battery | ||
//Frame2 Charging procedure | ||
//Frame3 Operating state | ||
//Frame4-5 Active error message | ||
//Frame6-7 Battery charge voltage setpoint | ||
break; | ||
default: | ||
break; | ||
} | ||
} | ||
|
||
void send_can_inverter() { | ||
unsigned long currentMillis = millis(); | ||
|
||
if (currentMillis - previousMillis100ms >= INTERVAL_100_MS) { | ||
previousMillis100ms = currentMillis; | ||
|
||
transmit_can(&SMA_351, can_config.inverter); | ||
transmit_can(&SMA_355, can_config.inverter); | ||
transmit_can(&SMA_356, can_config.inverter); | ||
transmit_can(&SMA_35A, can_config.inverter); | ||
transmit_can(&SMA_35B, can_config.inverter); | ||
transmit_can(&SMA_35E, can_config.inverter); | ||
transmit_can(&SMA_35F, can_config.inverter); | ||
|
||
//Remote quick stop (optional) | ||
if (datalayer.battery.status.bms_status == FAULT) { | ||
transmit_can(&SMA_00F, can_config.inverter); | ||
//After receiving this message, Sunny Island will immediately go into standby. | ||
//Please send start command, to start again. Manual start is also possible. | ||
} | ||
} | ||
} | ||
|
||
void setup_inverter(void) { // Performs one time setup at startup over CAN bus | ||
strncpy(datalayer.system.info.inverter_protocol, "SMA Low Voltage (48V) protocol via CAN", 63); | ||
datalayer.system.info.inverter_protocol[63] = '\0'; | ||
} | ||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
#ifndef SMA_LV_CAN_H | ||
#define SMA_LV_CAN_H | ||
#include "../include.h" | ||
|
||
#define CAN_INVERTER_SELECTED | ||
|
||
#define READY_STATE 0x03 | ||
#define STOP_STATE 0x02 | ||
|
||
void transmit_can(CAN_frame* tx_frame, int interface); | ||
void setup_inverter(void); | ||
|
||
#endif |