mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-06 03:50:13 +02:00
Merge pull request #857 from M4GNV5/feat/daly-rs485
New Battery: Daly SmartBMS Support
This commit is contained in:
commit
641356622e
9 changed files with 284 additions and 21 deletions
|
@ -210,7 +210,7 @@ void core_loop(void* task_time_us) {
|
|||
|
||||
// Input, Runs as fast as possible
|
||||
receive_can(); // Receive CAN messages
|
||||
#ifdef RS485_INVERTER_SELECTED
|
||||
#if defined(RS485_INVERTER_SELECTED) || defined(RS485_BATTERY_SELECTED)
|
||||
receive_RS485(); // Process serial2 RS485 interface
|
||||
#endif // RS485_INVERTER_SELECTED
|
||||
#if defined(SERIAL_LINK_RECEIVER) || defined(SERIAL_LINK_TRANSMITTER)
|
||||
|
@ -255,6 +255,10 @@ void core_loop(void* task_time_us) {
|
|||
// Output
|
||||
transmit_can(); // Send CAN messages to all components
|
||||
|
||||
#ifdef RS485_BATTERY_SELECTED
|
||||
transmit_rs485();
|
||||
#endif // RS485_BATTERY_SELECTED
|
||||
|
||||
END_TIME_MEASUREMENT_MAX(cantx, datalayer.system.status.time_cantx_us);
|
||||
END_TIME_MEASUREMENT_MAX(all, datalayer.system.status.core_task_10s_max_us);
|
||||
#ifdef FUNCTION_TIME_MEASUREMENT
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
//#define MG_5_BATTERY
|
||||
//#define NISSAN_LEAF_BATTERY
|
||||
//#define PYLON_BATTERY
|
||||
//#define DALY_BMS
|
||||
//#define RJXZS_BMS
|
||||
//#define RANGE_ROVER_PHEV_BATTERY
|
||||
//#define RENAULT_KANGOO_BATTERY
|
||||
|
|
|
@ -86,6 +86,10 @@ void setup_can_shunt();
|
|||
#include "PYLON-BATTERY.h"
|
||||
#endif
|
||||
|
||||
#ifdef DALY_BMS
|
||||
#include "DALY-BMS.h"
|
||||
#endif
|
||||
|
||||
#ifdef RJXZS_BMS
|
||||
#include "RJXZS-BMS.h"
|
||||
#endif
|
||||
|
@ -131,10 +135,16 @@ void setup_can_shunt();
|
|||
#include "SERIAL-LINK-RECEIVER-FROM-BATTERY.h"
|
||||
#endif
|
||||
|
||||
void handle_incoming_can_frame_battery(CAN_frame rx_frame);
|
||||
void update_values_battery();
|
||||
void transmit_can_battery();
|
||||
void setup_battery(void);
|
||||
void update_values_battery();
|
||||
|
||||
#ifdef RS485_BATTERY_SELECTED
|
||||
void transmit_rs485();
|
||||
void receive_RS485();
|
||||
#else
|
||||
void handle_incoming_can_frame_battery(CAN_frame rx_frame);
|
||||
void transmit_can_battery();
|
||||
#endif
|
||||
|
||||
#ifdef DOUBLE_BATTERY
|
||||
void update_values_battery2();
|
||||
|
|
201
Software/src/battery/DALY-BMS.cpp
Normal file
201
Software/src/battery/DALY-BMS.cpp
Normal file
|
@ -0,0 +1,201 @@
|
|||
#include "DALY-BMS.h"
|
||||
#include <cstdint>
|
||||
#include "../include.h"
|
||||
#include "RJXZS-BMS.h"
|
||||
#ifdef DALY_BMS
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../devboard/utils/events.h"
|
||||
#include "RENAULT-TWIZY.h"
|
||||
|
||||
/* Do not change code below unless you are sure what you are doing */
|
||||
|
||||
static uint32_t lastPacket = 0;
|
||||
static int16_t temperature_min_dC = 0;
|
||||
static int16_t temperature_max_dC = 0;
|
||||
static int16_t current_dA = 0;
|
||||
static uint16_t voltage_dV = 0;
|
||||
static uint32_t remaining_capacity_mAh = 0;
|
||||
static uint16_t cellvoltages_mV[48] = {0};
|
||||
static uint16_t cellvoltage_min_mV = 0;
|
||||
static uint16_t cellvoltage_max_mV = 0;
|
||||
static uint16_t SOC = 0;
|
||||
static bool has_fault = false;
|
||||
|
||||
void update_values_battery() {
|
||||
datalayer.battery.status.real_soc = SOC;
|
||||
datalayer.battery.status.voltage_dV = voltage_dV; //value is *10 (3700 = 370.0)
|
||||
datalayer.battery.status.current_dA = current_dA; //value is *10 (150 = 15.0)
|
||||
datalayer.battery.status.remaining_capacity_Wh = (remaining_capacity_mAh * (uint32_t)voltage_dV) / 10000;
|
||||
|
||||
datalayer.battery.status.max_charge_power_W = (BATTERY_MAX_CHARGE_AMP * voltage_dV) / 100;
|
||||
datalayer.battery.status.max_discharge_power_W = (BATTERY_MAX_DISCHARGE_AMP * voltage_dV) / 100;
|
||||
|
||||
uint32_t adaptive_power_limit = 999999;
|
||||
if (SOC < 2000)
|
||||
adaptive_power_limit = ((uint32_t)SOC * POWER_PER_PERCENT) / 100;
|
||||
else if (SOC > 8000)
|
||||
adaptive_power_limit = ((10000 - (uint32_t)SOC) * POWER_PER_PERCENT) / 100;
|
||||
|
||||
if (adaptive_power_limit < datalayer.battery.status.max_charge_power_W)
|
||||
datalayer.battery.status.max_charge_power_W = adaptive_power_limit;
|
||||
if (adaptive_power_limit < datalayer.battery.status.max_discharge_power_W)
|
||||
datalayer.battery.status.max_discharge_power_W = adaptive_power_limit;
|
||||
|
||||
memcpy(datalayer.battery.status.cell_voltages_mV, cellvoltages_mV, sizeof(cellvoltages_mV));
|
||||
datalayer.battery.status.cell_min_voltage_mV = cellvoltage_min_mV;
|
||||
datalayer.battery.status.cell_max_voltage_mV = cellvoltage_max_mV;
|
||||
|
||||
datalayer.battery.status.temperature_min_dC = temperature_min_dC;
|
||||
datalayer.battery.status.temperature_max_dC = temperature_max_dC;
|
||||
|
||||
datalayer.battery.status.real_bms_status = has_fault ? BMS_FAULT : BMS_ACTIVE;
|
||||
}
|
||||
|
||||
void setup_battery(void) { // Performs one time setup at startup
|
||||
strncpy(datalayer.system.info.battery_protocol, "DALY RS485", 63);
|
||||
datalayer.system.info.battery_protocol[63] = '\0';
|
||||
datalayer.battery.info.number_of_cells = CELL_COUNT;
|
||||
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
|
||||
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV;
|
||||
datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV;
|
||||
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
|
||||
datalayer.battery.info.total_capacity_Wh = BATTERY_WH_MAX;
|
||||
}
|
||||
|
||||
uint8_t calculate_checksum(uint8_t buff[12]) {
|
||||
uint8_t check = 0;
|
||||
for (uint8_t i = 0; i < 12; i++) {
|
||||
check += buff[i];
|
||||
}
|
||||
return check;
|
||||
}
|
||||
|
||||
uint16_t decode_uint16be(uint8_t data[8], uint8_t offset) {
|
||||
uint16_t upper = data[offset];
|
||||
uint16_t lower = data[offset + 1];
|
||||
return (upper << 8) | lower;
|
||||
}
|
||||
int16_t decode_int16be(uint8_t data[8], uint8_t offset) {
|
||||
int16_t upper = data[offset];
|
||||
int16_t lower = data[offset + 1];
|
||||
return (upper << 8) | lower;
|
||||
}
|
||||
uint32_t decode_uint32be(uint8_t data[8], uint8_t offset) {
|
||||
return (((uint32_t)data[offset]) << 24) | (((uint32_t)data[offset + 1]) << 16) | (((uint32_t)data[offset + 2]) << 8) |
|
||||
((uint32_t)data[offset + 3]);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_VIA_USB
|
||||
void dump_buff(const char* msg, uint8_t* buff, uint8_t len) {
|
||||
Serial.print("[DALY-BMS] ");
|
||||
Serial.print(msg);
|
||||
for (int i = 0; i < len; i++) {
|
||||
Serial.print(buff[i] >> 4, HEX);
|
||||
Serial.print(buff[i] & 0xf, HEX);
|
||||
Serial.print(" ");
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
#endif
|
||||
|
||||
void decode_packet(uint8_t command, uint8_t data[8]) {
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
|
||||
switch (command) {
|
||||
case 0x90:
|
||||
voltage_dV = decode_uint16be(data, 0);
|
||||
current_dA = decode_int16be(data, 4) - 30000;
|
||||
SOC = decode_uint16be(data, 6) * 10;
|
||||
break;
|
||||
case 0x91:
|
||||
cellvoltage_max_mV = decode_uint16be(data, 0);
|
||||
cellvoltage_min_mV = decode_uint16be(data, 3);
|
||||
break;
|
||||
case 0x92:
|
||||
temperature_max_dC = (data[0] - 40) * 10;
|
||||
temperature_min_dC = (data[1] - 40) * 10;
|
||||
break;
|
||||
case 0x93:
|
||||
remaining_capacity_mAh = decode_uint32be(data, 4);
|
||||
break;
|
||||
case 0x94:
|
||||
break;
|
||||
case 0x95:
|
||||
if (data[0] > 0 && data[0] <= 16) {
|
||||
uint8_t frame_index = (data[0] - 1) * 3;
|
||||
cellvoltages_mV[frame_index + 0] = decode_uint16be(data, 1);
|
||||
cellvoltages_mV[frame_index + 1] = decode_uint16be(data, 3);
|
||||
cellvoltages_mV[frame_index + 2] = decode_uint16be(data, 5);
|
||||
}
|
||||
break;
|
||||
case 0x96:
|
||||
break;
|
||||
case 0x97:
|
||||
break;
|
||||
case 0x98:
|
||||
// for now we do not handle individual faults. All of them are 0 when ok, and 1 when a fault occurs
|
||||
has_fault = false;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (data[i] != 0x00) {
|
||||
has_fault = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void transmit_rs485() {
|
||||
static uint32_t lastSend = 0;
|
||||
static uint8_t nextCommand = 0x90;
|
||||
|
||||
if (millis() - lastSend > 100) {
|
||||
uint8_t tx_buff[13] = {0};
|
||||
tx_buff[0] = 0xA5;
|
||||
tx_buff[1] = 0x40;
|
||||
tx_buff[2] = nextCommand;
|
||||
tx_buff[3] = 8;
|
||||
tx_buff[12] = calculate_checksum(tx_buff);
|
||||
|
||||
#ifdef DEBUG_VIA_USB
|
||||
dump_buff("transmitting: ", tx_buff, 13);
|
||||
#endif
|
||||
|
||||
Serial2.write(tx_buff, 13);
|
||||
lastSend = millis();
|
||||
|
||||
nextCommand++;
|
||||
if (nextCommand > 0x98)
|
||||
nextCommand = 0x90;
|
||||
}
|
||||
}
|
||||
|
||||
void receive_RS485() {
|
||||
static uint8_t recv_buff[13] = {0};
|
||||
static uint8_t recv_len = 0;
|
||||
|
||||
while (Serial2.available()) {
|
||||
recv_buff[recv_len] = Serial2.read();
|
||||
|
||||
recv_len++;
|
||||
|
||||
if (recv_len > 0 && recv_buff[0] != 0xA5 || recv_len > 1 && recv_buff[1] != 0x01 ||
|
||||
recv_len > 2 && (recv_buff[2] < 0x90 || recv_buff[2] > 0x98) || recv_len > 3 && recv_buff[3] != 8 ||
|
||||
recv_len > 12 && recv_buff[12] != calculate_checksum(recv_buff)) {
|
||||
|
||||
#ifdef DEBUG_VIA_USB
|
||||
dump_buff("dropping partial rx: ", recv_buff, recv_len);
|
||||
#endif
|
||||
recv_len = 0;
|
||||
}
|
||||
|
||||
if (recv_len > 12) {
|
||||
#ifdef DEBUG_VIA_USB
|
||||
dump_buff("decoding successfull rx: ", recv_buff, recv_len);
|
||||
#endif
|
||||
decode_packet(recv_buff[2], &recv_buff[4]);
|
||||
recv_len = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
17
Software/src/battery/DALY-BMS.h
Normal file
17
Software/src/battery/DALY-BMS.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
#ifndef DALY_BMS_H
|
||||
#define DALY_BMS_H
|
||||
|
||||
/* Tweak these according to your battery build */
|
||||
#define CELL_COUNT 14
|
||||
#define MAX_PACK_VOLTAGE_DV 588 //588 = 58.8V
|
||||
#define MIN_PACK_VOLTAGE_DV 518 //518 = 51.8V
|
||||
#define MAX_CELL_VOLTAGE_MV 4250 //Battery is put into emergency stop if one cell goes over this value
|
||||
#define MIN_CELL_VOLTAGE_MV 2700 //Battery is put into emergency stop if one cell goes below this value
|
||||
#define POWER_PER_PERCENT 50 // below 20% and above 80% limit power to 50W * SOC (i.e. 150W at 3%, 500W at 10%, ...)
|
||||
|
||||
/* Do not modify any rows below*/
|
||||
#define BATTERY_SELECTED
|
||||
#define RS485_BATTERY_SELECTED
|
||||
#define RS485_BAUDRATE 9600
|
||||
|
||||
#endif
|
|
@ -109,7 +109,9 @@ void transmit_can() {
|
|||
return; //Global block of CAN messages
|
||||
}
|
||||
|
||||
#ifndef RS485_BATTERY_SELECTED
|
||||
transmit_can_battery();
|
||||
#endif
|
||||
|
||||
#ifdef CAN_INVERTER_SELECTED
|
||||
transmit_can_inverter();
|
||||
|
@ -302,7 +304,9 @@ void map_can_frame_to_variable(CAN_frame* rx_frame, int interface) {
|
|||
#endif
|
||||
|
||||
if (interface == can_config.battery) {
|
||||
#ifndef RS485_BATTERY_SELECTED
|
||||
handle_incoming_can_frame_battery(*rx_frame);
|
||||
#endif
|
||||
#ifdef CHADEMO_BATTERY
|
||||
ISA_handleFrame(rx_frame);
|
||||
#endif
|
||||
|
|
|
@ -29,9 +29,9 @@ void init_rs485() {
|
|||
pinMode(PIN_5V_EN, OUTPUT);
|
||||
digitalWrite(PIN_5V_EN, HIGH);
|
||||
#endif // PIN_5V_EN
|
||||
#ifdef RS485_INVERTER_SELECTED
|
||||
Serial2.begin(57600, SERIAL_8N1, RS485_RX_PIN, RS485_TX_PIN);
|
||||
#endif // RS485_INVERTER_SELECTED
|
||||
#if defined(RS485_INVERTER_SELECTED) || defined(RS485_BATTERY_SELECTED)
|
||||
Serial2.begin(RS485_BAUDRATE, SERIAL_8N1, RS485_RX_PIN, RS485_TX_PIN);
|
||||
#endif // RS485_INVERTER_SELECTED || RS485_BATTERY_SELECTED
|
||||
#ifdef MODBUS_INVERTER_SELECTED
|
||||
#ifdef BYD_MODBUS
|
||||
// Init Static data to the RTU Modbus
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "../include.h"
|
||||
|
||||
#define RS485_INVERTER_SELECTED
|
||||
#define RS485_BAUDRATE 57600
|
||||
//#define DEBUG_KOSTAL_RS485_DATA // Enable this line to get TX / RX printed out via logging
|
||||
|
||||
#if defined(DEBUG_KOSTAL_RS485_DATA) && !defined(DEBUG_LOG)
|
||||
|
|
|
@ -39,12 +39,25 @@ CAN_frame PYLON_35E = {.FD = false,
|
|||
MANUFACTURER_NAME[7],
|
||||
}};
|
||||
|
||||
// when e.g. the min temperature is 0, max is 100 and the warning percent is 80%
|
||||
// a warning should be generated at 20 (i.e. at 20% of the value range)
|
||||
// this function calculates this 20% point for a given min/max
|
||||
int16_t warning_threshold_of_min(int16_t min_val, int16_t max_val) {
|
||||
int16_t diff = max_val - min_val;
|
||||
return min_val + (diff * (100 - WARNINGS_PERCENT)) / 100;
|
||||
}
|
||||
|
||||
void update_values_can_inverter() {
|
||||
// This function maps all the values fetched from battery CAN to the correct CAN messages
|
||||
|
||||
// TODO: officially this value is "battery charge voltage". Do we need to add something here to the actual voltage?
|
||||
PYLON_351.data.u8[0] = datalayer.battery.status.voltage_dV & 0xff;
|
||||
PYLON_351.data.u8[1] = datalayer.battery.status.voltage_dV >> 8;
|
||||
// Set "battery charge voltage" to volts + 1 or user supplied value
|
||||
uint16_t charge_voltage_dV = datalayer.battery.status.voltage_dV + 1;
|
||||
if (datalayer.battery.settings.user_set_voltage_limits_active)
|
||||
charge_voltage_dV = datalayer.battery.settings.max_user_set_charge_voltage_dV;
|
||||
if (charge_voltage_dV > datalayer.battery.info.max_design_voltage_dV)
|
||||
charge_voltage_dV = datalayer.battery.info.max_design_voltage_dV;
|
||||
PYLON_351.data.u8[0] = charge_voltage_dV & 0xff;
|
||||
PYLON_351.data.u8[1] = charge_voltage_dV >> 8;
|
||||
PYLON_351.data.u8[2] = datalayer.battery.status.max_charge_current_dA & 0xff;
|
||||
PYLON_351.data.u8[3] = datalayer.battery.status.max_charge_current_dA >> 8;
|
||||
PYLON_351.data.u8[4] = datalayer.battery.status.max_discharge_current_dA & 0xff;
|
||||
|
@ -55,12 +68,14 @@ void update_values_can_inverter() {
|
|||
PYLON_355.data.u8[2] = (datalayer.battery.status.soh_pptt / 10) & 0xff;
|
||||
PYLON_355.data.u8[3] = (datalayer.battery.status.soh_pptt / 10) >> 8;
|
||||
|
||||
PYLON_356.data.u8[0] = datalayer.battery.status.voltage_dV & 0xff;
|
||||
PYLON_356.data.u8[1] = datalayer.battery.status.voltage_dV >> 8;
|
||||
int16_t voltage_cV = datalayer.battery.status.voltage_dV * 10;
|
||||
int16_t temperature = (datalayer.battery.status.temperature_min_dC + datalayer.battery.status.temperature_max_dC) / 2;
|
||||
PYLON_356.data.u8[0] = voltage_cV & 0xff;
|
||||
PYLON_356.data.u8[1] = voltage_cV >> 8;
|
||||
PYLON_356.data.u8[2] = datalayer.battery.status.current_dA & 0xff;
|
||||
PYLON_356.data.u8[3] = datalayer.battery.status.current_dA >> 8;
|
||||
PYLON_356.data.u8[4] = datalayer.battery.status.temperature_max_dC & 0xff;
|
||||
PYLON_356.data.u8[5] = datalayer.battery.status.temperature_max_dC >> 8;
|
||||
PYLON_356.data.u8[4] = temperature & 0xff;
|
||||
PYLON_356.data.u8[5] = temperature >> 8;
|
||||
|
||||
// initialize all errors and warnings to 0
|
||||
PYLON_359.data.u8[0] = 0x00;
|
||||
|
@ -78,20 +93,23 @@ void update_values_can_inverter() {
|
|||
PYLON_359.data.u8[0] |= 0x10;
|
||||
if (datalayer.battery.status.temperature_max_dC >= BATTERY_MAXTEMPERATURE)
|
||||
PYLON_359.data.u8[0] |= 0x0C;
|
||||
if (datalayer.battery.status.voltage_dV * 100 <= datalayer.battery.info.min_cell_voltage_mV)
|
||||
if (datalayer.battery.status.voltage_dV <= datalayer.battery.info.min_design_voltage_dV)
|
||||
PYLON_359.data.u8[0] |= 0x04;
|
||||
// we never set PYLON_359.data.u8[1] |= 0x80 called "BMS internal"
|
||||
if (datalayer.battery.status.bms_status == FAULT)
|
||||
PYLON_359.data.u8[1] |= 0x80;
|
||||
if (datalayer.battery.status.current_dA <= -1 * datalayer.battery.status.max_charge_current_dA)
|
||||
PYLON_359.data.u8[1] |= 0x01;
|
||||
|
||||
// WARNINGS (using same rules as errors but reporting earlier)
|
||||
if (datalayer.battery.status.current_dA >= datalayer.battery.status.max_discharge_current_dA * WARNINGS_PERCENT / 100)
|
||||
PYLON_359.data.u8[2] |= 0x80;
|
||||
if (datalayer.battery.status.temperature_min_dC <= BATTERY_MINTEMPERATURE * WARNINGS_PERCENT / 100)
|
||||
if (datalayer.battery.status.temperature_min_dC <=
|
||||
warning_threshold_of_min(BATTERY_MINTEMPERATURE, BATTERY_MAXTEMPERATURE))
|
||||
PYLON_359.data.u8[2] |= 0x10;
|
||||
if (datalayer.battery.status.temperature_max_dC >= BATTERY_MAXTEMPERATURE * WARNINGS_PERCENT / 100)
|
||||
PYLON_359.data.u8[2] |= 0x0C;
|
||||
if (datalayer.battery.status.voltage_dV * 100 <= datalayer.battery.info.min_cell_voltage_mV + 100)
|
||||
if (datalayer.battery.status.voltage_dV <= warning_threshold_of_min(datalayer.battery.info.min_design_voltage_dV,
|
||||
datalayer.battery.info.max_design_voltage_dV))
|
||||
PYLON_359.data.u8[2] |= 0x04;
|
||||
// we never set PYLON_359.data.u8[3] |= 0x80 called "BMS internal"
|
||||
if (datalayer.battery.status.current_dA <=
|
||||
|
@ -99,10 +117,17 @@ void update_values_can_inverter() {
|
|||
PYLON_359.data.u8[3] |= 0x01;
|
||||
|
||||
PYLON_35C.data.u8[0] = 0xC0; // enable charging and discharging
|
||||
PYLON_35C.data.u8[1] = 0x00;
|
||||
if (datalayer.battery.status.real_soc <= datalayer.battery.settings.min_percentage)
|
||||
if (datalayer.battery.status.bms_status == FAULT)
|
||||
PYLON_35C.data.u8[1] = 0x00; // disable all
|
||||
else if (datalayer.battery.settings.user_set_voltage_limits_active &&
|
||||
datalayer.battery.status.voltage_dV > datalayer.battery.settings.max_user_set_charge_voltage_dV)
|
||||
PYLON_35C.data.u8[1] = 0x40; // only allow discharging
|
||||
else if (datalayer.battery.settings.user_set_voltage_limits_active &&
|
||||
datalayer.battery.status.voltage_dV < datalayer.battery.settings.max_user_set_discharge_voltage_dV)
|
||||
PYLON_35C.data.u8[1] = 0xA0; // enable charing, set charge immediately
|
||||
else if (datalayer.battery.status.real_soc <= datalayer.battery.settings.min_percentage)
|
||||
PYLON_35C.data.u8[0] = 0xA0; // enable charing, set charge immediately
|
||||
if (datalayer.battery.status.real_soc >= datalayer.battery.settings.max_percentage)
|
||||
else if (datalayer.battery.status.real_soc >= datalayer.battery.settings.max_percentage)
|
||||
PYLON_35C.data.u8[0] = 0x40; // enable discharging only
|
||||
|
||||
// PYLON_35E is pre-filled with the manufacturer name
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue