mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-04 18:29:48 +02:00
Merge branch 'main' into bugfix/nissan-remaining-capacity
This commit is contained in:
commit
ca7914df9d
7 changed files with 165 additions and 23 deletions
|
@ -10,7 +10,7 @@ ci:
|
||||||
|
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||||
rev: v20.1.3
|
rev: v20.1.5
|
||||||
hooks:
|
hooks:
|
||||||
- id: clang-format
|
- id: clang-format
|
||||||
args: [-Werror] # change formatting warnings to errors, hook includes -i (Inplace edit) by default
|
args: [-Werror] # change formatting warnings to errors, hook includes -i (Inplace edit) by default
|
||||||
|
|
|
@ -449,6 +449,36 @@ void NissanLeafBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (group_7bb == 0x06) //Balancing resistor status
|
||||||
|
{
|
||||||
|
if (rx_frame.data.u8[0] == 0x10) { //First frame (10 1A 61 06 [14 55 55 51])
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
// Byte 4 - 7 (bits 0-31)
|
||||||
|
for (int byte_i = 0; byte_i < 4; byte_i++) {
|
||||||
|
battery_balancing_shunts[byte_i * 8 + i] = (rx_frame.data.u8[4 + byte_i] & (1 << i)) >> i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rx_frame.data.u8[0] == 0x21) { // Second frame (21 [50 55 41 2B 56 54 15])
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
// Byte 1 to 7 (bits 32-87)
|
||||||
|
for (int byte_i = 0; byte_i < 7; byte_i++) {
|
||||||
|
battery_balancing_shunts[32 + byte_i * 8 + i] = (rx_frame.data.u8[1 + byte_i] & (1 << i)) >> i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rx_frame.data.u8[0] == 0x22) { //Third frame (22 51 FF FF FF FF FF FF)
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
// Byte 1 (bits 88-95)
|
||||||
|
battery_balancing_shunts[88 + i] = (rx_frame.data.u8[1] & (1 << i)) >> i;
|
||||||
|
}
|
||||||
|
memcpy(datalayer_battery->status.cell_balancing_status, battery_balancing_shunts, 96 * sizeof(bool));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rx_frame.data.u8[0] == 0x23) { //Fourth frame (23 FF FF FF FF FF FF FF)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (group_7bb == 0x83) //BatteryPartNumber
|
if (group_7bb == 0x83) //BatteryPartNumber
|
||||||
{
|
{
|
||||||
if (rx_frame.data.u8[0] == 0x10) { //First frame (101A6183334E4B32)
|
if (rx_frame.data.u8[0] == 0x10) { //First frame (101A6183334E4B32)
|
||||||
|
@ -700,7 +730,7 @@ void NissanLeafBattery::transmit_can(unsigned long currentMillis) {
|
||||||
if (!stop_battery_query) {
|
if (!stop_battery_query) {
|
||||||
|
|
||||||
// Move to the next group
|
// Move to the next group
|
||||||
PIDindex = (PIDindex + 1) % 6; // 6 = amount of elements in the PIDgroups[]
|
PIDindex = (PIDindex + 1) % 7; // 7 = amount of elements in the PIDgroups[]
|
||||||
LEAF_GROUP_REQUEST.data.u8[2] = PIDgroups[PIDindex];
|
LEAF_GROUP_REQUEST.data.u8[2] = PIDgroups[PIDindex];
|
||||||
|
|
||||||
transmit_can_frame(&LEAF_GROUP_REQUEST, can_interface);
|
transmit_can_frame(&LEAF_GROUP_REQUEST, can_interface);
|
||||||
|
|
|
@ -82,7 +82,7 @@ class NissanLeafBattery : public CanBattery {
|
||||||
.ID = 0x1D4,
|
.ID = 0x1D4,
|
||||||
.data = {0x6E, 0x6E, 0x00, 0x04, 0x07, 0x46, 0xE0, 0x44}};
|
.data = {0x6E, 0x6E, 0x00, 0x04, 0x07, 0x46, 0xE0, 0x44}};
|
||||||
// Active polling messages
|
// Active polling messages
|
||||||
uint8_t PIDgroups[6] = {0x01, 0x02, 0x04, 0x83, 0x84, 0x90};
|
uint8_t PIDgroups[7] = {0x01, 0x02, 0x04, 0x06, 0x83, 0x84, 0x90};
|
||||||
uint8_t PIDindex = 0;
|
uint8_t PIDindex = 0;
|
||||||
CAN_frame LEAF_GROUP_REQUEST = {.FD = false,
|
CAN_frame LEAF_GROUP_REQUEST = {.FD = false,
|
||||||
.ext_ID = false,
|
.ext_ID = false,
|
||||||
|
@ -155,7 +155,8 @@ class NissanLeafBattery : public CanBattery {
|
||||||
uint8_t group_7bb = 0;
|
uint8_t group_7bb = 0;
|
||||||
bool stop_battery_query = true;
|
bool stop_battery_query = true;
|
||||||
uint8_t hold_off_with_polling_10seconds = 2; //Paused for 20 seconds on startup
|
uint8_t hold_off_with_polling_10seconds = 2; //Paused for 20 seconds on startup
|
||||||
uint16_t battery_cell_voltages[97]; //array with all the cellvoltages
|
uint16_t battery_cell_voltages[96]; //array with all the cellvoltages
|
||||||
|
bool battery_balancing_shunts[96]; //array with all the balancing resistors
|
||||||
uint8_t battery_cellcounter = 0;
|
uint8_t battery_cellcounter = 0;
|
||||||
uint16_t battery_min_max_voltage[2]; //contains cell min[0] and max[1] values in mV
|
uint16_t battery_min_max_voltage[2]; //contains cell min[0] and max[1] values in mV
|
||||||
uint16_t battery_HX = 0; //Internal resistance
|
uint16_t battery_HX = 0; //Internal resistance
|
||||||
|
|
|
@ -108,8 +108,16 @@ void RenaultZoeGen2Battery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
switch (rx_frame.ID) {
|
switch (rx_frame.ID) {
|
||||||
case 0x18DAF1DB: // LBC Reply from active polling
|
case 0x18DAF1DB: // LBC Reply from active polling
|
||||||
//frame 2 & 3 contains
|
|
||||||
|
if (rx_frame.data.u8[0] == 0x10) { //First frame of a group
|
||||||
|
transmit_can_frame(&ZOE_POLL_FLOW_CONTROL, can_config.battery);
|
||||||
|
//frame 2 & 3 contains which PID is sent
|
||||||
|
reply_poll = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rx_frame.data.u8[0] < 0x10) { //One line responses
|
||||||
reply_poll = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3];
|
reply_poll = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3];
|
||||||
|
}
|
||||||
|
|
||||||
switch (reply_poll) {
|
switch (reply_poll) {
|
||||||
case POLL_SOC:
|
case POLL_SOC:
|
||||||
|
@ -200,6 +208,29 @@ void RenaultZoeGen2Battery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||||
battery_bms_state = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
|
battery_bms_state = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
|
||||||
break;
|
break;
|
||||||
case POLL_BALANCE_SWITCHES:
|
case POLL_BALANCE_SWITCHES:
|
||||||
|
if (rx_frame.data.u8[0] == 0x10) {
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
// Byte 4 - 7 (bits 0-31)
|
||||||
|
for (int byte_i = 0; byte_i < 4; byte_i++) {
|
||||||
|
battery_balancing_shunts[byte_i * 8 + i] = (rx_frame.data.u8[4 + byte_i] & (1 << i)) >> i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rx_frame.data.u8[0] == 0x21) {
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
// Byte 1 to 7 (bits 32-87)
|
||||||
|
for (int byte_i = 0; byte_i < 7; byte_i++) {
|
||||||
|
battery_balancing_shunts[32 + byte_i * 8 + i] = (rx_frame.data.u8[1 + byte_i] & (1 << i)) >> i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rx_frame.data.u8[0] == 0x22) {
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
// Byte 1 (bits 88-95)
|
||||||
|
battery_balancing_shunts[88 + i] = (rx_frame.data.u8[1] & (1 << i)) >> i;
|
||||||
|
}
|
||||||
|
memcpy(datalayer.battery.status.cell_balancing_status, battery_balancing_shunts, 96 * sizeof(bool));
|
||||||
|
}
|
||||||
battery_balance_switches = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
|
battery_balance_switches = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
|
||||||
break;
|
break;
|
||||||
case POLL_ENERGY_COMPLETE:
|
case POLL_ENERGY_COMPLETE:
|
||||||
|
|
|
@ -205,6 +205,7 @@ class RenaultZoeGen2Battery : public CanBattery {
|
||||||
uint32_t ZOE_376_time_now_s = 1745452800; // Initialized to make the battery think it is April 24, 2025
|
uint32_t ZOE_376_time_now_s = 1745452800; // Initialized to make the battery think it is April 24, 2025
|
||||||
unsigned long kProductionTimestamp_s =
|
unsigned long kProductionTimestamp_s =
|
||||||
1614454107; // Production timestamp in seconds since January 1, 1970. Production timestamp used: February 25, 2021 at 8:08:27 AM GMT
|
1614454107; // Production timestamp in seconds since January 1, 1970. Production timestamp used: February 25, 2021 at 8:08:27 AM GMT
|
||||||
|
bool battery_balancing_shunts[96];
|
||||||
|
|
||||||
CAN_frame ZOE_373 = {
|
CAN_frame ZOE_373 = {
|
||||||
.FD = false,
|
.FD = false,
|
||||||
|
@ -225,6 +226,11 @@ class RenaultZoeGen2Battery : public CanBattery {
|
||||||
.DLC = 8,
|
.DLC = 8,
|
||||||
.ID = 0x18DADBF1,
|
.ID = 0x18DADBF1,
|
||||||
.data = {0x03, 0x22, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
.data = {0x03, 0x22, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
CAN_frame ZOE_POLL_FLOW_CONTROL = {.FD = false,
|
||||||
|
.ext_ID = true,
|
||||||
|
.DLC = 8,
|
||||||
|
.ID = 0x18DADBF1,
|
||||||
|
.data = {0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
//NVROL Reset
|
//NVROL Reset
|
||||||
CAN_frame ZOE_NVROL_1_18DADBF1 = {.FD = false,
|
CAN_frame ZOE_NVROL_1_18DADBF1 = {.FD = false,
|
||||||
.ext_ID = true,
|
.ext_ID = true,
|
||||||
|
|
|
@ -81,6 +81,11 @@ typedef struct {
|
||||||
* Use with battery.info.number_of_cells to get valid data.
|
* Use with battery.info.number_of_cells to get valid data.
|
||||||
*/
|
*/
|
||||||
uint16_t cell_voltages_mV[MAX_AMOUNT_CELLS];
|
uint16_t cell_voltages_mV[MAX_AMOUNT_CELLS];
|
||||||
|
/** All balancing resistors status inside the pack, either on(1) or off(0).
|
||||||
|
* Use with battery.info.number_of_cells to get valid data.
|
||||||
|
* Not available for all battery manufacturers.
|
||||||
|
*/
|
||||||
|
bool cell_balancing_status[MAX_AMOUNT_CELLS];
|
||||||
/** The "real" SOC reported from the battery, in integer-percent x 100. 9550 = 95.50% */
|
/** The "real" SOC reported from the battery, in integer-percent x 100. 9550 = 95.50% */
|
||||||
uint16_t real_soc;
|
uint16_t real_soc;
|
||||||
/** The SOC reported to the inverter, in integer-percent x 100. 9550 = 95.50%.
|
/** The SOC reported to the inverter, in integer-percent x 100. 9550 = 95.50%.
|
||||||
|
|
|
@ -46,6 +46,24 @@ String cellmonitor_processor(const String& var) {
|
||||||
content += "<div id='graph'></div>";
|
content += "<div id='graph'></div>";
|
||||||
// Display single hovered value
|
// Display single hovered value
|
||||||
content += "<div id='valueDisplay'>Value: ...</div>";
|
content += "<div id='valueDisplay'>Value: ...</div>";
|
||||||
|
//Legend for graph
|
||||||
|
content +=
|
||||||
|
"<span style='color: white; background-color: blue; font-weight: bold; padding: 2px 8px; border-radius: 4px; "
|
||||||
|
"margin-right: 15px;'>Idle</span>";
|
||||||
|
bool battery_balancing = false;
|
||||||
|
for (uint8_t i = 0u; i < datalayer.battery.info.number_of_cells; i++) {
|
||||||
|
battery_balancing = datalayer.battery.status.cell_balancing_status[i];
|
||||||
|
if (battery_balancing)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (battery_balancing) {
|
||||||
|
content +=
|
||||||
|
"<span style='color: black; background-color: #00FFFF; font-weight: bold; padding: 2px 8px; border-radius: "
|
||||||
|
"4px; margin-right: 15px;'>Balancing</span>";
|
||||||
|
}
|
||||||
|
content +=
|
||||||
|
"<span style='color: white; background-color: red; font-weight: bold; padding: 2px 8px; border-radius: "
|
||||||
|
"4px;'>Min/Max</span>";
|
||||||
|
|
||||||
// Close the block
|
// Close the block
|
||||||
content += "</div>";
|
content += "</div>";
|
||||||
|
@ -62,6 +80,25 @@ String cellmonitor_processor(const String& var) {
|
||||||
content += "<div id='graph2'></div>";
|
content += "<div id='graph2'></div>";
|
||||||
// Display single hovered value
|
// Display single hovered value
|
||||||
content += "<div id='valueDisplay2'>Value: ...</div>";
|
content += "<div id='valueDisplay2'>Value: ...</div>";
|
||||||
|
//Legend for graph
|
||||||
|
content +=
|
||||||
|
"<span style='color: white; background-color: blue; font-weight: bold; padding: 2px 8px; border-radius: 4px; "
|
||||||
|
"margin-right: 15px;'>Idle</span>";
|
||||||
|
|
||||||
|
bool battery2_balancing = false;
|
||||||
|
for (uint8_t i = 0u; i < datalayer.battery2.info.number_of_cells; i++) {
|
||||||
|
battery2_balancing = datalayer.battery2.status.cell_balancing_status[i];
|
||||||
|
if (battery2_balancing)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (battery2_balancing) {
|
||||||
|
content +=
|
||||||
|
"<span style='color: black; background-color: #00FFFF; font-weight: bold; padding: 2px 8px; border-radius: "
|
||||||
|
"4px; margin-right: 15px;'>Balancing</span>";
|
||||||
|
}
|
||||||
|
content +=
|
||||||
|
"<span style='color: white; background-color: red; font-weight: bold; padding: 2px 8px; border-radius: "
|
||||||
|
"4px;'>Min/Max</span>";
|
||||||
|
|
||||||
// Close the block
|
// Close the block
|
||||||
content += "</div>";
|
content += "</div>";
|
||||||
|
@ -80,6 +117,15 @@ String cellmonitor_processor(const String& var) {
|
||||||
}
|
}
|
||||||
content += "];";
|
content += "];";
|
||||||
|
|
||||||
|
content += "const balancing = [";
|
||||||
|
for (uint8_t i = 0u; i < datalayer.battery.info.number_of_cells; i++) {
|
||||||
|
if (datalayer.battery.status.cell_voltages_mV[i] == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
content += datalayer.battery.status.cell_balancing_status[i] ? "true," : "false,";
|
||||||
|
}
|
||||||
|
content += "];";
|
||||||
|
|
||||||
content += "const min_mv = Math.min(...data) - 20;";
|
content += "const min_mv = Math.min(...data) - 20;";
|
||||||
content += "const max_mv = Math.max(...data) + 20;";
|
content += "const max_mv = Math.max(...data) + 20;";
|
||||||
content += "const min_index = data.indexOf(Math.min(...data));";
|
content += "const min_index = data.indexOf(Math.min(...data));";
|
||||||
|
@ -110,20 +156,27 @@ String cellmonitor_processor(const String& var) {
|
||||||
"bar.id = `barIndex${index}`;"
|
"bar.id = `barIndex${index}`;"
|
||||||
"bar.style.height = `${mV_limited}px`;"
|
"bar.style.height = `${mV_limited}px`;"
|
||||||
"bar.style.width = `${750/data.length}px`;"
|
"bar.style.width = `${750/data.length}px`;"
|
||||||
|
"if (balancing[index]) {"
|
||||||
|
" bar.style.backgroundColor = '#00FFFF';" // Cyan color for balancing
|
||||||
|
" bar.style.borderColor = '#00FFFF';"
|
||||||
|
"} else {"
|
||||||
|
" bar.style.backgroundColor = 'blue';" // Normal blue for non-balancing
|
||||||
|
" bar.style.borderColor = 'white';"
|
||||||
|
"}"
|
||||||
|
|
||||||
"const cell = document.getElementById(`cellIndex${index}`);"
|
"const cell = document.getElementById(`cellIndex${index}`);"
|
||||||
|
|
||||||
"checkMinMax(cell, bar, index);"
|
"checkMinMax(cell, bar, index);"
|
||||||
|
|
||||||
"bar.addEventListener('mouseenter', () => {"
|
"bar.addEventListener('mouseenter', () => {"
|
||||||
"valueDisplay.textContent = `Value: ${mV}`;"
|
" valueDisplay.textContent = `Value: ${mV}` + (balancing[index] ? ' (balancing)' : '');"
|
||||||
"bar.style.backgroundColor = `lightblue`;"
|
" bar.style.backgroundColor = balancing[index] ? '#80FFFF' : 'lightblue';"
|
||||||
"cell.style.backgroundColor = `blue`;"
|
" cell.style.backgroundColor = balancing[index] ? '#006666' : 'blue';"
|
||||||
"});"
|
"});"
|
||||||
|
|
||||||
"bar.addEventListener('mouseleave', () => {"
|
"bar.addEventListener('mouseleave', () => {"
|
||||||
"valueDisplay.textContent = 'Value: ...';"
|
"valueDisplay.textContent = 'Value: ...';"
|
||||||
"bar.style.backgroundColor = `blue`;"
|
"bar.style.backgroundColor = balancing[index] ? '#00FFFF' : 'blue';" // Restore cyan if balancing, else blue
|
||||||
"cell.style.removeProperty('background-color');"
|
"cell.style.removeProperty('background-color');"
|
||||||
"});"
|
"});"
|
||||||
|
|
||||||
|
@ -140,20 +193,20 @@ String cellmonitor_processor(const String& var) {
|
||||||
"cell.id = `cellIndex${index}`;"
|
"cell.id = `cellIndex${index}`;"
|
||||||
"let cellContent = `Cell ${index + 1}<br>${mV} mV`;"
|
"let cellContent = `Cell ${index + 1}<br>${mV} mV`;"
|
||||||
"if (mV < 3000) {"
|
"if (mV < 3000) {"
|
||||||
"cellContent = `<span class='low-voltage'>${cellContent}</span>`;"
|
" cellContent = `<span class='low-voltage'>${cellContent}</span>`;"
|
||||||
"}"
|
"}"
|
||||||
"cell.innerHTML = cellContent;"
|
"cell.innerHTML = cellContent;"
|
||||||
|
|
||||||
"cell.addEventListener('mouseenter', () => {"
|
"cell.addEventListener('mouseenter', () => {"
|
||||||
"let bar = document.getElementById(`barIndex${index}`);"
|
"let bar = document.getElementById(`barIndex${index}`);"
|
||||||
"valueDisplay.textContent = `Value: ${mV}`;"
|
"valueDisplay.textContent = `Value: ${mV}`;"
|
||||||
"bar.style.backgroundColor = `lightblue`;"
|
"bar.style.backgroundColor = balancing[index] ? '#80FFFF' : 'lightblue';" // Lighter cyan if balancing
|
||||||
"cell.style.backgroundColor = `blue`;"
|
"cell.style.backgroundColor = balancing[index] ? '#006666' : 'blue';" // Darker cyan if balancing
|
||||||
"});"
|
"});"
|
||||||
|
|
||||||
"cell.addEventListener('mouseleave', () => {"
|
"cell.addEventListener('mouseleave', () => {"
|
||||||
"let bar = document.getElementById(`barIndex${index}`);"
|
"let bar = document.getElementById(`barIndex${index}`);"
|
||||||
"bar.style.backgroundColor = `blue`;"
|
"bar.style.backgroundColor = balancing[index] ? '#00FFFF' : 'blue';" // Restore original color
|
||||||
"cell.style.removeProperty('background-color');"
|
"cell.style.removeProperty('background-color');"
|
||||||
"});"
|
"});"
|
||||||
|
|
||||||
|
@ -195,6 +248,15 @@ String cellmonitor_processor(const String& var) {
|
||||||
}
|
}
|
||||||
content += "];";
|
content += "];";
|
||||||
|
|
||||||
|
content += "const balancing2 = [";
|
||||||
|
for (uint8_t i = 0u; i < datalayer.battery2.info.number_of_cells; i++) {
|
||||||
|
if (datalayer.battery2.status.cell_voltages_mV[i] == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
content += datalayer.battery2.status.cell_balancing_status[i] ? "true," : "false,";
|
||||||
|
}
|
||||||
|
content += "];";
|
||||||
|
|
||||||
content += "const min_mv2 = Math.min(...data2) - 20;";
|
content += "const min_mv2 = Math.min(...data2) - 20;";
|
||||||
content += "const max_mv2 = Math.max(...data2) + 20;";
|
content += "const max_mv2 = Math.max(...data2) + 20;";
|
||||||
content += "const min_index2 = data2.indexOf(Math.min(...data2));";
|
content += "const min_index2 = data2.indexOf(Math.min(...data2));";
|
||||||
|
@ -223,20 +285,26 @@ String cellmonitor_processor(const String& var) {
|
||||||
"bar2.id = `barIndex2${index2}`;"
|
"bar2.id = `barIndex2${index2}`;"
|
||||||
"bar2.style.height = `${mV_limited2}px`;"
|
"bar2.style.height = `${mV_limited2}px`;"
|
||||||
"bar2.style.width = `${750/data2.length}px`;"
|
"bar2.style.width = `${750/data2.length}px`;"
|
||||||
|
"if (balancing2[index2]) {"
|
||||||
|
" bar2.style.backgroundColor = '#00FFFF';" // Cyan color for balancing
|
||||||
|
" bar2.style.borderColor = '#00FFFF';"
|
||||||
|
"} else {"
|
||||||
|
" bar2.style.backgroundColor = 'blue';" // Normal blue for non-balancing
|
||||||
|
" bar2.style.borderColor = 'white';"
|
||||||
|
"}"
|
||||||
"const cell2 = document.getElementById(`cellIndex2${index2}`);"
|
"const cell2 = document.getElementById(`cellIndex2${index2}`);"
|
||||||
|
|
||||||
"checkMinMax2(cell2, bar2, index2);"
|
"checkMinMax2(cell2, bar2, index2);"
|
||||||
|
|
||||||
"bar2.addEventListener('mouseenter', () => {"
|
"bar2.addEventListener('mouseenter', () => {"
|
||||||
"valueDisplay2.textContent = `Value: ${mV}`;"
|
" valueDisplay2.textContent = `Value: ${mV}` + (balancing[index2] ? ' (balancing)' : '');"
|
||||||
"bar2.style.backgroundColor = `lightblue`;"
|
" bar2.style.backgroundColor = balancing2[index2] ? '#80FFFF' : 'lightblue';"
|
||||||
"cell2.style.backgroundColor = `blue`;"
|
" cell2.style.backgroundColor = balancing2[index2] ? '#006666' : 'blue';"
|
||||||
"});"
|
"});"
|
||||||
|
|
||||||
"bar2.addEventListener('mouseleave', () => {"
|
"bar2.addEventListener('mouseleave', () => {"
|
||||||
"valueDisplay2.textContent = 'Value: ...';"
|
"valueDisplay2.textContent = 'Value: ...';"
|
||||||
"bar2.style.backgroundColor = `blue`;"
|
"bar2.style.backgroundColor = balancing2[index2] ? '#00FFFF' : 'blue';" // Restore cyan if balancing, else blue
|
||||||
"cell2.style.removeProperty('background-color');"
|
"cell2.style.removeProperty('background-color');"
|
||||||
"});"
|
"});"
|
||||||
|
|
||||||
|
@ -260,13 +328,13 @@ String cellmonitor_processor(const String& var) {
|
||||||
"cell2.addEventListener('mouseenter', () => {"
|
"cell2.addEventListener('mouseenter', () => {"
|
||||||
"let bar2 = document.getElementById(`barIndex2${index2}`);"
|
"let bar2 = document.getElementById(`barIndex2${index2}`);"
|
||||||
"valueDisplay2.textContent = `Value: ${mV}`;"
|
"valueDisplay2.textContent = `Value: ${mV}`;"
|
||||||
"bar2.style.backgroundColor = `lightblue`;"
|
"bar2.style.backgroundColor = balancing2[index2] ? '#80FFFF' : 'lightblue';" // Lighter cyan if balancing
|
||||||
"cell2.style.backgroundColor = `blue`;"
|
"cell2.style.backgroundColor = balancing2[index2] ? '#006666' : 'blue';" // Darker cyan if balancing
|
||||||
"});"
|
"});"
|
||||||
|
|
||||||
"cell2.addEventListener('mouseleave', () => {"
|
"cell2.addEventListener('mouseleave', () => {"
|
||||||
"let bar2 = document.getElementById(`barIndex2${index2}`);"
|
"let bar2 = document.getElementById(`barIndex2${index2}`);"
|
||||||
"bar2.style.backgroundColor = `blue`;"
|
"bar2.style.backgroundColor = balancing2[index2] ? '#00FFFF' : 'blue';" // Restore original color
|
||||||
"cell2.style.removeProperty('background-color');"
|
"cell2.style.removeProperty('background-color');"
|
||||||
"});"
|
"});"
|
||||||
|
|
||||||
|
@ -281,7 +349,8 @@ String cellmonitor_processor(const String& var) {
|
||||||
"const max_mv2 = Math.max(...data2);"
|
"const max_mv2 = Math.max(...data2);"
|
||||||
"const cell_dev2 = max_mv2 - min_mv2;"
|
"const cell_dev2 = max_mv2 - min_mv2;"
|
||||||
"const voltVal2 = document.getElementById('voltageValues2');"
|
"const voltVal2 = document.getElementById('voltageValues2');"
|
||||||
"voltVal2.innerHTML = `Max Voltage : ${max_mv2} mV<br>Min Voltage: ${min_mv2} mV<br>Voltage Deviation: "
|
"voltVal2.innerHTML = `Battery #2<br>Max Voltage : ${max_mv2} mV<br>Min Voltage: ${min_mv2} mV<br>Voltage "
|
||||||
|
"Deviation: "
|
||||||
"${cell_dev2} mV`"
|
"${cell_dev2} mV`"
|
||||||
"}";
|
"}";
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue