Merge from main and fix conflicts

This commit is contained in:
Jaakko Haakana 2025-06-02 23:55:36 +03:00
commit c968f13a79
11 changed files with 181 additions and 39 deletions

View file

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

View file

@ -458,6 +458,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)
@ -709,7 +739,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);

View file

@ -93,7 +93,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,
@ -163,7 +163,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

View file

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

View file

@ -213,6 +213,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,
@ -233,6 +234,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,

View file

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

View file

@ -49,6 +49,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>";
@ -65,6 +83,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>";
@ -83,6 +120,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));";
@ -113,20 +159,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');"
"});" "});"
@ -150,13 +203,13 @@ String cellmonitor_processor(const String& var) {
"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');"
"});" "});"
@ -198,6 +251,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));";
@ -226,20 +288,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');"
"});" "});"
@ -263,13 +331,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');"
"});" "});"
@ -284,7 +352,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`"
"}"; "}";

View file

@ -3,6 +3,12 @@
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../include.h" #include "../include.h"
//#define SEND_0 //If defined, the messages will have ID ending with 0 (useful for some inverters)
#define SEND_1 //If defined, the messages will have ID ending with 1 (useful for some inverters)
#define INVERT_LOW_HIGH_BYTES //If defined, certain frames will have inverted low/high bytes \
//useful for some inverters like Sofar that report the voltages incorrect otherwise
#define SET_30K_OFFSET //If defined, current values are sent with a 30k offest (useful for ferroamp)
void FerroampCanInverter:: void FerroampCanInverter::
update_values() { //This function maps all the values fetched from battery CAN to the correct CAN messages update_values() { //This function maps all the values fetched from battery CAN to the correct CAN messages
//There are more mappings that could be added, but this should be enough to use as a starting point //There are more mappings that could be added, but this should be enough to use as a starting point

View file

@ -20,12 +20,6 @@ class FerroampCanInverter : public CanInverterProtocol {
void send_system_data(); void send_system_data();
void send_setup_info(); void send_setup_info();
//#define SEND_0 //If defined, the messages will have ID ending with 0 (useful for some inverters)
#define SEND_1 //If defined, the messages will have ID ending with 1 (useful for some inverters)
#define INVERT_LOW_HIGH_BYTES //If defined, certain frames will have inverted low/high bytes \
//useful for some inverters like Sofar that report the voltages incorrect otherwise
#define SET_30K_OFFSET //If defined, current values are sent with a 30k offest (useful for ferroamp)
/* Some inverters need to see a specific amount of cells/modules to emulate a specific Pylon battery. /* Some inverters need to see a specific amount of cells/modules to emulate a specific Pylon battery.
Change the following only if your inverter is generating fault codes about voltage range */ Change the following only if your inverter is generating fault codes about voltage range */
static const int TOTAL_CELL_AMOUNT = 120; //Adjust this parameter in steps of 120 to add another 14,2kWh of capacity static const int TOTAL_CELL_AMOUNT = 120; //Adjust this parameter in steps of 120 to add another 14,2kWh of capacity
@ -51,14 +45,14 @@ Change the following only if your inverter is generating fault codes about volta
.ID = 0x7320, .ID = 0x7320,
.data = {(TOTAL_CELL_AMOUNT & 0xFF), (uint8_t)(TOTAL_CELL_AMOUNT >> 8), MODULES_IN_SERIES, .data = {(TOTAL_CELL_AMOUNT & 0xFF), (uint8_t)(TOTAL_CELL_AMOUNT >> 8), MODULES_IN_SERIES,
CELLS_PER_MODULE, (uint8_t)(VOLTAGE_LEVEL & 0x00FF), (uint8_t)(VOLTAGE_LEVEL >> 8), CELLS_PER_MODULE, (uint8_t)(VOLTAGE_LEVEL & 0x00FF), (uint8_t)(VOLTAGE_LEVEL >> 8),
AH_CAPACITY, (uint8_t)(AH_CAPACITY >> 8)}}; (uint8_t)(AH_CAPACITY & 0x00FF), (uint8_t)(AH_CAPACITY >> 8)}};
CAN_frame PYLON_7321 = {.FD = false, CAN_frame PYLON_7321 = {.FD = false,
.ext_ID = true, .ext_ID = true,
.DLC = 8, .DLC = 8,
.ID = 0x7321, .ID = 0x7321,
.data = {(TOTAL_CELL_AMOUNT & 0xFF), (uint8_t)(TOTAL_CELL_AMOUNT >> 8), MODULES_IN_SERIES, .data = {(TOTAL_CELL_AMOUNT & 0xFF), (uint8_t)(TOTAL_CELL_AMOUNT >> 8), MODULES_IN_SERIES,
CELLS_PER_MODULE, (uint8_t)(VOLTAGE_LEVEL & 0x00FF), (uint8_t)(VOLTAGE_LEVEL >> 8), CELLS_PER_MODULE, (uint8_t)(VOLTAGE_LEVEL & 0x00FF), (uint8_t)(VOLTAGE_LEVEL >> 8),
AH_CAPACITY, (uint8_t)(AH_CAPACITY >> 8)}}; (uint8_t)(AH_CAPACITY & 0x00FF), (uint8_t)(AH_CAPACITY >> 8)}};
CAN_frame PYLON_4210 = {.FD = false, CAN_frame PYLON_4210 = {.FD = false,
.ext_ID = true, .ext_ID = true,
.DLC = 8, .DLC = 8,

View file

@ -3,6 +3,12 @@
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../include.h" #include "../include.h"
#define SEND_0 //If defined, the messages will have ID ending with 0 (useful for some inverters)
//#define SEND_1 //If defined, the messages will have ID ending with 1 (useful for some inverters)
#define INVERT_LOW_HIGH_BYTES //If defined, certain frames will have inverted low/high bytes \
//useful for some inverters like Sofar that report the voltages incorrect otherwise
//#define SET_30K_OFFSET //If defined, current values are sent with a 30k offest (useful for ferroamp)
void PylonInverter:: void PylonInverter::
update_values() { //This function maps all the values fetched from battery CAN to the correct CAN messages update_values() { //This function maps all the values fetched from battery CAN to the correct CAN messages

View file

@ -20,12 +20,6 @@ class PylonInverter : public CanInverterProtocol {
void send_system_data(); void send_system_data();
void send_setup_info(); void send_setup_info();
#define SEND_0 //If defined, the messages will have ID ending with 0 (useful for some inverters)
//#define SEND_1 //If defined, the messages will have ID ending with 1 (useful for some inverters)
#define INVERT_LOW_HIGH_BYTES //If defined, certain frames will have inverted low/high bytes \
//useful for some inverters like Sofar that report the voltages incorrect otherwise
//#define SET_30K_OFFSET //If defined, current values are sent with a 30k offest (useful for ferroamp)
/* Some inverters need to see a specific amount of cells/modules to emulate a specific Pylon battery. /* Some inverters need to see a specific amount of cells/modules to emulate a specific Pylon battery.
Change the following only if your inverter is generating fault codes about voltage range */ Change the following only if your inverter is generating fault codes about voltage range */
static const int TOTAL_CELL_AMOUNT = 120; static const int TOTAL_CELL_AMOUNT = 120;
@ -52,14 +46,14 @@ class PylonInverter : public CanInverterProtocol {
.ID = 0x7320, .ID = 0x7320,
.data = {TOTAL_CELL_AMOUNT, (uint8_t)(TOTAL_CELL_AMOUNT >> 8), MODULES_IN_SERIES, .data = {TOTAL_CELL_AMOUNT, (uint8_t)(TOTAL_CELL_AMOUNT >> 8), MODULES_IN_SERIES,
CELLS_PER_MODULE, (uint8_t)(VOLTAGE_LEVEL & 0x00FF), (uint8_t)(VOLTAGE_LEVEL >> 8), CELLS_PER_MODULE, (uint8_t)(VOLTAGE_LEVEL & 0x00FF), (uint8_t)(VOLTAGE_LEVEL >> 8),
AH_CAPACITY, (uint8_t)(AH_CAPACITY >> 8)}}; (uint8_t)(AH_CAPACITY & 0x00FF), (uint8_t)(AH_CAPACITY >> 8)}};
CAN_frame PYLON_7321 = {.FD = false, CAN_frame PYLON_7321 = {.FD = false,
.ext_ID = true, .ext_ID = true,
.DLC = 8, .DLC = 8,
.ID = 0x7321, .ID = 0x7321,
.data = {TOTAL_CELL_AMOUNT, (uint8_t)(TOTAL_CELL_AMOUNT >> 8), MODULES_IN_SERIES, .data = {TOTAL_CELL_AMOUNT, (uint8_t)(TOTAL_CELL_AMOUNT >> 8), MODULES_IN_SERIES,
CELLS_PER_MODULE, (uint8_t)(VOLTAGE_LEVEL & 0x00FF), (uint8_t)(VOLTAGE_LEVEL >> 8), CELLS_PER_MODULE, (uint8_t)(VOLTAGE_LEVEL & 0x00FF), (uint8_t)(VOLTAGE_LEVEL >> 8),
AH_CAPACITY, (uint8_t)(AH_CAPACITY >> 8)}}; (uint8_t)(AH_CAPACITY & 0x00FF), (uint8_t)(AH_CAPACITY >> 8)}};
CAN_frame PYLON_4210 = {.FD = false, CAN_frame PYLON_4210 = {.FD = false,
.ext_ID = true, .ext_ID = true,
.DLC = 8, .DLC = 8,