Convert Solax inverter to use the base class

This commit is contained in:
Jaakko Haakana 2025-05-14 17:20:00 +03:00
parent 09e719690f
commit 4c09166b9e
2 changed files with 117 additions and 106 deletions

View file

@ -1,5 +1,6 @@
#include "../include.h"
#ifdef SOLAX_CAN
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "SOLAX-CAN.h"
@ -9,104 +10,13 @@
// If you are having BattVoltFault issues, configure the above values according to wiki page
// https://github.com/dalathegreat/Battery-Emulator/wiki/Solax-inverters
/* Do not change code below unless you are sure what you are doing */
static int16_t temperature_average = 0;
static uint8_t STATE = BATTERY_ANNOUNCE;
static unsigned long LastFrameTime = 0;
static uint8_t number_of_batteries = 1;
static uint16_t capped_capacity_Wh;
static uint16_t capped_remaining_capacity_Wh;
//CAN message translations from this amazing repository: https://github.com/rand12345/solax_can_bus
CAN_frame SOLAX_1801 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1801,
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
CAN_frame SOLAX_1872 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1872,
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; //BMS_Limits
CAN_frame SOLAX_1873 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1873,
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; //BMS_PackData
CAN_frame SOLAX_1874 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1874,
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; //BMS_CellData
CAN_frame SOLAX_1875 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1875,
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; //BMS_Status
CAN_frame SOLAX_1876 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1876,
.data = {0x0, 0x0, 0xE2, 0x0C, 0x0, 0x0, 0xD7, 0x0C}}; //BMS_PackTemps
CAN_frame SOLAX_1877 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1877,
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
CAN_frame SOLAX_1878 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1878,
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; //BMS_PackStats
CAN_frame SOLAX_1879 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1879,
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
CAN_frame SOLAX_187E = {.FD = false, //Needed for Ultra
.ext_ID = true,
.DLC = 8,
.ID = 0x187E,
.data = {0x60, 0xEA, 0x0, 0x0, 0x64, 0x0, 0x0, 0x0}};
CAN_frame SOLAX_187D = {.FD = false, //Needed for Ultra
.ext_ID = true,
.DLC = 8,
.ID = 0x187D,
.data = {0x8B, 0x01, 0x0, 0x0, 0x8B, 0x1, 0x0, 0x0}};
CAN_frame SOLAX_187C = {.FD = false, //Needed for Ultra
.ext_ID = true,
.DLC = 8,
.ID = 0x187C,
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
CAN_frame SOLAX_187B = {.FD = false, //Needed for Ultra
.ext_ID = true,
.DLC = 8,
.ID = 0x187B,
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
CAN_frame SOLAX_187A = {.FD = false, //Needed for Ultra
.ext_ID = true,
.DLC = 8,
.ID = 0x187A,
.data = {0x01, 0x50, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
CAN_frame SOLAX_1881 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1881,
.data = {0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; // E.g.: 0 6 S B M S F A
CAN_frame SOLAX_1882 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1882,
.data = {0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; // E.g.: 0 2 3 A B 0 5 2
CAN_frame SOLAX_100A001 = {.FD = false, .ext_ID = true, .DLC = 0, .ID = 0x100A001, .data = {}};
// __builtin_bswap64 needed to convert to ESP32 little endian format
// Byte[4] defines the requested contactor state: 1 = Closed , 0 = Open
#define Contactor_Open_Payload __builtin_bswap64(0x0200010000000000)
#define Contactor_Close_Payload __builtin_bswap64(0x0200010001000000)
void update_values_can_inverter() { //This function maps all the values fetched from battery CAN to the correct CAN messages
void SolaxInverter::
update_values() { //This function maps all the values fetched from battery CAN to the correct CAN messages
// If not receiveing any communication from the inverter, open contactors and return to battery announce state
if (millis() - LastFrameTime >= SolaxTimeout) {
datalayer.system.status.inverter_allows_contactor_closing = false;
@ -206,11 +116,11 @@ void update_values_can_inverter() { //This function maps all the values fetched
SOLAX_187E.data.u8[5] = (uint8_t)(datalayer.battery.status.reported_soc / 100);
}
void transmit_can_inverter(unsigned long currentMillis) {
void SolaxInverter::transmit_can(unsigned long currentMillis) {
// No periodic sending used on this protocol, we react only on incoming CAN messages!
}
void map_can_frame_to_variable_inverter(CAN_frame rx_frame) {
void SolaxInverter::map_can_frame_to_variable(CAN_frame rx_frame) {
if (rx_frame.ID == 0x1871) {
datalayer.system.status.CAN_inverter_still_alive = CAN_STILL_ALIVE;
@ -297,7 +207,8 @@ void map_can_frame_to_variable_inverter(CAN_frame rx_frame) {
#endif
}
}
void setup_inverter(void) { // Performs one time setup at startup
void SolaxInverter::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.inverter_protocol, "SolaX Triple Power LFP over CAN bus", 63);
datalayer.system.info.inverter_protocol[63] = '\0';
datalayer.system.status.inverter_allows_contactor_closing = false; // The inverter needs to allow first

View file

@ -2,19 +2,119 @@
#define SOLAX_CAN_H
#include "../include.h"
#define CAN_INVERTER_SELECTED
#include "CanInverterProtocol.h"
#define CAN_INVERTER_SELECTED
#define SELECTED_INVERTER_CLASS SolaxInverter
class SolaxInverter : public CanInverterProtocol {
public:
void setup();
void update_values();
void transmit_can(unsigned long currentMillis);
void map_can_frame_to_variable(CAN_frame rx_frame);
private:
// Timeout in milliseconds
#define SolaxTimeout 2000
static const int SolaxTimeout = 2000;
//SOLAX BMS States Definition
#define BATTERY_ANNOUNCE 0
#define WAITING_FOR_CONTACTOR 1
#define CONTACTOR_CLOSED 2
#define FAULT_SOLAX 3
#define UPDATING_FW 4
static const int BATTERY_ANNOUNCE = 0;
static const int WAITING_FOR_CONTACTOR = 1;
static const int CONTACTOR_CLOSED = 2;
static const int FAULT_SOLAX = 3;
static const int UPDATING_FW = 4;
void transmit_can_frame(CAN_frame* tx_frame, int interface);
void setup_inverter(void);
int16_t temperature_average = 0;
uint8_t STATE = BATTERY_ANNOUNCE;
unsigned long LastFrameTime = 0;
uint8_t number_of_batteries = 1;
uint16_t capped_capacity_Wh;
uint16_t capped_remaining_capacity_Wh;
//CAN message translations from this amazing repository: https://github.com/rand12345/solax_can_bus
CAN_frame SOLAX_1801 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1801,
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
CAN_frame SOLAX_1872 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1872,
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; //BMS_Limits
CAN_frame SOLAX_1873 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1873,
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; //BMS_PackData
CAN_frame SOLAX_1874 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1874,
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; //BMS_CellData
CAN_frame SOLAX_1875 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1875,
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; //BMS_Status
CAN_frame SOLAX_1876 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1876,
.data = {0x0, 0x0, 0xE2, 0x0C, 0x0, 0x0, 0xD7, 0x0C}}; //BMS_PackTemps
CAN_frame SOLAX_1877 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1877,
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
CAN_frame SOLAX_1878 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1878,
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; //BMS_PackStats
CAN_frame SOLAX_1879 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1879,
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
CAN_frame SOLAX_187E = {.FD = false, //Needed for Ultra
.ext_ID = true,
.DLC = 8,
.ID = 0x187E,
.data = {0x60, 0xEA, 0x0, 0x0, 0x64, 0x0, 0x0, 0x0}};
CAN_frame SOLAX_187D = {.FD = false, //Needed for Ultra
.ext_ID = true,
.DLC = 8,
.ID = 0x187D,
.data = {0x8B, 0x01, 0x0, 0x0, 0x8B, 0x1, 0x0, 0x0}};
CAN_frame SOLAX_187C = {.FD = false, //Needed for Ultra
.ext_ID = true,
.DLC = 8,
.ID = 0x187C,
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
CAN_frame SOLAX_187B = {.FD = false, //Needed for Ultra
.ext_ID = true,
.DLC = 8,
.ID = 0x187B,
.data = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
CAN_frame SOLAX_187A = {.FD = false, //Needed for Ultra
.ext_ID = true,
.DLC = 8,
.ID = 0x187A,
.data = {0x01, 0x50, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
CAN_frame SOLAX_1881 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1881,
.data = {0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; // E.g.: 0 6 S B M S F A
CAN_frame SOLAX_1882 = {.FD = false,
.ext_ID = true,
.DLC = 8,
.ID = 0x1882,
.data = {0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; // E.g.: 0 2 3 A B 0 5 2
CAN_frame SOLAX_100A001 = {.FD = false, .ext_ID = true, .DLC = 0, .ID = 0x100A001, .data = {}};
};
#endif