Skip to content

Commit

Permalink
Add skeleton for Goodwe CAN
Browse files Browse the repository at this point in the history
  • Loading branch information
dalathegreat committed Nov 7, 2023
1 parent 0680f9e commit 58e1e41
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 1 deletion.
19 changes: 18 additions & 1 deletion Software/Software.ino
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ uint16_t min_volt_modbus_byd = min_voltage;
uint16_t max_volt_modbus_byd = max_voltage;
uint16_t min_volt_sma_can = min_voltage;
uint16_t max_volt_sma_can = max_voltage;
uint16_t min_volt_goodwe_can = min_voltage;
uint16_t max_volt_goodwe_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 @@ -220,6 +222,9 @@ void setup()
#ifdef SOFAR_CAN
Serial.println("SOFAR CAN protocol selected");
#endif
#ifdef GOODWE_CAN
Serial.println("Goodwe CAN protocol selected");
#endif
//Inform user what battery is used
#ifdef BATTERY_TYPE_LEAF
Serial.println("Nissan LEAF battery selected");
Expand Down Expand Up @@ -299,6 +304,9 @@ void handle_can()
#ifdef SMA_CAN
receive_can_sma(rx_frame);
#endif
#ifdef GOODWE_CAN
receive_can_goodwe(rx_frame);
#endif
#ifdef CHADEMO
receive_can_chademo(rx_frame);
#endif
Expand All @@ -322,6 +330,9 @@ void handle_can()
#ifdef CAN_BYD
send_can_byd();
#endif
#ifdef GOODWE_CAN
send_can_goodwe();
#endif
#ifdef SMA_CAN
send_can_sma();
#endif
Expand Down Expand Up @@ -426,10 +437,16 @@ void handle_inverter()
#ifdef SMA_CAN
update_values_can_sma();
#endif
#ifdef GOODWE_CAN
update_values_can_goodwe();
#endif
#ifdef SOFAR_CAN
update_values_can_sofar();
#endif
#ifdef PYLON_CAN
update_values_can_pylon();
#endif
#ifdef CHADEMO
#ifdef CHADEMO
update_values_can_chademo();
#endif

Expand Down
1 change: 1 addition & 0 deletions Software/USER_SETTINGS.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
//#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
//#define SOFAR_CAN //Enable this line to emulate a "Sofar Energy Storage Inverter High Voltage BMS General Protocol (Extended Frame)" over CAN bus
//#define GOODWE_CAN //Enable this line to emulate a "Goodwe default battery" 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
105 changes: 105 additions & 0 deletions Software/src/inverter/GOODWE-CAN.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#include "GOODWE-CAN.h"
#include "../devboard/can/ESP32CAN.h"
#include "../lib/ThomasBarth-ESP32-CAN-Driver/CAN_config.h"

/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillis1s = 0; // will store last time a 2s CAN Message was send
static const int interval1s = 1000; // interval (ms) at which send CAN Messages

//Actual content messages
CAN_frame_t GOODWE_453 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x453,.data = {0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame_t GOODWE_455 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x455,.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame_t GOODWE_456 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x456,.data = {0xF1, 0x0E, 0x64, 0x00, 0xFA, 0x00, 0x1D, 0x0C}};
CAN_frame_t GOODWE_457 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x457,.data = {0xC1, 0x1B, 0x34, 0x21, 0x00, 0x00, 0x00, 0x00}};
CAN_frame_t GOODWE_458 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x458,.data = {0x55, 0x0E, 0x00, 0x00, 0xC5, 0x00, 0x00, 0x00}};
CAN_frame_t GOODWE_45A = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x45A,.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame_t GOODWE_460 = {.FIR = {.B = {.DLC = 2,.FF = CAN_frame_std,}},.MsgID = 0x460,.data = {0x00, 0x00}};
CAN_frame_t GOODWE_425 = {.FIR = {.B = {.DLC = 8,.FF = CAN_frame_std,}},.MsgID = 0x425,.data = {0x55, 0x0E, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00}};

static int discharge_current = 0;
static int charge_current = 0;
static int temperature_average = 0;

static int inverter_voltage = 0;
static int inverter_current = 0;

void update_values_can_goodwe()
{ //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_goodwe_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_goodwe_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);

//Map values to CAN messages
//Charge voltage (eg 400.0V = 4000 , 16bits long)
GOODWE_456.data.u8[0] = (max_volt_goodwe_can >> 8);
GOODWE_456.data.u8[1] = (max_volt_goodwe_can & 0x00FF);
//Maximum charge current allowed (Unit: A+1)
GOODWE_456.data.u8[2] = (charge_current >> 8);
GOODWE_456.data.u8[3] = (charge_current & 0x00FF);
//Maximum discharge current allowed (Unit: A+1)
GOODWE_456.data.u8[4] = (discharge_current >> 8);
GOODWE_456.data.u8[5] = (discharge_current & 0x00FF);
//Discharge voltage (eg 300.0V = 3000 , 16bits long)
GOODWE_456.data.u8[6] = (min_volt_goodwe_can >> 8);
GOODWE_456.data.u8[7] = (min_volt_goodwe_can & 0x00FF);

//SOC (100.00%)
GOODWE_457.data.u8[0] = (SOC >> 8);
GOODWE_457.data.u8[1] = (SOC & 0x00FF);
//StateOfHealth (100.00%)
GOODWE_457.data.u8[2] = (StateOfHealth >> 8);
GOODWE_457.data.u8[3] = (StateOfHealth & 0x00FF);

//Battery Voltage (ex 370.0)
GOODWE_458.data.u8[0] = (battery_voltage >> 8);
GOODWE_458.data.u8[1] = (battery_voltage & 0x00FF);
//Current (ex 81.0A)
GOODWE_458.data.u8[2] = (battery_current >> 8);
GOODWE_458.data.u8[3] = (battery_current & 0x00FF);
//Temperature average
GOODWE_458.data.u8[4] = (temperature_average >> 8);
GOODWE_458.data.u8[5] = (temperature_average & 0x00FF);
}

void receive_can_goodwe(CAN_frame_t rx_frame)
{
switch (rx_frame.MsgID)
{
case 0x420: //Message originating from GW6000-EH. Indicates timeout
//Todo, add USB debug message if this happens?
break;
case 0x425: //Inverter Vbat, Ibat
//inverter_voltage = frame0 and frame 1
//inverter_current = frame2 and frame 3
break;
default:
break;
}
}

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

// Send 1s CAN Message
if (currentMillis - previousMillis1s >= interval1s)
{
previousMillis1s = currentMillis;

ESP32Can.CANWriteFrame(&GOODWE_453);
ESP32Can.CANWriteFrame(&GOODWE_455);
ESP32Can.CANWriteFrame(&GOODWE_456);
ESP32Can.CANWriteFrame(&GOODWE_457);
ESP32Can.CANWriteFrame(&GOODWE_458);
ESP32Can.CANWriteFrame(&GOODWE_45A);
ESP32Can.CANWriteFrame(&GOODWE_460);
}
}

39 changes: 39 additions & 0 deletions Software/src/inverter/GOODWE-CAN.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#ifndef GOODWE_CAN_H
#define GOODWE_CAN_H
#include <Arduino.h>
#include "../devboard/can/ESP32CAN.h"
#include "../../USER_SETTINGS.h"

// These parameters need to be mapped for the inverter
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 uint8_t batteryAllowsContactorClosing; //Bool, 1=true, 0=false
extern uint8_t LEDcolor; //Enum, 0-2
extern uint16_t min_volt_goodwe_can;
extern uint16_t max_volt_goodwe_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_goodwe();
void send_can_goodwe();
void receive_can_goodwe(CAN_frame_t rx_frame);

#endif
4 changes: 4 additions & 0 deletions Software/src/inverter/INVERTERS.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
#include "SOFAR-CAN.h"
#endif

#ifdef GOODWE_CAN
#include "GOODWE-CAN.h"
#endif

#ifdef PYLON_CAN
#include "PYLON-CAN.h"
#endif
Expand Down

0 comments on commit 58e1e41

Please sign in to comment.