mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-03 09:49:32 +02:00
Merge pull request #1193 from kyberias/batt-rend
Batteries implement HTML renderers for status and commands
This commit is contained in:
commit
b17c7f804e
52 changed files with 2632 additions and 2300 deletions
|
@ -319,7 +319,6 @@ void init_serial() {
|
|||
#endif // DEBUG_VIA_USB
|
||||
}
|
||||
|
||||
#ifdef DOUBLE_BATTERY
|
||||
void check_interconnect_available() {
|
||||
if (datalayer.battery.status.voltage_dV == 0 || datalayer.battery2.status.voltage_dV == 0) {
|
||||
return; // Both voltage values need to be available to start check
|
||||
|
@ -339,7 +338,6 @@ void check_interconnect_available() {
|
|||
set_event(EVENT_VOLTAGE_DIFFERENCE, (uint8_t)(voltage_diff / 10));
|
||||
}
|
||||
}
|
||||
#endif // DOUBLE_BATTERY
|
||||
|
||||
void update_calculated_values() {
|
||||
/* Update CPU temperature*/
|
||||
|
@ -399,11 +397,11 @@ void update_calculated_values() {
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef DOUBLE_BATTERY
|
||||
/* Calculate active power based on voltage and current for battery 2*/
|
||||
datalayer.battery2.status.active_power_W =
|
||||
(datalayer.battery2.status.current_dA * (datalayer.battery2.status.voltage_dV / 100));
|
||||
#endif // DOUBLE_BATTERY
|
||||
if (battery2) {
|
||||
/* Calculate active power based on voltage and current for battery 2*/
|
||||
datalayer.battery2.status.active_power_W =
|
||||
(datalayer.battery2.status.current_dA * (datalayer.battery2.status.voltage_dV / 100));
|
||||
}
|
||||
|
||||
if (datalayer.battery.settings.soc_scaling_active) {
|
||||
/** SOC Scaling
|
||||
|
@ -445,57 +443,62 @@ void update_calculated_values() {
|
|||
datalayer.battery.status.reported_remaining_capacity_Wh = datalayer.battery.status.remaining_capacity_Wh;
|
||||
}
|
||||
|
||||
#ifdef DOUBLE_BATTERY
|
||||
// If battery info is valid
|
||||
if (datalayer.battery2.info.total_capacity_Wh > 0 && datalayer.battery.status.real_soc > 0) {
|
||||
if (battery2) {
|
||||
// If battery info is valid
|
||||
if (datalayer.battery2.info.total_capacity_Wh > 0 && datalayer.battery.status.real_soc > 0) {
|
||||
|
||||
datalayer.battery2.info.reported_total_capacity_Wh = scaled_total_capacity;
|
||||
// Scale remaining capacity based on scaled SOC
|
||||
datalayer.battery2.status.reported_remaining_capacity_Wh = (scaled_total_capacity * scaled_soc) / 10000;
|
||||
datalayer.battery2.info.reported_total_capacity_Wh = scaled_total_capacity;
|
||||
// Scale remaining capacity based on scaled SOC
|
||||
datalayer.battery2.status.reported_remaining_capacity_Wh = (scaled_total_capacity * scaled_soc) / 10000;
|
||||
|
||||
} else {
|
||||
// Fallback if scaling cannot be performed
|
||||
datalayer.battery2.info.reported_total_capacity_Wh = datalayer.battery2.info.total_capacity_Wh;
|
||||
datalayer.battery2.status.reported_remaining_capacity_Wh = datalayer.battery2.status.remaining_capacity_Wh;
|
||||
} else {
|
||||
// Fallback if scaling cannot be performed
|
||||
datalayer.battery2.info.reported_total_capacity_Wh = datalayer.battery2.info.total_capacity_Wh;
|
||||
datalayer.battery2.status.reported_remaining_capacity_Wh = datalayer.battery2.status.remaining_capacity_Wh;
|
||||
}
|
||||
|
||||
//Since we are running double battery, the scaled value of battery1 becomes the sum of battery1+battery2
|
||||
//This way the inverter connected to the system sees both batteries as one large battery
|
||||
datalayer.battery.info.reported_total_capacity_Wh += datalayer.battery2.info.reported_total_capacity_Wh;
|
||||
datalayer.battery.status.reported_remaining_capacity_Wh +=
|
||||
datalayer.battery2.status.reported_remaining_capacity_Wh;
|
||||
}
|
||||
|
||||
//Since we are running double battery, the scaled value of battery1 becomes the sum of battery1+battery2
|
||||
//This way the inverter connected to the system sees both batteries as one large battery
|
||||
datalayer.battery.info.reported_total_capacity_Wh += datalayer.battery2.info.reported_total_capacity_Wh;
|
||||
datalayer.battery.status.reported_remaining_capacity_Wh += datalayer.battery2.status.reported_remaining_capacity_Wh;
|
||||
|
||||
#endif // DOUBLE_BATTERY
|
||||
|
||||
} else { // soc_scaling_active == false. No SOC window wanted. Set scaled to same as real.
|
||||
datalayer.battery.status.reported_soc = datalayer.battery.status.real_soc;
|
||||
datalayer.battery.status.reported_remaining_capacity_Wh = datalayer.battery.status.remaining_capacity_Wh;
|
||||
datalayer.battery.info.reported_total_capacity_Wh = datalayer.battery.info.total_capacity_Wh;
|
||||
#ifdef DOUBLE_BATTERY
|
||||
datalayer.battery2.status.reported_soc = datalayer.battery2.status.real_soc;
|
||||
datalayer.battery2.status.reported_remaining_capacity_Wh = datalayer.battery2.status.remaining_capacity_Wh;
|
||||
datalayer.battery2.info.reported_total_capacity_Wh = datalayer.battery2.info.total_capacity_Wh;
|
||||
#endif
|
||||
}
|
||||
#ifdef DOUBLE_BATTERY
|
||||
// Perform extra SOC sanity checks on double battery setups
|
||||
if (datalayer.battery.status.real_soc < 100) { //If this battery is under 1.00%, use this as SOC instead of average
|
||||
datalayer.battery.status.reported_soc = datalayer.battery.status.real_soc;
|
||||
datalayer.battery.status.reported_remaining_capacity_Wh = datalayer.battery.status.remaining_capacity_Wh;
|
||||
}
|
||||
if (datalayer.battery2.status.real_soc < 100) { //If this battery is under 1.00%, use this as SOC instead of average
|
||||
datalayer.battery.status.reported_soc = datalayer.battery2.status.real_soc;
|
||||
datalayer.battery.status.reported_remaining_capacity_Wh = datalayer.battery2.status.remaining_capacity_Wh;
|
||||
|
||||
if (battery2) {
|
||||
datalayer.battery2.status.reported_soc = datalayer.battery2.status.real_soc;
|
||||
datalayer.battery2.status.reported_remaining_capacity_Wh = datalayer.battery2.status.remaining_capacity_Wh;
|
||||
datalayer.battery2.info.reported_total_capacity_Wh = datalayer.battery2.info.total_capacity_Wh;
|
||||
}
|
||||
}
|
||||
|
||||
if (datalayer.battery.status.real_soc > 9900) { //If this battery is over 99.00%, use this as SOC instead of average
|
||||
datalayer.battery.status.reported_soc = datalayer.battery.status.real_soc;
|
||||
datalayer.battery.status.reported_remaining_capacity_Wh = datalayer.battery.status.remaining_capacity_Wh;
|
||||
if (battery2) {
|
||||
// Perform extra SOC sanity checks on double battery setups
|
||||
if (datalayer.battery.status.real_soc < 100) { //If this battery is under 1.00%, use this as SOC instead of average
|
||||
datalayer.battery.status.reported_soc = datalayer.battery.status.real_soc;
|
||||
datalayer.battery.status.reported_remaining_capacity_Wh = datalayer.battery.status.remaining_capacity_Wh;
|
||||
}
|
||||
if (datalayer.battery2.status.real_soc <
|
||||
100) { //If this battery is under 1.00%, use this as SOC instead of average
|
||||
datalayer.battery.status.reported_soc = datalayer.battery2.status.real_soc;
|
||||
datalayer.battery.status.reported_remaining_capacity_Wh = datalayer.battery2.status.remaining_capacity_Wh;
|
||||
}
|
||||
|
||||
if (datalayer.battery.status.real_soc >
|
||||
9900) { //If this battery is over 99.00%, use this as SOC instead of average
|
||||
datalayer.battery.status.reported_soc = datalayer.battery.status.real_soc;
|
||||
datalayer.battery.status.reported_remaining_capacity_Wh = datalayer.battery.status.remaining_capacity_Wh;
|
||||
}
|
||||
if (datalayer.battery2.status.real_soc >
|
||||
9900) { //If this battery is over 99.00%, use this as SOC instead of average
|
||||
datalayer.battery.status.reported_soc = datalayer.battery2.status.real_soc;
|
||||
datalayer.battery.status.reported_remaining_capacity_Wh = datalayer.battery2.status.remaining_capacity_Wh;
|
||||
}
|
||||
}
|
||||
if (datalayer.battery2.status.real_soc > 9900) { //If this battery is over 99.00%, use this as SOC instead of average
|
||||
datalayer.battery.status.reported_soc = datalayer.battery2.status.real_soc;
|
||||
datalayer.battery.status.reported_remaining_capacity_Wh = datalayer.battery2.status.remaining_capacity_Wh;
|
||||
}
|
||||
#endif // DOUBLE_BATTERY
|
||||
// Check if millis has overflowed. Used in events to keep better track of time
|
||||
if (currentMillis < lastMillisOverflowCheck) { // Overflow detected
|
||||
datalayer.system.status.millisrolloverCount++;
|
||||
|
|
|
@ -11,11 +11,8 @@
|
|||
// to support battery class selection at compile-time
|
||||
#ifdef SELECTED_BATTERY_CLASS
|
||||
|
||||
static Battery* battery = nullptr;
|
||||
|
||||
#ifdef DOUBLE_BATTERY
|
||||
static Battery* battery2 = nullptr;
|
||||
#endif
|
||||
Battery* battery = nullptr;
|
||||
Battery* battery2 = nullptr;
|
||||
|
||||
void setup_battery() {
|
||||
// Instantiate the battery only once just in case this function gets called multiple times.
|
||||
|
|
|
@ -2,6 +2,13 @@
|
|||
#define BATTERIES_H
|
||||
#include "../../USER_SETTINGS.h"
|
||||
|
||||
class Battery;
|
||||
|
||||
// Currently initialized objects for primary and secondary battery.
|
||||
// Null value indicates that battery is not configured/initialized
|
||||
extern Battery* battery;
|
||||
extern Battery* battery2;
|
||||
|
||||
#ifdef BMW_SBOX
|
||||
#include "BMW-SBOX.h"
|
||||
void handle_incoming_can_frame_shunt(CAN_frame rx_frame);
|
||||
|
@ -162,9 +169,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame);
|
|||
void transmit_can_battery(unsigned long currentMillis);
|
||||
#endif
|
||||
|
||||
#ifdef DOUBLE_BATTERY
|
||||
void update_values_battery2();
|
||||
void handle_incoming_can_frame_battery2(CAN_frame rx_frame);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "../datalayer/datalayer.h"
|
||||
#include "../datalayer/datalayer_extended.h"
|
||||
#include "../include.h"
|
||||
#include "BMW-I3-HTML.h"
|
||||
#include "CanBattery.h"
|
||||
|
||||
#define BATTERY_SELECTED
|
||||
|
@ -12,11 +13,12 @@
|
|||
class BmwI3Battery : public CanBattery {
|
||||
public:
|
||||
// Use this constructor for the second battery.
|
||||
BmwI3Battery(DATALAYER_BATTERY_TYPE* datalayer_ptr, bool* contactor_closing_allowed_ptr, int targetCan, int wakeup) {
|
||||
BmwI3Battery(DATALAYER_BATTERY_TYPE* datalayer_ptr, bool* contactor_closing_allowed_ptr, CAN_Interface targetCan,
|
||||
int wakeup)
|
||||
: CanBattery(targetCan) {
|
||||
datalayer_battery = datalayer_ptr;
|
||||
contactor_closing_allowed = contactor_closing_allowed_ptr;
|
||||
allows_contactor_closing = nullptr;
|
||||
can_interface = targetCan;
|
||||
wakeup_pin = wakeup;
|
||||
*allows_contactor_closing = true;
|
||||
|
||||
|
@ -29,7 +31,6 @@ class BmwI3Battery : public CanBattery {
|
|||
datalayer_battery = &datalayer.battery;
|
||||
allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing;
|
||||
contactor_closing_allowed = nullptr;
|
||||
can_interface = can_config.battery;
|
||||
wakeup_pin = WUP_PIN1;
|
||||
}
|
||||
|
||||
|
@ -38,6 +39,11 @@ class BmwI3Battery : public CanBattery {
|
|||
virtual void update_values();
|
||||
virtual void transmit_can(unsigned long currentMillis);
|
||||
|
||||
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
|
||||
|
||||
private:
|
||||
BmwI3HtmlRenderer renderer;
|
||||
|
||||
private:
|
||||
const int MAX_CELL_VOLTAGE_60AH = 4110; // Battery is put into emergency stop if one cell goes over this value
|
||||
const int MIN_CELL_VOLTAGE_60AH = 2700; // Battery is put into emergency stop if one cell goes below this value
|
||||
|
@ -63,7 +69,6 @@ class BmwI3Battery : public CanBattery {
|
|||
bool* contactor_closing_allowed;
|
||||
|
||||
int wakeup_pin;
|
||||
int can_interface;
|
||||
|
||||
unsigned long previousMillis20 = 0; // will store last time a 20ms CAN Message was send
|
||||
unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send
|
||||
|
|
98
Software/src/battery/BMW-I3-HTML.h
Normal file
98
Software/src/battery/BMW-I3-HTML.h
Normal file
|
@ -0,0 +1,98 @@
|
|||
#ifndef _BMW_I3_HTML_H
|
||||
#define _BMW_I3_HTML_H
|
||||
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../datalayer/datalayer_extended.h"
|
||||
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
|
||||
|
||||
class BmwI3HtmlRenderer : public BatteryHtmlRenderer {
|
||||
public:
|
||||
String get_status_html() {
|
||||
String content;
|
||||
|
||||
content += "<h4>SOC raw: " + String(datalayer_extended.bmwi3.SOC_raw) + "</h4>";
|
||||
content += "<h4>SOC dash: " + String(datalayer_extended.bmwi3.SOC_dash) + "</h4>";
|
||||
content += "<h4>SOC OBD2: " + String(datalayer_extended.bmwi3.SOC_OBD2) + "</h4>";
|
||||
static const char* statusText[16] = {
|
||||
"Not evaluated", "OK", "Error!", "Invalid signal", "", "", "", "", "", "", "", "", "", "", "", ""};
|
||||
content += "<h4>Interlock: " + String(statusText[datalayer_extended.bmwi3.ST_interlock]) + "</h4>";
|
||||
content += "<h4>Isolation external: " + String(statusText[datalayer_extended.bmwi3.ST_iso_ext]) + "</h4>";
|
||||
content += "<h4>Isolation internal: " + String(statusText[datalayer_extended.bmwi3.ST_iso_int]) + "</h4>";
|
||||
content += "<h4>Isolation: " + String(statusText[datalayer_extended.bmwi3.ST_isolation]) + "</h4>";
|
||||
content += "<h4>Cooling valve: " + String(statusText[datalayer_extended.bmwi3.ST_valve_cooling]) + "</h4>";
|
||||
content += "<h4>Emergency: " + String(statusText[datalayer_extended.bmwi3.ST_EMG]) + "</h4>";
|
||||
static const char* prechargeText[16] = {"Not evaluated",
|
||||
"Not active, closing not blocked",
|
||||
"Error precharge blocked",
|
||||
"Invalid signal",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
""};
|
||||
content += "<h4>Precharge: " + String(prechargeText[datalayer_extended.bmwi3.ST_precharge]) +
|
||||
"</h4>"; //Still unclear of enum
|
||||
static const char* DCSWText[16] = {"Contactors open",
|
||||
"Precharge ongoing",
|
||||
"Contactors engaged",
|
||||
"Invalid signal",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
""};
|
||||
content += "<h4>Contactor status: " + String(DCSWText[datalayer_extended.bmwi3.ST_DCSW]) + "</h4>";
|
||||
static const char* contText[16] = {"Contactors OK",
|
||||
"One contactor welded!",
|
||||
"Two contactors welded!",
|
||||
"Invalid signal",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
""};
|
||||
content += "<h4>Contactor weld: " + String(contText[datalayer_extended.bmwi3.ST_WELD]) + "</h4>";
|
||||
static const char* valveText[16] = {"OK",
|
||||
"Short circuit to GND",
|
||||
"Short circuit to 12V",
|
||||
"Line break",
|
||||
"",
|
||||
"",
|
||||
"Driver error",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"Stuck",
|
||||
"Stuck",
|
||||
"",
|
||||
"Invalid Signal"};
|
||||
content += "<h4>Cold shutoff valve: " + String(contText[datalayer_extended.bmwi3.ST_cold_shutoff_valve]) + "</h4>";
|
||||
|
||||
return content;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
|
@ -2,6 +2,7 @@
|
|||
#define BMW_IX_BATTERY_H
|
||||
#include <Arduino.h>
|
||||
#include "../include.h"
|
||||
#include "BMW-IX-HTML.h"
|
||||
#include "CanBattery.h"
|
||||
|
||||
#define BATTERY_SELECTED
|
||||
|
@ -13,8 +14,15 @@ class BmwIXBattery : public CanBattery {
|
|||
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
|
||||
virtual void update_values();
|
||||
virtual void transmit_can(unsigned long currentMillis);
|
||||
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
|
||||
|
||||
bool supports_contactor_close() { return true; }
|
||||
|
||||
void request_open_contactors() { datalayer_extended.bmwix.UserRequestContactorOpen = true; }
|
||||
void request_close_contactors() { datalayer_extended.bmwix.UserRequestContactorClose = true; }
|
||||
|
||||
private:
|
||||
BmwIXHtmlRenderer renderer;
|
||||
static const int MAX_PACK_VOLTAGE_DV = 4650; //4650 = 465.0V
|
||||
static const int MIN_PACK_VOLTAGE_DV = 3000;
|
||||
static const int MAX_CELL_DEVIATION_MV = 250;
|
||||
|
|
54
Software/src/battery/BMW-IX-HTML.h
Normal file
54
Software/src/battery/BMW-IX-HTML.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
#ifndef _BMW_IX_HTML_H
|
||||
#define _BMW_IX_HTML_H
|
||||
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../datalayer/datalayer_extended.h"
|
||||
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
|
||||
|
||||
class BmwIXHtmlRenderer : public BatteryHtmlRenderer {
|
||||
public:
|
||||
String get_status_html() {
|
||||
String content;
|
||||
|
||||
content +=
|
||||
"<h4>Battery Voltage after Contactor: " + String(datalayer_extended.bmwix.battery_voltage_after_contactor) +
|
||||
" dV</h4>";
|
||||
content += "<h4>Max Design Voltage: " + String(datalayer.battery.info.max_design_voltage_dV) + " dV</h4>";
|
||||
content += "<h4>Min Design Voltage: " + String(datalayer.battery.info.min_design_voltage_dV) + " dV</h4>";
|
||||
content += "<h4>Max Cell Design Voltage: " + String(datalayer.battery.info.max_cell_voltage_mV) + " mV</h4>";
|
||||
content += "<h4>Min Cell Design Voltage: " + String(datalayer.battery.info.min_cell_voltage_mV) + " mV</h4>";
|
||||
content +=
|
||||
"<h4>Min Cell Voltage Data Age: " + String(datalayer_extended.bmwix.min_cell_voltage_data_age) + " ms</h4>";
|
||||
content +=
|
||||
"<h4>Max Cell Voltage Data Age: " + String(datalayer_extended.bmwix.max_cell_voltage_data_age) + " ms</h4>";
|
||||
content += "<h4>Allowed Discharge Power: " + String(datalayer.battery.status.max_discharge_power_W) + " W</h4>";
|
||||
content += "<h4>Allowed Charge Power: " + String(datalayer.battery.status.max_charge_power_W) + " W</h4>";
|
||||
content += "<h4>T30 Terminal Voltage: " + String(datalayer_extended.bmwix.T30_Voltage) + " mV</h4>";
|
||||
content += "<h4>Detected Cell Count: " + String(datalayer.battery.info.number_of_cells) + "</h4>";
|
||||
static const char* balanceText[5] = {"0 No balancing mode active", "1 Voltage-Controlled Balancing Mode",
|
||||
"2 Time-Controlled Balancing Mode with Demand Calculation at End of Charging",
|
||||
"3 Time-Controlled Balancing Mode with Demand Calculation at Resting Voltage",
|
||||
"4 No balancing mode active, qualifier invalid"};
|
||||
content += "<h4>Balancing: " + String((balanceText[datalayer_extended.bmwix.balancing_status])) + "</h4>";
|
||||
static const char* hvilText[2] = {"Error (Loop Open)", "OK (Loop Closed)"};
|
||||
content += "<h4>HVIL Status: " + String(hvilText[datalayer_extended.bmwix.hvil_status]) + "</h4>";
|
||||
content += "<h4>BMS Uptime: " + String(datalayer_extended.bmwix.bms_uptime) + " seconds</h4>";
|
||||
content += "<h4>BMS Allowed Charge Amps: " + String(datalayer_extended.bmwix.allowable_charge_amps) + " A</h4>";
|
||||
content +=
|
||||
"<h4>BMS Allowed Disharge Amps: " + String(datalayer_extended.bmwix.allowable_discharge_amps) + " A</h4>";
|
||||
content += "<br>";
|
||||
content += "<h3>HV Isolation (2147483647kOhm = maximum/invalid)</h3>";
|
||||
content += "<h4>Isolation Positive: " + String(datalayer_extended.bmwix.iso_safety_positive) + " kOhm</h4>";
|
||||
content += "<h4>Isolation Negative: " + String(datalayer_extended.bmwix.iso_safety_negative) + " kOhm</h4>";
|
||||
content += "<h4>Isolation Parallel: " + String(datalayer_extended.bmwix.iso_safety_parallel) + " kOhm</h4>";
|
||||
static const char* pyroText[5] = {"0 Value Invalid", "1 Successfully Blown", "2 Disconnected",
|
||||
"3 Not Activated - Pyro Intact", "4 Unknown"};
|
||||
content += "<h4>Pyro Status PSS1: " + String((pyroText[datalayer_extended.bmwix.pyro_status_pss1])) + "</h4>";
|
||||
content += "<h4>Pyro Status PSS4: " + String((pyroText[datalayer_extended.bmwix.pyro_status_pss4])) + "</h4>";
|
||||
content += "<h4>Pyro Status PSS6: " + String((pyroText[datalayer_extended.bmwix.pyro_status_pss6])) + "</h4>";
|
||||
|
||||
return content;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
|
@ -2,6 +2,7 @@
|
|||
#define BMW_PHEV_BATTERY_H
|
||||
#include <Arduino.h>
|
||||
#include "../include.h"
|
||||
#include "BMW-PHEV-HTML.h"
|
||||
#include "CanBattery.h"
|
||||
|
||||
#define BATTERY_SELECTED
|
||||
|
@ -14,7 +15,11 @@ class BmwPhevBattery : public CanBattery {
|
|||
virtual void update_values();
|
||||
virtual void transmit_can(unsigned long currentMillis);
|
||||
|
||||
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
|
||||
|
||||
private:
|
||||
BmwPhevHtmlRenderer renderer;
|
||||
|
||||
static const int MAX_PACK_VOLTAGE_DV = 4650; //4650 = 465.0V
|
||||
static const int MIN_PACK_VOLTAGE_DV = 3000;
|
||||
static const int MAX_CELL_DEVIATION_MV = 250;
|
||||
|
|
132
Software/src/battery/BMW-PHEV-HTML.h
Normal file
132
Software/src/battery/BMW-PHEV-HTML.h
Normal file
|
@ -0,0 +1,132 @@
|
|||
#ifndef _BMW_PHEV_HTML_H
|
||||
#define _BMW_PHEV_HTML_H
|
||||
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../datalayer/datalayer_extended.h"
|
||||
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
|
||||
|
||||
class BmwPhevHtmlRenderer : public BatteryHtmlRenderer {
|
||||
public:
|
||||
String get_status_html() {
|
||||
String content;
|
||||
|
||||
content +=
|
||||
"<h4>Battery Voltage after Contactor: " + String(datalayer_extended.bmwphev.battery_voltage_after_contactor) +
|
||||
" dV</h4>";
|
||||
content += "<h4>Allowed Discharge Power: " + String(datalayer.battery.status.max_discharge_power_W) + " W</h4>";
|
||||
content += "<h4>Allowed Charge Power: " + String(datalayer.battery.status.max_charge_power_W) + " W</h4>";
|
||||
static const char* balanceText[5] = {"0 Balancing Inactive - Balancing not needed", "1 Balancing Active",
|
||||
"2 Balancing Inactive - Cells not in rest break wait 10mins",
|
||||
"3 Balancing Inactive", "4 Unknown"};
|
||||
content += "<h4>Balancing: " + String((balanceText[datalayer_extended.bmwphev.balancing_status])) + "</h4>";
|
||||
static const char* pyroText[5] = {"0 Value Invalid", "1 Successfully Blown", "2 Disconnected",
|
||||
"3 Not Activated - Pyro Intact", "4 Unknown"};
|
||||
static const char* statusText[16] = {
|
||||
"Not evaluated", "OK", "Error!", "Invalid signal", "", "", "", "", "", "", "", "", "", "", "", ""};
|
||||
content += "<h4>Interlock: " + String(statusText[datalayer_extended.bmwphev.ST_interlock]) + "</h4>";
|
||||
content += "<h4>Isolation external: " + String(statusText[datalayer_extended.bmwphev.ST_iso_ext]) + "</h4>";
|
||||
content += "<h4>Isolation internal: " + String(statusText[datalayer_extended.bmwphev.ST_iso_int]) + "</h4>";
|
||||
content += "<h4>Isolation: " + String(statusText[datalayer_extended.bmwphev.ST_isolation]) + "</h4>";
|
||||
content += "<h4>Cooling valve: " + String(statusText[datalayer_extended.bmwphev.ST_valve_cooling]) + "</h4>";
|
||||
content += "<h4>Emergency: " + String(statusText[datalayer_extended.bmwphev.ST_EMG]) + "</h4>";
|
||||
static const char* prechargeText[16] = {"Not evaluated",
|
||||
"Not active, closing not blocked",
|
||||
"Error precharge blocked",
|
||||
"Invalid signal",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
""};
|
||||
content += "<h4>Precharge: " + String(prechargeText[datalayer_extended.bmwphev.ST_precharge]) +
|
||||
"</h4>"; //Still unclear of enum
|
||||
static const char* DCSWText[16] = {"Contactors open",
|
||||
"Precharge ongoing",
|
||||
"Contactors engaged",
|
||||
"Invalid signal",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
""};
|
||||
content += "<h4>Contactor status: " + String(DCSWText[datalayer_extended.bmwphev.ST_DCSW]) + "</h4>";
|
||||
static const char* contText[16] = {"Contactors OK",
|
||||
"One contactor welded!",
|
||||
"Two contactors welded!",
|
||||
"Invalid signal",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
""};
|
||||
content += "<h4>Contactor weld: " + String(contText[datalayer_extended.bmwphev.ST_WELD]) + "</h4>";
|
||||
static const char* valveText[16] = {"OK",
|
||||
"Short circuit to GND",
|
||||
"Short circuit to 12V",
|
||||
"Line break",
|
||||
"",
|
||||
"",
|
||||
"Driver error",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"Stuck",
|
||||
"Stuck",
|
||||
"",
|
||||
"Invalid Signal"};
|
||||
content +=
|
||||
"<h4>Cold shutoff valve: " + String(valveText[datalayer_extended.bmwphev.ST_cold_shutoff_valve]) + "</h4>";
|
||||
content +=
|
||||
"<h4>Min Cell Voltage Data Age: " + String(datalayer_extended.bmwphev.min_cell_voltage_data_age) + " ms</h4>";
|
||||
content +=
|
||||
"<h4>Max Cell Voltage Data Age: " + String(datalayer_extended.bmwphev.max_cell_voltage_data_age) + " ms</h4>";
|
||||
content += "<h4>Max Design Voltage: " + String(datalayer.battery.info.max_design_voltage_dV) + " dV</h4>";
|
||||
content += "<h4>Min Design Voltage: " + String(datalayer.battery.info.min_design_voltage_dV) + " dV</h4>";
|
||||
content += "<h4>BMS Allowed Charge Amps: " + String(datalayer_extended.bmwphev.allowable_charge_amps) + " A</h4>";
|
||||
content +=
|
||||
"<h4>BMS Allowed Disharge Amps: " + String(datalayer_extended.bmwphev.allowable_discharge_amps) + " A</h4>";
|
||||
content += "<h4>Detected Cell Count: " + String(datalayer.battery.info.number_of_cells) + "</h4>";
|
||||
content += "<h4>iso_safety_int_kohm: " + String(datalayer_extended.bmwphev.iso_safety_int_kohm) + "</h4>";
|
||||
content += "<h4>iso_safety_ext_kohm: " + String(datalayer_extended.bmwphev.iso_safety_ext_kohm) + "</h4>";
|
||||
content += "<h4>iso_safety_trg_kohm: " + String(datalayer_extended.bmwphev.iso_safety_trg_kohm) + "</h4>";
|
||||
content += "<h4>iso_safety_ext_plausible: " + String(datalayer_extended.bmwphev.iso_safety_ext_plausible) + "</h4>";
|
||||
content += "<h4>iso_safety_int_plausible: " + String(datalayer_extended.bmwphev.iso_safety_int_plausible) + "</h4>";
|
||||
content += "<h4>iso_safety_trg_plausible: " + String(datalayer_extended.bmwphev.iso_safety_trg_plausible) + "</h4>";
|
||||
content += "<h4>iso_safety_kohm: " + String(datalayer_extended.bmwphev.iso_safety_kohm) + "</h4>";
|
||||
content += "<h4>iso_safety_kohm_quality: " + String(datalayer_extended.bmwphev.iso_safety_kohm_quality) + "</h4>";
|
||||
content += "<br>";
|
||||
content += "<h4>Todo";
|
||||
content += "<br>";
|
||||
content += "<h4>Max Cell Design Voltage: " + String(datalayer.battery.info.max_cell_voltage_mV) + " mV</h4>";
|
||||
content += "<h4>Min Cell Design Voltage: " + String(datalayer.battery.info.min_cell_voltage_mV) + " mV</h4>";
|
||||
content += "<h4>T30 Terminal Voltage: " + String(datalayer_extended.bmwphev.T30_Voltage) + " mV</h4>";
|
||||
content += "<br>";
|
||||
|
||||
return content;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
|
@ -3,6 +3,7 @@
|
|||
#include <Arduino.h>
|
||||
#include "../include.h"
|
||||
|
||||
#include "BOLT-AMPERA-HTML.h"
|
||||
#include "CanBattery.h"
|
||||
|
||||
#define BATTERY_SELECTED
|
||||
|
@ -15,7 +16,10 @@ class BoltAmperaBattery : public CanBattery {
|
|||
virtual void update_values();
|
||||
virtual void transmit_can(unsigned long currentMillis);
|
||||
|
||||
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
|
||||
|
||||
private:
|
||||
BoltAmperaHtmlRenderer renderer;
|
||||
static const int MAX_PACK_VOLTAGE_DV = 4150; //5000 = 500.0V
|
||||
static const int MIN_PACK_VOLTAGE_DV = 2500;
|
||||
static const int MAX_CELL_DEVIATION_MV = 150;
|
||||
|
|
52
Software/src/battery/BOLT-AMPERA-HTML.h
Normal file
52
Software/src/battery/BOLT-AMPERA-HTML.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
#ifndef _BOLT_AMPERA_HTML_H
|
||||
#define _BOLT_AMPERA_HTML_H
|
||||
|
||||
#include "../datalayer/datalayer_extended.h"
|
||||
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
|
||||
|
||||
class BoltAmperaHtmlRenderer : public BatteryHtmlRenderer {
|
||||
public:
|
||||
String get_status_html() {
|
||||
String content;
|
||||
|
||||
content += "<h4>5V Reference: " + String(datalayer_extended.boltampera.battery_5V_ref) + "</h4>";
|
||||
content += "<h4>Module 1 temp: " + String(datalayer_extended.boltampera.battery_module_temp_1) + "</h4>";
|
||||
content += "<h4>Module 2 temp: " + String(datalayer_extended.boltampera.battery_module_temp_2) + "</h4>";
|
||||
content += "<h4>Module 3 temp: " + String(datalayer_extended.boltampera.battery_module_temp_3) + "</h4>";
|
||||
content += "<h4>Module 4 temp: " + String(datalayer_extended.boltampera.battery_module_temp_4) + "</h4>";
|
||||
content += "<h4>Module 5 temp: " + String(datalayer_extended.boltampera.battery_module_temp_5) + "</h4>";
|
||||
content += "<h4>Module 6 temp: " + String(datalayer_extended.boltampera.battery_module_temp_6) + "</h4>";
|
||||
content +=
|
||||
"<h4>Cell average voltage: " + String(datalayer_extended.boltampera.battery_cell_average_voltage) + "</h4>";
|
||||
content +=
|
||||
"<h4>Cell average voltage 2: " + String(datalayer_extended.boltampera.battery_cell_average_voltage_2) + "</h4>";
|
||||
content += "<h4>Terminal voltage: " + String(datalayer_extended.boltampera.battery_terminal_voltage) + "</h4>";
|
||||
content +=
|
||||
"<h4>Ignition power mode: " + String(datalayer_extended.boltampera.battery_ignition_power_mode) + "</h4>";
|
||||
content += "<h4>Battery current (7E7): " + String(datalayer_extended.boltampera.battery_current_7E7) + "</h4>";
|
||||
content += "<h4>Capacity MY17-18: " + String(datalayer_extended.boltampera.battery_capacity_my17_18) + "</h4>";
|
||||
content += "<h4>Capacity MY19+: " + String(datalayer_extended.boltampera.battery_capacity_my19plus) + "</h4>";
|
||||
content += "<h4>SOC Display: " + String(datalayer_extended.boltampera.battery_SOC_display) + "</h4>";
|
||||
content += "<h4>SOC Raw highprec: " + String(datalayer_extended.boltampera.battery_SOC_raw_highprec) + "</h4>";
|
||||
content += "<h4>Max temp: " + String(datalayer_extended.boltampera.battery_max_temperature) + "</h4>";
|
||||
content += "<h4>Min temp: " + String(datalayer_extended.boltampera.battery_min_temperature) + "</h4>";
|
||||
content += "<h4>Cell max mV: " + String(datalayer_extended.boltampera.battery_max_cell_voltage) + "</h4>";
|
||||
content += "<h4>Cell min mV: " + String(datalayer_extended.boltampera.battery_min_cell_voltage) + "</h4>";
|
||||
content += "<h4>Lowest cell: " + String(datalayer_extended.boltampera.battery_lowest_cell) + "</h4>";
|
||||
content += "<h4>Highest cell: " + String(datalayer_extended.boltampera.battery_highest_cell) + "</h4>";
|
||||
content +=
|
||||
"<h4>Internal resistance: " + String(datalayer_extended.boltampera.battery_internal_resistance) + "</h4>";
|
||||
content += "<h4>Voltage: " + String(datalayer_extended.boltampera.battery_voltage_polled) + "</h4>";
|
||||
content += "<h4>Isolation Ohm: " + String(datalayer_extended.boltampera.battery_vehicle_isolation) + "</h4>";
|
||||
content += "<h4>Isolation kOhm: " + String(datalayer_extended.boltampera.battery_isolation_kohm) + "</h4>";
|
||||
content += "<h4>HV locked: " + String(datalayer_extended.boltampera.battery_HV_locked) + "</h4>";
|
||||
content += "<h4>Crash event: " + String(datalayer_extended.boltampera.battery_crash_event) + "</h4>";
|
||||
content += "<h4>HVIL: " + String(datalayer_extended.boltampera.battery_HVIL) + "</h4>";
|
||||
content += "<h4>HVIL status: " + String(datalayer_extended.boltampera.battery_HVIL_status) + "</h4>";
|
||||
content += "<h4>Current (7E4): " + String(datalayer_extended.boltampera.battery_current_7E4) + "</h4>";
|
||||
|
||||
return content;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
|
@ -5,6 +5,7 @@
|
|||
#include "../datalayer/datalayer_extended.h"
|
||||
#include "../include.h"
|
||||
|
||||
#include "BYD-ATTO-3-HTML.h"
|
||||
#include "CanBattery.h"
|
||||
|
||||
#define USE_ESTIMATED_SOC // If enabled, SOC is estimated from pack voltage. Useful for locked packs. \
|
||||
|
@ -33,18 +34,17 @@
|
|||
class BydAttoBattery : public CanBattery {
|
||||
public:
|
||||
// Use this constructor for the second battery.
|
||||
BydAttoBattery(DATALAYER_BATTERY_TYPE* datalayer_ptr, DATALAYER_INFO_BYDATTO3* extended, int targetCan) {
|
||||
BydAttoBattery(DATALAYER_BATTERY_TYPE* datalayer_ptr, DATALAYER_INFO_BYDATTO3* extended, CAN_Interface targetCan)
|
||||
: CanBattery(targetCan), renderer(extended) {
|
||||
datalayer_battery = datalayer_ptr;
|
||||
datalayer_bydatto = extended;
|
||||
allows_contactor_closing = nullptr;
|
||||
can_interface = targetCan;
|
||||
}
|
||||
|
||||
// Use the default constructor to create the first or single battery.
|
||||
BydAttoBattery() {
|
||||
BydAttoBattery() : renderer(&datalayer_extended.bydAtto3) {
|
||||
datalayer_battery = &datalayer.battery;
|
||||
allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing;
|
||||
can_interface = can_config.battery;
|
||||
datalayer_bydatto = &datalayer_extended.bydAtto3;
|
||||
}
|
||||
|
||||
|
@ -53,13 +53,18 @@ class BydAttoBattery : public CanBattery {
|
|||
virtual void update_values();
|
||||
virtual void transmit_can(unsigned long currentMillis);
|
||||
|
||||
bool supports_reset_crash() { return true; }
|
||||
|
||||
void reset_crash() { datalayer_bydatto->UserRequestCrashReset = true; }
|
||||
|
||||
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
|
||||
|
||||
private:
|
||||
BydAtto3HtmlRenderer renderer;
|
||||
DATALAYER_BATTERY_TYPE* datalayer_battery;
|
||||
DATALAYER_INFO_BYDATTO3* datalayer_bydatto;
|
||||
bool* allows_contactor_closing;
|
||||
|
||||
int can_interface;
|
||||
|
||||
static const int POLL_FOR_BATTERY_SOC = 0x0005;
|
||||
static const uint8_t NOT_DETERMINED_YET = 0;
|
||||
static const uint8_t STANDARD_RANGE = 1;
|
||||
|
|
54
Software/src/battery/BYD-ATTO-3-HTML.h
Normal file
54
Software/src/battery/BYD-ATTO-3-HTML.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
#ifndef _BYD_ATTO_3_HTML_H
|
||||
#define _BYD_ATTO_3_HTML_H
|
||||
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../datalayer/datalayer_extended.h"
|
||||
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
|
||||
|
||||
class BydAtto3HtmlRenderer : public BatteryHtmlRenderer {
|
||||
public:
|
||||
BydAtto3HtmlRenderer(DATALAYER_INFO_BYDATTO3* dl) : byd_datalayer(dl) {}
|
||||
|
||||
String get_status_html() {
|
||||
String content;
|
||||
|
||||
static const char* SOCmethod[2] = {"Estimated from voltage", "Measured by BMS"};
|
||||
content += "<h4>SOC method used: " + String(SOCmethod[byd_datalayer->SOC_method]) + "</h4>";
|
||||
content += "<h4>SOC estimated: " + String(byd_datalayer->SOC_estimated) + "</h4>";
|
||||
content += "<h4>SOC highprec: " + String(byd_datalayer->SOC_highprec) + "</h4>";
|
||||
content += "<h4>SOC OBD2: " + String(byd_datalayer->SOC_polled) + "</h4>";
|
||||
content += "<h4>Voltage periodic: " + String(byd_datalayer->voltage_periodic) + "</h4>";
|
||||
content += "<h4>Voltage OBD2: " + String(byd_datalayer->voltage_polled) + "</h4>";
|
||||
content += "<h4>Temperature sensor 1: " + String(byd_datalayer->battery_temperatures[0]) + "</h4>";
|
||||
content += "<h4>Temperature sensor 2: " + String(byd_datalayer->battery_temperatures[1]) + "</h4>";
|
||||
content += "<h4>Temperature sensor 3: " + String(byd_datalayer->battery_temperatures[2]) + "</h4>";
|
||||
content += "<h4>Temperature sensor 4: " + String(byd_datalayer->battery_temperatures[3]) + "</h4>";
|
||||
content += "<h4>Temperature sensor 5: " + String(byd_datalayer->battery_temperatures[4]) + "</h4>";
|
||||
content += "<h4>Temperature sensor 6: " + String(byd_datalayer->battery_temperatures[5]) + "</h4>";
|
||||
content += "<h4>Temperature sensor 7: " + String(byd_datalayer->battery_temperatures[6]) + "</h4>";
|
||||
content += "<h4>Temperature sensor 8: " + String(byd_datalayer->battery_temperatures[7]) + "</h4>";
|
||||
content += "<h4>Temperature sensor 9: " + String(byd_datalayer->battery_temperatures[8]) + "</h4>";
|
||||
content += "<h4>Temperature sensor 10: " + String(byd_datalayer->battery_temperatures[9]) + "</h4>";
|
||||
content += "<h4>Unknown0: " + String(byd_datalayer->unknown0) + "</h4>";
|
||||
content += "<h4>Unknown1: " + String(byd_datalayer->unknown1) + "</h4>";
|
||||
content += "<h4>Charge power raw: " + String(byd_datalayer->chargePower) + "</h4>";
|
||||
content += "<h4>Unknown3: " + String(byd_datalayer->unknown3) + "</h4>";
|
||||
content += "<h4>Unknown4: " + String(byd_datalayer->unknown4) + "</h4>";
|
||||
content += "<h4>Unknown5: " + String(byd_datalayer->unknown5) + "</h4>";
|
||||
content += "<h4>Unknown6: " + String(byd_datalayer->unknown6) + "</h4>";
|
||||
content += "<h4>Unknown7: " + String(byd_datalayer->unknown7) + "</h4>";
|
||||
content += "<h4>Unknown8: " + String(byd_datalayer->unknown8) + "</h4>";
|
||||
content += "<h4>Unknown9: " + String(byd_datalayer->unknown9) + "</h4>";
|
||||
content += "<h4>Unknown10: " + String(byd_datalayer->unknown10) + "</h4>";
|
||||
content += "<h4>Unknown11: " + String(byd_datalayer->unknown11) + "</h4>";
|
||||
content += "<h4>Unknown12: " + String(byd_datalayer->unknown12) + "</h4>";
|
||||
content += "<h4>Unknown13: " + String(byd_datalayer->unknown12) + "</h4>";
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
private:
|
||||
DATALAYER_INFO_BYDATTO3* byd_datalayer;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,12 +1,56 @@
|
|||
#ifndef BATTERY_H
|
||||
#define BATTERY_H
|
||||
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
|
||||
|
||||
// Abstract base class for next-generation battery implementations.
|
||||
// Defines the interface to call battery specific functionality.
|
||||
class Battery {
|
||||
public:
|
||||
virtual void setup(void) = 0;
|
||||
virtual void update_values() = 0;
|
||||
|
||||
// The name of the comm interface the battery is using.
|
||||
virtual String interface_name() = 0;
|
||||
|
||||
// These are commands from external I/O (UI, MQTT etc.)
|
||||
// Override in battery if it supports them. Otherwise they are NOP.
|
||||
|
||||
virtual bool supports_clear_isolation() { return false; }
|
||||
virtual bool supports_reset_BMS() { return false; }
|
||||
virtual bool supports_reset_crash() { return false; }
|
||||
virtual bool supports_reset_NVROL() { return false; }
|
||||
virtual bool supports_reset_DTC() { return false; }
|
||||
virtual bool supports_read_DTC() { return false; }
|
||||
virtual bool supports_reset_SOH() { return false; }
|
||||
virtual bool supports_reset_BECM() { return false; }
|
||||
virtual bool supports_contactor_close() { return false; }
|
||||
virtual bool supports_set_fake_voltage() { return false; }
|
||||
virtual bool supports_manual_balancing() { return false; }
|
||||
virtual bool supports_real_BMS_status() { return false; }
|
||||
|
||||
virtual void clear_isolation() {}
|
||||
virtual void reset_BMS() {}
|
||||
virtual void reset_crash() {}
|
||||
virtual void reset_NVROL() {}
|
||||
virtual void reset_DTC() {}
|
||||
virtual void read_DTC() {}
|
||||
virtual void reset_SOH() {}
|
||||
virtual void reset_BECM() {}
|
||||
virtual void request_open_contactors() {}
|
||||
virtual void request_close_contactors() {}
|
||||
|
||||
virtual void set_fake_voltage(float v) {}
|
||||
virtual float get_voltage() { static_cast<float>(datalayer.battery.status.voltage_dV) / 10.0; }
|
||||
|
||||
// This allows for battery specific SOC plausibility calculations to be performed.
|
||||
virtual bool soc_plausible() { return true; }
|
||||
|
||||
virtual BatteryHtmlRenderer& get_status_renderer() { return defaultRenderer; }
|
||||
|
||||
private:
|
||||
BatteryDefaultRenderer defaultRenderer;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define CELLPOWER_BMS_H
|
||||
#include <Arduino.h>
|
||||
#include "../include.h"
|
||||
#include "CELLPOWER-HTML.h"
|
||||
#include "CanBattery.h"
|
||||
|
||||
#define BATTERY_SELECTED
|
||||
|
@ -14,7 +15,10 @@ class CellPowerBms : public CanBattery {
|
|||
virtual void update_values();
|
||||
virtual void transmit_can(unsigned long currentMillis);
|
||||
|
||||
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
|
||||
|
||||
private:
|
||||
CellpowerHtmlRenderer renderer;
|
||||
/* Tweak these according to your battery build */
|
||||
static const int MAX_PACK_VOLTAGE_DV = 5000; //5000 = 500.0V
|
||||
static const int MIN_PACK_VOLTAGE_DV = 1500;
|
||||
|
|
150
Software/src/battery/CELLPOWER-HTML.h
Normal file
150
Software/src/battery/CELLPOWER-HTML.h
Normal file
|
@ -0,0 +1,150 @@
|
|||
#ifndef _CELLPOWER_HTML_H
|
||||
#define _CELLPOWER_HTML_H
|
||||
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../datalayer/datalayer_extended.h"
|
||||
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
|
||||
|
||||
class CellpowerHtmlRenderer : public BatteryHtmlRenderer {
|
||||
public:
|
||||
String get_status_html() {
|
||||
String content;
|
||||
|
||||
static const char* falseTrue[2] = {"False", "True"};
|
||||
content += "<h3>States:</h3>";
|
||||
content += "<h4>Discharge: " + String(falseTrue[datalayer_extended.cellpower.system_state_discharge]) + "</h4>";
|
||||
content += "<h4>Charge: " + String(falseTrue[datalayer_extended.cellpower.system_state_charge]) + "</h4>";
|
||||
content +=
|
||||
"<h4>Cellbalancing: " + String(falseTrue[datalayer_extended.cellpower.system_state_cellbalancing]) + "</h4>";
|
||||
content +=
|
||||
"<h4>Tricklecharging: " + String(falseTrue[datalayer_extended.cellpower.system_state_tricklecharge]) + "</h4>";
|
||||
content += "<h4>Idle: " + String(falseTrue[datalayer_extended.cellpower.system_state_idle]) + "</h4>";
|
||||
content += "<h4>Charge completed: " + String(falseTrue[datalayer_extended.cellpower.system_state_chargecompleted]) +
|
||||
"</h4>";
|
||||
content +=
|
||||
"<h4>Maintenance charge: " + String(falseTrue[datalayer_extended.cellpower.system_state_maintenancecharge]) +
|
||||
"</h4>";
|
||||
content += "<h3>IO:</h3>";
|
||||
content +=
|
||||
"<h4>Main positive relay: " + String(falseTrue[datalayer_extended.cellpower.IO_state_main_positive_relay]) +
|
||||
"</h4>";
|
||||
content +=
|
||||
"<h4>Main negative relay: " + String(falseTrue[datalayer_extended.cellpower.IO_state_main_negative_relay]) +
|
||||
"</h4>";
|
||||
content +=
|
||||
"<h4>Charge enabled: " + String(falseTrue[datalayer_extended.cellpower.IO_state_charge_enable]) + "</h4>";
|
||||
content +=
|
||||
"<h4>Precharge relay: " + String(falseTrue[datalayer_extended.cellpower.IO_state_precharge_relay]) + "</h4>";
|
||||
content +=
|
||||
"<h4>Discharge enable: " + String(falseTrue[datalayer_extended.cellpower.IO_state_discharge_enable]) + "</h4>";
|
||||
content += "<h4>IO 6: " + String(falseTrue[datalayer_extended.cellpower.IO_state_IO_6]) + "</h4>";
|
||||
content += "<h4>IO 7: " + String(falseTrue[datalayer_extended.cellpower.IO_state_IO_7]) + "</h4>";
|
||||
content += "<h4>IO 8: " + String(falseTrue[datalayer_extended.cellpower.IO_state_IO_8]) + "</h4>";
|
||||
content += "<h3>Errors:</h3>";
|
||||
content +=
|
||||
"<h4>Cell overvoltage: " + String(falseTrue[datalayer_extended.cellpower.error_Cell_overvoltage]) + "</h4>";
|
||||
content +=
|
||||
"<h4>Cell undervoltage: " + String(falseTrue[datalayer_extended.cellpower.error_Cell_undervoltage]) + "</h4>";
|
||||
content += "<h4>Cell end of life voltage: " +
|
||||
String(falseTrue[datalayer_extended.cellpower.error_Cell_end_of_life_voltage]) + "</h4>";
|
||||
content +=
|
||||
"<h4>Cell voltage misread: " + String(falseTrue[datalayer_extended.cellpower.error_Cell_voltage_misread]) +
|
||||
"</h4>";
|
||||
content +=
|
||||
"<h4>Cell over temperature: " + String(falseTrue[datalayer_extended.cellpower.error_Cell_over_temperature]) +
|
||||
"</h4>";
|
||||
content +=
|
||||
"<h4>Cell under temperature: " + String(falseTrue[datalayer_extended.cellpower.error_Cell_under_temperature]) +
|
||||
"</h4>";
|
||||
content += "<h4>Cell unmanaged: " + String(falseTrue[datalayer_extended.cellpower.error_Cell_unmanaged]) + "</h4>";
|
||||
content +=
|
||||
"<h4>LMU over temperature: " + String(falseTrue[datalayer_extended.cellpower.error_LMU_over_temperature]) +
|
||||
"</h4>";
|
||||
content +=
|
||||
"<h4>LMU under temperature: " + String(falseTrue[datalayer_extended.cellpower.error_LMU_under_temperature]) +
|
||||
"</h4>";
|
||||
content += "<h4>Temp sensor open circuit: " +
|
||||
String(falseTrue[datalayer_extended.cellpower.error_Temp_sensor_open_circuit]) + "</h4>";
|
||||
content += "<h4>Temp sensor short circuit: " +
|
||||
String(falseTrue[datalayer_extended.cellpower.error_Temp_sensor_short_circuit]) + "</h4>";
|
||||
content += "<h4>SUB comm: " + String(falseTrue[datalayer_extended.cellpower.error_SUB_communication]) + "</h4>";
|
||||
content += "<h4>LMU comm: " + String(falseTrue[datalayer_extended.cellpower.error_LMU_communication]) + "</h4>";
|
||||
content +=
|
||||
"<h4>Over current In: " + String(falseTrue[datalayer_extended.cellpower.error_Over_current_IN]) + "</h4>";
|
||||
content +=
|
||||
"<h4>Over current Out: " + String(falseTrue[datalayer_extended.cellpower.error_Over_current_OUT]) + "</h4>";
|
||||
content += "<h4>Short circuit: " + String(falseTrue[datalayer_extended.cellpower.error_Short_circuit]) + "</h4>";
|
||||
content += "<h4>Leak detected: " + String(falseTrue[datalayer_extended.cellpower.error_Leak_detected]) + "</h4>";
|
||||
content +=
|
||||
"<h4>Leak detection failed: " + String(falseTrue[datalayer_extended.cellpower.error_Leak_detection_failed]) +
|
||||
"</h4>";
|
||||
content +=
|
||||
"<h4>Voltage diff: " + String(falseTrue[datalayer_extended.cellpower.error_Voltage_difference]) + "</h4>";
|
||||
content += "<h4>BMCU supply overvoltage: " +
|
||||
String(falseTrue[datalayer_extended.cellpower.error_BMCU_supply_over_voltage]) + "</h4>";
|
||||
content += "<h4>BMCU supply undervoltage: " +
|
||||
String(falseTrue[datalayer_extended.cellpower.error_BMCU_supply_under_voltage]) + "</h4>";
|
||||
content += "<h4>Main positive contactor: " +
|
||||
String(falseTrue[datalayer_extended.cellpower.error_Main_positive_contactor]) + "</h4>";
|
||||
content += "<h4>Main negative contactor: " +
|
||||
String(falseTrue[datalayer_extended.cellpower.error_Main_negative_contactor]) + "</h4>";
|
||||
content += "<h4>Precharge contactor: " + String(falseTrue[datalayer_extended.cellpower.error_Precharge_contactor]) +
|
||||
"</h4>";
|
||||
content +=
|
||||
"<h4>Midpack contactor: " + String(falseTrue[datalayer_extended.cellpower.error_Midpack_contactor]) + "</h4>";
|
||||
content +=
|
||||
"<h4>Precharge timeout: " + String(falseTrue[datalayer_extended.cellpower.error_Precharge_timeout]) + "</h4>";
|
||||
content += "<h4>EMG connector override: " +
|
||||
String(falseTrue[datalayer_extended.cellpower.error_Emergency_connector_override]) + "</h4>";
|
||||
content += "<h3>Warnings:</h3>";
|
||||
content +=
|
||||
"<h4>High cell voltage: " + String(falseTrue[datalayer_extended.cellpower.warning_High_cell_voltage]) + "</h4>";
|
||||
content +=
|
||||
"<h4>Low cell voltage: " + String(falseTrue[datalayer_extended.cellpower.warning_Low_cell_voltage]) + "</h4>";
|
||||
content +=
|
||||
"<h4>High cell temperature: " + String(falseTrue[datalayer_extended.cellpower.warning_High_cell_temperature]) +
|
||||
"</h4>";
|
||||
content +=
|
||||
"<h4>Low cell temperature: " + String(falseTrue[datalayer_extended.cellpower.warning_Low_cell_temperature]) +
|
||||
"</h4>";
|
||||
content +=
|
||||
"<h4>High LMU temperature: " + String(falseTrue[datalayer_extended.cellpower.warning_High_LMU_temperature]) +
|
||||
"</h4>";
|
||||
content +=
|
||||
"<h4>Low LMU temperature: " + String(falseTrue[datalayer_extended.cellpower.warning_Low_LMU_temperature]) +
|
||||
"</h4>";
|
||||
content +=
|
||||
"<h4>SUB comm interf: " + String(falseTrue[datalayer_extended.cellpower.warning_SUB_communication_interfered]) +
|
||||
"</h4>";
|
||||
content +=
|
||||
"<h4>LMU comm interf: " + String(falseTrue[datalayer_extended.cellpower.warning_LMU_communication_interfered]) +
|
||||
"</h4>";
|
||||
content +=
|
||||
"<h4>High current In: " + String(falseTrue[datalayer_extended.cellpower.warning_High_current_IN]) + "</h4>";
|
||||
content +=
|
||||
"<h4>High current Out: " + String(falseTrue[datalayer_extended.cellpower.warning_High_current_OUT]) + "</h4>";
|
||||
content += "<h4>Pack resistance diff: " +
|
||||
String(falseTrue[datalayer_extended.cellpower.warning_Pack_resistance_difference]) + "</h4>";
|
||||
content +=
|
||||
"<h4>High pack resistance: " + String(falseTrue[datalayer_extended.cellpower.warning_High_pack_resistance]) +
|
||||
"</h4>";
|
||||
content += "<h4>Cell resistance diff: " +
|
||||
String(falseTrue[datalayer_extended.cellpower.warning_Cell_resistance_difference]) + "</h4>";
|
||||
content +=
|
||||
"<h4>High cell resistance: " + String(falseTrue[datalayer_extended.cellpower.warning_High_cell_resistance]) +
|
||||
"</h4>";
|
||||
content += "<h4>High BMCU supply voltage: " +
|
||||
String(falseTrue[datalayer_extended.cellpower.warning_High_BMCU_supply_voltage]) + "</h4>";
|
||||
content += "<h4>Low BMCU supply voltage: " +
|
||||
String(falseTrue[datalayer_extended.cellpower.warning_Low_BMCU_supply_voltage]) + "</h4>";
|
||||
content += "<h4>Low SOC: " + String(falseTrue[datalayer_extended.cellpower.warning_Low_SOC]) + "</h4>";
|
||||
content += "<h4>Balancing required: " +
|
||||
String(falseTrue[datalayer_extended.cellpower.warning_Balancing_required_OCV_model]) + "</h4>";
|
||||
content += "<h4>Charger not responding: " +
|
||||
String(falseTrue[datalayer_extended.cellpower.warning_Charger_not_responding]) + "</h4>";
|
||||
|
||||
return content;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
|
@ -2,6 +2,7 @@
|
|||
#define CMFA_EV_BATTERY_H
|
||||
#include "../include.h"
|
||||
|
||||
#include "CMFA-EV-HTML.h"
|
||||
#include "CanBattery.h"
|
||||
|
||||
#define BATTERY_SELECTED
|
||||
|
@ -14,7 +15,11 @@ class CmfaEvBattery : public CanBattery {
|
|||
virtual void update_values();
|
||||
virtual void transmit_can(unsigned long currentMillis);
|
||||
|
||||
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
|
||||
|
||||
private:
|
||||
CmfaEvHtmlRenderer renderer;
|
||||
|
||||
uint16_t rescale_raw_SOC(uint32_t raw_SOC);
|
||||
|
||||
static const int MAX_PACK_VOLTAGE_DV = 3040; // 5000 = 500.0V
|
||||
|
|
39
Software/src/battery/CMFA-EV-HTML.h
Normal file
39
Software/src/battery/CMFA-EV-HTML.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
#ifndef _CMFA_EV_HTML_H
|
||||
#define _CMFA_EV_HTML_H
|
||||
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../datalayer/datalayer_extended.h"
|
||||
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
|
||||
|
||||
class CmfaEvHtmlRenderer : public BatteryHtmlRenderer {
|
||||
public:
|
||||
String get_status_html() {
|
||||
String content;
|
||||
|
||||
content += "<h4>SOC U: " + String(datalayer_extended.CMFAEV.soc_u) + "percent</h4>";
|
||||
content += "<h4>SOC Z: " + String(datalayer_extended.CMFAEV.soc_z) + "percent</h4>";
|
||||
content += "<h4>SOH Average: " + String(datalayer_extended.CMFAEV.soh_average) + "pptt</h4>";
|
||||
content += "<h4>12V voltage: " + String(datalayer_extended.CMFAEV.lead_acid_voltage) + "mV</h4>";
|
||||
content += "<h4>Highest cell number: " + String(datalayer_extended.CMFAEV.highest_cell_voltage_number) + "</h4>";
|
||||
content += "<h4>Lowest cell number: " + String(datalayer_extended.CMFAEV.lowest_cell_voltage_number) + "</h4>";
|
||||
content += "<h4>Max regen power: " + String(datalayer_extended.CMFAEV.max_regen_power) + "</h4>";
|
||||
content += "<h4>Max discharge power: " + String(datalayer_extended.CMFAEV.max_discharge_power) + "</h4>";
|
||||
content += "<h4>Max charge power: " + String(datalayer_extended.CMFAEV.maximum_charge_power) + "</h4>";
|
||||
content += "<h4>SOH available power: " + String(datalayer_extended.CMFAEV.SOH_available_power) + "</h4>";
|
||||
content += "<h4>SOH generated power: " + String(datalayer_extended.CMFAEV.SOH_generated_power) + "</h4>";
|
||||
content += "<h4>Average temperature: " + String(datalayer_extended.CMFAEV.average_temperature) + "dC</h4>";
|
||||
content += "<h4>Maximum temperature: " + String(datalayer_extended.CMFAEV.maximum_temperature) + "dC</h4>";
|
||||
content += "<h4>Minimum temperature: " + String(datalayer_extended.CMFAEV.minimum_temperature) + "dC</h4>";
|
||||
content +=
|
||||
"<h4>Cumulative energy discharged: " + String(datalayer_extended.CMFAEV.cumulative_energy_when_discharging) +
|
||||
"Wh</h4>";
|
||||
content += "<h4>Cumulative energy charged: " + String(datalayer_extended.CMFAEV.cumulative_energy_when_charging) +
|
||||
"Wh</h4>";
|
||||
content +=
|
||||
"<h4>Cumulative energy regen: " + String(datalayer_extended.CMFAEV.cumulative_energy_in_regen) + "Wh</h4>";
|
||||
|
||||
return content;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
|
@ -10,6 +10,15 @@ class CanBattery : public Battery {
|
|||
public:
|
||||
virtual void handle_incoming_can_frame(CAN_frame rx_frame) = 0;
|
||||
virtual void transmit_can(unsigned long currentMillis) = 0;
|
||||
|
||||
String interface_name() { return getCANInterfaceName(can_interface); }
|
||||
|
||||
protected:
|
||||
CAN_Interface can_interface;
|
||||
|
||||
CanBattery() { can_interface = can_config.battery; }
|
||||
|
||||
CanBattery(CAN_Interface interface) { can_interface = interface; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "../include.h"
|
||||
|
||||
#include "CanBattery.h"
|
||||
#include "ECMP-HTML.h"
|
||||
|
||||
#define BATTERY_SELECTED
|
||||
#define SELECTED_BATTERY_CLASS EcmpBattery
|
||||
|
@ -15,7 +16,10 @@ class EcmpBattery : public CanBattery {
|
|||
virtual void update_values();
|
||||
virtual void transmit_can(unsigned long currentMillis);
|
||||
|
||||
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
|
||||
|
||||
private:
|
||||
EcmpHtmlRenderer renderer;
|
||||
static const int MAX_PACK_VOLTAGE_DV = 4546;
|
||||
static const int MIN_PACK_VOLTAGE_DV = 3210;
|
||||
static const int MAX_CELL_DEVIATION_MV = 100;
|
||||
|
|
28
Software/src/battery/ECMP-HTML.h
Normal file
28
Software/src/battery/ECMP-HTML.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
#ifndef _ECMP_HTML_H
|
||||
#define _ECMP_HTML_H
|
||||
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../datalayer/datalayer_extended.h"
|
||||
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
|
||||
|
||||
class EcmpHtmlRenderer : public BatteryHtmlRenderer {
|
||||
public:
|
||||
String get_status_html() {
|
||||
String content;
|
||||
|
||||
content += "<h4>Main Connector State: ";
|
||||
if (datalayer_extended.stellantisECMP.MainConnectorState == 0) {
|
||||
content += "Contactors open</h4>";
|
||||
} else if (datalayer_extended.stellantisECMP.MainConnectorState == 0x01) {
|
||||
content += "Precharged</h4>";
|
||||
} else {
|
||||
content += "Invalid</h4>";
|
||||
}
|
||||
content +=
|
||||
"<h4>Insulation Resistance: " + String(datalayer_extended.stellantisECMP.InsulationResistance) + "kOhm</h4>";
|
||||
|
||||
return content;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
|
@ -4,6 +4,7 @@
|
|||
#include "../datalayer/datalayer_extended.h"
|
||||
#include "../include.h"
|
||||
#include "CanBattery.h"
|
||||
#include "GEELY-GEOMETRY-C-HTML.h"
|
||||
|
||||
#define BATTERY_SELECTED
|
||||
#define SELECTED_BATTERY_CLASS GeelyGeometryCBattery
|
||||
|
@ -36,7 +37,10 @@ class GeelyGeometryCBattery : public CanBattery {
|
|||
virtual void update_values();
|
||||
virtual void transmit_can(unsigned long currentMillis);
|
||||
|
||||
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
|
||||
|
||||
private:
|
||||
GeelyGeometryCHtmlRenderer renderer;
|
||||
const int MAX_PACK_VOLTAGE_70_DV 4420 //70kWh
|
||||
const int MIN_PACK_VOLTAGE_70_DV 2860 const int MAX_PACK_VOLTAGE_53_DV 4160 //53kWh
|
||||
const int MIN_PACK_VOLTAGE_53_DV 2700 const int MAX_CELL_DEVIATION_MV 150 const int
|
||||
|
|
59
Software/src/battery/GEELY-GEOMETRY-C-HTML.h
Normal file
59
Software/src/battery/GEELY-GEOMETRY-C-HTML.h
Normal file
|
@ -0,0 +1,59 @@
|
|||
#ifndef _GEELY_GEOMETRY_C_HTML_H
|
||||
#define _GEELY_GEOMETRY_C_HTML_H
|
||||
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../datalayer/datalayer_extended.h"
|
||||
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
|
||||
|
||||
class GeelyGeometryCHtmlRenderer : public BatteryHtmlRenderer {
|
||||
public:
|
||||
String get_status_html() {
|
||||
String content;
|
||||
|
||||
char readableSerialNumber[29]; // One extra space for null terminator
|
||||
memcpy(readableSerialNumber, datalayer_extended.geometryC.BatterySerialNumber,
|
||||
sizeof(datalayer_extended.geometryC.BatterySerialNumber));
|
||||
readableSerialNumber[28] = '\0'; // Null terminate the string
|
||||
char readableSoftwareVersion[17]; // One extra space for null terminator
|
||||
memcpy(readableSoftwareVersion, datalayer_extended.geometryC.BatterySoftwareVersion,
|
||||
sizeof(datalayer_extended.geometryC.BatterySoftwareVersion));
|
||||
readableSoftwareVersion[16] = '\0'; // Null terminate the string
|
||||
char readableHardwareVersion[17]; // One extra space for null terminator
|
||||
memcpy(readableHardwareVersion, datalayer_extended.geometryC.BatteryHardwareVersion,
|
||||
sizeof(datalayer_extended.geometryC.BatteryHardwareVersion));
|
||||
readableHardwareVersion[16] = '\0'; // Null terminate the string
|
||||
content += "<h4>Serial number: " + String(readableSoftwareVersion) + "</h4>";
|
||||
content += "<h4>Software version: " + String(readableSerialNumber) + "</h4>";
|
||||
content += "<h4>Hardware version: " + String(readableHardwareVersion) + "</h4>";
|
||||
content += "<h4>SOC display: " + String(datalayer_extended.geometryC.soc) + "ppt</h4>";
|
||||
content += "<h4>CC2 voltage: " + String(datalayer_extended.geometryC.CC2voltage) + "mV</h4>";
|
||||
content += "<h4>Cell max voltage number: " + String(datalayer_extended.geometryC.cellMaxVoltageNumber) + "</h4>";
|
||||
content += "<h4>Cell min voltage number: " + String(datalayer_extended.geometryC.cellMinVoltageNumber) + "</h4>";
|
||||
content += "<h4>Cell total amount: " + String(datalayer_extended.geometryC.cellTotalAmount) + "S</h4>";
|
||||
content += "<h4>Specificial Voltage: " + String(datalayer_extended.geometryC.specificialVoltage) + "dV</h4>";
|
||||
content += "<h4>Unknown1: " + String(datalayer_extended.geometryC.unknown1) + "</h4>";
|
||||
content += "<h4>Raw SOC max: " + String(datalayer_extended.geometryC.rawSOCmax) + "</h4>";
|
||||
content += "<h4>Raw SOC min: " + String(datalayer_extended.geometryC.rawSOCmin) + "</h4>";
|
||||
content += "<h4>Unknown4: " + String(datalayer_extended.geometryC.unknown4) + "</h4>";
|
||||
content += "<h4>Capacity module max: " + String((datalayer_extended.geometryC.capModMax / 10)) + "Ah</h4>";
|
||||
content += "<h4>Capacity module min: " + String((datalayer_extended.geometryC.capModMin / 10)) + "Ah</h4>";
|
||||
content += "<h4>Unknown7: " + String(datalayer_extended.geometryC.unknown7) + "</h4>";
|
||||
content += "<h4>Unknown8: " + String(datalayer_extended.geometryC.unknown8) + "</h4>";
|
||||
content +=
|
||||
"<h4>Module 1 temperature: " + String(datalayer_extended.geometryC.ModuleTemperatures[0]) + " °C</h4>";
|
||||
content +=
|
||||
"<h4>Module 2 temperature: " + String(datalayer_extended.geometryC.ModuleTemperatures[1]) + " °C</h4>";
|
||||
content +=
|
||||
"<h4>Module 3 temperature: " + String(datalayer_extended.geometryC.ModuleTemperatures[2]) + " °C</h4>";
|
||||
content +=
|
||||
"<h4>Module 4 temperature: " + String(datalayer_extended.geometryC.ModuleTemperatures[3]) + " °C</h4>";
|
||||
content +=
|
||||
"<h4>Module 5 temperature: " + String(datalayer_extended.geometryC.ModuleTemperatures[4]) + " °C</h4>";
|
||||
content +=
|
||||
"<h4>Module 6 temperature: " + String(datalayer_extended.geometryC.ModuleTemperatures[5]) + " °C</h4>";
|
||||
|
||||
return content;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
|
@ -5,6 +5,7 @@
|
|||
#include "../datalayer/datalayer_extended.h"
|
||||
#include "../include.h"
|
||||
#include "CanBattery.h"
|
||||
#include "KIA-HYUNDAI-64-HTML.h"
|
||||
|
||||
#define BATTERY_SELECTED
|
||||
#define SELECTED_BATTERY_CLASS KiaHyundai64Battery
|
||||
|
@ -13,20 +14,19 @@ class KiaHyundai64Battery : public CanBattery {
|
|||
public:
|
||||
// Use this constructor for the second battery.
|
||||
KiaHyundai64Battery(DATALAYER_BATTERY_TYPE* datalayer_ptr, DATALAYER_INFO_KIAHYUNDAI64* extended_ptr,
|
||||
bool* contactor_closing_allowed_ptr, int targetCan) {
|
||||
bool* contactor_closing_allowed_ptr, CAN_Interface targetCan)
|
||||
: CanBattery(targetCan), renderer(extended_ptr) {
|
||||
datalayer_battery = datalayer_ptr;
|
||||
contactor_closing_allowed = contactor_closing_allowed_ptr;
|
||||
allows_contactor_closing = nullptr;
|
||||
can_interface = targetCan;
|
||||
datalayer_battery_extended = extended_ptr;
|
||||
}
|
||||
|
||||
// Use the default constructor to create the first or single battery.
|
||||
KiaHyundai64Battery() {
|
||||
KiaHyundai64Battery() : renderer(&datalayer_extended.KiaHyundai64) {
|
||||
datalayer_battery = &datalayer.battery;
|
||||
allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing;
|
||||
contactor_closing_allowed = nullptr;
|
||||
can_interface = can_config.battery;
|
||||
datalayer_battery_extended = &datalayer_extended.KiaHyundai64;
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,11 @@ class KiaHyundai64Battery : public CanBattery {
|
|||
virtual void update_values();
|
||||
virtual void transmit_can(unsigned long currentMillis);
|
||||
|
||||
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
|
||||
|
||||
private:
|
||||
KiaHyundai64HtmlRenderer renderer;
|
||||
|
||||
DATALAYER_BATTERY_TYPE* datalayer_battery;
|
||||
DATALAYER_INFO_KIAHYUNDAI64* datalayer_battery_extended;
|
||||
|
||||
|
@ -45,8 +49,6 @@ class KiaHyundai64Battery : public CanBattery {
|
|||
// If not null, this battery listens to this boolean to determine whether contactor closing is allowed
|
||||
bool* contactor_closing_allowed;
|
||||
|
||||
int can_interface;
|
||||
|
||||
void update_number_of_cells();
|
||||
|
||||
static const int MAX_PACK_VOLTAGE_98S_DV = 4110; //5000 = 500.0V
|
||||
|
|
35
Software/src/battery/KIA-HYUNDAI-64-HTML.h
Normal file
35
Software/src/battery/KIA-HYUNDAI-64-HTML.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
#ifndef _KIA_HYUNDAI_64_HTML_H
|
||||
#define _KIA_HYUNDAI_64_HTML_H
|
||||
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../datalayer/datalayer_extended.h"
|
||||
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
|
||||
|
||||
class KiaHyundai64HtmlRenderer : public BatteryHtmlRenderer {
|
||||
public:
|
||||
KiaHyundai64HtmlRenderer(DATALAYER_INFO_KIAHYUNDAI64* dl) : kia_datalayer(dl) {}
|
||||
|
||||
String get_status_html() {
|
||||
String content;
|
||||
|
||||
auto print_hyundai = [&content](DATALAYER_INFO_KIAHYUNDAI64& data) {
|
||||
content += "<h4>Cells: " + String(data.total_cell_count) + "S</h4>";
|
||||
content += "<h4>12V voltage: " + String(data.battery_12V / 10.0, 1) + "</h4>";
|
||||
content += "<h4>Waterleakage: " + String(data.waterleakageSensor) + "</h4>";
|
||||
content += "<h4>Temperature, water inlet: " + String(data.temperature_water_inlet) + "</h4>";
|
||||
content += "<h4>Temperature, power relay: " + String(data.powerRelayTemperature) + "</h4>";
|
||||
content += "<h4>Batterymanagement mode: " + String(data.batteryManagementMode) + "</h4>";
|
||||
content += "<h4>BMS ignition: " + String(data.BMS_ign) + "</h4>";
|
||||
content += "<h4>Battery relay: " + String(data.batteryRelay) + "</h4>";
|
||||
};
|
||||
|
||||
print_hyundai(*kia_datalayer);
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
private:
|
||||
DATALAYER_INFO_KIAHYUNDAI64* kia_datalayer;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -3,6 +3,7 @@
|
|||
#include <Arduino.h>
|
||||
#include "../include.h"
|
||||
#include "CanBattery.h"
|
||||
#include "MEB-HTML.h"
|
||||
|
||||
#define BATTERY_SELECTED
|
||||
#define SELECTED_BATTERY_CLASS MebBattery
|
||||
|
@ -13,8 +14,12 @@ class MebBattery : public CanBattery {
|
|||
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
|
||||
virtual void update_values();
|
||||
virtual void transmit_can(unsigned long currentMillis);
|
||||
bool supports_real_BMS_status() { return true; }
|
||||
|
||||
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
|
||||
|
||||
private:
|
||||
MebHtmlRenderer renderer;
|
||||
static const int MAX_PACK_VOLTAGE_84S_DV = 3528; //5000 = 500.0V
|
||||
static const int MIN_PACK_VOLTAGE_84S_DV = 2520;
|
||||
static const int MAX_PACK_VOLTAGE_96S_DV = 4032;
|
||||
|
|
286
Software/src/battery/MEB-HTML.h
Normal file
286
Software/src/battery/MEB-HTML.h
Normal file
|
@ -0,0 +1,286 @@
|
|||
#ifndef _MEB_HTML_H
|
||||
#define _MEB_HTML_H
|
||||
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../datalayer/datalayer_extended.h"
|
||||
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
|
||||
|
||||
class MebHtmlRenderer : public BatteryHtmlRenderer {
|
||||
public:
|
||||
String get_status_html() {
|
||||
String content;
|
||||
|
||||
content += datalayer_extended.meb.SDSW ? "<h4>Service disconnect switch: Missing!</h4>"
|
||||
: "<h4>Service disconnect switch: OK</h4>";
|
||||
content += datalayer_extended.meb.pilotline ? "<h4>Pilotline: Open!</h4>" : "<h4>Pilotline: OK</h4>";
|
||||
content += datalayer_extended.meb.transportmode ? "<h4>Transportmode: Locked!</h4>" : "<h4>Transportmode: OK</h4>";
|
||||
content += datalayer_extended.meb.shutdown_active ? "<h4>Shutdown: Active!</h4>" : "<h4>Shutdown: No</h4>";
|
||||
content += datalayer_extended.meb.componentprotection ? "<h4>Component protection: Active!</h4>"
|
||||
: "<h4>Component protection: No</h4>";
|
||||
content += "<h4>HVIL status: ";
|
||||
switch (datalayer_extended.meb.HVIL) {
|
||||
case 0:
|
||||
content += String("Init");
|
||||
break;
|
||||
case 1:
|
||||
content += String("Closed");
|
||||
break;
|
||||
case 2:
|
||||
content += String("Open!");
|
||||
break;
|
||||
case 3:
|
||||
content += String("Fault");
|
||||
break;
|
||||
default:
|
||||
content += String("?");
|
||||
}
|
||||
content += "</h4><h4>KL30C status: ";
|
||||
switch (datalayer_extended.meb.BMS_Kl30c_Status) {
|
||||
case 0:
|
||||
content += String("Init");
|
||||
break;
|
||||
case 1:
|
||||
content += String("Closed");
|
||||
break;
|
||||
case 2:
|
||||
content += String("Open!");
|
||||
break;
|
||||
case 3:
|
||||
content += String("Fault");
|
||||
break;
|
||||
default:
|
||||
content += String("?");
|
||||
}
|
||||
content += "</h4><h4>BMS mode: ";
|
||||
switch (datalayer_extended.meb.BMS_mode) {
|
||||
case 0:
|
||||
content += String("HV inactive");
|
||||
break;
|
||||
case 1:
|
||||
content += String("HV active");
|
||||
break;
|
||||
case 2:
|
||||
content += String("Balancing");
|
||||
break;
|
||||
case 3:
|
||||
content += String("Extern charging");
|
||||
break;
|
||||
case 4:
|
||||
content += String("AC charging");
|
||||
break;
|
||||
case 5:
|
||||
content += String("Battery error");
|
||||
break;
|
||||
case 6:
|
||||
content += String("DC charging");
|
||||
break;
|
||||
case 7:
|
||||
content += String("Init");
|
||||
break;
|
||||
default:
|
||||
content += String("?");
|
||||
}
|
||||
content += String("</h4><h4>Charging: ") + (datalayer_extended.meb.charging_active ? "active" : "not active");
|
||||
content += String("</h4><h4>Balancing: ");
|
||||
switch (datalayer_extended.meb.balancing_active) {
|
||||
case 0:
|
||||
content += String("init");
|
||||
break;
|
||||
case 1:
|
||||
content += String("active");
|
||||
break;
|
||||
case 2:
|
||||
content += String("inactive");
|
||||
break;
|
||||
default:
|
||||
content += String("?");
|
||||
}
|
||||
content +=
|
||||
String("</h4><h4>Slow charging: ") + (datalayer_extended.meb.balancing_request ? "requested" : "not requested");
|
||||
content += "</h4><h4>Diagnostic: ";
|
||||
switch (datalayer_extended.meb.battery_diagnostic) {
|
||||
case 0:
|
||||
content += String("Init");
|
||||
break;
|
||||
case 1:
|
||||
content += String("Battery display");
|
||||
break;
|
||||
case 4:
|
||||
content += String("Battery display OK");
|
||||
break;
|
||||
case 6:
|
||||
content += String("Battery display check");
|
||||
break;
|
||||
case 7:
|
||||
content += String("Fault");
|
||||
break;
|
||||
default:
|
||||
content += String("?");
|
||||
}
|
||||
content += "</h4><h4>HV line status: ";
|
||||
switch (datalayer_extended.meb.status_HV_line) {
|
||||
case 0:
|
||||
content += String("Init");
|
||||
break;
|
||||
case 1:
|
||||
content += String("No open HV line detected");
|
||||
break;
|
||||
case 2:
|
||||
content += String("Open HV line");
|
||||
break;
|
||||
case 3:
|
||||
content += String("Fault");
|
||||
break;
|
||||
default:
|
||||
content += String("? ") + String(datalayer_extended.meb.status_HV_line);
|
||||
}
|
||||
content += "</h4>";
|
||||
content += datalayer_extended.meb.BMS_fault_performance ? "<h4>BMS fault performance: Active!</h4>"
|
||||
: "<h4>BMS fault performance: Off</h4>";
|
||||
content += datalayer_extended.meb.BMS_fault_emergency_shutdown_crash
|
||||
? "<h4>BMS fault emergency shutdown crash: Active!</h4>"
|
||||
: "<h4>BMS fault emergency shutdown crash: Off</h4>";
|
||||
content += datalayer_extended.meb.BMS_error_shutdown_request ? "<h4>BMS error shutdown request: Active!</h4>"
|
||||
: "<h4>BMS error shutdown request: Inactive</h4>";
|
||||
content += datalayer_extended.meb.BMS_error_shutdown ? "<h4>BMS error shutdown: Active!</h4>"
|
||||
: "<h4>BMS error shutdown: Off</h4>";
|
||||
content += "<h4>Welded contactors: ";
|
||||
switch (datalayer_extended.meb.BMS_welded_contactors_status) {
|
||||
case 0:
|
||||
content += String("Init");
|
||||
break;
|
||||
case 1:
|
||||
content += String("No contactor welded");
|
||||
break;
|
||||
case 2:
|
||||
content += String("At least 1 contactor welded");
|
||||
break;
|
||||
case 3:
|
||||
content += String("Protection status detection error");
|
||||
break;
|
||||
default:
|
||||
content += String("?");
|
||||
}
|
||||
content += "</h4><h4>Warning support: ";
|
||||
switch (datalayer_extended.meb.warning_support) {
|
||||
case 0:
|
||||
content += String("OK");
|
||||
break;
|
||||
case 1:
|
||||
content += String("Not OK");
|
||||
break;
|
||||
case 6:
|
||||
content += String("Init");
|
||||
break;
|
||||
case 7:
|
||||
content += String("Fault");
|
||||
break;
|
||||
default:
|
||||
content += String("?");
|
||||
}
|
||||
content += "</h4><h4>Interm. Voltage (" + String(datalayer_extended.meb.BMS_voltage_intermediate_dV / 10.0, 1) +
|
||||
"V) status: ";
|
||||
switch (datalayer_extended.meb.BMS_status_voltage_free) {
|
||||
case 0:
|
||||
content += String("Init");
|
||||
break;
|
||||
case 1:
|
||||
content += String("BMS interm circuit voltage free (U<20V)");
|
||||
break;
|
||||
case 2:
|
||||
content += String("BMS interm circuit not voltage free (U >= 25V)");
|
||||
break;
|
||||
case 3:
|
||||
content += String("Error");
|
||||
break;
|
||||
default:
|
||||
content += String("?");
|
||||
}
|
||||
content += "</h4><h4>BMS error status: ";
|
||||
switch (datalayer_extended.meb.BMS_error_status) {
|
||||
case 0:
|
||||
content += String("Component IO");
|
||||
break;
|
||||
case 1:
|
||||
content += String("Iso Error 1");
|
||||
break;
|
||||
case 2:
|
||||
content += String("Iso Error 2");
|
||||
break;
|
||||
case 3:
|
||||
content += String("Interlock");
|
||||
break;
|
||||
case 4:
|
||||
content += String("SD");
|
||||
break;
|
||||
case 5:
|
||||
content += String("Performance red");
|
||||
break;
|
||||
case 6:
|
||||
content += String("No component function");
|
||||
break;
|
||||
case 7:
|
||||
content += String("Init");
|
||||
break;
|
||||
default:
|
||||
content += String("?");
|
||||
}
|
||||
content += "</h4><h4>BMS voltage: " + String(datalayer_extended.meb.BMS_voltage_dV / 10.0, 1) + "</h4>";
|
||||
content += datalayer_extended.meb.BMS_OBD_MIL ? "<h4>OBD MIL: ON!</h4>" : "<h4>OBD MIL: Off</h4>";
|
||||
content +=
|
||||
datalayer_extended.meb.BMS_error_lamp_req ? "<h4>Red error lamp: ON!</h4>" : "<h4>Red error lamp: Off</h4>";
|
||||
content += datalayer_extended.meb.BMS_warning_lamp_req ? "<h4>Yellow warning lamp: ON!</h4>"
|
||||
: "<h4>Yellow warning lamp: Off</h4>";
|
||||
content += "<h4>Isolation resistance: " + String(datalayer_extended.meb.isolation_resistance) + " kOhm</h4>";
|
||||
content +=
|
||||
datalayer_extended.meb.battery_heating ? "<h4>Battery heating: Active!</h4>" : "<h4>Battery heating: Off</h4>";
|
||||
const char* rt_enum[] = {"No", "Error level 1", "Error level 2", "Error level 3"};
|
||||
content += "<h4>Overcurrent: " + String(rt_enum[datalayer_extended.meb.rt_overcurrent & 0x03]) + "</h4>";
|
||||
content += "<h4>CAN fault: " + String(rt_enum[datalayer_extended.meb.rt_CAN_fault & 0x03]) + "</h4>";
|
||||
content += "<h4>Overcharged: " + String(rt_enum[datalayer_extended.meb.rt_overcharge & 0x03]) + "</h4>";
|
||||
content += "<h4>SOC too high: " + String(rt_enum[datalayer_extended.meb.rt_SOC_high & 0x03]) + "</h4>";
|
||||
content += "<h4>SOC too low: " + String(rt_enum[datalayer_extended.meb.rt_SOC_low & 0x03]) + "</h4>";
|
||||
content += "<h4>SOC jumping: " + String(rt_enum[datalayer_extended.meb.rt_SOC_jumping & 0x03]) + "</h4>";
|
||||
content += "<h4>Temp difference: " + String(rt_enum[datalayer_extended.meb.rt_temp_difference & 0x03]) + "</h4>";
|
||||
content += "<h4>Cell overtemp: " + String(rt_enum[datalayer_extended.meb.rt_cell_overtemp & 0x03]) + "</h4>";
|
||||
content += "<h4>Cell undertemp: " + String(rt_enum[datalayer_extended.meb.rt_cell_undertemp & 0x03]) + "</h4>";
|
||||
content +=
|
||||
"<h4>Battery overvoltage: " + String(rt_enum[datalayer_extended.meb.rt_battery_overvolt & 0x03]) + "</h4>";
|
||||
content +=
|
||||
"<h4>Battery undervoltage: " + String(rt_enum[datalayer_extended.meb.rt_battery_undervol & 0x03]) + "</h4>";
|
||||
content += "<h4>Cell overvoltage: " + String(rt_enum[datalayer_extended.meb.rt_cell_overvolt & 0x03]) + "</h4>";
|
||||
content += "<h4>Cell undervoltage: " + String(rt_enum[datalayer_extended.meb.rt_cell_undervol & 0x03]) + "</h4>";
|
||||
content += "<h4>Cell imbalance: " + String(rt_enum[datalayer_extended.meb.rt_cell_imbalance & 0x03]) + "</h4>";
|
||||
content +=
|
||||
"<h4>Battery unathorized: " + String(rt_enum[datalayer_extended.meb.rt_battery_unathorized & 0x03]) + "</h4>";
|
||||
content +=
|
||||
"<h4>Battery temperature: " + String(datalayer_extended.meb.battery_temperature_dC / 10.f, 1) + " °C</h4>";
|
||||
for (int i = 0; i < 3; i++) {
|
||||
content += "<h4>Temperature points " + String(i * 6 + 1) + "-" + String(i * 6 + 6) + " :";
|
||||
for (int j = 0; j < 6; j++)
|
||||
content += " " + String(datalayer_extended.meb.temp_points[i * 6 + j], 1);
|
||||
content += " °C</h4>";
|
||||
}
|
||||
bool temps_done = false;
|
||||
for (int i = 0; i < 7 && !temps_done; i++) {
|
||||
content += "<h4>Cell temperatures " + String(i * 8 + 1) + "-" + String(i * 8 + 8) + " :";
|
||||
for (int j = 0; j < 8; j++) {
|
||||
if (datalayer_extended.meb.celltemperature_dC[i * 8 + j] == 865) {
|
||||
temps_done = true;
|
||||
break;
|
||||
} else {
|
||||
content += " " + String(datalayer_extended.meb.celltemperature_dC[i * 8 + j] / 10.f, 1);
|
||||
}
|
||||
}
|
||||
content += " °C</h4>";
|
||||
}
|
||||
content +=
|
||||
"<h4>Total charged: " + String(datalayer.battery.status.total_charged_battery_Wh / 1000.0, 1) + " kWh</h4>";
|
||||
content += "<h4>Total discharged: " + String(datalayer.battery.status.total_discharged_battery_Wh / 1000.0, 1) +
|
||||
" kWh</h4>";
|
||||
|
||||
return content;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
|
@ -20,6 +20,11 @@ short ShortMaskedSumAndProduct(short param_1, short param_2);
|
|||
unsigned int MaskedBitwiseRotateMultiply(unsigned int param_1, unsigned int param_2);
|
||||
unsigned int CryptAlgo(unsigned int param_1, unsigned int param_2, unsigned int param_3);
|
||||
|
||||
// Note this should only be allowed/used on 2011-2017 24/30kWh batteries!
|
||||
bool NissanLeafBattery::supports_reset_SOH() {
|
||||
return LEAF_battery_Type != ZE1_BATTERY;
|
||||
}
|
||||
|
||||
void NissanLeafBattery::
|
||||
update_values() { /* This function maps all the values fetched via CAN to the correct parameters used for modbus */
|
||||
/* Start with mapping all values */
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "../datalayer/datalayer_extended.h"
|
||||
#include "../include.h"
|
||||
#include "CanBattery.h"
|
||||
#include "NISSAN-LEAF-HTML.h"
|
||||
|
||||
#define BATTERY_SELECTED
|
||||
#define SELECTED_BATTERY_CLASS NissanLeafBattery
|
||||
|
@ -19,11 +20,12 @@
|
|||
class NissanLeafBattery : public CanBattery {
|
||||
public:
|
||||
// Use this constructor for the second battery.
|
||||
NissanLeafBattery(DATALAYER_BATTERY_TYPE* datalayer_ptr, DATALAYER_INFO_NISSAN_LEAF* extended, int targetCan) {
|
||||
NissanLeafBattery(DATALAYER_BATTERY_TYPE* datalayer_ptr, DATALAYER_INFO_NISSAN_LEAF* extended,
|
||||
CAN_Interface targetCan)
|
||||
: CanBattery(targetCan) {
|
||||
datalayer_battery = datalayer_ptr;
|
||||
allows_contactor_closing = nullptr;
|
||||
datalayer_nissan = extended;
|
||||
can_interface = targetCan;
|
||||
|
||||
battery_Total_Voltage2 = 0;
|
||||
}
|
||||
|
@ -33,7 +35,6 @@ class NissanLeafBattery : public CanBattery {
|
|||
datalayer_battery = &datalayer.battery;
|
||||
allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing;
|
||||
datalayer_nissan = &datalayer_extended.nissanleaf;
|
||||
can_interface = can_config.battery;
|
||||
}
|
||||
|
||||
virtual void setup(void);
|
||||
|
@ -41,7 +42,15 @@ class NissanLeafBattery : public CanBattery {
|
|||
virtual void update_values();
|
||||
virtual void transmit_can(unsigned long currentMillis);
|
||||
|
||||
bool supports_reset_SOH();
|
||||
|
||||
void reset_SOH() { datalayer_extended.nissanleaf.UserRequestSOHreset = true; }
|
||||
|
||||
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
|
||||
|
||||
private:
|
||||
NissanLeafHtmlRenderer renderer;
|
||||
|
||||
bool is_message_corrupt(CAN_frame rx_frame);
|
||||
void clearSOH(void);
|
||||
|
||||
|
@ -51,8 +60,6 @@ class NissanLeafBattery : public CanBattery {
|
|||
// If not null, this battery decides when the contactor can be closed and writes the value here.
|
||||
bool* allows_contactor_closing;
|
||||
|
||||
int can_interface;
|
||||
|
||||
unsigned long previousMillis10 = 0; // will store last time a 10ms CAN Message was send
|
||||
unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send
|
||||
unsigned long previousMillis10s = 0; // will store last time a 1s CAN Message was send
|
||||
|
@ -60,6 +67,10 @@ class NissanLeafBattery : public CanBattery {
|
|||
uint8_t mprun10 = 0; //counter 0-3
|
||||
uint8_t mprun100 = 0; //counter 0-3
|
||||
|
||||
static const int ZE0_BATTERY = 0;
|
||||
static const int AZE0_BATTERY = 1;
|
||||
static const int ZE1_BATTERY = 2;
|
||||
|
||||
// These CAN messages need to be sent towards the battery to keep it alive
|
||||
CAN_frame LEAF_1F2 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
|
@ -116,10 +127,7 @@ class NissanLeafBattery : public CanBattery {
|
|||
196, 65, 75, 206, 76, 201, 195, 70, 215, 82, 88, 221, 255, 122, 112, 245, 100, 225, 235, 110, 175, 42,
|
||||
32, 165, 52, 177, 187, 62, 28, 153, 147, 22, 135, 2, 8, 141};
|
||||
|
||||
//Nissan LEAF battery parameters from constantly sent CAN
|
||||
#define ZE0_BATTERY 0
|
||||
#define AZE0_BATTERY 1
|
||||
#define ZE1_BATTERY 2
|
||||
//Nissan LEAF battery parameters from constantly sent CAN
|
||||
uint8_t LEAF_battery_Type = ZE0_BATTERY;
|
||||
bool battery_can_alive = false;
|
||||
#define WH_PER_GID 77 //One GID is this amount of Watt hours
|
||||
|
|
52
Software/src/battery/NISSAN-LEAF-HTML.h
Normal file
52
Software/src/battery/NISSAN-LEAF-HTML.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
#ifndef _NISSAN_LEAF_HTML_H
|
||||
#define _NISSAN_LEAF_HTML_H
|
||||
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../datalayer/datalayer_extended.h"
|
||||
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
|
||||
|
||||
class NissanLeafHtmlRenderer : public BatteryHtmlRenderer {
|
||||
public:
|
||||
String get_status_html() {
|
||||
String content;
|
||||
|
||||
static const char* LEAFgen[] = {"ZE0", "AZE0", "ZE1"};
|
||||
content += "<h4>LEAF generation: " + String(LEAFgen[datalayer_extended.nissanleaf.LEAF_gen]) + "</h4>";
|
||||
char readableSerialNumber[16]; // One extra space for null terminator
|
||||
memcpy(readableSerialNumber, datalayer_extended.nissanleaf.BatterySerialNumber,
|
||||
sizeof(datalayer_extended.nissanleaf.BatterySerialNumber));
|
||||
readableSerialNumber[15] = '\0'; // Null terminate the string
|
||||
content += "<h4>Serial number: " + String(readableSerialNumber) + "</h4>";
|
||||
char readablePartNumber[8]; // One extra space for null terminator
|
||||
memcpy(readablePartNumber, datalayer_extended.nissanleaf.BatteryPartNumber,
|
||||
sizeof(datalayer_extended.nissanleaf.BatteryPartNumber));
|
||||
readablePartNumber[7] = '\0'; // Null terminate the string
|
||||
content += "<h4>Part number: " + String(readablePartNumber) + "</h4>";
|
||||
char readableBMSID[9]; // One extra space for null terminator
|
||||
memcpy(readableBMSID, datalayer_extended.nissanleaf.BMSIDcode, sizeof(datalayer_extended.nissanleaf.BMSIDcode));
|
||||
readableBMSID[8] = '\0'; // Null terminate the string
|
||||
content += "<h4>BMS ID: " + String(readableBMSID) + "</h4>";
|
||||
content += "<h4>GIDS: " + String(datalayer_extended.nissanleaf.GIDS) + "</h4>";
|
||||
content += "<h4>Regen kW: " + String(datalayer_extended.nissanleaf.ChargePowerLimit) + "</h4>";
|
||||
content += "<h4>Charge kW: " + String(datalayer_extended.nissanleaf.MaxPowerForCharger) + "</h4>";
|
||||
content += "<h4>Interlock: " + String(datalayer_extended.nissanleaf.Interlock) + "</h4>";
|
||||
content += "<h4>Insulation: " + String(datalayer_extended.nissanleaf.Insulation) + "</h4>";
|
||||
content += "<h4>Relay cut request: " + String(datalayer_extended.nissanleaf.RelayCutRequest) + "</h4>";
|
||||
content += "<h4>Failsafe status: " + String(datalayer_extended.nissanleaf.FailsafeStatus) + "</h4>";
|
||||
content += "<h4>Fully charged: " + String(datalayer_extended.nissanleaf.Full) + "</h4>";
|
||||
content += "<h4>Battery empty: " + String(datalayer_extended.nissanleaf.Empty) + "</h4>";
|
||||
content += "<h4>Main relay ON: " + String(datalayer_extended.nissanleaf.MainRelayOn) + "</h4>";
|
||||
content += "<h4>Heater present: " + String(datalayer_extended.nissanleaf.HeatExist) + "</h4>";
|
||||
content += "<h4>Heating stopped: " + String(datalayer_extended.nissanleaf.HeatingStop) + "</h4>";
|
||||
content += "<h4>Heating started: " + String(datalayer_extended.nissanleaf.HeatingStart) + "</h4>";
|
||||
content += "<h4>Heating requested: " + String(datalayer_extended.nissanleaf.HeaterSendRequest) + "</h4>";
|
||||
content += "<h4>CryptoChallenge: " + String(datalayer_extended.nissanleaf.CryptoChallenge) + "</h4>";
|
||||
content += "<h4>SolvedChallenge: " + String(datalayer_extended.nissanleaf.SolvedChallengeMSB) +
|
||||
String(datalayer_extended.nissanleaf.SolvedChallengeLSB) + "</h4>";
|
||||
content += "<h4>Challenge failed: " + String(datalayer_extended.nissanleaf.challengeFailed) + "</h4>";
|
||||
|
||||
return content;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
|
@ -12,11 +12,11 @@
|
|||
class PylonBattery : public CanBattery {
|
||||
public:
|
||||
// Use this constructor for the second battery.
|
||||
PylonBattery(DATALAYER_BATTERY_TYPE* datalayer_ptr, bool* contactor_closing_allowed_ptr, int targetCan) {
|
||||
PylonBattery(DATALAYER_BATTERY_TYPE* datalayer_ptr, bool* contactor_closing_allowed_ptr, CAN_Interface targetCan)
|
||||
: CanBattery(targetCan) {
|
||||
datalayer_battery = datalayer_ptr;
|
||||
contactor_closing_allowed = contactor_closing_allowed_ptr;
|
||||
allows_contactor_closing = nullptr;
|
||||
can_interface = targetCan;
|
||||
}
|
||||
|
||||
// Use the default constructor to create the first or single battery.
|
||||
|
@ -24,7 +24,6 @@ class PylonBattery : public CanBattery {
|
|||
datalayer_battery = &datalayer.battery;
|
||||
allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing;
|
||||
contactor_closing_allowed = nullptr;
|
||||
can_interface = can_config.battery;
|
||||
}
|
||||
|
||||
virtual void setup(void);
|
||||
|
@ -48,8 +47,6 @@ class PylonBattery : public CanBattery {
|
|||
// If not null, this battery listens to this boolean to determine whether contactor closing is allowed
|
||||
bool* contactor_closing_allowed;
|
||||
|
||||
int can_interface;
|
||||
|
||||
unsigned long previousMillis1000 = 0; // will store last time a 1s CAN Message was sent
|
||||
|
||||
//Actual content messages
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define RENAULT_ZOE_GEN1_BATTERY_H
|
||||
|
||||
#include "CanBattery.h"
|
||||
#include "RENAULT-ZOE-GEN1-HTML.h"
|
||||
|
||||
#define BATTERY_SELECTED
|
||||
#define SELECTED_BATTERY_CLASS RenaultZoeGen1Battery
|
||||
|
@ -18,6 +19,11 @@ class RenaultZoeGen1Battery : public CanBattery {
|
|||
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
|
||||
virtual void update_values();
|
||||
virtual void transmit_can(unsigned long currentMillis);
|
||||
|
||||
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
|
||||
|
||||
private:
|
||||
RenaultZoeGen1HtmlRenderer renderer;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
26
Software/src/battery/RENAULT-ZOE-GEN1-HTML.h
Normal file
26
Software/src/battery/RENAULT-ZOE-GEN1-HTML.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
#ifndef _RENAULT_ZOE_GEN1_HTML_H
|
||||
#define _RENAULT_ZOE_GEN1_HTML_H
|
||||
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../datalayer/datalayer_extended.h"
|
||||
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
|
||||
|
||||
class RenaultZoeGen1HtmlRenderer : public BatteryHtmlRenderer {
|
||||
public:
|
||||
String get_status_html() {
|
||||
String content;
|
||||
|
||||
content += "<h4>CUV " + String(datalayer_extended.zoe.CUV) + "</h4>";
|
||||
content += "<h4>HVBIR " + String(datalayer_extended.zoe.HVBIR) + "</h4>";
|
||||
content += "<h4>HVBUV " + String(datalayer_extended.zoe.HVBUV) + "</h4>";
|
||||
content += "<h4>EOCR " + String(datalayer_extended.zoe.EOCR) + "</h4>";
|
||||
content += "<h4>HVBOC " + String(datalayer_extended.zoe.HVBOC) + "</h4>";
|
||||
content += "<h4>HVBOT " + String(datalayer_extended.zoe.HVBOT) + "</h4>";
|
||||
content += "<h4>HVBOV " + String(datalayer_extended.zoe.HVBOV) + "</h4>";
|
||||
content += "<h4>COV " + String(datalayer_extended.zoe.COV) + "</h4>";
|
||||
|
||||
return content;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
|
@ -3,6 +3,7 @@
|
|||
#include "../include.h"
|
||||
|
||||
#include "CanBattery.h"
|
||||
#include "RENAULT-ZOE-GEN2-HTML.h"
|
||||
|
||||
#define BATTERY_SELECTED
|
||||
#define SELECTED_BATTERY_CLASS RenaultZoeGen2Battery
|
||||
|
@ -14,7 +15,14 @@ class RenaultZoeGen2Battery : public CanBattery {
|
|||
virtual void update_values();
|
||||
virtual void transmit_can(unsigned long currentMillis);
|
||||
|
||||
bool supports_reset_NVROL() { return true; }
|
||||
|
||||
void reset_NVROL() { datalayer_extended.zoePH2.UserRequestNVROLReset = true; }
|
||||
|
||||
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
|
||||
|
||||
private:
|
||||
RenaultZoeGen2HtmlRenderer renderer;
|
||||
static const int MAX_PACK_VOLTAGE_DV = 4100; //5000 = 500.0V
|
||||
static const int MIN_PACK_VOLTAGE_DV = 3000;
|
||||
static const int MAX_CELL_DEVIATION_MV = 150;
|
||||
|
|
62
Software/src/battery/RENAULT-ZOE-GEN2-HTML.h
Normal file
62
Software/src/battery/RENAULT-ZOE-GEN2-HTML.h
Normal file
|
@ -0,0 +1,62 @@
|
|||
#ifndef _RENAULT_ZOE_GEN2_HTML_H
|
||||
#define _RENAULT_ZOE_GEN2_HTML_H
|
||||
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../datalayer/datalayer_extended.h"
|
||||
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
|
||||
|
||||
class RenaultZoeGen2HtmlRenderer : public BatteryHtmlRenderer {
|
||||
public:
|
||||
String get_status_html() {
|
||||
String content;
|
||||
|
||||
content += "<h4>soc: " + String(datalayer_extended.zoePH2.battery_soc) + "</h4>";
|
||||
content += "<h4>usable soc: " + String(datalayer_extended.zoePH2.battery_usable_soc) + "</h4>";
|
||||
content += "<h4>soh: " + String(datalayer_extended.zoePH2.battery_soh) + "</h4>";
|
||||
content += "<h4>pack voltage: " + String(datalayer_extended.zoePH2.battery_pack_voltage) + "</h4>";
|
||||
content += "<h4>max cell voltage: " + String(datalayer_extended.zoePH2.battery_max_cell_voltage) + "</h4>";
|
||||
content += "<h4>min cell voltage: " + String(datalayer_extended.zoePH2.battery_min_cell_voltage) + "</h4>";
|
||||
content += "<h4>12v: " + String(datalayer_extended.zoePH2.battery_12v) + "</h4>";
|
||||
content += "<h4>avg temp: " + String(datalayer_extended.zoePH2.battery_avg_temp) + "</h4>";
|
||||
content += "<h4>min temp: " + String(datalayer_extended.zoePH2.battery_min_temp) + "</h4>";
|
||||
content += "<h4>max temp: " + String(datalayer_extended.zoePH2.battery_max_temp) + "</h4>";
|
||||
content += "<h4>max power: " + String(datalayer_extended.zoePH2.battery_max_power) + "</h4>";
|
||||
content += "<h4>interlock: " + String(datalayer_extended.zoePH2.battery_interlock) + "</h4>";
|
||||
content += "<h4>kwh: " + String(datalayer_extended.zoePH2.battery_kwh) + "</h4>";
|
||||
content += "<h4>current: " + String(datalayer_extended.zoePH2.battery_current) + "</h4>";
|
||||
content += "<h4>current offset: " + String(datalayer_extended.zoePH2.battery_current_offset) + "</h4>";
|
||||
content += "<h4>max generated: " + String(datalayer_extended.zoePH2.battery_max_generated) + "</h4>";
|
||||
content += "<h4>max available: " + String(datalayer_extended.zoePH2.battery_max_available) + "</h4>";
|
||||
content += "<h4>current voltage: " + String(datalayer_extended.zoePH2.battery_current_voltage) + "</h4>";
|
||||
content += "<h4>charging status: " + String(datalayer_extended.zoePH2.battery_charging_status) + "</h4>";
|
||||
content += "<h4>remaining charge: " + String(datalayer_extended.zoePH2.battery_remaining_charge) + "</h4>";
|
||||
content +=
|
||||
"<h4>balance capacity total: " + String(datalayer_extended.zoePH2.battery_balance_capacity_total) + "</h4>";
|
||||
content += "<h4>balance time total: " + String(datalayer_extended.zoePH2.battery_balance_time_total) + "</h4>";
|
||||
content +=
|
||||
"<h4>balance capacity sleep: " + String(datalayer_extended.zoePH2.battery_balance_capacity_sleep) + "</h4>";
|
||||
content += "<h4>balance time sleep: " + String(datalayer_extended.zoePH2.battery_balance_time_sleep) + "</h4>";
|
||||
content +=
|
||||
"<h4>balance capacity wake: " + String(datalayer_extended.zoePH2.battery_balance_capacity_wake) + "</h4>";
|
||||
content += "<h4>balance time wake: " + String(datalayer_extended.zoePH2.battery_balance_time_wake) + "</h4>";
|
||||
content += "<h4>bms state: " + String(datalayer_extended.zoePH2.battery_bms_state) + "</h4>";
|
||||
content += "<h4>balance switches: " + String(datalayer_extended.zoePH2.battery_balance_switches) + "</h4>";
|
||||
content += "<h4>energy complete: " + String(datalayer_extended.zoePH2.battery_energy_complete) + "</h4>";
|
||||
content += "<h4>energy partial: " + String(datalayer_extended.zoePH2.battery_energy_partial) + "</h4>";
|
||||
content += "<h4>slave failures: " + String(datalayer_extended.zoePH2.battery_slave_failures) + "</h4>";
|
||||
content += "<h4>mileage: " + String(datalayer_extended.zoePH2.battery_mileage) + "</h4>";
|
||||
content += "<h4>fan speed: " + String(datalayer_extended.zoePH2.battery_fan_speed) + "</h4>";
|
||||
content += "<h4>fan period: " + String(datalayer_extended.zoePH2.battery_fan_period) + "</h4>";
|
||||
content += "<h4>fan control: " + String(datalayer_extended.zoePH2.battery_fan_control) + "</h4>";
|
||||
content += "<h4>fan duty: " + String(datalayer_extended.zoePH2.battery_fan_duty) + "</h4>";
|
||||
content += "<h4>temporisation: " + String(datalayer_extended.zoePH2.battery_temporisation) + "</h4>";
|
||||
content += "<h4>time: " + String(datalayer_extended.zoePH2.battery_time) + "</h4>";
|
||||
content += "<h4>pack time: " + String(datalayer_extended.zoePH2.battery_pack_time) + "</h4>";
|
||||
content += "<h4>soc min: " + String(datalayer_extended.zoePH2.battery_soc_min) + "</h4>";
|
||||
content += "<h4>soc max: " + String(datalayer_extended.zoePH2.battery_soc_max) + "</h4>";
|
||||
|
||||
return content;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
|
@ -10,6 +10,8 @@ class RS485Battery : public Battery {
|
|||
public:
|
||||
virtual void receive_RS485() = 0;
|
||||
virtual void transmit_rs485() = 0;
|
||||
|
||||
String interface_name() { return "RS485"; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -11,17 +11,15 @@
|
|||
class SantaFePhevBattery : public CanBattery {
|
||||
public:
|
||||
// Use this constructor for the second battery.
|
||||
SantaFePhevBattery(DATALAYER_BATTERY_TYPE* datalayer_ptr, int targetCan) {
|
||||
SantaFePhevBattery(DATALAYER_BATTERY_TYPE* datalayer_ptr, CAN_Interface targetCan) : CanBattery(targetCan) {
|
||||
datalayer_battery = datalayer_ptr;
|
||||
allows_contactor_closing = nullptr;
|
||||
can_interface = targetCan;
|
||||
}
|
||||
|
||||
// Use the default constructor to create the first or single battery.
|
||||
SantaFePhevBattery() {
|
||||
datalayer_battery = &datalayer.battery;
|
||||
allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing;
|
||||
can_interface = can_config.battery;
|
||||
}
|
||||
|
||||
virtual void setup(void);
|
||||
|
@ -35,8 +33,6 @@ class SantaFePhevBattery : public CanBattery {
|
|||
// If not null, this battery decides when the contactor can be closed and writes the value here.
|
||||
bool* allows_contactor_closing;
|
||||
|
||||
int can_interface;
|
||||
|
||||
static const int MAX_PACK_VOLTAGE_DV = 4040; //5000 = 500.0V
|
||||
static const int MIN_PACK_VOLTAGE_DV = 2880;
|
||||
static const int MAX_CELL_DEVIATION_MV = 250;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "../datalayer/datalayer.h"
|
||||
#include "../include.h"
|
||||
#include "CanBattery.h"
|
||||
#include "TESLA-HTML.h"
|
||||
|
||||
#define BATTERY_SELECTED
|
||||
#ifdef TESLA_MODEL_3Y_BATTERY
|
||||
|
@ -23,6 +24,17 @@ class TeslaBattery : public CanBattery {
|
|||
virtual void update_values();
|
||||
virtual void transmit_can(unsigned long currentMillis);
|
||||
|
||||
bool supports_clear_isolation() { return true; }
|
||||
void clear_isolation() { datalayer.battery.settings.user_requests_tesla_isolation_clear = true; }
|
||||
|
||||
bool supports_reset_BMS() { return true; }
|
||||
void reset_BMS() { datalayer.battery.settings.user_requests_tesla_bms_reset = true; }
|
||||
|
||||
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
|
||||
|
||||
private:
|
||||
TeslaHtmlRenderer renderer;
|
||||
|
||||
protected:
|
||||
/* Modify these if needed */
|
||||
static const int MAXCHARGEPOWERALLOWED =
|
||||
|
|
431
Software/src/battery/TESLA-HTML.h
Normal file
431
Software/src/battery/TESLA-HTML.h
Normal file
|
@ -0,0 +1,431 @@
|
|||
#ifndef _TESLA_HTML_H
|
||||
#define _TESLA_HTML_H
|
||||
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../datalayer/datalayer_extended.h"
|
||||
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
|
||||
|
||||
class TeslaHtmlRenderer : public BatteryHtmlRenderer {
|
||||
public:
|
||||
String get_status_html() {
|
||||
String content;
|
||||
|
||||
float beginning_of_life = static_cast<float>(datalayer_extended.tesla.battery_beginning_of_life);
|
||||
float battTempPct = static_cast<float>(datalayer_extended.tesla.battery_battTempPct) * 0.4;
|
||||
float dcdcLvBusVolt = static_cast<float>(datalayer_extended.tesla.battery_dcdcLvBusVolt) * 0.0390625;
|
||||
float dcdcHvBusVolt = static_cast<float>(datalayer_extended.tesla.battery_dcdcHvBusVolt) * 0.146484;
|
||||
float dcdcLvOutputCurrent = static_cast<float>(datalayer_extended.tesla.battery_dcdcLvOutputCurrent) * 0.1;
|
||||
float nominal_full_pack_energy =
|
||||
static_cast<float>(datalayer_extended.tesla.battery_nominal_full_pack_energy) * 0.1;
|
||||
float nominal_full_pack_energy_m0 =
|
||||
static_cast<float>(datalayer_extended.tesla.battery_nominal_full_pack_energy_m0) * 0.02;
|
||||
float nominal_energy_remaining =
|
||||
static_cast<float>(datalayer_extended.tesla.battery_nominal_energy_remaining) * 0.1;
|
||||
float nominal_energy_remaining_m0 =
|
||||
static_cast<float>(datalayer_extended.tesla.battery_nominal_energy_remaining_m0) * 0.02;
|
||||
float ideal_energy_remaining = static_cast<float>(datalayer_extended.tesla.battery_ideal_energy_remaining) * 0.1;
|
||||
float ideal_energy_remaining_m0 =
|
||||
static_cast<float>(datalayer_extended.tesla.battery_ideal_energy_remaining_m0) * 0.02;
|
||||
float energy_to_charge_complete =
|
||||
static_cast<float>(datalayer_extended.tesla.battery_energy_to_charge_complete) * 0.1;
|
||||
float energy_to_charge_complete_m1 =
|
||||
static_cast<float>(datalayer_extended.tesla.battery_energy_to_charge_complete_m1) * 0.02;
|
||||
float energy_buffer = static_cast<float>(datalayer_extended.tesla.battery_energy_buffer) * 0.1;
|
||||
float energy_buffer_m1 = static_cast<float>(datalayer_extended.tesla.battery_energy_buffer_m1) * 0.01;
|
||||
float expected_energy_remaining_m1 =
|
||||
static_cast<float>(datalayer_extended.tesla.battery_expected_energy_remaining_m1) * 0.02;
|
||||
float total_discharge = static_cast<float>(datalayer.battery.status.total_discharged_battery_Wh) * 0.001;
|
||||
float total_charge = static_cast<float>(datalayer.battery.status.total_charged_battery_Wh) * 0.001;
|
||||
float packMass = static_cast<float>(datalayer_extended.tesla.battery_packMass);
|
||||
float platformMaxBusVoltage =
|
||||
static_cast<float>(datalayer_extended.tesla.battery_platformMaxBusVoltage) * 0.1 + 375;
|
||||
float bms_min_voltage = static_cast<float>(datalayer_extended.tesla.battery_bms_min_voltage) * 0.01 * 2;
|
||||
float bms_max_voltage = static_cast<float>(datalayer_extended.tesla.battery_bms_max_voltage) * 0.01 * 2;
|
||||
float max_charge_current = static_cast<float>(datalayer_extended.tesla.battery_max_charge_current);
|
||||
float max_discharge_current = static_cast<float>(datalayer_extended.tesla.battery_max_discharge_current);
|
||||
float soc_ave = static_cast<float>(datalayer_extended.tesla.battery_soc_ave) * 0.1;
|
||||
float soc_max = static_cast<float>(datalayer_extended.tesla.battery_soc_max) * 0.1;
|
||||
float soc_min = static_cast<float>(datalayer_extended.tesla.battery_soc_min) * 0.1;
|
||||
float soc_ui = static_cast<float>(datalayer_extended.tesla.battery_soc_ui) * 0.1;
|
||||
float BrickVoltageMax = static_cast<float>(datalayer_extended.tesla.battery_BrickVoltageMax) * 0.002;
|
||||
float BrickVoltageMin = static_cast<float>(datalayer_extended.tesla.battery_BrickVoltageMin) * 0.002;
|
||||
float BrickModelTMax = static_cast<float>(datalayer_extended.tesla.battery_BrickModelTMax) * 0.5 - 40;
|
||||
float BrickModelTMin = static_cast<float>(datalayer_extended.tesla.battery_BrickModelTMin) * 0.5 - 40;
|
||||
float isolationResistance = static_cast<float>(datalayer_extended.tesla.battery_BMS_isolationResistance) * 10;
|
||||
float PCS_dcdcMaxOutputCurrentAllowed =
|
||||
static_cast<float>(datalayer_extended.tesla.battery_PCS_dcdcMaxOutputCurrentAllowed) * 0.1;
|
||||
float PCS_dcdcTemp = static_cast<float>(datalayer_extended.tesla.PCS_dcdcTemp) * 0.1 + 40;
|
||||
float PCS_ambientTemp = static_cast<float>(datalayer_extended.tesla.PCS_ambientTemp) * 0.1 + 40;
|
||||
float PCS_chgPhATemp = static_cast<float>(datalayer_extended.tesla.PCS_chgPhATemp) * 0.1 + 40;
|
||||
float PCS_chgPhBTemp = static_cast<float>(datalayer_extended.tesla.PCS_chgPhBTemp) * 0.1 + 40;
|
||||
float PCS_chgPhCTemp = static_cast<float>(datalayer_extended.tesla.PCS_chgPhCTemp) * 0.1 + 40;
|
||||
float BMS_maxRegenPower = static_cast<float>(datalayer_extended.tesla.BMS_maxRegenPower) * 0.01;
|
||||
float BMS_maxDischargePower = static_cast<float>(datalayer_extended.tesla.BMS_maxDischargePower) * 0.013;
|
||||
float BMS_maxStationaryHeatPower = static_cast<float>(datalayer_extended.tesla.BMS_maxStationaryHeatPower) * 0.01;
|
||||
float BMS_hvacPowerBudget = static_cast<float>(datalayer_extended.tesla.BMS_hvacPowerBudget) * 0.02;
|
||||
float BMS_powerDissipation = static_cast<float>(datalayer_extended.tesla.BMS_powerDissipation) * 0.02;
|
||||
float BMS_flowRequest = static_cast<float>(datalayer_extended.tesla.BMS_flowRequest) * 0.3;
|
||||
float BMS_inletActiveCoolTargetT =
|
||||
static_cast<float>(datalayer_extended.tesla.BMS_inletActiveCoolTargetT) * 0.25 - 25;
|
||||
float BMS_inletPassiveTargetT = static_cast<float>(datalayer_extended.tesla.BMS_inletPassiveTargetT) * 0.25 - 25;
|
||||
float BMS_inletActiveHeatTargetT =
|
||||
static_cast<float>(datalayer_extended.tesla.BMS_inletActiveHeatTargetT) * 0.25 - 25;
|
||||
float BMS_packTMin = static_cast<float>(datalayer_extended.tesla.BMS_packTMin) * 0.25 - 25;
|
||||
float BMS_packTMax = static_cast<float>(datalayer_extended.tesla.BMS_packTMax) * 0.25 - 25;
|
||||
float PCS_dcdcMaxLvOutputCurrent = static_cast<float>(datalayer_extended.tesla.PCS_dcdcMaxLvOutputCurrent) * 0.1;
|
||||
float PCS_dcdcCurrentLimit = static_cast<float>(datalayer_extended.tesla.PCS_dcdcCurrentLimit) * 0.1;
|
||||
float PCS_dcdcLvOutputCurrentTempLimit =
|
||||
static_cast<float>(datalayer_extended.tesla.PCS_dcdcLvOutputCurrentTempLimit) * 0.1;
|
||||
float PCS_dcdcUnifiedCommand = static_cast<float>(datalayer_extended.tesla.PCS_dcdcUnifiedCommand) * 0.001;
|
||||
float PCS_dcdcCLAControllerOutput =
|
||||
static_cast<float>(datalayer_extended.tesla.PCS_dcdcCLAControllerOutput * 0.001);
|
||||
float PCS_dcdcTankVoltage = static_cast<float>(datalayer_extended.tesla.PCS_dcdcTankVoltage);
|
||||
float PCS_dcdcTankVoltageTarget = static_cast<float>(datalayer_extended.tesla.PCS_dcdcTankVoltageTarget);
|
||||
float PCS_dcdcClaCurrentFreq = static_cast<float>(datalayer_extended.tesla.PCS_dcdcClaCurrentFreq) * 0.0976563;
|
||||
float PCS_dcdcTCommMeasured = static_cast<float>(datalayer_extended.tesla.PCS_dcdcTCommMeasured) * 0.00195313;
|
||||
float PCS_dcdcShortTimeUs = static_cast<float>(datalayer_extended.tesla.PCS_dcdcShortTimeUs) * 0.000488281;
|
||||
float PCS_dcdcHalfPeriodUs = static_cast<float>(datalayer_extended.tesla.PCS_dcdcHalfPeriodUs) * 0.000488281;
|
||||
float PCS_dcdcIntervalMaxFrequency = static_cast<float>(datalayer_extended.tesla.PCS_dcdcIntervalMaxFrequency);
|
||||
float PCS_dcdcIntervalMaxHvBusVolt =
|
||||
static_cast<float>(datalayer_extended.tesla.PCS_dcdcIntervalMaxHvBusVolt) * 0.1;
|
||||
float PCS_dcdcIntervalMaxLvBusVolt =
|
||||
static_cast<float>(datalayer_extended.tesla.PCS_dcdcIntervalMaxLvBusVolt) * 0.1;
|
||||
float PCS_dcdcIntervalMaxLvOutputCurr =
|
||||
static_cast<float>(datalayer_extended.tesla.PCS_dcdcIntervalMaxLvOutputCurr);
|
||||
float PCS_dcdcIntervalMinFrequency = static_cast<float>(datalayer_extended.tesla.PCS_dcdcIntervalMinFrequency);
|
||||
float PCS_dcdcIntervalMinHvBusVolt =
|
||||
static_cast<float>(datalayer_extended.tesla.PCS_dcdcIntervalMinHvBusVolt) * 0.1;
|
||||
float PCS_dcdcIntervalMinLvBusVolt =
|
||||
static_cast<float>(datalayer_extended.tesla.PCS_dcdcIntervalMinLvBusVolt) * 0.1;
|
||||
float PCS_dcdcIntervalMinLvOutputCurr =
|
||||
static_cast<float>(datalayer_extended.tesla.PCS_dcdcIntervalMinLvOutputCurr);
|
||||
float PCS_dcdc12vSupportLifetimekWh =
|
||||
static_cast<float>(datalayer_extended.tesla.PCS_dcdc12vSupportLifetimekWh) * 0.01;
|
||||
float HVP_hvp1v5Ref = static_cast<float>(datalayer_extended.tesla.HVP_hvp1v5Ref) * 0.1;
|
||||
float HVP_shuntCurrentDebug = static_cast<float>(datalayer_extended.tesla.HVP_shuntCurrentDebug) * 0.1;
|
||||
float HVP_dcLinkVoltage = static_cast<float>(datalayer_extended.tesla.HVP_dcLinkVoltage) * 0.1;
|
||||
float HVP_packVoltage = static_cast<float>(datalayer_extended.tesla.HVP_packVoltage) * 0.1;
|
||||
float HVP_fcLinkVoltage = static_cast<float>(datalayer_extended.tesla.HVP_fcLinkVoltage) * 0.1;
|
||||
float HVP_packContVoltage = static_cast<float>(datalayer_extended.tesla.HVP_packContVoltage) * 0.1;
|
||||
float HVP_packNegativeV = static_cast<float>(datalayer_extended.tesla.HVP_packNegativeV) * 0.1;
|
||||
float HVP_packPositiveV = static_cast<float>(datalayer_extended.tesla.HVP_packPositiveV) * 0.1;
|
||||
float HVP_pyroAnalog = static_cast<float>(datalayer_extended.tesla.HVP_pyroAnalog) * 0.1;
|
||||
float HVP_dcLinkNegativeV = static_cast<float>(datalayer_extended.tesla.HVP_dcLinkNegativeV) * 0.1;
|
||||
float HVP_dcLinkPositiveV = static_cast<float>(datalayer_extended.tesla.HVP_dcLinkPositiveV) * 0.1;
|
||||
float HVP_fcLinkNegativeV = static_cast<float>(datalayer_extended.tesla.HVP_fcLinkNegativeV) * 0.1;
|
||||
float HVP_fcContCoilCurrent = static_cast<float>(datalayer_extended.tesla.HVP_fcContCoilCurrent) * 0.1;
|
||||
float HVP_fcContVoltage = static_cast<float>(datalayer_extended.tesla.HVP_fcContVoltage) * 0.1;
|
||||
float HVP_hvilInVoltage = static_cast<float>(datalayer_extended.tesla.HVP_hvilInVoltage) * 0.1;
|
||||
float HVP_hvilOutVoltage = static_cast<float>(datalayer_extended.tesla.HVP_hvilOutVoltage) * 0.1;
|
||||
float HVP_fcLinkPositiveV = static_cast<float>(datalayer_extended.tesla.HVP_fcLinkPositiveV) * 0.1;
|
||||
float HVP_packContCoilCurrent = static_cast<float>(datalayer_extended.tesla.HVP_packContCoilCurrent) * 0.1;
|
||||
float HVP_battery12V = static_cast<float>(datalayer_extended.tesla.HVP_battery12V) * 0.1;
|
||||
float HVP_shuntRefVoltageDbg = static_cast<float>(datalayer_extended.tesla.HVP_shuntRefVoltageDbg) * 0.001;
|
||||
float HVP_shuntAuxCurrentDbg = static_cast<float>(datalayer_extended.tesla.HVP_shuntAuxCurrentDbg) * 0.1;
|
||||
float HVP_shuntBarTempDbg = static_cast<float>(datalayer_extended.tesla.HVP_shuntBarTempDbg) * 0.01;
|
||||
float HVP_shuntAsicTempDbg = static_cast<float>(datalayer_extended.tesla.HVP_shuntAsicTempDbg) * 0.01;
|
||||
|
||||
static const char* contactorText[] = {"UNKNOWN(0)", "OPEN", "CLOSING", "BLOCKED", "OPENING",
|
||||
"CLOSED", "UNKNOWN(6)", "WELDED", "POS_CL", "NEG_CL",
|
||||
"UNKNOWN(10)", "UNKNOWN(11)", "UNKNOWN(12)"};
|
||||
static const char* hvilStatusState[] = {"NOT Ok",
|
||||
"STATUS_OK",
|
||||
"CURRENT_SOURCE_FAULT",
|
||||
"INTERNAL_OPEN_FAULT",
|
||||
"VEHICLE_OPEN_FAULT",
|
||||
"PENTHOUSE_LID_OPEN_FAULT",
|
||||
"UNKNOWN_LOCATION_OPEN_FAULT",
|
||||
"VEHICLE_NODE_FAULT",
|
||||
"NO_12V_SUPPLY",
|
||||
"VEHICLE_OR_PENTHOUSE_LID_OPENFAULT",
|
||||
"UNKNOWN(10)",
|
||||
"UNKNOWN(11)",
|
||||
"UNKNOWN(12)",
|
||||
"UNKNOWN(13)",
|
||||
"UNKNOWN(14)",
|
||||
"UNKNOWN(15)"};
|
||||
static const char* contactorState[] = {"SNA", "OPEN", "PRECHARGE", "BLOCKED",
|
||||
"PULLED_IN", "OPENING", "ECONOMIZED", "WELDED",
|
||||
"UNKNOWN(8)", "UNKNOWN(9)", "UNKNOWN(10)", "UNKNOWN(11)"};
|
||||
static const char* BMS_state[] = {"STANDBY", "DRIVE", "SUPPORT", "CHARGE", "FEIM",
|
||||
"CLEAR_FAULT", "FAULT", "WELD", "TEST", "SNA"};
|
||||
static const char* BMS_contactorState[] = {"SNA", "OPEN", "OPENING", "CLOSING", "CLOSED", "WELDED", "BLOCKED"};
|
||||
static const char* BMS_hvState[] = {"DOWN", "COMING_UP", "GOING_DOWN", "UP_FOR_DRIVE",
|
||||
"UP_FOR_CHARGE", "UP_FOR_DC_CHARGE", "UP"};
|
||||
static const char* BMS_uiChargeStatus[] = {"DISCONNECTED", "NO_POWER", "ABOUT_TO_CHARGE",
|
||||
"CHARGING", "CHARGE_COMPLETE", "CHARGE_STOPPED"};
|
||||
static const char* PCS_dcdcStatus[] = {"IDLE", "ACTIVE", "FAULTED"};
|
||||
static const char* PCS_dcdcMainState[] = {"STANDBY", "12V_SUPPORT_ACTIVE", "PRECHARGE_STARTUP",
|
||||
"PRECHARGE_ACTIVE", "DIS_HVBUS_ACTIVE", "SHUTDOWN",
|
||||
"FAULTED"};
|
||||
static const char* PCS_dcdcSubState[] = {"PWR_UP_INIT",
|
||||
"STANDBY",
|
||||
"12V_SUPPORT_ACTIVE",
|
||||
"DIS_HVBUS",
|
||||
"PCHG_FAST_DIS_HVBUS",
|
||||
"PCHG_SLOW_DIS_HVBUS",
|
||||
"PCHG_DWELL_CHARGE",
|
||||
"PCHG_DWELL_WAIT",
|
||||
"PCHG_DI_RECOVERY_WAIT",
|
||||
"PCHG_ACTIVE",
|
||||
"PCHG_FLT_FAST_DIS_HVBUS",
|
||||
"SHUTDOWN",
|
||||
"12V_SUPPORT_FAULTED",
|
||||
"DIS_HVBUS_FAULTED",
|
||||
"PCHG_FAULTED",
|
||||
"CLEAR_FAULTS",
|
||||
"FAULTED",
|
||||
"NUM"};
|
||||
static const char* BMS_powerLimitState[] = {"NOT_CALCULATED_FOR_DRIVE", "CALCULATED_FOR_DRIVE"};
|
||||
static const char* HVP_status[] = {"INVALID", "NOT_AVAILABLE", "STALE", "VALID"};
|
||||
static const char* HVP_contactor[] = {"NOT_ACTIVE", "ACTIVE", "COMPLETED"};
|
||||
static const char* falseTrue[] = {"False", "True"};
|
||||
static const char* noYes[] = {"No", "Yes"};
|
||||
static const char* Fault[] = {"NOT_ACTIVE", "ACTIVE"};
|
||||
|
||||
//0x20A 522 HVP_contatorState
|
||||
content += "<h4>Contactor Status: " + String(contactorText[datalayer_extended.tesla.status_contactor]) + "</h4>";
|
||||
content += "<h4>HVIL: " + String(hvilStatusState[datalayer_extended.tesla.hvil_status]) + "</h4>";
|
||||
content +=
|
||||
"<h4>Negative contactor: " + String(contactorState[datalayer_extended.tesla.packContNegativeState]) + "</h4>";
|
||||
content +=
|
||||
"<h4>Positive contactor: " + String(contactorState[datalayer_extended.tesla.packContPositiveState]) + "</h4>";
|
||||
content += "<h4>Closing allowed?: " + String(noYes[datalayer_extended.tesla.packCtrsClosingAllowed]) + "</h4>";
|
||||
content += "<h4>Pyrotest in Progress: " + String(noYes[datalayer_extended.tesla.pyroTestInProgress]) + "</h4>";
|
||||
content += "<h4>Contactors Open Now Requested: " +
|
||||
String(noYes[datalayer_extended.tesla.battery_packCtrsOpenNowRequested]) + "</h4>";
|
||||
content +=
|
||||
"<h4>Contactors Open Requested: " + String(noYes[datalayer_extended.tesla.battery_packCtrsOpenRequested]) +
|
||||
"</h4>";
|
||||
content += "<h4>Contactors Request Status: " +
|
||||
String(HVP_contactor[datalayer_extended.tesla.battery_packCtrsRequestStatus]) + "</h4>";
|
||||
content += "<h4>Contactors Reset Request Required: " +
|
||||
String(noYes[datalayer_extended.tesla.battery_packCtrsResetRequestRequired]) + "</h4>";
|
||||
content +=
|
||||
"<h4>DC Link Allowed to Energize: " + String(noYes[datalayer_extended.tesla.battery_dcLinkAllowedToEnergize]) +
|
||||
"</h4>";
|
||||
char readableSerialNumber[15]; // One extra space for null terminator
|
||||
memcpy(readableSerialNumber, datalayer_extended.tesla.BMS_SerialNumber,
|
||||
sizeof(datalayer_extended.tesla.BMS_SerialNumber));
|
||||
readableSerialNumber[14] = '\0'; // Null terminate the string
|
||||
content += "<h4>BMS Serial number: " + String(readableSerialNumber) + "</h4>";
|
||||
// Comment what data you would like to display, order can be changed.
|
||||
//0x352 850 BMS_energyStatus
|
||||
if (datalayer_extended.tesla.BMS352_mux == false) {
|
||||
content += "<h3>BMS 0x352 w/o mux</h3>"; //if using older BMS <2021 and comment 0x352 without MUX
|
||||
content += "<h4>Calculated SOH: " + String(nominal_full_pack_energy * 100 / beginning_of_life) + "</h4>";
|
||||
content += "<h4>Nominal Full Pack Energy: " + String(nominal_full_pack_energy) + " KWh</h4>";
|
||||
content += "<h4>Nominal Energy Remaining: " + String(nominal_energy_remaining) + " KWh</h4>";
|
||||
content += "<h4>Ideal Energy Remaining: " + String(ideal_energy_remaining) + " KWh</h4>";
|
||||
content += "<h4>Energy to Charge Complete: " + String(energy_to_charge_complete) + " KWh</h4>";
|
||||
content += "<h4>Energy Buffer: " + String(energy_buffer) + " KWh</h4>";
|
||||
content += "<h4>Full Charge Complete: " + String(noYes[datalayer_extended.tesla.battery_full_charge_complete]) +
|
||||
"</h4>"; //bool
|
||||
}
|
||||
//0x352 850 BMS_energyStatus
|
||||
if (datalayer_extended.tesla.BMS352_mux == true) {
|
||||
content += "<h3>BMS 0x352 w/ mux</h3>"; //if using newer BMS >2021 and comment 0x352 with MUX
|
||||
content += "<h4>Calculated SOH: " + String(nominal_full_pack_energy_m0 * 100 / beginning_of_life) + "</h4>";
|
||||
content += "<h4>Nominal Full Pack Energy: " + String(nominal_full_pack_energy_m0) + " KWh</h4>";
|
||||
content += "<h4>Nominal Energy Remaining: " + String(nominal_energy_remaining_m0) + " KWh</h4>";
|
||||
content += "<h4>Ideal Energy Remaining: " + String(ideal_energy_remaining_m0) + " KWh</h4>";
|
||||
content += "<h4>Energy to Charge Complete: " + String(energy_to_charge_complete_m1) + " KWh</h4>";
|
||||
content += "<h4>Energy Buffer: " + String(energy_buffer_m1) + " KWh</h4>";
|
||||
content += "<h4>Expected Energy Remaining: " + String(expected_energy_remaining_m1) + " KWh</h4>";
|
||||
content += "<h4>Fully Charged: " + String(noYes[datalayer_extended.tesla.battery_fully_charged]) + "</h4>";
|
||||
}
|
||||
//0x3D2 978 BMS_kwhCounter
|
||||
content += "<h4>Total Discharge: " + String(total_discharge) + " KWh</h4>";
|
||||
content += "<h4>Total Charge: " + String(total_charge) + " KWh</h4>";
|
||||
//0x292 658 BMS_socStates
|
||||
content += "<h4>Battery Beginning of Life: " + String(beginning_of_life) + " KWh</h4>";
|
||||
content += "<h4>Battery SOC UI: " + String(soc_ui) + " </h4>";
|
||||
content += "<h4>Battery SOC Ave: " + String(soc_ave) + " </h4>";
|
||||
content += "<h4>Battery SOC Max: " + String(soc_max) + " </h4>";
|
||||
content += "<h4>Battery SOC Min: " + String(soc_min) + " </h4>";
|
||||
content += "<h4>Battery Temp Percent: " + String(battTempPct) + " </h4>";
|
||||
//0x2B4 PCS_dcdcRailStatus
|
||||
content += "<h4>PCS Lv Output: " + String(dcdcLvOutputCurrent) + " A</h4>";
|
||||
content += "<h4>PCS Lv Bus: " + String(dcdcLvBusVolt) + " V</h4>";
|
||||
content += "<h4>PCS Hv Bus: " + String(dcdcHvBusVolt) + " V</h4>";
|
||||
//0x392 BMS_packConfig
|
||||
//content += "<h4>packConfigMultiplexer: " + String(datalayer_extended.tesla.battery_packConfigMultiplexer) + "</h4>"; // Not giving useable data
|
||||
//content += "<h4>moduleType: " + String(datalayer_extended.tesla.battery_moduleType) + "</h4>"; // Not giving useable data
|
||||
//content += "<h4>reserveConfig: " + String(datalayer_extended.tesla.battery_reservedConfig) + "</h4>"; // Not giving useable data
|
||||
content += "<h4>Battery Pack Mass: " + String(packMass) + " KG</h4>";
|
||||
content += "<h4>Platform Max Bus Voltage: " + String(platformMaxBusVoltage) + " V</h4>";
|
||||
//0x2D2 722 BMSVAlimits
|
||||
content += "<h4>BMS Min Voltage: " + String(bms_min_voltage) + " V</h4>";
|
||||
content += "<h4>BMS Max Voltage: " + String(bms_max_voltage) + " V</h4>";
|
||||
content += "<h4>Max Charge Current: " + String(max_charge_current) + " A</h4>";
|
||||
content += "<h4>Max Discharge Current: " + String(max_discharge_current) + " A</h4>";
|
||||
//0x332 818 BMS_bmbMinMax
|
||||
content += "<h4>Brick Voltage Max: " + String(BrickVoltageMax) + " V</h4>";
|
||||
content += "<h4>Brick Voltage Min: " + String(BrickVoltageMin) + " V</h4>";
|
||||
content += "<h4>Brick Temp Max Num: " + String(datalayer_extended.tesla.battery_BrickTempMaxNum) + " </h4>";
|
||||
content += "<h4>Brick Temp Min Num: " + String(datalayer_extended.tesla.battery_BrickTempMinNum) + " </h4>";
|
||||
//content += "<h4>Brick Model Temp Max: " + String(BrickModelTMax) + " C</h4>";// Not giving useable data
|
||||
//content += "<h4>Brick Model Temp Min: " + String(BrickModelTMin) + " C</h4>";// Not giving useable data
|
||||
//0x2A4 676 PCS_thermalStatus
|
||||
content += "<h4>PCS dcdc Temp: " + String(PCS_dcdcTemp) + " DegC</h4>";
|
||||
content += "<h4>PCS Ambient Temp: " + String(PCS_ambientTemp) + " DegC</h4>";
|
||||
content += "<h4>PCS Chg PhA Temp: " + String(PCS_chgPhATemp) + " DegC</h4>";
|
||||
content += "<h4>PCS Chg PhB Temp: " + String(PCS_chgPhBTemp) + " DegC</h4>";
|
||||
content += "<h4>PCS Chg PhC Temp: " + String(PCS_chgPhCTemp) + " DegC</h4>";
|
||||
//0x252 594 BMS_powerAvailable
|
||||
content += "<h4>Max Regen Power: " + String(BMS_maxRegenPower) + " KW</h4>";
|
||||
content += "<h4>Max Discharge Power: " + String(BMS_maxDischargePower) + " KW</h4>";
|
||||
//content += "<h4>Max Stationary Heat Power: " + String(BMS_maxStationaryHeatPower) + " KWh</h4>"; // Not giving useable data
|
||||
//content += "<h4>HVAC Power Budget: " + String(BMS_hvacPowerBudget) + " KW</h4>"; // Not giving useable data
|
||||
//content += "<h4>Not Enough Power For Heat Pump: " + String(noYes[datalayer_extended.tesla.BMS_notEnoughPowerForHeatPump]) + "</h4>"; // Not giving useable data
|
||||
content +=
|
||||
"<h4>Power Limit State: " + String(BMS_powerLimitState[datalayer_extended.tesla.BMS_powerLimitState]) + "</h4>";
|
||||
//content += "<h4>Inverter TQF: " + String(datalayer_extended.tesla.BMS_inverterTQF) + "</h4>"; // Not giving useable data
|
||||
//0x212 530 BMS_status
|
||||
content += "<h4>Isolation Resistance: " + String(isolationResistance) + " kOhms</h4>";
|
||||
content +=
|
||||
"<h4>BMS Contactor State: " + String(BMS_contactorState[datalayer_extended.tesla.battery_BMS_contactorState]) +
|
||||
"</h4>";
|
||||
content += "<h4>BMS State: " + String(BMS_state[datalayer_extended.tesla.battery_BMS_state]) + "</h4>";
|
||||
content += "<h4>BMS HV State: " + String(BMS_hvState[datalayer_extended.tesla.battery_BMS_hvState]) + "</h4>";
|
||||
content += "<h4>BMS UI Charge Status: " + String(BMS_uiChargeStatus[datalayer_extended.tesla.battery_BMS_hvState]) +
|
||||
"</h4>";
|
||||
content +=
|
||||
"<h4>BMS PCS PWM Enabled: " + String(Fault[datalayer_extended.tesla.battery_BMS_pcsPwmEnabled]) + "</h4>";
|
||||
//0x312 786 BMS_thermalStatus
|
||||
content += "<h4>Power Dissipation: " + String(BMS_powerDissipation) + " kW</h4>";
|
||||
content += "<h4>Flow Request: " + String(BMS_flowRequest) + " LPM</h4>";
|
||||
content += "<h4>Inlet Active Cool Target Temp: " + String(BMS_inletActiveCoolTargetT) + " DegC</h4>";
|
||||
content += "<h4>Inlet Passive Target Temp: " + String(BMS_inletPassiveTargetT) + " DegC</h4>";
|
||||
content += "<h4>Inlet Active Heat Target Temp: " + String(BMS_inletActiveHeatTargetT) + " DegC</h4>";
|
||||
content += "<h4>Pack Temp Min: " + String(BMS_packTMin) + " DegC</h4>";
|
||||
content += "<h4>Pack Temp Max: " + String(BMS_packTMax) + " DegC</h4>";
|
||||
content += "<h4>PCS No Flow Request: " + String(Fault[datalayer_extended.tesla.BMS_pcsNoFlowRequest]) + "</h4>";
|
||||
content += "<h4>BMS No Flow Request: " + String(Fault[datalayer_extended.tesla.BMS_noFlowRequest]) + "</h4>";
|
||||
//0x224 548 PCS_dcdcStatus
|
||||
content +=
|
||||
"<h4>Precharge Status: " + String(PCS_dcdcStatus[datalayer_extended.tesla.battery_PCS_dcdcPrechargeStatus]) +
|
||||
"</h4>";
|
||||
content +=
|
||||
"<h4>12V Support Status: " + String(PCS_dcdcStatus[datalayer_extended.tesla.battery_PCS_dcdc12VSupportStatus]) +
|
||||
"</h4>";
|
||||
content += "<h4>HV Bus Discharge Status: " +
|
||||
String(PCS_dcdcStatus[datalayer_extended.tesla.battery_PCS_dcdcHvBusDischargeStatus]) + "</h4>";
|
||||
content +=
|
||||
"<h4>Main State: " + String(PCS_dcdcMainState[datalayer_extended.tesla.battery_PCS_dcdcMainState]) + "</h4>";
|
||||
content +=
|
||||
"<h4>Sub State: " + String(PCS_dcdcSubState[datalayer_extended.tesla.battery_PCS_dcdcSubState]) + "</h4>";
|
||||
content += "<h4>PCS Faulted: " + String(Fault[datalayer_extended.tesla.battery_PCS_dcdcFaulted]) + "</h4>";
|
||||
content +=
|
||||
"<h4>Output Is Limited: " + String(Fault[datalayer_extended.tesla.battery_PCS_dcdcOutputIsLimited]) + "</h4>";
|
||||
content += "<h4>Max Output Current Allowed: " + String(PCS_dcdcMaxOutputCurrentAllowed) + " A</h4>";
|
||||
content += "<h4>Precharge Rty Cnt: " + String(falseTrue[datalayer_extended.tesla.battery_PCS_dcdcPrechargeRtyCnt]) +
|
||||
"</h4>";
|
||||
content +=
|
||||
"<h4>12V Support Rty Cnt: " + String(falseTrue[datalayer_extended.tesla.battery_PCS_dcdc12VSupportRtyCnt]) +
|
||||
"</h4>";
|
||||
content += "<h4>Discharge Rty Cnt: " + String(falseTrue[datalayer_extended.tesla.battery_PCS_dcdcDischargeRtyCnt]) +
|
||||
"</h4>";
|
||||
content +=
|
||||
"<h4>PWM Enable Line: " + String(Fault[datalayer_extended.tesla.battery_PCS_dcdcPwmEnableLine]) + "</h4>";
|
||||
content += "<h4>Supporting Fixed LV Target: " +
|
||||
String(Fault[datalayer_extended.tesla.battery_PCS_dcdcSupportingFixedLvTarget]) + "</h4>";
|
||||
content += "<h4>Precharge Restart Cnt: " +
|
||||
String(falseTrue[datalayer_extended.tesla.battery_PCS_dcdcPrechargeRestartCnt]) + "</h4>";
|
||||
content += "<h4>Initial Precharge Substate: " +
|
||||
String(PCS_dcdcSubState[datalayer_extended.tesla.battery_PCS_dcdcInitialPrechargeSubState]) + "</h4>";
|
||||
//0x2C4 708 PCS_logging
|
||||
content += "<h4>PCS_dcdcMaxLvOutputCurrent: " + String(PCS_dcdcMaxLvOutputCurrent) + " A</h4>";
|
||||
content += "<h4>PCS_dcdcCurrentLimit: " + String(PCS_dcdcCurrentLimit) + " A</h4>";
|
||||
content += "<h4>PCS_dcdcLvOutputCurrentTempLimit: " + String(PCS_dcdcLvOutputCurrentTempLimit) + " A</h4>";
|
||||
content += "<h4>PCS_dcdcUnifiedCommand: " + String(PCS_dcdcUnifiedCommand) + "</h4>";
|
||||
content += "<h4>PCS_dcdcCLAControllerOutput: " + String(PCS_dcdcCLAControllerOutput) + "</h4>";
|
||||
content += "<h4>PCS_dcdcTankVoltage: " + String(PCS_dcdcTankVoltage) + " V</h4>";
|
||||
content += "<h4>PCS_dcdcTankVoltageTarget: " + String(PCS_dcdcTankVoltageTarget) + " V</h4>";
|
||||
content += "<h4>PCS_dcdcClaCurrentFreq: " + String(PCS_dcdcClaCurrentFreq) + " kHz</h4>";
|
||||
content += "<h4>PCS_dcdcTCommMeasured: " + String(PCS_dcdcTCommMeasured) + " us</h4>";
|
||||
content += "<h4>PCS_dcdcShortTimeUs: " + String(PCS_dcdcShortTimeUs) + " us</h4>";
|
||||
content += "<h4>PCS_dcdcHalfPeriodUs: " + String(PCS_dcdcHalfPeriodUs) + " us</h4>";
|
||||
content += "<h4>PCS_dcdcIntervalMaxFrequency: " + String(PCS_dcdcIntervalMaxFrequency) + " kHz</h4>";
|
||||
content += "<h4>PCS_dcdcIntervalMaxHvBusVolt: " + String(PCS_dcdcIntervalMaxHvBusVolt) + " V</h4>";
|
||||
content += "<h4>PCS_dcdcIntervalMaxLvBusVolt: " + String(PCS_dcdcIntervalMaxLvBusVolt) + " V</h4>";
|
||||
content += "<h4>PCS_dcdcIntervalMaxLvOutputCurr: " + String(PCS_dcdcIntervalMaxLvOutputCurr) + " A</h4>";
|
||||
content += "<h4>PCS_dcdcIntervalMinFrequency: " + String(PCS_dcdcIntervalMinFrequency) + " kHz</h4>";
|
||||
content += "<h4>PCS_dcdcIntervalMinHvBusVolt: " + String(PCS_dcdcIntervalMinHvBusVolt) + " V</h4>";
|
||||
content += "<h4>PCS_dcdcIntervalMinLvBusVolt: " + String(PCS_dcdcIntervalMinLvBusVolt) + " V</h4>";
|
||||
content += "<h4>PCS_dcdcIntervalMinLvOutputCurr: " + String(PCS_dcdcIntervalMinLvOutputCurr) + " A</h4>";
|
||||
content += "<h4>PCS_dcdc12vSupportLifetimekWh: " + String(PCS_dcdc12vSupportLifetimekWh) + " kWh</h4>";
|
||||
//0x7AA 1962 HVP_debugMessage
|
||||
content += "<h4>HVP_battery12V: " + String(HVP_battery12V) + " V</h4>";
|
||||
content += "<h4>HVP_dcLinkVoltage: " + String(HVP_dcLinkVoltage) + " V</h4>";
|
||||
content += "<h4>HVP_packVoltage: " + String(HVP_packVoltage) + " V</h4>";
|
||||
content += "<h4>HVP_packContVoltage: " + String(HVP_packContVoltage) + " V</h4>";
|
||||
content += "<h4>HVP_packContCoilCurrent: " + String(HVP_packContCoilCurrent) + " A</h4>";
|
||||
content += "<h4>HVP_pyroAnalog: " + String(HVP_pyroAnalog) + " V</h4>";
|
||||
content += "<h4>HVP_hvp1v5Ref: " + String(HVP_hvp1v5Ref) + " V</h4>";
|
||||
content += "<h4>HVP_hvilInVoltage: " + String(HVP_hvilInVoltage) + " V</h4>";
|
||||
content += "<h4>HVP_hvilOutVoltage: " + String(HVP_hvilOutVoltage) + " V</h4>";
|
||||
content +=
|
||||
"<h4>HVP_gpioPassivePyroDepl: " + String(Fault[datalayer_extended.tesla.HVP_gpioPassivePyroDepl]) + "</h4>";
|
||||
content += "<h4>HVP_gpioPyroIsoEn: " + String(Fault[datalayer_extended.tesla.HVP_gpioPyroIsoEn]) + "</h4>";
|
||||
content += "<h4>HVP_gpioCpFaultIn: " + String(Fault[datalayer_extended.tesla.HVP_gpioCpFaultIn]) + "</h4>";
|
||||
content +=
|
||||
"<h4>HVP_gpioPackContPowerEn: " + String(Fault[datalayer_extended.tesla.HVP_gpioPackContPowerEn]) + "</h4>";
|
||||
content += "<h4>HVP_gpioHvCablesOk: " + String(Fault[datalayer_extended.tesla.HVP_gpioHvCablesOk]) + "</h4>";
|
||||
content += "<h4>HVP_gpioHvpSelfEnable: " + String(Fault[datalayer_extended.tesla.HVP_gpioHvpSelfEnable]) + "</h4>";
|
||||
content += "<h4>HVP_gpioLed: " + String(Fault[datalayer_extended.tesla.HVP_gpioLed]) + "</h4>";
|
||||
content += "<h4>HVP_gpioCrashSignal: " + String(Fault[datalayer_extended.tesla.HVP_gpioCrashSignal]) + "</h4>";
|
||||
content +=
|
||||
"<h4>HVP_gpioShuntDataReady: " + String(Fault[datalayer_extended.tesla.HVP_gpioShuntDataReady]) + "</h4>";
|
||||
content += "<h4>HVP_gpioFcContPosAux: " + String(Fault[datalayer_extended.tesla.HVP_gpioFcContPosAux]) + "</h4>";
|
||||
content += "<h4>HVP_gpioFcContNegAux: " + String(Fault[datalayer_extended.tesla.HVP_gpioFcContNegAux]) + "</h4>";
|
||||
content += "<h4>HVP_gpioBmsEout: " + String(Fault[datalayer_extended.tesla.HVP_gpioBmsEout]) + "</h4>";
|
||||
content += "<h4>HVP_gpioCpFaultOut: " + String(Fault[datalayer_extended.tesla.HVP_gpioCpFaultOut]) + "</h4>";
|
||||
content += "<h4>HVP_gpioPyroPor: " + String(Fault[datalayer_extended.tesla.HVP_gpioPyroPor]) + "</h4>";
|
||||
content += "<h4>HVP_gpioShuntEn: " + String(Fault[datalayer_extended.tesla.HVP_gpioShuntEn]) + "</h4>";
|
||||
content += "<h4>HVP_gpioHvpVerEn: " + String(Fault[datalayer_extended.tesla.HVP_gpioHvpVerEn]) + "</h4>";
|
||||
content +=
|
||||
"<h4>HVP_gpioPackCoontPosFlywheel: " + String(Fault[datalayer_extended.tesla.HVP_gpioPackCoontPosFlywheel]) +
|
||||
"</h4>";
|
||||
content += "<h4>HVP_gpioCpLatchEnable: " + String(Fault[datalayer_extended.tesla.HVP_gpioCpLatchEnable]) + "</h4>";
|
||||
content += "<h4>HVP_gpioPcsEnable: " + String(Fault[datalayer_extended.tesla.HVP_gpioPcsEnable]) + "</h4>";
|
||||
content +=
|
||||
"<h4>HVP_gpioPcsDcdcPwmEnable: " + String(Fault[datalayer_extended.tesla.HVP_gpioPcsDcdcPwmEnable]) + "</h4>";
|
||||
content += "<h4>HVP_gpioPcsChargePwmEnable: " + String(Fault[datalayer_extended.tesla.HVP_gpioPcsChargePwmEnable]) +
|
||||
"</h4>";
|
||||
content +=
|
||||
"<h4>HVP_gpioFcContPowerEnable: " + String(Fault[datalayer_extended.tesla.HVP_gpioFcContPowerEnable]) + "</h4>";
|
||||
content += "<h4>HVP_gpioHvilEnable: " + String(Fault[datalayer_extended.tesla.HVP_gpioHvilEnable]) + "</h4>";
|
||||
content += "<h4>HVP_gpioSecDrdy: " + String(Fault[datalayer_extended.tesla.HVP_gpioSecDrdy]) + "</h4>";
|
||||
content += "<h4>HVP_shuntCurrentDebug: " + String(HVP_shuntCurrentDebug) + " A</h4>";
|
||||
content += "<h4>HVP_packCurrentMia: " + String(noYes[datalayer_extended.tesla.HVP_packCurrentMia]) + "</h4>";
|
||||
content += "<h4>HVP_auxCurrentMia: " + String(noYes[datalayer_extended.tesla.HVP_auxCurrentMia]) + "</h4>";
|
||||
content += "<h4>HVP_currentSenseMia: " + String(noYes[datalayer_extended.tesla.HVP_currentSenseMia]) + "</h4>";
|
||||
content +=
|
||||
"<h4>HVP_shuntRefVoltageMismatch: " + String(noYes[datalayer_extended.tesla.HVP_shuntRefVoltageMismatch]) +
|
||||
"</h4>";
|
||||
content +=
|
||||
"<h4>HVP_shuntThermistorMia: " + String(noYes[datalayer_extended.tesla.HVP_shuntThermistorMia]) + "</h4>";
|
||||
content += "<h4>HVP_shuntHwMia: " + String(noYes[datalayer_extended.tesla.HVP_shuntHwMia]) + "</h4>";
|
||||
//content += "<h4>HVP_fcLinkVoltage: " + String(HVP_fcLinkVoltage) + " V</h4>"; // Not giving useable data
|
||||
//content += "<h4>HVP_packNegativeV: " + String(HVP_packNegativeV) + " V</h4>"; // Not giving useable data
|
||||
//content += "<h4>HVP_packPositiveV: " + String(HVP_packPositiveV) + " V</h4>"; // Not giving useable data
|
||||
//content += "<h4>HVP_dcLinkNegativeV: " + String(HVP_dcLinkNegativeV) + " V</h4>"; // Not giving useable data
|
||||
//content += "<h4>HVP_dcLinkPositiveV: " + String(HVP_dcLinkPositiveV) + " V</h4>"; // Not giving useable data
|
||||
//content += "<h4>HVP_fcLinkNegativeV: " + String(HVP_fcLinkNegativeV) + " V</h4>"; // Not giving useable data
|
||||
//content += "<h4>HVP_fcContCoilCurrent: " + String(HVP_fcContCoilCurrent) + " A</h4>"; // Not giving useable data
|
||||
//content += "<h4>HVP_fcContVoltage: " + String(HVP_fcContVoltage) + " V</h4>"; // Not giving useable data
|
||||
//content += "<h4>HVP_fcLinkPositiveV: " + String(HVP_fcLinkPositiveV) + " V</h4>"; // Not giving useable data
|
||||
//content += "<h4>HVP_shuntRefVoltageDbg: " + String(HVP_shuntRefVoltageDbg) + " V</h4>"; // Not giving useable data
|
||||
//content += "<h4>HVP_shuntAuxCurrentDbg: " + String(HVP_shuntAuxCurrentDbg) + " A</h4>"; // Not giving useable data
|
||||
//content += "<h4>HVP_shuntBarTempDbg: " + String(HVP_shuntBarTempDbg) + " DegC</h4>"; // Not giving useable data
|
||||
//content += "<h4>HVP_shuntAsicTempDbg: " + String(HVP_shuntAsicTempDbg) + " DegC</h4>"; // Not giving useable data
|
||||
//content += "<h4>HVP_shuntAuxCurrentStatus: " + String(HVP_status[datalayer_extended.tesla.HVP_shuntAuxCurrentStatus]) + "</h4>"; // Not giving useable data
|
||||
//content += "<h4>HVP_shuntBarTempStatus: " + String(HVP_status[datalayer_extended.tesla.HVP_shuntBarTempStatus]) + "</h4>"; // Not giving useable data
|
||||
//content += "<h4>HVP_shuntAsicTempStatus: " + String(HVP_status[datalayer_extended.tesla.HVP_shuntAsicTempStatus]) + "</h4>"; // Not giving useable data
|
||||
|
||||
return content;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
|
@ -10,16 +10,14 @@
|
|||
class TestFakeBattery : public CanBattery {
|
||||
public:
|
||||
// Use this constructor for the second battery.
|
||||
TestFakeBattery(DATALAYER_BATTERY_TYPE* datalayer_ptr, int targetCan) {
|
||||
TestFakeBattery(DATALAYER_BATTERY_TYPE* datalayer_ptr, CAN_Interface targetCan) : CanBattery(targetCan) {
|
||||
datalayer_battery = datalayer_ptr;
|
||||
can_interface = targetCan;
|
||||
allows_contactor_closing = nullptr;
|
||||
}
|
||||
|
||||
// Use the default constructor to create the first or single battery.
|
||||
TestFakeBattery() {
|
||||
datalayer_battery = &datalayer.battery;
|
||||
can_interface = can_config.battery;
|
||||
allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing;
|
||||
}
|
||||
|
||||
|
@ -28,9 +26,11 @@ class TestFakeBattery : public CanBattery {
|
|||
virtual void update_values();
|
||||
virtual void transmit_can(unsigned long currentMillis);
|
||||
|
||||
bool supports_set_fake_voltage() { return true; }
|
||||
void set_fake_voltage(float val) { datalayer.battery.status.voltage_dV = val * 10; }
|
||||
|
||||
private:
|
||||
DATALAYER_BATTERY_TYPE* datalayer_battery;
|
||||
int can_interface;
|
||||
// If not null, this battery decides when the contactor can be closed and writes the value here.
|
||||
bool* allows_contactor_closing;
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "../include.h"
|
||||
|
||||
#include "CanBattery.h"
|
||||
#include "VOLVO-SPA-HTML.h"
|
||||
|
||||
#define BATTERY_SELECTED
|
||||
#define SELECTED_BATTERY_CLASS VolvoSpaBattery
|
||||
|
@ -15,6 +16,15 @@ class VolvoSpaBattery : public CanBattery {
|
|||
virtual void update_values();
|
||||
virtual void transmit_can(unsigned long currentMillis);
|
||||
|
||||
bool supports_reset_DTC() { return true; }
|
||||
void reset_DTC() { datalayer_extended.VolvoPolestar.UserRequestDTCreset = true; }
|
||||
|
||||
bool supports_read_DTC() { return true; }
|
||||
void read_DTC() { datalayer_extended.VolvoPolestar.UserRequestDTCreadout = true; }
|
||||
|
||||
bool supports_reset_BECM() { return true; }
|
||||
void reset_BECM() { datalayer_extended.VolvoPolestar.UserRequestBECMecuReset = true; }
|
||||
|
||||
private:
|
||||
void readCellVoltages();
|
||||
|
||||
|
|
108
Software/src/battery/VOLVO-SPA-HTML.h
Normal file
108
Software/src/battery/VOLVO-SPA-HTML.h
Normal file
|
@ -0,0 +1,108 @@
|
|||
#ifndef _VOLVO_SPA_HTML_H
|
||||
#define _VOLVO_SPA_HTML_H
|
||||
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../datalayer/datalayer_extended.h"
|
||||
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
|
||||
|
||||
class VolvoSpaHtmlRenderer : public BatteryHtmlRenderer {
|
||||
public:
|
||||
String get_status_html() {
|
||||
String content;
|
||||
|
||||
content += "<h4>BECM reported SOC: " + String(datalayer_extended.VolvoPolestar.soc_bms) + "</h4>";
|
||||
content += "<h4>Calculated SOC: " + String(datalayer_extended.VolvoPolestar.soc_calc) + "</h4>";
|
||||
content += "<h4>Rescaled SOC: " + String(datalayer_extended.VolvoPolestar.soc_rescaled / 10) + "</h4>";
|
||||
content += "<h4>BECM reported SOH: " + String(datalayer_extended.VolvoPolestar.soh_bms) + "</h4>";
|
||||
content += "<h4>BECM supply voltage: " + String(datalayer_extended.VolvoPolestar.BECMsupplyVoltage) + " mV</h4>";
|
||||
|
||||
content += "<h4>HV voltage: " + String(datalayer_extended.VolvoPolestar.BECMBatteryVoltage) + " V</h4>";
|
||||
content += "<h4>HV current: " + String(datalayer_extended.VolvoPolestar.BECMBatteryCurrent) + " A</h4>";
|
||||
content += "<h4>Dynamic max voltage: " + String(datalayer_extended.VolvoPolestar.BECMUDynMaxLim) + " V</h4>";
|
||||
content += "<h4>Dynamic min voltage: " + String(datalayer_extended.VolvoPolestar.BECMUDynMinLim) + " V</h4>";
|
||||
|
||||
content +=
|
||||
"<h4>Discharge power limit 1: " + String(datalayer_extended.VolvoPolestar.HvBattPwrLimDcha1) + " kW</h4>";
|
||||
content +=
|
||||
"<h4>Discharge soft power limit: " + String(datalayer_extended.VolvoPolestar.HvBattPwrLimDchaSoft) + " kW</h4>";
|
||||
content +=
|
||||
"<h4>Discharge power limit slow aging: " + String(datalayer_extended.VolvoPolestar.HvBattPwrLimDchaSlowAgi) +
|
||||
" kW</h4>";
|
||||
content +=
|
||||
"<h4>Charge power limit slow aging: " + String(datalayer_extended.VolvoPolestar.HvBattPwrLimChrgSlowAgi) +
|
||||
" kW</h4>";
|
||||
|
||||
content += "<h4>HV system relay status: ";
|
||||
switch (datalayer_extended.VolvoPolestar.HVSysRlySts) {
|
||||
case 0:
|
||||
content += String("Open");
|
||||
break;
|
||||
case 1:
|
||||
content += String("Closed");
|
||||
break;
|
||||
case 2:
|
||||
content += String("KeepStatus");
|
||||
break;
|
||||
case 3:
|
||||
content += String("OpenAndRequestActiveDischarge");
|
||||
break;
|
||||
default:
|
||||
content += String("Not valid");
|
||||
}
|
||||
content += "</h4><h4>HV system relay status 1: ";
|
||||
switch (datalayer_extended.VolvoPolestar.HVSysDCRlySts1) {
|
||||
case 0:
|
||||
content += String("Open");
|
||||
break;
|
||||
case 1:
|
||||
content += String("Closed");
|
||||
break;
|
||||
case 2:
|
||||
content += String("KeepStatus");
|
||||
break;
|
||||
case 3:
|
||||
content += String("Fault");
|
||||
break;
|
||||
default:
|
||||
content += String("Not valid");
|
||||
}
|
||||
content += "</h4><h4>HV system relay status 2: ";
|
||||
switch (datalayer_extended.VolvoPolestar.HVSysDCRlySts2) {
|
||||
case 0:
|
||||
content += String("Open");
|
||||
break;
|
||||
case 1:
|
||||
content += String("Closed");
|
||||
break;
|
||||
case 2:
|
||||
content += String("KeepStatus");
|
||||
break;
|
||||
case 3:
|
||||
content += String("Fault");
|
||||
break;
|
||||
default:
|
||||
content += String("Not valid");
|
||||
}
|
||||
content += "</h4><h4>HV system isolation resistance monitoring status: ";
|
||||
switch (datalayer_extended.VolvoPolestar.HVSysIsoRMonrSts) {
|
||||
case 0:
|
||||
content += String("Not valid 1");
|
||||
break;
|
||||
case 1:
|
||||
content += String("False");
|
||||
break;
|
||||
case 2:
|
||||
content += String("True");
|
||||
break;
|
||||
case 3:
|
||||
content += String("Not valid 2");
|
||||
break;
|
||||
default:
|
||||
content += String("Not valid");
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
|
@ -4,6 +4,7 @@
|
|||
#include "../include.h"
|
||||
|
||||
#include "CanBattery.h"
|
||||
#include "VOLVO-SPA-HYBRID-HTML.h"
|
||||
|
||||
#define BATTERY_SELECTED
|
||||
#define SELECTED_BATTERY_CLASS VolvoSpaHybridBattery
|
||||
|
@ -15,7 +16,19 @@ class VolvoSpaHybridBattery : public CanBattery {
|
|||
virtual void update_values();
|
||||
virtual void transmit_can(unsigned long currentMillis);
|
||||
|
||||
bool supports_reset_DTC() { return true; }
|
||||
void reset_DTC() { datalayer_extended.VolvoHybrid.UserRequestDTCreset = true; }
|
||||
|
||||
bool supports_read_DTC() { return true; }
|
||||
void read_DTC() { datalayer_extended.VolvoHybrid.UserRequestDTCreadout = true; }
|
||||
|
||||
bool supports_reset_BECM() { return true; }
|
||||
void reset_BECM() { datalayer_extended.VolvoHybrid.UserRequestBECMecuReset = true; }
|
||||
|
||||
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
|
||||
|
||||
private:
|
||||
VolvoSpaHybridHtmlRenderer renderer;
|
||||
void readCellVoltages();
|
||||
|
||||
static const int MAX_PACK_VOLTAGE_DV = 4294; //5000 = 500.0V
|
||||
|
|
101
Software/src/battery/VOLVO-SPA-HYBRID-HTML.h
Normal file
101
Software/src/battery/VOLVO-SPA-HYBRID-HTML.h
Normal file
|
@ -0,0 +1,101 @@
|
|||
#ifndef _VOLVO_SPA_HYBRID_HTML_H
|
||||
#define _VOLVO_SPA_HYBRID_HTML_H
|
||||
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../datalayer/datalayer_extended.h"
|
||||
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
|
||||
|
||||
class VolvoSpaHybridHtmlRenderer : public BatteryHtmlRenderer {
|
||||
public:
|
||||
String get_status_html() {
|
||||
String content;
|
||||
|
||||
content += "<h4>BECM reported SOC: " + String(datalayer_extended.VolvoHybrid.soc_bms) + "</h4>";
|
||||
content += "<h4>Calculated SOC: " + String(datalayer_extended.VolvoHybrid.soc_calc) + "</h4>";
|
||||
content += "<h4>Rescaled SOC: " + String(datalayer_extended.VolvoHybrid.soc_rescaled / 10) + "</h4>";
|
||||
content += "<h4>BECM reported SOH: " + String(datalayer_extended.VolvoHybrid.soh_bms) + "</h4>";
|
||||
content += "<h4>BECM supply voltage: " + String(datalayer_extended.VolvoHybrid.BECMsupplyVoltage) + " mV</h4>";
|
||||
|
||||
content += "<h4>HV voltage: " + String(datalayer_extended.VolvoHybrid.BECMBatteryVoltage) + " V</h4>";
|
||||
content += "<h4>HV current: " + String(datalayer_extended.VolvoHybrid.BECMBatteryCurrent) + " A</h4>";
|
||||
content += "<h4>Dynamic max voltage: " + String(datalayer_extended.VolvoHybrid.BECMUDynMaxLim) + " V</h4>";
|
||||
content += "<h4>Dynamic min voltage: " + String(datalayer_extended.VolvoHybrid.BECMUDynMinLim) + " V</h4>";
|
||||
|
||||
content += "<h4>Discharge power limit 1: " + String(datalayer_extended.VolvoHybrid.HvBattPwrLimDcha1) + " kW</h4>";
|
||||
content +=
|
||||
"<h4>Discharge soft power limit: " + String(datalayer_extended.VolvoHybrid.HvBattPwrLimDchaSoft) + " kW</h4>";
|
||||
|
||||
content += "<h4>HV system relay status: ";
|
||||
switch (datalayer_extended.VolvoHybrid.HVSysRlySts) {
|
||||
case 0:
|
||||
content += String("Open");
|
||||
break;
|
||||
case 1:
|
||||
content += String("Closed");
|
||||
break;
|
||||
case 2:
|
||||
content += String("KeepStatus");
|
||||
break;
|
||||
case 3:
|
||||
content += String("OpenAndRequestActiveDischarge");
|
||||
break;
|
||||
default:
|
||||
content += String("Not valid");
|
||||
}
|
||||
content += "</h4><h4>HV system relay status 1: ";
|
||||
switch (datalayer_extended.VolvoHybrid.HVSysDCRlySts1) {
|
||||
case 0:
|
||||
content += String("Open");
|
||||
break;
|
||||
case 1:
|
||||
content += String("Closed");
|
||||
break;
|
||||
case 2:
|
||||
content += String("KeepStatus");
|
||||
break;
|
||||
case 3:
|
||||
content += String("Fault");
|
||||
break;
|
||||
default:
|
||||
content += String("Not valid");
|
||||
}
|
||||
content += "</h4><h4>HV system relay status 2: ";
|
||||
switch (datalayer_extended.VolvoHybrid.HVSysDCRlySts2) {
|
||||
case 0:
|
||||
content += String("Open");
|
||||
break;
|
||||
case 1:
|
||||
content += String("Closed");
|
||||
break;
|
||||
case 2:
|
||||
content += String("KeepStatus");
|
||||
break;
|
||||
case 3:
|
||||
content += String("Fault");
|
||||
break;
|
||||
default:
|
||||
content += String("Not valid");
|
||||
}
|
||||
content += "</h4><h4>HV system isolation resistance monitoring status: ";
|
||||
switch (datalayer_extended.VolvoHybrid.HVSysIsoRMonrSts) {
|
||||
case 0:
|
||||
content += String("Not valid 1");
|
||||
break;
|
||||
case 1:
|
||||
content += String("False");
|
||||
break;
|
||||
case 2:
|
||||
content += String("True");
|
||||
break;
|
||||
case 3:
|
||||
content += String("Not valid 2");
|
||||
break;
|
||||
default:
|
||||
content += String("Not valid");
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
|
@ -310,12 +310,12 @@ typedef struct {
|
|||
|
||||
/** True if the inverter allows for the contactors to close */
|
||||
bool inverter_allows_contactor_closing = true;
|
||||
#ifdef CONTACTOR_CONTROL
|
||||
|
||||
/** True if the contactor controlled by battery-emulator is closed */
|
||||
bool contactors_engaged = false;
|
||||
/** True if the contactor controlled by battery-emulator is closed. Determined by check_interconnect_available(); if voltage is OK */
|
||||
bool contactors_battery2_engaged = false;
|
||||
#endif
|
||||
|
||||
/** True if the BMS is being reset, by cutting power towards it */
|
||||
bool BMS_reset_in_progress = false;
|
||||
/** True if the BMS is starting up */
|
||||
|
|
|
@ -241,65 +241,65 @@ void update_machineryprotection() {
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef DOUBLE_BATTERY // Additional Double-Battery safeties are checked here
|
||||
// Check if the Battery 2 BMS is still sending CAN messages. If we go 60s without messages we raise a warning
|
||||
// Additional Double-Battery safeties are checked here
|
||||
if (battery2) {
|
||||
// Check if the Battery 2 BMS is still sending CAN messages. If we go 60s without messages we raise a warning
|
||||
|
||||
// Pause function is on
|
||||
if (emulator_pause_request_ON) {
|
||||
datalayer.battery2.status.max_discharge_power_W = 0;
|
||||
datalayer.battery2.status.max_charge_power_W = 0;
|
||||
}
|
||||
|
||||
if (!datalayer.battery2.status.CAN_battery_still_alive) {
|
||||
set_event(EVENT_CAN_BATTERY2_MISSING, can_config.battery_double);
|
||||
} else {
|
||||
datalayer.battery2.status.CAN_battery_still_alive--;
|
||||
clear_event(EVENT_CAN_BATTERY2_MISSING);
|
||||
}
|
||||
|
||||
// Too many malformed CAN messages recieved!
|
||||
if (datalayer.battery2.status.CAN_error_counter > MAX_CAN_FAILURES) {
|
||||
set_event(EVENT_CAN_CORRUPTED_WARNING, can_config.battery_double);
|
||||
} else {
|
||||
clear_event(EVENT_CAN_CORRUPTED_WARNING);
|
||||
}
|
||||
|
||||
// Cell overvoltage, critical latching error without automatic reset. Requires user action.
|
||||
if (datalayer.battery2.status.cell_max_voltage_mV >= datalayer.battery2.info.max_cell_voltage_mV) {
|
||||
set_event(EVENT_CELL_OVER_VOLTAGE, 0);
|
||||
}
|
||||
// Cell undervoltage, critical latching error without automatic reset. Requires user action.
|
||||
if (datalayer.battery2.status.cell_min_voltage_mV <= datalayer.battery2.info.min_cell_voltage_mV) {
|
||||
set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
|
||||
}
|
||||
|
||||
// Check diff between highest and lowest cell
|
||||
cell_deviation_mV = (datalayer.battery2.status.cell_max_voltage_mV - datalayer.battery2.status.cell_min_voltage_mV);
|
||||
if (cell_deviation_mV > datalayer.battery2.info.max_cell_voltage_deviation_mV) {
|
||||
set_event(EVENT_CELL_DEVIATION_HIGH, (cell_deviation_mV / 20));
|
||||
} else {
|
||||
clear_event(EVENT_CELL_DEVIATION_HIGH);
|
||||
}
|
||||
|
||||
// Check if SOH% between the packs is too large
|
||||
if ((datalayer.battery.status.soh_pptt != 9900) && (datalayer.battery2.status.soh_pptt != 9900)) {
|
||||
// Both values available, check diff
|
||||
uint16_t soh_diff_pptt;
|
||||
if (datalayer.battery.status.soh_pptt > datalayer.battery2.status.soh_pptt) {
|
||||
soh_diff_pptt = datalayer.battery.status.soh_pptt - datalayer.battery2.status.soh_pptt;
|
||||
} else {
|
||||
soh_diff_pptt = datalayer.battery2.status.soh_pptt - datalayer.battery.status.soh_pptt;
|
||||
// Pause function is on
|
||||
if (emulator_pause_request_ON) {
|
||||
datalayer.battery2.status.max_discharge_power_W = 0;
|
||||
datalayer.battery2.status.max_charge_power_W = 0;
|
||||
}
|
||||
|
||||
if (soh_diff_pptt > MAX_SOH_DEVIATION_PPTT) {
|
||||
set_event(EVENT_SOH_DIFFERENCE, (uint8_t)(MAX_SOH_DEVIATION_PPTT / 100));
|
||||
if (!datalayer.battery2.status.CAN_battery_still_alive) {
|
||||
set_event(EVENT_CAN_BATTERY2_MISSING, can_config.battery_double);
|
||||
} else {
|
||||
clear_event(EVENT_SOH_DIFFERENCE);
|
||||
datalayer.battery2.status.CAN_battery_still_alive--;
|
||||
clear_event(EVENT_CAN_BATTERY2_MISSING);
|
||||
}
|
||||
|
||||
// Too many malformed CAN messages recieved!
|
||||
if (datalayer.battery2.status.CAN_error_counter > MAX_CAN_FAILURES) {
|
||||
set_event(EVENT_CAN_CORRUPTED_WARNING, can_config.battery_double);
|
||||
} else {
|
||||
clear_event(EVENT_CAN_CORRUPTED_WARNING);
|
||||
}
|
||||
|
||||
// Cell overvoltage, critical latching error without automatic reset. Requires user action.
|
||||
if (datalayer.battery2.status.cell_max_voltage_mV >= datalayer.battery2.info.max_cell_voltage_mV) {
|
||||
set_event(EVENT_CELL_OVER_VOLTAGE, 0);
|
||||
}
|
||||
// Cell undervoltage, critical latching error without automatic reset. Requires user action.
|
||||
if (datalayer.battery2.status.cell_min_voltage_mV <= datalayer.battery2.info.min_cell_voltage_mV) {
|
||||
set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
|
||||
}
|
||||
|
||||
// Check diff between highest and lowest cell
|
||||
cell_deviation_mV = (datalayer.battery2.status.cell_max_voltage_mV - datalayer.battery2.status.cell_min_voltage_mV);
|
||||
if (cell_deviation_mV > datalayer.battery2.info.max_cell_voltage_deviation_mV) {
|
||||
set_event(EVENT_CELL_DEVIATION_HIGH, (cell_deviation_mV / 20));
|
||||
} else {
|
||||
clear_event(EVENT_CELL_DEVIATION_HIGH);
|
||||
}
|
||||
|
||||
// Check if SOH% between the packs is too large
|
||||
if ((datalayer.battery.status.soh_pptt != 9900) && (datalayer.battery2.status.soh_pptt != 9900)) {
|
||||
// Both values available, check diff
|
||||
uint16_t soh_diff_pptt;
|
||||
if (datalayer.battery.status.soh_pptt > datalayer.battery2.status.soh_pptt) {
|
||||
soh_diff_pptt = datalayer.battery.status.soh_pptt - datalayer.battery2.status.soh_pptt;
|
||||
} else {
|
||||
soh_diff_pptt = datalayer.battery2.status.soh_pptt - datalayer.battery.status.soh_pptt;
|
||||
}
|
||||
|
||||
if (soh_diff_pptt > MAX_SOH_DEVIATION_PPTT) {
|
||||
set_event(EVENT_SOH_DIFFERENCE, (uint8_t)(MAX_SOH_DEVIATION_PPTT / 100));
|
||||
} else {
|
||||
clear_event(EVENT_SOH_DIFFERENCE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // DOUBLE_BATTERY
|
||||
|
||||
//Safeties verified, Zero charge/discharge ampere values incase any safety wrote the W to 0
|
||||
if (datalayer.battery.status.max_discharge_power_W == 0) {
|
||||
datalayer.battery.status.max_discharge_current_dA = 0;
|
||||
|
@ -357,10 +357,10 @@ void setBatteryPause(bool pause_battery, bool pause_CAN, bool equipment_stop, bo
|
|||
emulator_pause_status = PAUSING;
|
||||
datalayer.battery.status.max_discharge_power_W = 0;
|
||||
datalayer.battery.status.max_charge_power_W = 0;
|
||||
#ifdef DOUBLE_BATTERY
|
||||
datalayer.battery2.status.max_discharge_power_W = 0;
|
||||
datalayer.battery2.status.max_charge_power_W = 0;
|
||||
#endif
|
||||
if (battery2) {
|
||||
datalayer.battery2.status.max_discharge_power_W = 0;
|
||||
datalayer.battery2.status.max_charge_power_W = 0;
|
||||
}
|
||||
|
||||
} else {
|
||||
clear_event(EVENT_PAUSE_BEGIN);
|
||||
|
|
18
Software/src/devboard/webserver/BatteryHtmlRenderer.h
Normal file
18
Software/src/devboard/webserver/BatteryHtmlRenderer.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#ifndef _BATTERY_HTML_RENDERER_H
|
||||
#define _BATTERY_HTML_RENDERER_H
|
||||
|
||||
#include <WString.h>
|
||||
|
||||
// Each battery can implement this interface to render more battery specific HTML
|
||||
// content
|
||||
class BatteryHtmlRenderer {
|
||||
public:
|
||||
virtual String get_status_html() = 0;
|
||||
};
|
||||
|
||||
class BatteryDefaultRenderer : public BatteryHtmlRenderer {
|
||||
public:
|
||||
String get_status_html() { return String("No extra information available for this battery type"); }
|
||||
};
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load diff
|
@ -13,4 +13,28 @@
|
|||
*/
|
||||
String advanced_battery_processor(const String& var);
|
||||
|
||||
class Battery;
|
||||
|
||||
// Each BatteryCommand defines a command that can be performed by a battery.
|
||||
// Whether the selected battery supports the command is determined at run-time
|
||||
// by calling the condition callback.
|
||||
struct BatteryCommand {
|
||||
// The unique name of the route in the API to execute the command or a function in Javascript
|
||||
const char* identifier;
|
||||
|
||||
// Display name for the command. Can be used in the UI.
|
||||
const char* title;
|
||||
|
||||
// Are you sure? prompt text. If null, no confirmation is asked.
|
||||
const char* prompt;
|
||||
|
||||
// Function to determine whether the given battery supports this command.
|
||||
std::function<bool(Battery*)> condition;
|
||||
|
||||
// Function that executes the command for the given battery.
|
||||
std::function<void(Battery*)> action;
|
||||
};
|
||||
|
||||
extern std::vector<BatteryCommand> battery_commands;
|
||||
|
||||
#endif
|
||||
|
|
|
@ -16,21 +16,24 @@ String cellmonitor_processor(const String& var) {
|
|||
content += ".cell { width: 48%; margin: 1%; padding: 10px; border: 1px solid white; text-align: center; }";
|
||||
content += ".low-voltage { color: red; }"; // Style for low voltage text
|
||||
content += ".voltage-values { margin-bottom: 10px; }"; // Style for voltage values section
|
||||
#ifdef DOUBLE_BATTERY
|
||||
content +=
|
||||
"#graph, #graph2 {display: flex;align-items: flex-end;height: 200px;border: 1px solid #ccc;position: "
|
||||
"relative;}";
|
||||
#else
|
||||
content += "#graph {display: flex;align-items: flex-end;height: 200px;border: 1px solid #ccc;position: relative;}";
|
||||
#endif
|
||||
|
||||
if (battery2) {
|
||||
content +=
|
||||
"#graph, #graph2 {display: flex;align-items: flex-end;height: 200px;border: 1px solid #ccc;position: "
|
||||
"relative;}";
|
||||
} else {
|
||||
content +=
|
||||
"#graph {display: flex;align-items: flex-end;height: 200px;border: 1px solid #ccc;position: relative;}";
|
||||
}
|
||||
content +=
|
||||
".bar {margin: 0 0px;background-color: blue;display: inline-block;position: relative;cursor: pointer;border: "
|
||||
"1px solid white; /* Add this line */}";
|
||||
#ifdef DOUBLE_BATTERY
|
||||
content += "#valueDisplay, #valueDisplay2 {text-align: left;font-weight: bold;margin-top: 10px;}";
|
||||
#else
|
||||
content += "#valueDisplay {text-align: left;font-weight: bold;margin-top: 10px;}";
|
||||
#endif
|
||||
|
||||
if (battery2) {
|
||||
content += "#valueDisplay, #valueDisplay2 {text-align: left;font-weight: bold;margin-top: 10px;}";
|
||||
} else {
|
||||
content += "#valueDisplay {text-align: left;font-weight: bold;margin-top: 10px;}";
|
||||
}
|
||||
content += "</style>";
|
||||
|
||||
content += "<button onclick='home()'>Back to main page</button>";
|
||||
|
@ -68,43 +71,43 @@ String cellmonitor_processor(const String& var) {
|
|||
// Close the block
|
||||
content += "</div>";
|
||||
|
||||
#ifdef DOUBLE_BATTERY
|
||||
// Start a new block with a specific background color
|
||||
content += "<div style='background-color: #303E41; padding: 10px; margin-bottom: 10px; border-radius: 50px'>";
|
||||
if (battery2) {
|
||||
// Start a new block with a specific background color
|
||||
content += "<div style='background-color: #303E41; padding: 10px; margin-bottom: 10px; border-radius: 50px'>";
|
||||
|
||||
// Display max, min, and deviation voltage values
|
||||
content += "<div id='voltageValues2' class='voltage-values'></div>";
|
||||
// Display cells
|
||||
content += "<div id='cellContainer2' class='container'></div>";
|
||||
// Display bars
|
||||
content += "<div id='graph2'></div>";
|
||||
// Display single hovered value
|
||||
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) {
|
||||
// Display max, min, and deviation voltage values
|
||||
content += "<div id='voltageValues2' class='voltage-values'></div>";
|
||||
// Display cells
|
||||
content += "<div id='cellContainer2' class='container'></div>";
|
||||
// Display bars
|
||||
content += "<div id='graph2'></div>";
|
||||
// Display single hovered value
|
||||
content += "<div id='valueDisplay2'>Value: ...</div>";
|
||||
//Legend for graph
|
||||
content +=
|
||||
"<span style='color: black; background-color: #00FFFF; font-weight: bold; padding: 2px 8px; border-radius: "
|
||||
"4px; margin-right: 15px;'>Balancing</span>";
|
||||
"<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
|
||||
content += "</div>";
|
||||
|
||||
content += "<button onclick='home()'>Back to main page</button>";
|
||||
}
|
||||
content +=
|
||||
"<span style='color: white; background-color: red; font-weight: bold; padding: 2px 8px; border-radius: "
|
||||
"4px;'>Min/Max</span>";
|
||||
|
||||
// Close the block
|
||||
content += "</div>";
|
||||
|
||||
content += "<button onclick='home()'>Back to main page</button>";
|
||||
#endif // DOUBLE_BATTERY
|
||||
|
||||
content += "<script>";
|
||||
// Populate cell data
|
||||
|
@ -237,136 +240,136 @@ String cellmonitor_processor(const String& var) {
|
|||
"available';";
|
||||
content += "}";
|
||||
|
||||
#ifdef DOUBLE_BATTERY
|
||||
// Populate cell data
|
||||
content += "const data2 = [";
|
||||
for (uint8_t i = 0u; i < datalayer.battery2.info.number_of_cells; i++) {
|
||||
if (datalayer.battery2.status.cell_voltages_mV[i] == 0) {
|
||||
continue;
|
||||
if (battery2) {
|
||||
// Populate cell data
|
||||
content += "const data2 = [";
|
||||
for (uint8_t i = 0u; i < datalayer.battery2.info.number_of_cells; i++) {
|
||||
if (datalayer.battery2.status.cell_voltages_mV[i] == 0) {
|
||||
continue;
|
||||
}
|
||||
content += String(datalayer.battery2.status.cell_voltages_mV[i]) + ",";
|
||||
}
|
||||
content += String(datalayer.battery2.status.cell_voltages_mV[i]) + ",";
|
||||
}
|
||||
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 += "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 += datalayer.battery2.status.cell_balancing_status[i] ? "true," : "false,";
|
||||
content += "];";
|
||||
|
||||
content += "const min_mv2 = Math.min(...data2) - 20;";
|
||||
content += "const max_mv2 = Math.max(...data2) + 20;";
|
||||
content += "const min_index2 = data2.indexOf(Math.min(...data2));";
|
||||
content += "const max_index2 = data2.indexOf(Math.max(...data2));";
|
||||
content += "const graphContainer2 = document.getElementById('graph2');";
|
||||
content += "const valueDisplay2 = document.getElementById('valueDisplay2');";
|
||||
content += "const cellContainer2 = document.getElementById('cellContainer2');";
|
||||
|
||||
// Arduino-style map() function
|
||||
content +=
|
||||
"function map2(value, fromLow, fromHigh, toLow, toHigh) {return (value - fromLow) * (toHigh - toLow) / "
|
||||
"(fromHigh - fromLow) + toLow;}";
|
||||
|
||||
// Mark cell and bar with highest/lowest values
|
||||
content +=
|
||||
"function checkMinMax2(cell2, bar2, index2) {if ((index2 == min_index2) || (index2 == max_index2)) "
|
||||
"{cell2.style.borderColor = 'red';bar2.style.borderColor = 'red';}}";
|
||||
|
||||
// Bar function. Basically get the mV, scale the height and add a bar div to its container
|
||||
content +=
|
||||
"function createBars2(data2) {"
|
||||
"data2.forEach((mV, index2) => {"
|
||||
"const bar2 = document.createElement('div');"
|
||||
"const mV_limited2 = map2(mV, min_mv2, max_mv2, 20, 200);"
|
||||
"bar2.className = 'bar';"
|
||||
"bar2.id = `barIndex2${index2}`;"
|
||||
"bar2.style.height = `${mV_limited2}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}`);"
|
||||
|
||||
"checkMinMax2(cell2, bar2, index2);"
|
||||
|
||||
"bar2.addEventListener('mouseenter', () => {"
|
||||
" valueDisplay2.textContent = `Value: ${mV}` + (balancing[index2] ? ' (balancing)' : '');"
|
||||
" bar2.style.backgroundColor = balancing2[index2] ? '#80FFFF' : 'lightblue';"
|
||||
" cell2.style.backgroundColor = balancing2[index2] ? '#006666' : 'blue';"
|
||||
"});"
|
||||
|
||||
"bar2.addEventListener('mouseleave', () => {"
|
||||
"valueDisplay2.textContent = 'Value: ...';"
|
||||
"bar2.style.backgroundColor = balancing2[index2] ? '#00FFFF' : 'blue';" // Restore cyan if balancing, else blue
|
||||
"cell2.style.removeProperty('background-color');"
|
||||
"});"
|
||||
|
||||
"graphContainer2.appendChild(bar2);"
|
||||
"});"
|
||||
"}";
|
||||
|
||||
// Cell population function. For each value, add a cell block with its value
|
||||
content +=
|
||||
"function createCells2(data2) {"
|
||||
"data2.forEach((mV, index2) => {"
|
||||
"const cell2 = document.createElement('div');"
|
||||
"cell2.className = 'cell';"
|
||||
"cell2.id = `cellIndex2${index2}`;"
|
||||
"let cellContent2 = `Cell ${index2 + 1}<br>${mV} mV`;"
|
||||
"if (mV < 3000) {"
|
||||
"cellContent2 = `<span class='low-voltage'>${cellContent2}</span>`;"
|
||||
"}"
|
||||
"cell2.innerHTML = cellContent2;"
|
||||
|
||||
"cell2.addEventListener('mouseenter', () => {"
|
||||
"let bar2 = document.getElementById(`barIndex2${index2}`);"
|
||||
"valueDisplay2.textContent = `Value: ${mV}`;"
|
||||
"bar2.style.backgroundColor = balancing2[index2] ? '#80FFFF' : 'lightblue';" // Lighter cyan if balancing
|
||||
"cell2.style.backgroundColor = balancing2[index2] ? '#006666' : 'blue';" // Darker cyan if balancing
|
||||
"});"
|
||||
|
||||
"cell2.addEventListener('mouseleave', () => {"
|
||||
"let bar2 = document.getElementById(`barIndex2${index2}`);"
|
||||
"bar2.style.backgroundColor = balancing2[index2] ? '#00FFFF' : 'blue';" // Restore original color
|
||||
"cell2.style.removeProperty('background-color');"
|
||||
"});"
|
||||
|
||||
"cellContainer2.appendChild(cell2);"
|
||||
"});"
|
||||
"}";
|
||||
|
||||
// On fetch, update the header of max/min/deviation client-side for consistency
|
||||
content +=
|
||||
"function updateVoltageValues2(data2) {"
|
||||
"const min_mv2 = Math.min(...data2);"
|
||||
"const max_mv2 = Math.max(...data2);"
|
||||
"const cell_dev2 = max_mv2 - min_mv2;"
|
||||
"const voltVal2 = document.getElementById('voltageValues2');"
|
||||
"voltVal2.innerHTML = `Battery #2<br>Max Voltage : ${max_mv2} mV<br>Min Voltage: ${min_mv2} mV<br>Voltage "
|
||||
"Deviation: "
|
||||
"${cell_dev2} mV`"
|
||||
"}";
|
||||
|
||||
// If we have values, do the thing. Otherwise, display friendly message and wait
|
||||
content += "if (data2.length != 0) {";
|
||||
content += "createCells2(data2);";
|
||||
content += "createBars2(data2);";
|
||||
content += "updateVoltageValues2(data2);";
|
||||
content += "}";
|
||||
content += "else {";
|
||||
content +=
|
||||
"document.getElementById('voltageValues2').textContent = 'Cell information not yet fetched, or information "
|
||||
"not "
|
||||
"available';";
|
||||
content += "}";
|
||||
}
|
||||
content += "];";
|
||||
|
||||
content += "const min_mv2 = Math.min(...data2) - 20;";
|
||||
content += "const max_mv2 = Math.max(...data2) + 20;";
|
||||
content += "const min_index2 = data2.indexOf(Math.min(...data2));";
|
||||
content += "const max_index2 = data2.indexOf(Math.max(...data2));";
|
||||
content += "const graphContainer2 = document.getElementById('graph2');";
|
||||
content += "const valueDisplay2 = document.getElementById('valueDisplay2');";
|
||||
content += "const cellContainer2 = document.getElementById('cellContainer2');";
|
||||
|
||||
// Arduino-style map() function
|
||||
content +=
|
||||
"function map2(value, fromLow, fromHigh, toLow, toHigh) {return (value - fromLow) * (toHigh - toLow) / "
|
||||
"(fromHigh - fromLow) + toLow;}";
|
||||
|
||||
// Mark cell and bar with highest/lowest values
|
||||
content +=
|
||||
"function checkMinMax2(cell2, bar2, index2) {if ((index2 == min_index2) || (index2 == max_index2)) "
|
||||
"{cell2.style.borderColor = 'red';bar2.style.borderColor = 'red';}}";
|
||||
|
||||
// Bar function. Basically get the mV, scale the height and add a bar div to its container
|
||||
content +=
|
||||
"function createBars2(data2) {"
|
||||
"data2.forEach((mV, index2) => {"
|
||||
"const bar2 = document.createElement('div');"
|
||||
"const mV_limited2 = map2(mV, min_mv2, max_mv2, 20, 200);"
|
||||
"bar2.className = 'bar';"
|
||||
"bar2.id = `barIndex2${index2}`;"
|
||||
"bar2.style.height = `${mV_limited2}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}`);"
|
||||
|
||||
"checkMinMax2(cell2, bar2, index2);"
|
||||
|
||||
"bar2.addEventListener('mouseenter', () => {"
|
||||
" valueDisplay2.textContent = `Value: ${mV}` + (balancing[index2] ? ' (balancing)' : '');"
|
||||
" bar2.style.backgroundColor = balancing2[index2] ? '#80FFFF' : 'lightblue';"
|
||||
" cell2.style.backgroundColor = balancing2[index2] ? '#006666' : 'blue';"
|
||||
"});"
|
||||
|
||||
"bar2.addEventListener('mouseleave', () => {"
|
||||
"valueDisplay2.textContent = 'Value: ...';"
|
||||
"bar2.style.backgroundColor = balancing2[index2] ? '#00FFFF' : 'blue';" // Restore cyan if balancing, else blue
|
||||
"cell2.style.removeProperty('background-color');"
|
||||
"});"
|
||||
|
||||
"graphContainer2.appendChild(bar2);"
|
||||
"});"
|
||||
"}";
|
||||
|
||||
// Cell population function. For each value, add a cell block with its value
|
||||
content +=
|
||||
"function createCells2(data2) {"
|
||||
"data2.forEach((mV, index2) => {"
|
||||
"const cell2 = document.createElement('div');"
|
||||
"cell2.className = 'cell';"
|
||||
"cell2.id = `cellIndex2${index2}`;"
|
||||
"let cellContent2 = `Cell ${index2 + 1}<br>${mV} mV`;"
|
||||
"if (mV < 3000) {"
|
||||
"cellContent2 = `<span class='low-voltage'>${cellContent2}</span>`;"
|
||||
"}"
|
||||
"cell2.innerHTML = cellContent2;"
|
||||
|
||||
"cell2.addEventListener('mouseenter', () => {"
|
||||
"let bar2 = document.getElementById(`barIndex2${index2}`);"
|
||||
"valueDisplay2.textContent = `Value: ${mV}`;"
|
||||
"bar2.style.backgroundColor = balancing2[index2] ? '#80FFFF' : 'lightblue';" // Lighter cyan if balancing
|
||||
"cell2.style.backgroundColor = balancing2[index2] ? '#006666' : 'blue';" // Darker cyan if balancing
|
||||
"});"
|
||||
|
||||
"cell2.addEventListener('mouseleave', () => {"
|
||||
"let bar2 = document.getElementById(`barIndex2${index2}`);"
|
||||
"bar2.style.backgroundColor = balancing2[index2] ? '#00FFFF' : 'blue';" // Restore original color
|
||||
"cell2.style.removeProperty('background-color');"
|
||||
"});"
|
||||
|
||||
"cellContainer2.appendChild(cell2);"
|
||||
"});"
|
||||
"}";
|
||||
|
||||
// On fetch, update the header of max/min/deviation client-side for consistency
|
||||
content +=
|
||||
"function updateVoltageValues2(data2) {"
|
||||
"const min_mv2 = Math.min(...data2);"
|
||||
"const max_mv2 = Math.max(...data2);"
|
||||
"const cell_dev2 = max_mv2 - min_mv2;"
|
||||
"const voltVal2 = document.getElementById('voltageValues2');"
|
||||
"voltVal2.innerHTML = `Battery #2<br>Max Voltage : ${max_mv2} mV<br>Min Voltage: ${min_mv2} mV<br>Voltage "
|
||||
"Deviation: "
|
||||
"${cell_dev2} mV`"
|
||||
"}";
|
||||
|
||||
// If we have values, do the thing. Otherwise, display friendly message and wait
|
||||
content += "if (data2.length != 0) {";
|
||||
content += "createCells2(data2);";
|
||||
content += "createBars2(data2);";
|
||||
content += "updateVoltageValues2(data2);";
|
||||
content += "}";
|
||||
content += "else {";
|
||||
content +=
|
||||
"document.getElementById('voltageValues2').textContent = 'Cell information not yet fetched, or information not "
|
||||
"available';";
|
||||
content += "}";
|
||||
|
||||
#endif //DOUBLE_BATTERY
|
||||
|
||||
// Automatic refresh is nice
|
||||
content += "setTimeout(function(){ location.reload(true); }, 20000);";
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include <Arduino.h>
|
||||
#include "../../charger/CHARGERS.h"
|
||||
#include "../../datalayer/datalayer.h"
|
||||
#include "../../include.h"
|
||||
|
||||
String settings_processor(const String& var) {
|
||||
if (var == "X") {
|
||||
|
@ -26,18 +27,13 @@ String settings_processor(const String& var) {
|
|||
"<h4 style='color: white;'>Password: ######## <span id='Password'></span> <button "
|
||||
"onclick='editPassword()'>Edit</button></h4>";
|
||||
|
||||
#ifndef RS485_BATTERY_SELECTED
|
||||
content += "<h4 style='color: white;'>Battery interface: <span id='Battery'>" +
|
||||
String(getCANInterfaceName(can_config.battery)) + "</span></h4>";
|
||||
#endif
|
||||
#ifdef RS485_BATTERY_SELECTED
|
||||
content += "<h4 style='color: white;'>Battery interface: RS485<span id='Battery'></span></h4>";
|
||||
#endif
|
||||
content +=
|
||||
"<h4 style='color: white;'>Battery interface: <span id='Battery'>" + battery->interface_name() + "</span></h4>";
|
||||
|
||||
#ifdef DOUBLE_BATTERY
|
||||
content += "<h4 style='color: white;'>Battery #2 interface: <span id='Battery'>" +
|
||||
String(getCANInterfaceName(can_config.battery_double)) + "</span></h4>";
|
||||
#endif // DOUBLE_BATTERY
|
||||
if (battery2) {
|
||||
content += "<h4 style='color: white;'>Battery #2 interface: <span id='Battery'>" + battery->interface_name() +
|
||||
"</span></h4>";
|
||||
}
|
||||
|
||||
if (inverter) {
|
||||
content += "<h4 style='color: white;'>Inverter interface: <span id='Inverter'>" +
|
||||
|
@ -93,53 +89,47 @@ String settings_processor(const String& var) {
|
|||
// Close the block
|
||||
content += "</div>";
|
||||
|
||||
#ifdef TEST_FAKE_BATTERY
|
||||
// Start a new block with blue background color
|
||||
content += "<div style='background-color: #2E37AD; padding: 10px; margin-bottom: 10px;border-radius: 50px'>";
|
||||
float voltageFloat =
|
||||
static_cast<float>(datalayer.battery.status.voltage_dV) / 10.0; // Convert to float and divide by 10
|
||||
content += "<h4 style='color: white;'>Fake battery voltage: " + String(voltageFloat, 1) +
|
||||
" V </span> <button onclick='editFakeBatteryVoltage()'>Edit</button></h4>";
|
||||
if (battery->supports_set_fake_voltage()) {
|
||||
content += "<div style='background-color: #2E37AD; padding: 10px; margin-bottom: 10px;border-radius: 50px'>";
|
||||
content += "<h4 style='color: white;'>Fake battery voltage: " + String(battery->get_voltage(), 1) +
|
||||
" V </span> <button onclick='editFakeBatteryVoltage()'>Edit</button></h4>";
|
||||
content += "</div>";
|
||||
}
|
||||
|
||||
// Close the block
|
||||
content += "</div>";
|
||||
#endif
|
||||
if (battery->supports_manual_balancing()) {
|
||||
// Start a new block with grey background color
|
||||
content += "<div style='background-color: #303E47; padding: 10px; margin-bottom: 10px;border-radius: 50px'>";
|
||||
|
||||
#ifdef TESLA_MODEL_3Y_BATTERY
|
||||
content +=
|
||||
"<h4 style='color: white;'>Manual LFP balancing: <span id='TSL_BAL_ACT'>" +
|
||||
String(datalayer.battery.settings.user_requests_balancing ? "<span>✓</span>"
|
||||
: "<span style='color: red;'>✕</span>") +
|
||||
"</span> <button onclick='editTeslaBalAct()'>Edit</button></h4>";
|
||||
content +=
|
||||
"<h4 style='color: " + String(datalayer.battery.settings.user_requests_balancing ? "white" : "darkgrey") +
|
||||
";'>Balancing max time: " + String(datalayer.battery.settings.balancing_time_ms / 60000.0, 1) +
|
||||
" Minutes </span> <button onclick='editBalTime()'>Edit</button></h4>";
|
||||
content +=
|
||||
"<h4 style='color: " + String(datalayer.battery.settings.user_requests_balancing ? "white" : "darkgrey") +
|
||||
";'>Balancing float power: " + String(datalayer.battery.settings.balancing_float_power_W / 1.0, 0) +
|
||||
" W </span> <button onclick='editBalFloatPower()'>Edit</button></h4>";
|
||||
content +=
|
||||
"<h4 style='color: " + String(datalayer.battery.settings.user_requests_balancing ? "white" : "darkgrey") +
|
||||
";'>Max battery voltage: " + String(datalayer.battery.settings.balancing_max_pack_voltage_dV / 10.0, 0) +
|
||||
" V </span> <button onclick='editBalMaxPackV()'>Edit</button></h4>";
|
||||
content +=
|
||||
"<h4 style='color: " + String(datalayer.battery.settings.user_requests_balancing ? "white" : "darkgrey") +
|
||||
";'>Max cell voltage: " + String(datalayer.battery.settings.balancing_max_cell_voltage_mV / 1.0, 0) +
|
||||
" mV </span> <button onclick='editBalMaxCellV()'>Edit</button></h4>";
|
||||
content +=
|
||||
"<h4 style='color: " + String(datalayer.battery.settings.user_requests_balancing ? "white" : "darkgrey") +
|
||||
";'>Max cell voltage deviation: " +
|
||||
String(datalayer.battery.settings.balancing_max_deviation_cell_voltage_mV / 1.0, 0) +
|
||||
" mV </span> <button onclick='editBalMaxDevCellV()'>Edit</button></h4>";
|
||||
|
||||
// Start a new block with grey background color
|
||||
content += "<div style='background-color: #303E47; padding: 10px; margin-bottom: 10px;border-radius: 50px'>";
|
||||
|
||||
content +=
|
||||
"<h4 style='color: white;'>Manual LFP balancing: <span id='TSL_BAL_ACT'>" +
|
||||
String(datalayer.battery.settings.user_requests_balancing ? "<span>✓</span>"
|
||||
: "<span style='color: red;'>✕</span>") +
|
||||
"</span> <button onclick='editTeslaBalAct()'>Edit</button></h4>";
|
||||
content +=
|
||||
"<h4 style='color: " + String(datalayer.battery.settings.user_requests_balancing ? "white" : "darkgrey") +
|
||||
";'>Balancing max time: " + String(datalayer.battery.settings.balancing_time_ms / 60000.0, 1) +
|
||||
" Minutes </span> <button onclick='editBalTime()'>Edit</button></h4>";
|
||||
content +=
|
||||
"<h4 style='color: " + String(datalayer.battery.settings.user_requests_balancing ? "white" : "darkgrey") +
|
||||
";'>Balancing float power: " + String(datalayer.battery.settings.balancing_float_power_W / 1.0, 0) +
|
||||
" W </span> <button onclick='editBalFloatPower()'>Edit</button></h4>";
|
||||
content +=
|
||||
"<h4 style='color: " + String(datalayer.battery.settings.user_requests_balancing ? "white" : "darkgrey") +
|
||||
";'>Max battery voltage: " + String(datalayer.battery.settings.balancing_max_pack_voltage_dV / 10.0, 0) +
|
||||
" V </span> <button onclick='editBalMaxPackV()'>Edit</button></h4>";
|
||||
content +=
|
||||
"<h4 style='color: " + String(datalayer.battery.settings.user_requests_balancing ? "white" : "darkgrey") +
|
||||
";'>Max cell voltage: " + String(datalayer.battery.settings.balancing_max_cell_voltage_mV / 1.0, 0) +
|
||||
" mV </span> <button onclick='editBalMaxCellV()'>Edit</button></h4>";
|
||||
content +=
|
||||
"<h4 style='color: " + String(datalayer.battery.settings.user_requests_balancing ? "white" : "darkgrey") +
|
||||
";'>Max cell voltage deviation: " +
|
||||
String(datalayer.battery.settings.balancing_max_deviation_cell_voltage_mV / 1.0, 0) +
|
||||
" mV </span> <button onclick='editBalMaxDevCellV()'>Edit</button></h4>";
|
||||
|
||||
// Close the block
|
||||
content += "</div>";
|
||||
#endif
|
||||
// Close the block
|
||||
content += "</div>";
|
||||
}
|
||||
|
||||
if (charger) {
|
||||
// Start a new block with orange background color
|
||||
|
@ -252,7 +242,6 @@ String settings_processor(const String& var) {
|
|||
"between 0 "
|
||||
"and 1000.0');}}}";
|
||||
|
||||
#ifdef TESLA_MODEL_3Y_BATTERY
|
||||
content +=
|
||||
"function editTeslaBalAct(){var value=prompt('Enable or disable forced LFP balancing. Makes the battery charge "
|
||||
"to 101percent. This should be performed once every month, to keep LFP batteries balanced. Ensure battery is "
|
||||
|
@ -291,16 +280,15 @@ String settings_processor(const String& var) {
|
|||
"XMLHttpRequest();xhr.onload=editComplete;xhr.onerror=editError;xhr.open('GET','/"
|
||||
"BalMaxDevCellV?value='+value,true);xhr.send();}else{alert('Invalid value. Please enter a value "
|
||||
"between 300 and 600');}}}";
|
||||
#endif
|
||||
|
||||
#ifdef TEST_FAKE_BATTERY
|
||||
content +=
|
||||
"function editFakeBatteryVoltage(){var value=prompt('Enter new fake battery "
|
||||
"voltage');if(value!==null){if(value>=0&&value<=5000){var xhr=new "
|
||||
"XMLHttpRequest();xhr.onload=editComplete;xhr.onerror=editError;xhr.open('GET','/"
|
||||
"updateFakeBatteryVoltage?value='+value,true);xhr.send();}else{alert('Invalid value. Please enter a value "
|
||||
"between 0 and 1000');}}}";
|
||||
#endif
|
||||
if (battery->supports_set_fake_voltage()) {
|
||||
content +=
|
||||
"function editFakeBatteryVoltage(){var value=prompt('Enter new fake battery "
|
||||
"voltage');if(value!==null){if(value>=0&&value<=5000){var xhr=new "
|
||||
"XMLHttpRequest();xhr.onload=editComplete;xhr.onerror=editError;xhr.open('GET','/"
|
||||
"updateFakeBatteryVoltage?value='+value,true);xhr.send();}else{alert('Invalid value. Please enter a value "
|
||||
"between 0 and 1000');}}}";
|
||||
}
|
||||
|
||||
if (charger) {
|
||||
content +=
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
#include <Preferences.h>
|
||||
#include <ctime>
|
||||
#include "../../../USER_SECRETS.h"
|
||||
#include "../../battery/BATTERIES.h"
|
||||
#include "../../battery/Battery.h"
|
||||
#include "../../datalayer/datalayer.h"
|
||||
#include "../../datalayer/datalayer_extended.h"
|
||||
#include "../../lib/bblanchon-ArduinoJson/ArduinoJson.h"
|
||||
|
@ -530,6 +532,32 @@ void init_webserver() {
|
|||
}
|
||||
});
|
||||
|
||||
for (const auto& cmd : battery_commands) {
|
||||
auto route = String("/") + cmd.identifier;
|
||||
server.on(
|
||||
route.c_str(), HTTP_PUT,
|
||||
[cmd](AsyncWebServerRequest* request) {
|
||||
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password)) {
|
||||
return request->requestAuthentication();
|
||||
}
|
||||
},
|
||||
nullptr,
|
||||
[cmd](AsyncWebServerRequest* request, uint8_t* data, size_t len, size_t index, size_t total) {
|
||||
String battIndex = "";
|
||||
if (len > 0) {
|
||||
battIndex += (char)data[0];
|
||||
}
|
||||
Battery* batt = battery;
|
||||
if (battIndex == "1") {
|
||||
batt = battery2;
|
||||
}
|
||||
if (batt) {
|
||||
cmd.action(batt);
|
||||
}
|
||||
request->send(200, "text/plain", "Command performed.");
|
||||
});
|
||||
}
|
||||
|
||||
// Route for editing BATTERY_USE_VOLTAGE_LIMITS
|
||||
server.on("/updateUseVoltageLimit", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
|
||||
|
@ -572,100 +600,6 @@ void init_webserver() {
|
|||
}
|
||||
});
|
||||
|
||||
// Route for clearing isolation faults on Tesla
|
||||
server.on("/teslaClearIsolation", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password)) {
|
||||
return request->requestAuthentication();
|
||||
}
|
||||
datalayer.battery.settings.user_requests_tesla_isolation_clear = true;
|
||||
request->send(200, "text/plain", "Updated successfully");
|
||||
});
|
||||
|
||||
// Route for resetting BMS on Tesla
|
||||
server.on("/teslaResetBMS", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password)) {
|
||||
return request->requestAuthentication();
|
||||
}
|
||||
datalayer.battery.settings.user_requests_tesla_bms_reset = true;
|
||||
request->send(200, "text/plain", "Updated successfully");
|
||||
});
|
||||
|
||||
// Route for triggering NVROL reset on Zoe Gen2 batteries
|
||||
server.on("/triggerNVROL", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password)) {
|
||||
return request->requestAuthentication();
|
||||
}
|
||||
datalayer_extended.zoePH2.UserRequestNVROLReset = true;
|
||||
request->send(200, "text/plain", "Updated successfully");
|
||||
});
|
||||
|
||||
// Route for closing BMW iX Contactors
|
||||
server.on("/bmwIxCloseContactorRequest", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password)) {
|
||||
return request->requestAuthentication();
|
||||
}
|
||||
datalayer_extended.bmwix.UserRequestContactorClose = true;
|
||||
request->send(200, "text/plain", "Updated successfully");
|
||||
});
|
||||
|
||||
// Route for opening BMW iX Contactors
|
||||
server.on("/bmwIxOpenContactorRequest", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password)) {
|
||||
return request->requestAuthentication();
|
||||
}
|
||||
datalayer_extended.bmwix.UserRequestContactorOpen = true;
|
||||
request->send(200, "text/plain", "Updated successfully");
|
||||
});
|
||||
|
||||
// Route for resetting SOH on Nissan LEAF batteries
|
||||
server.on("/resetSOH", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password)) {
|
||||
return request->requestAuthentication();
|
||||
}
|
||||
datalayer_extended.nissanleaf.UserRequestSOHreset = true;
|
||||
request->send(200, "text/plain", "Updated successfully");
|
||||
});
|
||||
|
||||
// Route for resetting Crash data on BYD Atto3 batteries
|
||||
server.on("/resetCrash", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password)) {
|
||||
return request->requestAuthentication();
|
||||
}
|
||||
datalayer_extended.bydAtto3.UserRequestCrashReset = true;
|
||||
request->send(200, "text/plain", "Updated successfully");
|
||||
});
|
||||
|
||||
// Route for erasing DTC on Volvo/Polestar batteries
|
||||
server.on("/volvoEraseDTC", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password)) {
|
||||
return request->requestAuthentication();
|
||||
}
|
||||
datalayer_extended.VolvoPolestar.UserRequestDTCreset = true;
|
||||
datalayer_extended.VolvoHybrid.UserRequestDTCreset = true;
|
||||
request->send(200, "text/plain", "Updated successfully");
|
||||
});
|
||||
|
||||
// Route for reading DTC on Volvo/Polestar batteries
|
||||
server.on("/volvoReadDTC", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password)) {
|
||||
return request->requestAuthentication();
|
||||
}
|
||||
datalayer_extended.VolvoPolestar.UserRequestDTCreadout = true;
|
||||
datalayer_extended.VolvoHybrid.UserRequestDTCreadout = true;
|
||||
request->send(200, "text/plain", "Updated successfully");
|
||||
});
|
||||
|
||||
// Route for performing ECU reset on Volvo/Polestar batteries
|
||||
server.on("/volvoBECMecuReset", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password)) {
|
||||
return request->requestAuthentication();
|
||||
}
|
||||
datalayer_extended.VolvoPolestar.UserRequestBECMecuReset = true;
|
||||
datalayer_extended.VolvoHybrid.UserRequestBECMecuReset = true;
|
||||
request->send(200, "text/plain", "Updated successfully");
|
||||
});
|
||||
|
||||
#ifdef TEST_FAKE_BATTERY
|
||||
// Route for editing FakeBatteryVoltage
|
||||
server.on("/updateFakeBatteryVoltage", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
|
||||
|
@ -677,13 +611,10 @@ void init_webserver() {
|
|||
String value = request->getParam("value")->value();
|
||||
float val = value.toFloat();
|
||||
|
||||
datalayer.battery.status.voltage_dV = val * 10;
|
||||
battery->set_fake_voltage(val);
|
||||
|
||||
request->send(200, "text/plain", "Updated successfully");
|
||||
});
|
||||
#endif // TEST_FAKE_BATTERY
|
||||
|
||||
#ifdef TESLA_MODEL_3Y_BATTERY
|
||||
|
||||
// Route for editing balancing enabled
|
||||
server.on("/TeslaBalAct", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
|
@ -768,7 +699,6 @@ void init_webserver() {
|
|||
request->send(400, "text/plain", "Bad Request");
|
||||
}
|
||||
});
|
||||
#endif
|
||||
|
||||
if (charger) {
|
||||
// Route for editing ChargerTargetV
|
||||
|
@ -1022,9 +952,9 @@ String processor(const String& var) {
|
|||
content += "</h4>";
|
||||
content += "<h4 style='color: white;'>Battery protocol: ";
|
||||
content += datalayer.system.info.battery_protocol;
|
||||
#ifdef DOUBLE_BATTERY
|
||||
content += " (Double battery)";
|
||||
#endif // DOUBLE_BATTERY
|
||||
if (battery2) {
|
||||
content += " (Double battery)";
|
||||
}
|
||||
if (datalayer.battery.info.chemistry == battery_chemistry_enum::LFP) {
|
||||
content += " (LFP)";
|
||||
}
|
||||
|
@ -1045,14 +975,14 @@ String processor(const String& var) {
|
|||
// Close the block
|
||||
content += "</div>";
|
||||
|
||||
#ifdef DOUBLE_BATTERY
|
||||
// Start a new block with a specific background color. Color changes depending on BMS status
|
||||
content += "<div style='display: flex; width: 100%;'>";
|
||||
content += "<div style='flex: 1; background-color: ";
|
||||
#else
|
||||
// Start a new block with a specific background color. Color changes depending on system status
|
||||
content += "<div style='background-color: ";
|
||||
#endif // DOUBLE_BATTERY
|
||||
if (battery2) {
|
||||
// Start a new block with a specific background color. Color changes depending on BMS status
|
||||
content += "<div style='display: flex; width: 100%;'>";
|
||||
content += "<div style='flex: 1; background-color: ";
|
||||
} else {
|
||||
// Start a new block with a specific background color. Color changes depending on system status
|
||||
content += "<div style='background-color: ";
|
||||
}
|
||||
|
||||
switch (led_get_color()) {
|
||||
case led_color::GREEN:
|
||||
|
@ -1176,27 +1106,27 @@ String processor(const String& var) {
|
|||
}
|
||||
content += "</h4>";
|
||||
|
||||
#ifdef MEB_BATTERY
|
||||
content += "<h4>Battery BMS status: ";
|
||||
switch (datalayer.battery.status.real_bms_status) {
|
||||
case BMS_ACTIVE:
|
||||
content += String("OK");
|
||||
break;
|
||||
case BMS_FAULT:
|
||||
content += String("FAULT");
|
||||
break;
|
||||
case BMS_DISCONNECTED:
|
||||
content += String("DISCONNECTED");
|
||||
break;
|
||||
case BMS_STANDBY:
|
||||
content += String("STANDBY");
|
||||
break;
|
||||
default:
|
||||
content += String("??");
|
||||
break;
|
||||
if (battery->supports_real_BMS_status()) {
|
||||
content += "<h4>Battery BMS status: ";
|
||||
switch (datalayer.battery.status.real_bms_status) {
|
||||
case BMS_ACTIVE:
|
||||
content += String("OK");
|
||||
break;
|
||||
case BMS_FAULT:
|
||||
content += String("FAULT");
|
||||
break;
|
||||
case BMS_DISCONNECTED:
|
||||
content += String("DISCONNECTED");
|
||||
break;
|
||||
case BMS_STANDBY:
|
||||
content += String("STANDBY");
|
||||
break;
|
||||
default:
|
||||
content += String("??");
|
||||
break;
|
||||
}
|
||||
content += "</h4>";
|
||||
}
|
||||
content += "</h4>";
|
||||
#endif
|
||||
|
||||
if (datalayer.battery.status.current_dA == 0) {
|
||||
content += "<h4>Battery idle</h4>";
|
||||
|
@ -1286,159 +1216,161 @@ String processor(const String& var) {
|
|||
// Close the block
|
||||
content += "</div>";
|
||||
|
||||
#ifdef DOUBLE_BATTERY
|
||||
content += "<div style='flex: 1; background-color: ";
|
||||
switch (datalayer.battery.status.bms_status) {
|
||||
case ACTIVE:
|
||||
content += "#2D3F2F;";
|
||||
break;
|
||||
case FAULT:
|
||||
content += "#A70107;";
|
||||
break;
|
||||
default:
|
||||
content += "#2D3F2F;";
|
||||
break;
|
||||
}
|
||||
// Add the common style properties
|
||||
content += "padding: 10px; margin-bottom: 10px; border-radius: 50px;'>";
|
||||
if (battery2) {
|
||||
content += "<div style='flex: 1; background-color: ";
|
||||
switch (datalayer.battery.status.bms_status) {
|
||||
case ACTIVE:
|
||||
content += "#2D3F2F;";
|
||||
break;
|
||||
case FAULT:
|
||||
content += "#A70107;";
|
||||
break;
|
||||
default:
|
||||
content += "#2D3F2F;";
|
||||
break;
|
||||
}
|
||||
// Add the common style properties
|
||||
content += "padding: 10px; margin-bottom: 10px; border-radius: 50px;'>";
|
||||
|
||||
// Display battery statistics within this block
|
||||
socRealFloat =
|
||||
static_cast<float>(datalayer.battery2.status.real_soc) / 100.0; // Convert to float and divide by 100
|
||||
//socScaledFloat; // Same value used for bat2
|
||||
sohFloat = static_cast<float>(datalayer.battery2.status.soh_pptt) / 100.0; // Convert to float and divide by 100
|
||||
voltageFloat =
|
||||
static_cast<float>(datalayer.battery2.status.voltage_dV) / 10.0; // Convert to float and divide by 10
|
||||
currentFloat =
|
||||
static_cast<float>(datalayer.battery2.status.current_dA) / 10.0; // Convert to float and divide by 10
|
||||
powerFloat = static_cast<float>(datalayer.battery2.status.active_power_W); // Convert to float
|
||||
tempMaxFloat = static_cast<float>(datalayer.battery2.status.temperature_max_dC) / 10.0; // Convert to float
|
||||
tempMinFloat = static_cast<float>(datalayer.battery2.status.temperature_min_dC) / 10.0; // Convert to float
|
||||
cell_delta_mv = datalayer.battery2.status.cell_max_voltage_mV - datalayer.battery2.status.cell_min_voltage_mV;
|
||||
// Display battery statistics within this block
|
||||
socRealFloat =
|
||||
static_cast<float>(datalayer.battery2.status.real_soc) / 100.0; // Convert to float and divide by 100
|
||||
//socScaledFloat; // Same value used for bat2
|
||||
sohFloat = static_cast<float>(datalayer.battery2.status.soh_pptt) / 100.0; // Convert to float and divide by 100
|
||||
voltageFloat =
|
||||
static_cast<float>(datalayer.battery2.status.voltage_dV) / 10.0; // Convert to float and divide by 10
|
||||
currentFloat =
|
||||
static_cast<float>(datalayer.battery2.status.current_dA) / 10.0; // Convert to float and divide by 10
|
||||
powerFloat = static_cast<float>(datalayer.battery2.status.active_power_W); // Convert to float
|
||||
tempMaxFloat = static_cast<float>(datalayer.battery2.status.temperature_max_dC) / 10.0; // Convert to float
|
||||
tempMinFloat = static_cast<float>(datalayer.battery2.status.temperature_min_dC) / 10.0; // Convert to float
|
||||
cell_delta_mv = datalayer.battery2.status.cell_max_voltage_mV - datalayer.battery2.status.cell_min_voltage_mV;
|
||||
|
||||
if (datalayer.battery.settings.soc_scaling_active)
|
||||
content += "<h4 style='color: white;'>Scaled SOC: " + String(socScaledFloat, 2) +
|
||||
"% (real: " + String(socRealFloat, 2) + "%)</h4>";
|
||||
else
|
||||
content += "<h4 style='color: white;'>SOC: " + String(socRealFloat, 2) + "%</h4>";
|
||||
if (datalayer.battery.settings.soc_scaling_active)
|
||||
content += "<h4 style='color: white;'>Scaled SOC: " + String(socScaledFloat, 2) +
|
||||
"% (real: " + String(socRealFloat, 2) + "%)</h4>";
|
||||
else
|
||||
content += "<h4 style='color: white;'>SOC: " + String(socRealFloat, 2) + "%</h4>";
|
||||
|
||||
content += "<h4 style='color: white;'>SOH: " + String(sohFloat, 2) + "%</h4>";
|
||||
content += "<h4 style='color: white;'>Voltage: " + String(voltageFloat, 1) +
|
||||
" V Current: " + String(currentFloat, 1) + " A</h4>";
|
||||
content += formatPowerValue("Power", powerFloat, "", 1);
|
||||
content += "<h4 style='color: white;'>SOH: " + String(sohFloat, 2) + "%</h4>";
|
||||
content += "<h4 style='color: white;'>Voltage: " + String(voltageFloat, 1) +
|
||||
" V Current: " + String(currentFloat, 1) + " A</h4>";
|
||||
content += formatPowerValue("Power", powerFloat, "", 1);
|
||||
|
||||
if (datalayer.battery.settings.soc_scaling_active)
|
||||
content += "<h4 style='color: white;'>Scaled total capacity: " +
|
||||
formatPowerValue(datalayer.battery2.info.reported_total_capacity_Wh, "h", 1) +
|
||||
" (real: " + formatPowerValue(datalayer.battery2.info.total_capacity_Wh, "h", 1) + ")</h4>";
|
||||
else
|
||||
content += formatPowerValue("Total capacity", datalayer.battery2.info.total_capacity_Wh, "h", 1);
|
||||
if (datalayer.battery.settings.soc_scaling_active)
|
||||
content += "<h4 style='color: white;'>Scaled total capacity: " +
|
||||
formatPowerValue(datalayer.battery2.info.reported_total_capacity_Wh, "h", 1) +
|
||||
" (real: " + formatPowerValue(datalayer.battery2.info.total_capacity_Wh, "h", 1) + ")</h4>";
|
||||
else
|
||||
content += formatPowerValue("Total capacity", datalayer.battery2.info.total_capacity_Wh, "h", 1);
|
||||
|
||||
if (datalayer.battery.settings.soc_scaling_active)
|
||||
content += "<h4 style='color: white;'>Scaled remaining capacity: " +
|
||||
formatPowerValue(datalayer.battery2.status.reported_remaining_capacity_Wh, "h", 1) +
|
||||
" (real: " + formatPowerValue(datalayer.battery2.status.remaining_capacity_Wh, "h", 1) + ")</h4>";
|
||||
else
|
||||
content += formatPowerValue("Remaining capacity", datalayer.battery2.status.remaining_capacity_Wh, "h", 1);
|
||||
if (datalayer.battery.settings.soc_scaling_active)
|
||||
content += "<h4 style='color: white;'>Scaled remaining capacity: " +
|
||||
formatPowerValue(datalayer.battery2.status.reported_remaining_capacity_Wh, "h", 1) +
|
||||
" (real: " + formatPowerValue(datalayer.battery2.status.remaining_capacity_Wh, "h", 1) + ")</h4>";
|
||||
else
|
||||
content += formatPowerValue("Remaining capacity", datalayer.battery2.status.remaining_capacity_Wh, "h", 1);
|
||||
|
||||
if (datalayer.system.settings.equipment_stop_active) {
|
||||
content += formatPowerValue("Max discharge power", datalayer.battery2.status.max_discharge_power_W, "", 1, "red");
|
||||
content += formatPowerValue("Max charge power", datalayer.battery2.status.max_charge_power_W, "", 1, "red");
|
||||
content += "<h4 style='color: red;'>Max discharge current: " + String(maxCurrentDischargeFloat, 1) + " A</h4>";
|
||||
content += "<h4 style='color: red;'>Max charge current: " + String(maxCurrentChargeFloat, 1) + " A</h4>";
|
||||
} else {
|
||||
content += formatPowerValue("Max discharge power", datalayer.battery2.status.max_discharge_power_W, "", 1);
|
||||
content += formatPowerValue("Max charge power", datalayer.battery2.status.max_charge_power_W, "", 1);
|
||||
content += "<h4 style='color: white;'>Max discharge current: " + String(maxCurrentDischargeFloat, 1) + " A</h4>";
|
||||
content += "<h4 style='color: white;'>Max charge current: " + String(maxCurrentChargeFloat, 1) + " A</h4>";
|
||||
}
|
||||
if (datalayer.system.settings.equipment_stop_active) {
|
||||
content +=
|
||||
formatPowerValue("Max discharge power", datalayer.battery2.status.max_discharge_power_W, "", 1, "red");
|
||||
content += formatPowerValue("Max charge power", datalayer.battery2.status.max_charge_power_W, "", 1, "red");
|
||||
content += "<h4 style='color: red;'>Max discharge current: " + String(maxCurrentDischargeFloat, 1) + " A</h4>";
|
||||
content += "<h4 style='color: red;'>Max charge current: " + String(maxCurrentChargeFloat, 1) + " A</h4>";
|
||||
} else {
|
||||
content += formatPowerValue("Max discharge power", datalayer.battery2.status.max_discharge_power_W, "", 1);
|
||||
content += formatPowerValue("Max charge power", datalayer.battery2.status.max_charge_power_W, "", 1);
|
||||
content +=
|
||||
"<h4 style='color: white;'>Max discharge current: " + String(maxCurrentDischargeFloat, 1) + " A</h4>";
|
||||
content += "<h4 style='color: white;'>Max charge current: " + String(maxCurrentChargeFloat, 1) + " A</h4>";
|
||||
}
|
||||
|
||||
content += "<h4>Cell min/max: " + String(datalayer.battery2.status.cell_min_voltage_mV) + " mV / " +
|
||||
String(datalayer.battery2.status.cell_max_voltage_mV) + " mV</h4>";
|
||||
if (cell_delta_mv > datalayer.battery2.info.max_cell_voltage_deviation_mV) {
|
||||
content += "<h4 style='color: red;'>Cell delta: " + String(cell_delta_mv) + " mV</h4>";
|
||||
} else {
|
||||
content += "<h4>Cell delta: " + String(cell_delta_mv) + " mV</h4>";
|
||||
}
|
||||
content +=
|
||||
"<h4>Temperature min/max: " + String(tempMinFloat, 1) + " °C / " + String(tempMaxFloat, 1) + " °C</h4>";
|
||||
if (datalayer.battery.status.bms_status == ACTIVE) {
|
||||
content += "<h4>System status: OK </h4>";
|
||||
} else if (datalayer.battery.status.bms_status == UPDATING) {
|
||||
content += "<h4>System status: UPDATING </h4>";
|
||||
} else {
|
||||
content += "<h4>System status: FAULT </h4>";
|
||||
}
|
||||
if (datalayer.battery2.status.current_dA == 0) {
|
||||
content += "<h4>Battery idle</h4>";
|
||||
} else if (datalayer.battery2.status.current_dA < 0) {
|
||||
content += "<h4>Battery discharging!</h4>";
|
||||
} else { // > 0
|
||||
content += "<h4>Battery charging!</h4>";
|
||||
}
|
||||
content += "<h4>Cell min/max: " + String(datalayer.battery2.status.cell_min_voltage_mV) + " mV / " +
|
||||
String(datalayer.battery2.status.cell_max_voltage_mV) + " mV</h4>";
|
||||
if (cell_delta_mv > datalayer.battery2.info.max_cell_voltage_deviation_mV) {
|
||||
content += "<h4 style='color: red;'>Cell delta: " + String(cell_delta_mv) + " mV</h4>";
|
||||
} else {
|
||||
content += "<h4>Cell delta: " + String(cell_delta_mv) + " mV</h4>";
|
||||
}
|
||||
content += "<h4>Temperature min/max: " + String(tempMinFloat, 1) + " °C / " + String(tempMaxFloat, 1) +
|
||||
" °C</h4>";
|
||||
if (datalayer.battery.status.bms_status == ACTIVE) {
|
||||
content += "<h4>System status: OK </h4>";
|
||||
} else if (datalayer.battery.status.bms_status == UPDATING) {
|
||||
content += "<h4>System status: UPDATING </h4>";
|
||||
} else {
|
||||
content += "<h4>System status: FAULT </h4>";
|
||||
}
|
||||
if (datalayer.battery2.status.current_dA == 0) {
|
||||
content += "<h4>Battery idle</h4>";
|
||||
} else if (datalayer.battery2.status.current_dA < 0) {
|
||||
content += "<h4>Battery discharging!</h4>";
|
||||
} else { // > 0
|
||||
content += "<h4>Battery charging!</h4>";
|
||||
}
|
||||
|
||||
content += "<h4>Automatic contactor closing allowed:</h4>";
|
||||
content += "<h4>Battery: ";
|
||||
if (datalayer.system.status.battery2_allowed_contactor_closing == true) {
|
||||
content += "<span>✓</span>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>✕</span>";
|
||||
}
|
||||
content += "<h4>Automatic contactor closing allowed:</h4>";
|
||||
content += "<h4>Battery: ";
|
||||
if (datalayer.system.status.battery2_allowed_contactor_closing == true) {
|
||||
content += "<span>✓</span>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>✕</span>";
|
||||
}
|
||||
|
||||
content += " Inverter: ";
|
||||
if (datalayer.system.status.inverter_allows_contactor_closing == true) {
|
||||
content += "<span>✓</span></h4>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>✕</span></h4>";
|
||||
}
|
||||
content += " Inverter: ";
|
||||
if (datalayer.system.status.inverter_allows_contactor_closing == true) {
|
||||
content += "<span>✓</span></h4>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>✕</span></h4>";
|
||||
}
|
||||
|
||||
if (emulator_pause_status == NORMAL)
|
||||
content += "<h4>Power status: " + String(get_emulator_pause_status().c_str()) + " </h4>";
|
||||
else
|
||||
content += "<h4 style='color: red;'>Power status: " + String(get_emulator_pause_status().c_str()) + " </h4>";
|
||||
if (emulator_pause_status == NORMAL)
|
||||
content += "<h4>Power status: " + String(get_emulator_pause_status().c_str()) + " </h4>";
|
||||
else
|
||||
content += "<h4 style='color: red;'>Power status: " + String(get_emulator_pause_status().c_str()) + " </h4>";
|
||||
|
||||
#ifdef CONTACTOR_CONTROL
|
||||
content += "<h4>Contactors controlled by emulator, state: ";
|
||||
if (datalayer.system.status.contactors_battery2_engaged) {
|
||||
content += "<span style='color: green;'>ON</span>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>OFF</span>";
|
||||
}
|
||||
content += "</h4>";
|
||||
content += "<h4>Contactors controlled by emulator, state: ";
|
||||
if (datalayer.system.status.contactors_battery2_engaged) {
|
||||
content += "<span style='color: green;'>ON</span>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>OFF</span>";
|
||||
}
|
||||
content += "</h4>";
|
||||
#ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY
|
||||
content += "<h4>Cont. Neg.: ";
|
||||
content += "<h4>Cont. Neg.: ";
|
||||
#ifdef PWM_CONTACTOR_CONTROL
|
||||
if (datalayer.system.status.contactors_battery2_engaged) {
|
||||
content += "<span style='color: green;'>Economized</span>";
|
||||
content += " Cont. Pos.: ";
|
||||
content += "<span style='color: green;'>Economized</span>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>✕</span>";
|
||||
content += " Cont. Pos.: ";
|
||||
content += "<span style='color: red;'>✕</span>";
|
||||
}
|
||||
if (datalayer.system.status.contactors_battery2_engaged) {
|
||||
content += "<span style='color: green;'>Economized</span>";
|
||||
content += " Cont. Pos.: ";
|
||||
content += "<span style='color: green;'>Economized</span>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>✕</span>";
|
||||
content += " Cont. Pos.: ";
|
||||
content += "<span style='color: red;'>✕</span>";
|
||||
}
|
||||
|
||||
#else // No PWM_CONTACTOR_CONTROL , we can read the pin and see feedback. Helpful if channel overloaded
|
||||
if (digitalRead(SECOND_NEGATIVE_CONTACTOR_PIN) == HIGH) {
|
||||
content += "<span style='color: green;'>✓</span>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>✕</span>";
|
||||
}
|
||||
if (digitalRead(SECOND_NEGATIVE_CONTACTOR_PIN) == HIGH) {
|
||||
content += "<span style='color: green;'>✓</span>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>✕</span>";
|
||||
}
|
||||
|
||||
content += " Cont. Pos.: ";
|
||||
if (digitalRead(SECOND_POSITIVE_CONTACTOR_PIN) == HIGH) {
|
||||
content += "<span style='color: green;'>✓</span>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>✕</span>";
|
||||
}
|
||||
content += " Cont. Pos.: ";
|
||||
if (digitalRead(SECOND_POSITIVE_CONTACTOR_PIN) == HIGH) {
|
||||
content += "<span style='color: green;'>✓</span>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>✕</span>";
|
||||
}
|
||||
#endif //no PWM_CONTACTOR_CONTROL
|
||||
content += "</h4>";
|
||||
content += "</h4>";
|
||||
#endif // CONTACTOR_CONTROL_DOUBLE_BATTERY
|
||||
#endif // CONTACTOR_CONTROL
|
||||
|
||||
content += "</div>";
|
||||
content += "</div>";
|
||||
#endif // DOUBLE_BATTERY
|
||||
content += "</div>";
|
||||
content += "</div>";
|
||||
}
|
||||
|
||||
if (charger) {
|
||||
// Start a new block with orange background color
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue