From 204594bdd0d1ec9975e4ab9473364f19e83bca10 Mon Sep 17 00:00:00 2001 From: Jaakko Haakana Date: Wed, 14 May 2025 17:21:47 +0300 Subject: [PATCH] Convert Growatt inverters to use the base class --- Software/src/inverter/GROWATT-HV-CAN.cpp | 146 +--------------------- Software/src/inverter/GROWATT-HV-CAN.h | 151 ++++++++++++++++++++++- Software/src/inverter/GROWATT-LV-CAN.cpp | 79 +----------- Software/src/inverter/GROWATT-LV-CAN.h | 82 +++++++++++- 4 files changed, 239 insertions(+), 219 deletions(-) diff --git a/Software/src/inverter/GROWATT-HV-CAN.cpp b/Software/src/inverter/GROWATT-HV-CAN.cpp index f561c2c7..108707f3 100644 --- a/Software/src/inverter/GROWATT-HV-CAN.cpp +++ b/Software/src/inverter/GROWATT-HV-CAN.cpp @@ -1,5 +1,6 @@ #include "../include.h" #ifdef GROWATT_HV_CAN +#include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "GROWATT-HV-CAN.h" @@ -21,143 +22,8 @@ FCC - Full charge capacity RM - Remaining capacity BMS - Battery Information Collector*/ -//Total number of Cells (1-512) -//(Total number of Cells = number of Packs in parallel * number of Modules in series * number of Cells in the module) -#define TOTAL_NUMBER_OF_CELLS 300 -// Number of Modules in series (1-32) -#define NUMBER_OF_MODULES_IN_SERIES 20 -// Number of packs in parallel (1-65536) -#define NUMBER_OF_PACKS_IN_PARALLEL 1 -//Manufacturer abbreviation, part 1 -#define MANUFACTURER_ASCII_0 0x47 //G -#define MANUFACTURER_ASCII_1 0x54 //T - -/* Do not change code below unless you are sure what you are doing */ - -//Actual content messages -CAN_frame GROWATT_3110 = {.FD = false, - .ext_ID = true, - .DLC = 8, - .ID = 0x3110, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame GROWATT_3120 = {.FD = false, - .ext_ID = true, - .DLC = 8, - .ID = 0x3120, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame GROWATT_3130 = {.FD = false, - .ext_ID = true, - .DLC = 8, - .ID = 0x3130, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame GROWATT_3140 = {.FD = false, - .ext_ID = true, - .DLC = 8, - .ID = 0x3140, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame GROWATT_3150 = {.FD = false, - .ext_ID = true, - .DLC = 8, - .ID = 0x3150, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame GROWATT_3160 = {.FD = false, - .ext_ID = true, - .DLC = 8, - .ID = 0x3160, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame GROWATT_3170 = {.FD = false, - .ext_ID = true, - .DLC = 8, - .ID = 0x3170, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame GROWATT_3180 = {.FD = false, - .ext_ID = true, - .DLC = 8, - .ID = 0x3180, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame GROWATT_3190 = {.FD = false, - .ext_ID = true, - .DLC = 8, - .ID = 0x3190, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame GROWATT_3200 = {.FD = false, - .ext_ID = true, - .DLC = 8, - .ID = 0x3200, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame GROWATT_3210 = {.FD = false, - .ext_ID = true, - .DLC = 8, - .ID = 0x3210, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame GROWATT_3220 = {.FD = false, - .ext_ID = true, - .DLC = 8, - .ID = 0x3220, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame GROWATT_3230 = {.FD = false, - .ext_ID = true, - .DLC = 8, - .ID = 0x3230, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame GROWATT_3240 = {.FD = false, - .ext_ID = true, - .DLC = 8, - .ID = 0x3240, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame GROWATT_3250 = {.FD = false, - .ext_ID = true, - .DLC = 8, - .ID = 0x3250, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame GROWATT_3260 = {.FD = false, - .ext_ID = true, - .DLC = 8, - .ID = 0x3260, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame GROWATT_3270 = {.FD = false, - .ext_ID = true, - .DLC = 8, - .ID = 0x3270, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame GROWATT_3280 = {.FD = false, - .ext_ID = true, - .DLC = 8, - .ID = 0x3280, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame GROWATT_3290 = {.FD = false, - .ext_ID = true, - .DLC = 8, - .ID = 0x3290, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame GROWATT_3F00 = {.FD = false, - .ext_ID = true, - .DLC = 8, - .ID = 0x3F00, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -static unsigned long previousMillis1s = 0; // will store last time a 1s CAN Message was send -static unsigned long previousMillisBatchSend = 0; -static uint32_t unix_time = 0; -static uint16_t ampere_hours_remaining = 0; -static uint16_t ampere_hours_full = 0; -static uint16_t send_times = 0; // Overflows every 18hours. Cumulative number, plus 1 for each transmission -static uint8_t safety_specification = 0; -static uint8_t charging_command = 0; -static uint8_t discharging_command = 0; -static uint8_t shielding_external_communication_failure = 0; -static uint8_t clearing_battery_fault = - 0; //When PCS receives the forced charge Mark 1 and Cell under- voltage protection fault, it will send 0XAA -static uint8_t ISO_detection_command = 0; -static uint8_t sleep_wakeup_control = 0; -static uint8_t PCS_working_status = 0; //00 standby, 01 operating -static uint8_t serial_number_counter = 0; //0-1-2-0-1-2... -static uint8_t can_message_batch_index = 0; -static const uint8_t delay_between_batches_ms = 10; -static bool inverter_alive = false; -static bool time_to_send_1s_data = false; - -void update_values_can_inverter() { //This function maps all the values fetched from battery CAN to the correct CAN messages +void GrowattHvInverter:: + update_values() { //This function maps all the values fetched from battery CAN to the correct CAN messages if (datalayer.battery.status.voltage_dV > 10) { // Only update value when we have voltage available to avoid div0 ampere_hours_remaining = @@ -493,7 +359,7 @@ void update_values_can_inverter() { //This function maps all the values fetched GROWATT_3F00.data.u8[7] = 0; // RESERVED } -void map_can_frame_to_variable_inverter(CAN_frame rx_frame) { +void GrowattHvInverter::map_can_frame_to_variable(CAN_frame rx_frame) { switch (rx_frame.ID) { case 0x3010: // Heartbeat command, 1000ms datalayer.system.status.CAN_inverter_still_alive = CAN_STILL_ALIVE; @@ -523,7 +389,7 @@ void map_can_frame_to_variable_inverter(CAN_frame rx_frame) { } } -void transmit_can_inverter(unsigned long currentMillis) { +void GrowattHvInverter::transmit_can(unsigned long currentMillis) { if (!inverter_alive) { return; //Dont send messages towards inverter until it has started @@ -585,7 +451,7 @@ void transmit_can_inverter(unsigned long currentMillis) { } } -void setup_inverter(void) { // Performs one time setup at startup over CAN bus +void GrowattHvInverter::setup(void) { // Performs one time setup at startup over CAN bus strncpy(datalayer.system.info.inverter_protocol, "Growatt High Voltage protocol via CAN", 63); datalayer.system.info.inverter_protocol[63] = '\0'; } diff --git a/Software/src/inverter/GROWATT-HV-CAN.h b/Software/src/inverter/GROWATT-HV-CAN.h index fa783414..677bfc86 100644 --- a/Software/src/inverter/GROWATT-HV-CAN.h +++ b/Software/src/inverter/GROWATT-HV-CAN.h @@ -2,9 +2,154 @@ #define GROWATT_HV_CAN_H #include "../include.h" -#define CAN_INVERTER_SELECTED +#include "CanInverterProtocol.h" -void transmit_can_frame(CAN_frame* tx_frame, int interface); -void setup_inverter(void); +#define CAN_INVERTER_SELECTED +#define SELECTED_INVERTER_CLASS GrowattHvInverter + +class GrowattHvInverter : 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: +//Total number of Cells (1-512) +//(Total number of Cells = number of Packs in parallel * number of Modules in series * number of Cells in the module) +#define TOTAL_NUMBER_OF_CELLS 300 +// Number of Modules in series (1-32) +#define NUMBER_OF_MODULES_IN_SERIES 20 +// Number of packs in parallel (1-65536) +#define NUMBER_OF_PACKS_IN_PARALLEL 1 +//Manufacturer abbreviation, part 1 +#define MANUFACTURER_ASCII_0 0x47 //G +#define MANUFACTURER_ASCII_1 0x54 //T + + /* Do not change code below unless you are sure what you are doing */ + + //Actual content messages + CAN_frame GROWATT_3110 = {.FD = false, + .ext_ID = true, + .DLC = 8, + .ID = 0x3110, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame GROWATT_3120 = {.FD = false, + .ext_ID = true, + .DLC = 8, + .ID = 0x3120, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame GROWATT_3130 = {.FD = false, + .ext_ID = true, + .DLC = 8, + .ID = 0x3130, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame GROWATT_3140 = {.FD = false, + .ext_ID = true, + .DLC = 8, + .ID = 0x3140, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame GROWATT_3150 = {.FD = false, + .ext_ID = true, + .DLC = 8, + .ID = 0x3150, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame GROWATT_3160 = {.FD = false, + .ext_ID = true, + .DLC = 8, + .ID = 0x3160, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame GROWATT_3170 = {.FD = false, + .ext_ID = true, + .DLC = 8, + .ID = 0x3170, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame GROWATT_3180 = {.FD = false, + .ext_ID = true, + .DLC = 8, + .ID = 0x3180, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame GROWATT_3190 = {.FD = false, + .ext_ID = true, + .DLC = 8, + .ID = 0x3190, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame GROWATT_3200 = {.FD = false, + .ext_ID = true, + .DLC = 8, + .ID = 0x3200, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame GROWATT_3210 = {.FD = false, + .ext_ID = true, + .DLC = 8, + .ID = 0x3210, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame GROWATT_3220 = {.FD = false, + .ext_ID = true, + .DLC = 8, + .ID = 0x3220, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame GROWATT_3230 = {.FD = false, + .ext_ID = true, + .DLC = 8, + .ID = 0x3230, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame GROWATT_3240 = {.FD = false, + .ext_ID = true, + .DLC = 8, + .ID = 0x3240, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame GROWATT_3250 = {.FD = false, + .ext_ID = true, + .DLC = 8, + .ID = 0x3250, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame GROWATT_3260 = {.FD = false, + .ext_ID = true, + .DLC = 8, + .ID = 0x3260, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame GROWATT_3270 = {.FD = false, + .ext_ID = true, + .DLC = 8, + .ID = 0x3270, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame GROWATT_3280 = {.FD = false, + .ext_ID = true, + .DLC = 8, + .ID = 0x3280, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame GROWATT_3290 = {.FD = false, + .ext_ID = true, + .DLC = 8, + .ID = 0x3290, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame GROWATT_3F00 = {.FD = false, + .ext_ID = true, + .DLC = 8, + .ID = 0x3F00, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + unsigned long previousMillis1s = 0; // will store last time a 1s CAN Message was send + unsigned long previousMillisBatchSend = 0; + uint32_t unix_time = 0; + uint16_t ampere_hours_remaining = 0; + uint16_t ampere_hours_full = 0; + uint16_t send_times = 0; // Overflows every 18hours. Cumulative number, plus 1 for each transmission + uint8_t safety_specification = 0; + uint8_t charging_command = 0; + uint8_t discharging_command = 0; + uint8_t shielding_external_communication_failure = 0; + uint8_t clearing_battery_fault = + 0; //When PCS receives the forced charge Mark 1 and Cell under- voltage protection fault, it will send 0XAA + uint8_t ISO_detection_command = 0; + uint8_t sleep_wakeup_control = 0; + uint8_t PCS_working_status = 0; //00 standby, 01 operating + uint8_t serial_number_counter = 0; //0-1-2-0-1-2... + uint8_t can_message_batch_index = 0; + const uint8_t delay_between_batches_ms = 10; + bool inverter_alive = false; + bool time_to_send_1s_data = false; +}; #endif diff --git a/Software/src/inverter/GROWATT-LV-CAN.cpp b/Software/src/inverter/GROWATT-LV-CAN.cpp index 671fce6c..7ab3233c 100644 --- a/Software/src/inverter/GROWATT-LV-CAN.cpp +++ b/Software/src/inverter/GROWATT-LV-CAN.cpp @@ -1,5 +1,6 @@ #include "../include.h" #ifdef GROWATT_LV_CAN +#include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "GROWATT-LV-CAN.h" @@ -10,76 +11,8 @@ Big-endian The inverter replies data every second (standard frame/decimal)0x301:*/ -/* Do not change code below unless you are sure what you are doing */ - -//Actual content messages -CAN_frame GROWATT_311 = {.FD = false, //Voltage and charge limits and status - .ext_ID = false, - .DLC = 8, - .ID = 0x311, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame GROWATT_312 = {.FD = false, //status bits , pack number, total cell number - .ext_ID = false, - .DLC = 8, - .ID = 0x312, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame GROWATT_313 = {.FD = false, //voltage, current, temp, soc, soh - .ext_ID = false, - .DLC = 8, - .ID = 0x313, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame GROWATT_314 = {.FD = false, //capacity, delta V, cycle count - .ext_ID = false, - .DLC = 8, - .ID = 0x314, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame GROWATT_319 = {.FD = false, //max/min cell voltage, num of cell max/min, protect pack ID - .ext_ID = false, - .DLC = 8, - .ID = 0x319, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame GROWATT_320 = {.FD = false, //manufacturer name, hw ver, sw ver, date and time - .ext_ID = false, - .DLC = 8, - .ID = 0x320, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame GROWATT_321 = {.FD = false, //Update status, ID - .ext_ID = false, - .DLC = 8, - .ID = 0x321, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -//Cellvoltages -CAN_frame GROWATT_315 = {.FD = false, //Cells 1-4 - .ext_ID = false, - .DLC = 8, - .ID = 0x315, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame GROWATT_316 = {.FD = false, //Cells 5-8 - .ext_ID = false, - .DLC = 8, - .ID = 0x316, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame GROWATT_317 = {.FD = false, //Cells 9-12 - .ext_ID = false, - .DLC = 8, - .ID = 0x317, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -CAN_frame GROWATT_318 = {.FD = false, //Cells 13-16 - .ext_ID = false, - .DLC = 8, - .ID = 0x318, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -#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 410 - -static uint16_t cell_delta_mV = 0; -static uint16_t ampere_hours_remaining = 0; -static uint16_t ampere_hours_full = 0; - -void update_values_can_inverter() { //This function maps all the values fetched from battery CAN to the correct CAN messages +void GrowattLvInverter:: + update_values() { //This function maps all the values fetched from battery CAN to the correct CAN messages cell_delta_mV = datalayer.battery.status.cell_max_voltage_mV - datalayer.battery.status.cell_min_voltage_mV; @@ -246,7 +179,7 @@ void update_values_can_inverter() { //This function maps all the values fetched GROWATT_318.data.u8[7] = (datalayer.battery.status.cell_voltages_mV[15] & 0x00FF); } -void map_can_frame_to_variable_inverter(CAN_frame rx_frame) { +void GrowattLvInverter::map_can_frame_to_variable(CAN_frame rx_frame) { switch (rx_frame.ID) { case 0x301: datalayer.system.status.CAN_inverter_still_alive = CAN_STILL_ALIVE; @@ -267,11 +200,11 @@ void map_can_frame_to_variable_inverter(CAN_frame rx_frame) { } } -void transmit_can_inverter(unsigned long currentMillis) { +void GrowattLvInverter::transmit_can(unsigned long currentMillis) { // No periodic sending for this battery type. Data is sent when inverter requests it } -void setup_inverter(void) { // Performs one time setup at startup over CAN bus +void GrowattLvInverter::setup(void) { // Performs one time setup at startup over CAN bus strncpy(datalayer.system.info.inverter_protocol, "Growatt Low Voltage (48V) protocol via CAN", 63); datalayer.system.info.inverter_protocol[63] = '\0'; } diff --git a/Software/src/inverter/GROWATT-LV-CAN.h b/Software/src/inverter/GROWATT-LV-CAN.h index fe6dce34..65e14bff 100644 --- a/Software/src/inverter/GROWATT-LV-CAN.h +++ b/Software/src/inverter/GROWATT-LV-CAN.h @@ -2,9 +2,85 @@ #define GROWATT_LV_CAN_H #include "../include.h" -#define CAN_INVERTER_SELECTED +#include "CanInverterProtocol.h" -void transmit_can_frame(CAN_frame* tx_frame, int interface); -void setup_inverter(void); +#define CAN_INVERTER_SELECTED +#define SELECTED_INVERTER_CLASS GrowattLvInverter + +class GrowattLvInverter : 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: + //Actual content messages + CAN_frame GROWATT_311 = {.FD = false, //Voltage and charge limits and status + .ext_ID = false, + .DLC = 8, + .ID = 0x311, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame GROWATT_312 = {.FD = false, //status bits , pack number, total cell number + .ext_ID = false, + .DLC = 8, + .ID = 0x312, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame GROWATT_313 = {.FD = false, //voltage, current, temp, soc, soh + .ext_ID = false, + .DLC = 8, + .ID = 0x313, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame GROWATT_314 = {.FD = false, //capacity, delta V, cycle count + .ext_ID = false, + .DLC = 8, + .ID = 0x314, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame GROWATT_319 = {.FD = false, //max/min cell voltage, num of cell max/min, protect pack ID + .ext_ID = false, + .DLC = 8, + .ID = 0x319, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame GROWATT_320 = {.FD = false, //manufacturer name, hw ver, sw ver, date and time + .ext_ID = false, + .DLC = 8, + .ID = 0x320, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame GROWATT_321 = {.FD = false, //Update status, ID + .ext_ID = false, + .DLC = 8, + .ID = 0x321, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + //Cellvoltages + CAN_frame GROWATT_315 = {.FD = false, //Cells 1-4 + .ext_ID = false, + .DLC = 8, + .ID = 0x315, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame GROWATT_316 = {.FD = false, //Cells 5-8 + .ext_ID = false, + .DLC = 8, + .ID = 0x316, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame GROWATT_317 = {.FD = false, //Cells 9-12 + .ext_ID = false, + .DLC = 8, + .ID = 0x317, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + CAN_frame GROWATT_318 = {.FD = false, //Cells 13-16 + .ext_ID = false, + .DLC = 8, + .ID = 0x318, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + +#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 410 + + uint16_t cell_delta_mV = 0; + uint16_t ampere_hours_remaining = 0; + uint16_t ampere_hours_full = 0; +}; #endif