Convert Growatt inverters to use the base class

This commit is contained in:
Jaakko Haakana 2025-05-14 17:21:47 +03:00
parent f22be6489b
commit 204594bdd0
4 changed files with 239 additions and 219 deletions

View file

@ -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';
}

View file

@ -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

View file

@ -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';
}

View file

@ -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