mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-05 10:49:42 +02:00
Merge branch 'main' into main
This commit is contained in:
commit
eb22302ac9
53 changed files with 2646 additions and 2318 deletions
32
README.md
32
README.md
|
@ -5,37 +5,33 @@
|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
This software enables EV battery packs to be used for stationary storage. It achieves this by converting the EV battery CAN data into a brand battery format that solar inverters can understand. This makes it extremely cheap and easy to use large EV batteries in a true plug'n'play fashion!
|
## What is Battery Emulator?
|
||||||
|
|
||||||
|
Many manufacturers sell home battery systems to enable homeowners to store power collected from the grid, or renewable sources, to use at times when electricity demand is higher. However almost all of these home battery systems charge a high cost for every kilowatt hour (kWh) of capacity you buy.
|
||||||
|
|
||||||
|
At the same time, EV manufacturers have been putting high capacity battery packs into their cars, with no firm plan for what should happen to those batteries if the car is damaged in a crash, or reaches the end of its life as a vehicle.
|
||||||
|
|
||||||
|
**Battery Emulator** enables EV battery packs to be repurposed for stationary storage. It acts as a translation layer between the EV battery and the home inverter. This makes it extremely cheap and easy to use large EV batteries in a true plug'n'play fashion!
|
||||||
|
|
||||||
> [!CAUTION]
|
> [!CAUTION]
|
||||||
> Working with high voltage is dangerous. Always follow local laws and regulations regarding high voltage work. If you are unsure about the rules in your country, consult a licensed electrician for more information.
|
> Working with high voltage is dangerous. Always follow local laws and regulations regarding high voltage work. If you are unsure about the rules in your country, consult a licensed electrician for more information.
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
## Quickstart guide 📜
|
## Quickstart guide 📜
|
||||||
- Pick a [supported solar inverter](https://github.com/dalathegreat/Battery-Emulator/wiki#supported-inverters-list) (solar panels optional) :sun_with_face:
|
- Pick a [supported inverter](https://github.com/dalathegreat/Battery-Emulator/wiki#supported-inverters-list) (solar panels optional) :sun_with_face:
|
||||||
- Pick a [supported battery](https://github.com/dalathegreat/Battery-Emulator/wiki#supported-batteries-list) :battery:
|
- Pick a [supported battery](https://github.com/dalathegreat/Battery-Emulator/wiki#supported-batteries-list) :battery:
|
||||||
- Order the Battery-Emulator [compatible hardware](https://github.com/dalathegreat/Battery-Emulator/wiki#where-do-i-get-the-hardware-needed) :robot:
|
- Order the Battery-Emulator [compatible hardware](https://github.com/dalathegreat/Battery-Emulator/wiki#where-do-i-get-the-hardware-needed) :robot:
|
||||||
- Follow the [installation guidelines](https://github.com/dalathegreat/Battery-Emulator/wiki/Installation-guidelines) section for how to install and commission your battery properly :notebook:
|
- Follow the [installation guidelines](https://github.com/dalathegreat/Battery-Emulator/wiki/Installation-guidelines) section for how to install and commission your battery properly :notebook:
|
||||||
|
|
||||||
## Installation basics 🪛
|
## Installation basics 🪛
|
||||||
1. Connect one end of the LilyGo RS485 to the Gen24 Modbus
|
1. Connect your Battery Emulator hardware to your EV battery
|
||||||
2. Connect the other end of the LilyGo to the CAN side of the battery
|
2. Connect your Battery Emulator hardware to your inverter
|
||||||
3. Wire up high voltage cable between the Gen24 and the battery
|
3. Wire up high voltage cable between the inverter and the battery
|
||||||
4. Add a 5-12V power source to power the LilyGo and 12V to the battery (uninterruptible PSU or 12V lead acid recommended in parallel)
|
4. Add a low voltage power supply to your Battery Emulator hardware
|
||||||
5. Some batteries need manual pre-charge circuit and positive/negative contactor control. Others are automatic. See the wiki for more info.
|
5. Configure any additional requirements to allow Battery Emulator to switch on your EV battery (also referred to as 'closing contactors')
|
||||||
6. Enjoy a big cheap grid connected battery!
|
6. Enjoy a big cheap grid connected battery!
|
||||||
|
|
||||||
## Wiring example, LEAF battery 💡
|
For examples showing wiring, see each battery type's own Wiki page. For instance the [Nissan LEAF page](https://github.com/dalathegreat/Battery-Emulator/wiki/Battery:-Nissan-LEAF---e%E2%80%90NV200)
|
||||||
Here's how to wire up the communication between the components.
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
Here's how to connect the high voltage lines
|
|
||||||

|
|
||||||
|
|
||||||
For more examples showing wiring, see each battery types own Wiki page. For instance the [Nissan LEAF page](https://github.com/dalathegreat/Battery-Emulator/wiki/Battery:-Nissan-LEAF---e%E2%80%90NV200)
|
|
||||||
|
|
||||||
## How to compile the software 💻
|
## How to compile the software 💻
|
||||||
|
|
||||||
|
|
|
@ -319,7 +319,6 @@ void init_serial() {
|
||||||
#endif // DEBUG_VIA_USB
|
#endif // DEBUG_VIA_USB
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DOUBLE_BATTERY
|
|
||||||
void check_interconnect_available() {
|
void check_interconnect_available() {
|
||||||
if (datalayer.battery.status.voltage_dV == 0 || datalayer.battery2.status.voltage_dV == 0) {
|
if (datalayer.battery.status.voltage_dV == 0 || datalayer.battery2.status.voltage_dV == 0) {
|
||||||
return; // Both voltage values need to be available to start check
|
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));
|
set_event(EVENT_VOLTAGE_DIFFERENCE, (uint8_t)(voltage_diff / 10));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif // DOUBLE_BATTERY
|
|
||||||
|
|
||||||
void update_calculated_values() {
|
void update_calculated_values() {
|
||||||
/* Update CPU temperature*/
|
/* Update CPU temperature*/
|
||||||
|
@ -399,11 +397,11 @@ void update_calculated_values() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DOUBLE_BATTERY
|
if (battery2) {
|
||||||
/* Calculate active power based on voltage and current for battery 2*/
|
/* Calculate active power based on voltage and current for battery 2*/
|
||||||
datalayer.battery2.status.active_power_W =
|
datalayer.battery2.status.active_power_W =
|
||||||
(datalayer.battery2.status.current_dA * (datalayer.battery2.status.voltage_dV / 100));
|
(datalayer.battery2.status.current_dA * (datalayer.battery2.status.voltage_dV / 100));
|
||||||
#endif // DOUBLE_BATTERY
|
}
|
||||||
|
|
||||||
if (datalayer.battery.settings.soc_scaling_active) {
|
if (datalayer.battery.settings.soc_scaling_active) {
|
||||||
/** SOC Scaling
|
/** SOC Scaling
|
||||||
|
@ -445,57 +443,62 @@ void update_calculated_values() {
|
||||||
datalayer.battery.status.reported_remaining_capacity_Wh = datalayer.battery.status.remaining_capacity_Wh;
|
datalayer.battery.status.reported_remaining_capacity_Wh = datalayer.battery.status.remaining_capacity_Wh;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DOUBLE_BATTERY
|
if (battery2) {
|
||||||
// If battery info is valid
|
// If battery info is valid
|
||||||
if (datalayer.battery2.info.total_capacity_Wh > 0 && datalayer.battery.status.real_soc > 0) {
|
if (datalayer.battery2.info.total_capacity_Wh > 0 && datalayer.battery.status.real_soc > 0) {
|
||||||
|
|
||||||
datalayer.battery2.info.reported_total_capacity_Wh = scaled_total_capacity;
|
datalayer.battery2.info.reported_total_capacity_Wh = scaled_total_capacity;
|
||||||
// Scale remaining capacity based on scaled SOC
|
// Scale remaining capacity based on scaled SOC
|
||||||
datalayer.battery2.status.reported_remaining_capacity_Wh = (scaled_total_capacity * scaled_soc) / 10000;
|
datalayer.battery2.status.reported_remaining_capacity_Wh = (scaled_total_capacity * scaled_soc) / 10000;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Fallback if scaling cannot be performed
|
// Fallback if scaling cannot be performed
|
||||||
datalayer.battery2.info.reported_total_capacity_Wh = datalayer.battery2.info.total_capacity_Wh;
|
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;
|
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.
|
} 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_soc = datalayer.battery.status.real_soc;
|
||||||
datalayer.battery.status.reported_remaining_capacity_Wh = datalayer.battery.status.remaining_capacity_Wh;
|
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;
|
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;
|
if (battery2) {
|
||||||
datalayer.battery2.status.reported_remaining_capacity_Wh = datalayer.battery2.status.remaining_capacity_Wh;
|
datalayer.battery2.status.reported_soc = datalayer.battery2.status.real_soc;
|
||||||
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;
|
||||||
#endif
|
datalayer.battery2.info.reported_total_capacity_Wh = datalayer.battery2.info.total_capacity_Wh;
|
||||||
}
|
}
|
||||||
#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 (datalayer.battery.status.real_soc > 9900) { //If this battery is over 99.00%, use this as SOC instead of average
|
if (battery2) {
|
||||||
datalayer.battery.status.reported_soc = datalayer.battery.status.real_soc;
|
// Perform extra SOC sanity checks on double battery setups
|
||||||
datalayer.battery.status.reported_remaining_capacity_Wh = datalayer.battery.status.remaining_capacity_Wh;
|
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
|
// Check if millis has overflowed. Used in events to keep better track of time
|
||||||
if (currentMillis < lastMillisOverflowCheck) { // Overflow detected
|
if (currentMillis < lastMillisOverflowCheck) { // Overflow detected
|
||||||
datalayer.system.status.millisrolloverCount++;
|
datalayer.system.status.millisrolloverCount++;
|
||||||
|
|
|
@ -11,11 +11,8 @@
|
||||||
// to support battery class selection at compile-time
|
// to support battery class selection at compile-time
|
||||||
#ifdef SELECTED_BATTERY_CLASS
|
#ifdef SELECTED_BATTERY_CLASS
|
||||||
|
|
||||||
static Battery* battery = nullptr;
|
Battery* battery = nullptr;
|
||||||
|
Battery* battery2 = nullptr;
|
||||||
#ifdef DOUBLE_BATTERY
|
|
||||||
static Battery* battery2 = nullptr;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void setup_battery() {
|
void setup_battery() {
|
||||||
// Instantiate the battery only once just in case this function gets called multiple times.
|
// Instantiate the battery only once just in case this function gets called multiple times.
|
||||||
|
|
|
@ -2,6 +2,13 @@
|
||||||
#define BATTERIES_H
|
#define BATTERIES_H
|
||||||
#include "../../USER_SETTINGS.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
|
#ifdef BMW_SBOX
|
||||||
#include "BMW-SBOX.h"
|
#include "BMW-SBOX.h"
|
||||||
void handle_incoming_can_frame_shunt(CAN_frame rx_frame);
|
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);
|
void transmit_can_battery(unsigned long currentMillis);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef DOUBLE_BATTERY
|
|
||||||
void update_values_battery2();
|
void update_values_battery2();
|
||||||
void handle_incoming_can_frame_battery2(CAN_frame rx_frame);
|
void handle_incoming_can_frame_battery2(CAN_frame rx_frame);
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "../datalayer/datalayer.h"
|
#include "../datalayer/datalayer.h"
|
||||||
#include "../datalayer/datalayer_extended.h"
|
#include "../datalayer/datalayer_extended.h"
|
||||||
#include "../include.h"
|
#include "../include.h"
|
||||||
|
#include "BMW-I3-HTML.h"
|
||||||
#include "CanBattery.h"
|
#include "CanBattery.h"
|
||||||
|
|
||||||
#define BATTERY_SELECTED
|
#define BATTERY_SELECTED
|
||||||
|
@ -12,11 +13,12 @@
|
||||||
class BmwI3Battery : public CanBattery {
|
class BmwI3Battery : public CanBattery {
|
||||||
public:
|
public:
|
||||||
// Use this constructor for the second battery.
|
// 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;
|
datalayer_battery = datalayer_ptr;
|
||||||
contactor_closing_allowed = contactor_closing_allowed_ptr;
|
contactor_closing_allowed = contactor_closing_allowed_ptr;
|
||||||
allows_contactor_closing = nullptr;
|
allows_contactor_closing = nullptr;
|
||||||
can_interface = targetCan;
|
|
||||||
wakeup_pin = wakeup;
|
wakeup_pin = wakeup;
|
||||||
*allows_contactor_closing = true;
|
*allows_contactor_closing = true;
|
||||||
|
|
||||||
|
@ -29,7 +31,6 @@ class BmwI3Battery : public CanBattery {
|
||||||
datalayer_battery = &datalayer.battery;
|
datalayer_battery = &datalayer.battery;
|
||||||
allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing;
|
allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing;
|
||||||
contactor_closing_allowed = nullptr;
|
contactor_closing_allowed = nullptr;
|
||||||
can_interface = can_config.battery;
|
|
||||||
wakeup_pin = WUP_PIN1;
|
wakeup_pin = WUP_PIN1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,6 +39,11 @@ class BmwI3Battery : public CanBattery {
|
||||||
virtual void update_values();
|
virtual void update_values();
|
||||||
virtual void transmit_can(unsigned long currentMillis);
|
virtual void transmit_can(unsigned long currentMillis);
|
||||||
|
|
||||||
|
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
BmwI3HtmlRenderer renderer;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const int MAX_CELL_VOLTAGE_60AH = 4110; // Battery is put into emergency stop if one cell goes over this value
|
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
|
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;
|
bool* contactor_closing_allowed;
|
||||||
|
|
||||||
int wakeup_pin;
|
int wakeup_pin;
|
||||||
int can_interface;
|
|
||||||
|
|
||||||
unsigned long previousMillis20 = 0; // will store last time a 20ms CAN Message was send
|
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
|
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
|
#define BMW_IX_BATTERY_H
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "../include.h"
|
#include "../include.h"
|
||||||
|
#include "BMW-IX-HTML.h"
|
||||||
#include "CanBattery.h"
|
#include "CanBattery.h"
|
||||||
|
|
||||||
#define BATTERY_SELECTED
|
#define BATTERY_SELECTED
|
||||||
|
@ -13,8 +14,15 @@ class BmwIXBattery : public CanBattery {
|
||||||
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
|
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
|
||||||
virtual void update_values();
|
virtual void update_values();
|
||||||
virtual void transmit_can(unsigned long currentMillis);
|
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:
|
private:
|
||||||
|
BmwIXHtmlRenderer renderer;
|
||||||
static const int MAX_PACK_VOLTAGE_DV = 4650; //4650 = 465.0V
|
static const int MAX_PACK_VOLTAGE_DV = 4650; //4650 = 465.0V
|
||||||
static const int MIN_PACK_VOLTAGE_DV = 3000;
|
static const int MIN_PACK_VOLTAGE_DV = 3000;
|
||||||
static const int MAX_CELL_DEVIATION_MV = 250;
|
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
|
#define BMW_PHEV_BATTERY_H
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "../include.h"
|
#include "../include.h"
|
||||||
|
#include "BMW-PHEV-HTML.h"
|
||||||
#include "CanBattery.h"
|
#include "CanBattery.h"
|
||||||
|
|
||||||
#define BATTERY_SELECTED
|
#define BATTERY_SELECTED
|
||||||
|
@ -14,7 +15,11 @@ class BmwPhevBattery : public CanBattery {
|
||||||
virtual void update_values();
|
virtual void update_values();
|
||||||
virtual void transmit_can(unsigned long currentMillis);
|
virtual void transmit_can(unsigned long currentMillis);
|
||||||
|
|
||||||
|
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
BmwPhevHtmlRenderer renderer;
|
||||||
|
|
||||||
static const int MAX_PACK_VOLTAGE_DV = 4650; //4650 = 465.0V
|
static const int MAX_PACK_VOLTAGE_DV = 4650; //4650 = 465.0V
|
||||||
static const int MIN_PACK_VOLTAGE_DV = 3000;
|
static const int MIN_PACK_VOLTAGE_DV = 3000;
|
||||||
static const int MAX_CELL_DEVIATION_MV = 250;
|
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 <Arduino.h>
|
||||||
#include "../include.h"
|
#include "../include.h"
|
||||||
|
|
||||||
|
#include "BOLT-AMPERA-HTML.h"
|
||||||
#include "CanBattery.h"
|
#include "CanBattery.h"
|
||||||
|
|
||||||
#define BATTERY_SELECTED
|
#define BATTERY_SELECTED
|
||||||
|
@ -15,7 +16,10 @@ class BoltAmperaBattery : public CanBattery {
|
||||||
virtual void update_values();
|
virtual void update_values();
|
||||||
virtual void transmit_can(unsigned long currentMillis);
|
virtual void transmit_can(unsigned long currentMillis);
|
||||||
|
|
||||||
|
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
BoltAmperaHtmlRenderer renderer;
|
||||||
static const int MAX_PACK_VOLTAGE_DV = 4150; //5000 = 500.0V
|
static const int MAX_PACK_VOLTAGE_DV = 4150; //5000 = 500.0V
|
||||||
static const int MIN_PACK_VOLTAGE_DV = 2500;
|
static const int MIN_PACK_VOLTAGE_DV = 2500;
|
||||||
static const int MAX_CELL_DEVIATION_MV = 150;
|
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 "../datalayer/datalayer_extended.h"
|
||||||
#include "../include.h"
|
#include "../include.h"
|
||||||
|
|
||||||
|
#include "BYD-ATTO-3-HTML.h"
|
||||||
#include "CanBattery.h"
|
#include "CanBattery.h"
|
||||||
|
|
||||||
#define USE_ESTIMATED_SOC // If enabled, SOC is estimated from pack voltage. Useful for locked packs. \
|
#define USE_ESTIMATED_SOC // If enabled, SOC is estimated from pack voltage. Useful for locked packs. \
|
||||||
|
@ -33,18 +34,17 @@
|
||||||
class BydAttoBattery : public CanBattery {
|
class BydAttoBattery : public CanBattery {
|
||||||
public:
|
public:
|
||||||
// Use this constructor for the second battery.
|
// 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_battery = datalayer_ptr;
|
||||||
datalayer_bydatto = extended;
|
datalayer_bydatto = extended;
|
||||||
allows_contactor_closing = nullptr;
|
allows_contactor_closing = nullptr;
|
||||||
can_interface = targetCan;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the default constructor to create the first or single battery.
|
// Use the default constructor to create the first or single battery.
|
||||||
BydAttoBattery() {
|
BydAttoBattery() : renderer(&datalayer_extended.bydAtto3) {
|
||||||
datalayer_battery = &datalayer.battery;
|
datalayer_battery = &datalayer.battery;
|
||||||
allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing;
|
allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing;
|
||||||
can_interface = can_config.battery;
|
|
||||||
datalayer_bydatto = &datalayer_extended.bydAtto3;
|
datalayer_bydatto = &datalayer_extended.bydAtto3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,13 +53,18 @@ class BydAttoBattery : public CanBattery {
|
||||||
virtual void update_values();
|
virtual void update_values();
|
||||||
virtual void transmit_can(unsigned long currentMillis);
|
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:
|
private:
|
||||||
|
BydAtto3HtmlRenderer renderer;
|
||||||
DATALAYER_BATTERY_TYPE* datalayer_battery;
|
DATALAYER_BATTERY_TYPE* datalayer_battery;
|
||||||
DATALAYER_INFO_BYDATTO3* datalayer_bydatto;
|
DATALAYER_INFO_BYDATTO3* datalayer_bydatto;
|
||||||
bool* allows_contactor_closing;
|
bool* allows_contactor_closing;
|
||||||
|
|
||||||
int can_interface;
|
|
||||||
|
|
||||||
static const int POLL_FOR_BATTERY_SOC = 0x0005;
|
static const int POLL_FOR_BATTERY_SOC = 0x0005;
|
||||||
static const uint8_t NOT_DETERMINED_YET = 0;
|
static const uint8_t NOT_DETERMINED_YET = 0;
|
||||||
static const uint8_t STANDARD_RANGE = 1;
|
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
|
#ifndef BATTERY_H
|
||||||
#define BATTERY_H
|
#define BATTERY_H
|
||||||
|
|
||||||
|
#include "../datalayer/datalayer.h"
|
||||||
|
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
|
||||||
|
|
||||||
// Abstract base class for next-generation battery implementations.
|
// Abstract base class for next-generation battery implementations.
|
||||||
// Defines the interface to call battery specific functionality.
|
// Defines the interface to call battery specific functionality.
|
||||||
class Battery {
|
class Battery {
|
||||||
public:
|
public:
|
||||||
virtual void setup(void) = 0;
|
virtual void setup(void) = 0;
|
||||||
virtual void update_values() = 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
|
#endif
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#define CELLPOWER_BMS_H
|
#define CELLPOWER_BMS_H
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "../include.h"
|
#include "../include.h"
|
||||||
|
#include "CELLPOWER-HTML.h"
|
||||||
#include "CanBattery.h"
|
#include "CanBattery.h"
|
||||||
|
|
||||||
#define BATTERY_SELECTED
|
#define BATTERY_SELECTED
|
||||||
|
@ -14,7 +15,10 @@ class CellPowerBms : public CanBattery {
|
||||||
virtual void update_values();
|
virtual void update_values();
|
||||||
virtual void transmit_can(unsigned long currentMillis);
|
virtual void transmit_can(unsigned long currentMillis);
|
||||||
|
|
||||||
|
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
CellpowerHtmlRenderer renderer;
|
||||||
/* Tweak these according to your battery build */
|
/* Tweak these according to your battery build */
|
||||||
static const int MAX_PACK_VOLTAGE_DV = 5000; //5000 = 500.0V
|
static const int MAX_PACK_VOLTAGE_DV = 5000; //5000 = 500.0V
|
||||||
static const int MIN_PACK_VOLTAGE_DV = 1500;
|
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
|
#define CMFA_EV_BATTERY_H
|
||||||
#include "../include.h"
|
#include "../include.h"
|
||||||
|
|
||||||
|
#include "CMFA-EV-HTML.h"
|
||||||
#include "CanBattery.h"
|
#include "CanBattery.h"
|
||||||
|
|
||||||
#define BATTERY_SELECTED
|
#define BATTERY_SELECTED
|
||||||
|
@ -14,7 +15,11 @@ class CmfaEvBattery : public CanBattery {
|
||||||
virtual void update_values();
|
virtual void update_values();
|
||||||
virtual void transmit_can(unsigned long currentMillis);
|
virtual void transmit_can(unsigned long currentMillis);
|
||||||
|
|
||||||
|
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
CmfaEvHtmlRenderer renderer;
|
||||||
|
|
||||||
uint16_t rescale_raw_SOC(uint32_t raw_SOC);
|
uint16_t rescale_raw_SOC(uint32_t raw_SOC);
|
||||||
|
|
||||||
static const int MAX_PACK_VOLTAGE_DV = 3040; // 5000 = 500.0V
|
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:
|
public:
|
||||||
virtual void handle_incoming_can_frame(CAN_frame rx_frame) = 0;
|
virtual void handle_incoming_can_frame(CAN_frame rx_frame) = 0;
|
||||||
virtual void transmit_can(unsigned long currentMillis) = 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
|
#endif
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "../include.h"
|
#include "../include.h"
|
||||||
|
|
||||||
#include "CanBattery.h"
|
#include "CanBattery.h"
|
||||||
|
#include "ECMP-HTML.h"
|
||||||
|
|
||||||
#define BATTERY_SELECTED
|
#define BATTERY_SELECTED
|
||||||
#define SELECTED_BATTERY_CLASS EcmpBattery
|
#define SELECTED_BATTERY_CLASS EcmpBattery
|
||||||
|
@ -15,7 +16,10 @@ class EcmpBattery : public CanBattery {
|
||||||
virtual void update_values();
|
virtual void update_values();
|
||||||
virtual void transmit_can(unsigned long currentMillis);
|
virtual void transmit_can(unsigned long currentMillis);
|
||||||
|
|
||||||
|
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
EcmpHtmlRenderer renderer;
|
||||||
static const int MAX_PACK_VOLTAGE_DV = 4546;
|
static const int MAX_PACK_VOLTAGE_DV = 4546;
|
||||||
static const int MIN_PACK_VOLTAGE_DV = 3210;
|
static const int MIN_PACK_VOLTAGE_DV = 3210;
|
||||||
static const int MAX_CELL_DEVIATION_MV = 100;
|
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 "../datalayer/datalayer_extended.h"
|
||||||
#include "../include.h"
|
#include "../include.h"
|
||||||
#include "CanBattery.h"
|
#include "CanBattery.h"
|
||||||
|
#include "GEELY-GEOMETRY-C-HTML.h"
|
||||||
|
|
||||||
#define BATTERY_SELECTED
|
#define BATTERY_SELECTED
|
||||||
#define SELECTED_BATTERY_CLASS GeelyGeometryCBattery
|
#define SELECTED_BATTERY_CLASS GeelyGeometryCBattery
|
||||||
|
@ -36,7 +37,10 @@ class GeelyGeometryCBattery : public CanBattery {
|
||||||
virtual void update_values();
|
virtual void update_values();
|
||||||
virtual void transmit_can(unsigned long currentMillis);
|
virtual void transmit_can(unsigned long currentMillis);
|
||||||
|
|
||||||
|
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
GeelyGeometryCHtmlRenderer renderer;
|
||||||
const int MAX_PACK_VOLTAGE_70_DV 4420 //70kWh
|
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_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
|
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 "../datalayer/datalayer_extended.h"
|
||||||
#include "../include.h"
|
#include "../include.h"
|
||||||
#include "CanBattery.h"
|
#include "CanBattery.h"
|
||||||
|
#include "KIA-HYUNDAI-64-HTML.h"
|
||||||
|
|
||||||
#define BATTERY_SELECTED
|
#define BATTERY_SELECTED
|
||||||
#define SELECTED_BATTERY_CLASS KiaHyundai64Battery
|
#define SELECTED_BATTERY_CLASS KiaHyundai64Battery
|
||||||
|
@ -13,20 +14,19 @@ class KiaHyundai64Battery : public CanBattery {
|
||||||
public:
|
public:
|
||||||
// Use this constructor for the second battery.
|
// Use this constructor for the second battery.
|
||||||
KiaHyundai64Battery(DATALAYER_BATTERY_TYPE* datalayer_ptr, DATALAYER_INFO_KIAHYUNDAI64* extended_ptr,
|
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;
|
datalayer_battery = datalayer_ptr;
|
||||||
contactor_closing_allowed = contactor_closing_allowed_ptr;
|
contactor_closing_allowed = contactor_closing_allowed_ptr;
|
||||||
allows_contactor_closing = nullptr;
|
allows_contactor_closing = nullptr;
|
||||||
can_interface = targetCan;
|
|
||||||
datalayer_battery_extended = extended_ptr;
|
datalayer_battery_extended = extended_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the default constructor to create the first or single battery.
|
// Use the default constructor to create the first or single battery.
|
||||||
KiaHyundai64Battery() {
|
KiaHyundai64Battery() : renderer(&datalayer_extended.KiaHyundai64) {
|
||||||
datalayer_battery = &datalayer.battery;
|
datalayer_battery = &datalayer.battery;
|
||||||
allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing;
|
allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing;
|
||||||
contactor_closing_allowed = nullptr;
|
contactor_closing_allowed = nullptr;
|
||||||
can_interface = can_config.battery;
|
|
||||||
datalayer_battery_extended = &datalayer_extended.KiaHyundai64;
|
datalayer_battery_extended = &datalayer_extended.KiaHyundai64;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +35,11 @@ class KiaHyundai64Battery : public CanBattery {
|
||||||
virtual void update_values();
|
virtual void update_values();
|
||||||
virtual void transmit_can(unsigned long currentMillis);
|
virtual void transmit_can(unsigned long currentMillis);
|
||||||
|
|
||||||
|
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
KiaHyundai64HtmlRenderer renderer;
|
||||||
|
|
||||||
DATALAYER_BATTERY_TYPE* datalayer_battery;
|
DATALAYER_BATTERY_TYPE* datalayer_battery;
|
||||||
DATALAYER_INFO_KIAHYUNDAI64* datalayer_battery_extended;
|
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
|
// If not null, this battery listens to this boolean to determine whether contactor closing is allowed
|
||||||
bool* contactor_closing_allowed;
|
bool* contactor_closing_allowed;
|
||||||
|
|
||||||
int can_interface;
|
|
||||||
|
|
||||||
void update_number_of_cells();
|
void update_number_of_cells();
|
||||||
|
|
||||||
static const int MAX_PACK_VOLTAGE_98S_DV = 4110; //5000 = 500.0V
|
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 <Arduino.h>
|
||||||
#include "../include.h"
|
#include "../include.h"
|
||||||
#include "CanBattery.h"
|
#include "CanBattery.h"
|
||||||
|
#include "MEB-HTML.h"
|
||||||
|
|
||||||
#define BATTERY_SELECTED
|
#define BATTERY_SELECTED
|
||||||
#define SELECTED_BATTERY_CLASS MebBattery
|
#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 handle_incoming_can_frame(CAN_frame rx_frame);
|
||||||
virtual void update_values();
|
virtual void update_values();
|
||||||
virtual void transmit_can(unsigned long currentMillis);
|
virtual void transmit_can(unsigned long currentMillis);
|
||||||
|
bool supports_real_BMS_status() { return true; }
|
||||||
|
|
||||||
|
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
MebHtmlRenderer renderer;
|
||||||
static const int MAX_PACK_VOLTAGE_84S_DV = 3528; //5000 = 500.0V
|
static const int MAX_PACK_VOLTAGE_84S_DV = 3528; //5000 = 500.0V
|
||||||
static const int MIN_PACK_VOLTAGE_84S_DV = 2520;
|
static const int MIN_PACK_VOLTAGE_84S_DV = 2520;
|
||||||
static const int MAX_PACK_VOLTAGE_96S_DV = 4032;
|
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 MaskedBitwiseRotateMultiply(unsigned int param_1, unsigned int param_2);
|
||||||
unsigned int CryptAlgo(unsigned int param_1, unsigned int param_2, unsigned int param_3);
|
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::
|
void NissanLeafBattery::
|
||||||
update_values() { /* This function maps all the values fetched via CAN to the correct parameters used for modbus */
|
update_values() { /* This function maps all the values fetched via CAN to the correct parameters used for modbus */
|
||||||
/* Start with mapping all values */
|
/* Start with mapping all values */
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "../datalayer/datalayer_extended.h"
|
#include "../datalayer/datalayer_extended.h"
|
||||||
#include "../include.h"
|
#include "../include.h"
|
||||||
#include "CanBattery.h"
|
#include "CanBattery.h"
|
||||||
|
#include "NISSAN-LEAF-HTML.h"
|
||||||
|
|
||||||
#define BATTERY_SELECTED
|
#define BATTERY_SELECTED
|
||||||
#define SELECTED_BATTERY_CLASS NissanLeafBattery
|
#define SELECTED_BATTERY_CLASS NissanLeafBattery
|
||||||
|
@ -19,11 +20,12 @@
|
||||||
class NissanLeafBattery : public CanBattery {
|
class NissanLeafBattery : public CanBattery {
|
||||||
public:
|
public:
|
||||||
// Use this constructor for the second battery.
|
// 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;
|
datalayer_battery = datalayer_ptr;
|
||||||
allows_contactor_closing = nullptr;
|
allows_contactor_closing = nullptr;
|
||||||
datalayer_nissan = extended;
|
datalayer_nissan = extended;
|
||||||
can_interface = targetCan;
|
|
||||||
|
|
||||||
battery_Total_Voltage2 = 0;
|
battery_Total_Voltage2 = 0;
|
||||||
}
|
}
|
||||||
|
@ -33,7 +35,6 @@ class NissanLeafBattery : public CanBattery {
|
||||||
datalayer_battery = &datalayer.battery;
|
datalayer_battery = &datalayer.battery;
|
||||||
allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing;
|
allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing;
|
||||||
datalayer_nissan = &datalayer_extended.nissanleaf;
|
datalayer_nissan = &datalayer_extended.nissanleaf;
|
||||||
can_interface = can_config.battery;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void setup(void);
|
virtual void setup(void);
|
||||||
|
@ -41,7 +42,15 @@ class NissanLeafBattery : public CanBattery {
|
||||||
virtual void update_values();
|
virtual void update_values();
|
||||||
virtual void transmit_can(unsigned long currentMillis);
|
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:
|
private:
|
||||||
|
NissanLeafHtmlRenderer renderer;
|
||||||
|
|
||||||
bool is_message_corrupt(CAN_frame rx_frame);
|
bool is_message_corrupt(CAN_frame rx_frame);
|
||||||
void clearSOH(void);
|
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.
|
// If not null, this battery decides when the contactor can be closed and writes the value here.
|
||||||
bool* allows_contactor_closing;
|
bool* allows_contactor_closing;
|
||||||
|
|
||||||
int can_interface;
|
|
||||||
|
|
||||||
unsigned long previousMillis10 = 0; // will store last time a 10ms CAN Message was send
|
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 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
|
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 mprun10 = 0; //counter 0-3
|
||||||
uint8_t mprun100 = 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
|
// These CAN messages need to be sent towards the battery to keep it alive
|
||||||
CAN_frame LEAF_1F2 = {.FD = false,
|
CAN_frame LEAF_1F2 = {.FD = false,
|
||||||
.ext_ID = 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,
|
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};
|
32, 165, 52, 177, 187, 62, 28, 153, 147, 22, 135, 2, 8, 141};
|
||||||
|
|
||||||
//Nissan LEAF battery parameters from constantly sent CAN
|
//Nissan LEAF battery parameters from constantly sent CAN
|
||||||
#define ZE0_BATTERY 0
|
|
||||||
#define AZE0_BATTERY 1
|
|
||||||
#define ZE1_BATTERY 2
|
|
||||||
uint8_t LEAF_battery_Type = ZE0_BATTERY;
|
uint8_t LEAF_battery_Type = ZE0_BATTERY;
|
||||||
bool battery_can_alive = false;
|
bool battery_can_alive = false;
|
||||||
#define WH_PER_GID 77 //One GID is this amount of Watt hours
|
#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 {
|
class PylonBattery : public CanBattery {
|
||||||
public:
|
public:
|
||||||
// Use this constructor for the second battery.
|
// 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;
|
datalayer_battery = datalayer_ptr;
|
||||||
contactor_closing_allowed = contactor_closing_allowed_ptr;
|
contactor_closing_allowed = contactor_closing_allowed_ptr;
|
||||||
allows_contactor_closing = nullptr;
|
allows_contactor_closing = nullptr;
|
||||||
can_interface = targetCan;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the default constructor to create the first or single battery.
|
// Use the default constructor to create the first or single battery.
|
||||||
|
@ -24,7 +24,6 @@ class PylonBattery : public CanBattery {
|
||||||
datalayer_battery = &datalayer.battery;
|
datalayer_battery = &datalayer.battery;
|
||||||
allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing;
|
allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing;
|
||||||
contactor_closing_allowed = nullptr;
|
contactor_closing_allowed = nullptr;
|
||||||
can_interface = can_config.battery;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void setup(void);
|
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
|
// If not null, this battery listens to this boolean to determine whether contactor closing is allowed
|
||||||
bool* contactor_closing_allowed;
|
bool* contactor_closing_allowed;
|
||||||
|
|
||||||
int can_interface;
|
|
||||||
|
|
||||||
unsigned long previousMillis1000 = 0; // will store last time a 1s CAN Message was sent
|
unsigned long previousMillis1000 = 0; // will store last time a 1s CAN Message was sent
|
||||||
|
|
||||||
//Actual content messages
|
//Actual content messages
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#define RENAULT_ZOE_GEN1_BATTERY_H
|
#define RENAULT_ZOE_GEN1_BATTERY_H
|
||||||
|
|
||||||
#include "CanBattery.h"
|
#include "CanBattery.h"
|
||||||
|
#include "RENAULT-ZOE-GEN1-HTML.h"
|
||||||
|
|
||||||
#define BATTERY_SELECTED
|
#define BATTERY_SELECTED
|
||||||
#define SELECTED_BATTERY_CLASS RenaultZoeGen1Battery
|
#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 handle_incoming_can_frame(CAN_frame rx_frame);
|
||||||
virtual void update_values();
|
virtual void update_values();
|
||||||
virtual void transmit_can(unsigned long currentMillis);
|
virtual void transmit_can(unsigned long currentMillis);
|
||||||
|
|
||||||
|
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
RenaultZoeGen1HtmlRenderer renderer;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#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 "../include.h"
|
||||||
|
|
||||||
#include "CanBattery.h"
|
#include "CanBattery.h"
|
||||||
|
#include "RENAULT-ZOE-GEN2-HTML.h"
|
||||||
|
|
||||||
#define BATTERY_SELECTED
|
#define BATTERY_SELECTED
|
||||||
#define SELECTED_BATTERY_CLASS RenaultZoeGen2Battery
|
#define SELECTED_BATTERY_CLASS RenaultZoeGen2Battery
|
||||||
|
@ -14,7 +15,14 @@ class RenaultZoeGen2Battery : public CanBattery {
|
||||||
virtual void update_values();
|
virtual void update_values();
|
||||||
virtual void transmit_can(unsigned long currentMillis);
|
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:
|
private:
|
||||||
|
RenaultZoeGen2HtmlRenderer renderer;
|
||||||
static const int MAX_PACK_VOLTAGE_DV = 4100; //5000 = 500.0V
|
static const int MAX_PACK_VOLTAGE_DV = 4100; //5000 = 500.0V
|
||||||
static const int MIN_PACK_VOLTAGE_DV = 3000;
|
static const int MIN_PACK_VOLTAGE_DV = 3000;
|
||||||
static const int MAX_CELL_DEVIATION_MV = 150;
|
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:
|
public:
|
||||||
virtual void receive_RS485() = 0;
|
virtual void receive_RS485() = 0;
|
||||||
virtual void transmit_rs485(unsigned long currentMillis) = 0;
|
virtual void transmit_rs485(unsigned long currentMillis) = 0;
|
||||||
|
String interface_name() { return "RS485"; }
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -11,17 +11,15 @@
|
||||||
class SantaFePhevBattery : public CanBattery {
|
class SantaFePhevBattery : public CanBattery {
|
||||||
public:
|
public:
|
||||||
// Use this constructor for the second battery.
|
// 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;
|
datalayer_battery = datalayer_ptr;
|
||||||
allows_contactor_closing = nullptr;
|
allows_contactor_closing = nullptr;
|
||||||
can_interface = targetCan;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the default constructor to create the first or single battery.
|
// Use the default constructor to create the first or single battery.
|
||||||
SantaFePhevBattery() {
|
SantaFePhevBattery() {
|
||||||
datalayer_battery = &datalayer.battery;
|
datalayer_battery = &datalayer.battery;
|
||||||
allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing;
|
allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing;
|
||||||
can_interface = can_config.battery;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void setup(void);
|
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.
|
// If not null, this battery decides when the contactor can be closed and writes the value here.
|
||||||
bool* allows_contactor_closing;
|
bool* allows_contactor_closing;
|
||||||
|
|
||||||
int can_interface;
|
|
||||||
|
|
||||||
static const int MAX_PACK_VOLTAGE_DV = 4040; //5000 = 500.0V
|
static const int MAX_PACK_VOLTAGE_DV = 4040; //5000 = 500.0V
|
||||||
static const int MIN_PACK_VOLTAGE_DV = 2880;
|
static const int MIN_PACK_VOLTAGE_DV = 2880;
|
||||||
static const int MAX_CELL_DEVIATION_MV = 250;
|
static const int MAX_CELL_DEVIATION_MV = 250;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "../datalayer/datalayer.h"
|
#include "../datalayer/datalayer.h"
|
||||||
#include "../include.h"
|
#include "../include.h"
|
||||||
#include "CanBattery.h"
|
#include "CanBattery.h"
|
||||||
|
#include "TESLA-HTML.h"
|
||||||
|
|
||||||
#define BATTERY_SELECTED
|
#define BATTERY_SELECTED
|
||||||
#ifdef TESLA_MODEL_3Y_BATTERY
|
#ifdef TESLA_MODEL_3Y_BATTERY
|
||||||
|
@ -23,6 +24,17 @@ class TeslaBattery : public CanBattery {
|
||||||
virtual void update_values();
|
virtual void update_values();
|
||||||
virtual void transmit_can(unsigned long currentMillis);
|
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:
|
protected:
|
||||||
/* Modify these if needed */
|
/* Modify these if needed */
|
||||||
static const int MAXCHARGEPOWERALLOWED =
|
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 {
|
class TestFakeBattery : public CanBattery {
|
||||||
public:
|
public:
|
||||||
// Use this constructor for the second battery.
|
// 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;
|
datalayer_battery = datalayer_ptr;
|
||||||
can_interface = targetCan;
|
|
||||||
allows_contactor_closing = nullptr;
|
allows_contactor_closing = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the default constructor to create the first or single battery.
|
// Use the default constructor to create the first or single battery.
|
||||||
TestFakeBattery() {
|
TestFakeBattery() {
|
||||||
datalayer_battery = &datalayer.battery;
|
datalayer_battery = &datalayer.battery;
|
||||||
can_interface = can_config.battery;
|
|
||||||
allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing;
|
allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,9 +26,11 @@ class TestFakeBattery : public CanBattery {
|
||||||
virtual void update_values();
|
virtual void update_values();
|
||||||
virtual void transmit_can(unsigned long currentMillis);
|
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:
|
private:
|
||||||
DATALAYER_BATTERY_TYPE* datalayer_battery;
|
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.
|
// If not null, this battery decides when the contactor can be closed and writes the value here.
|
||||||
bool* allows_contactor_closing;
|
bool* allows_contactor_closing;
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "../include.h"
|
#include "../include.h"
|
||||||
|
|
||||||
#include "CanBattery.h"
|
#include "CanBattery.h"
|
||||||
|
#include "VOLVO-SPA-HTML.h"
|
||||||
|
|
||||||
#define BATTERY_SELECTED
|
#define BATTERY_SELECTED
|
||||||
#define SELECTED_BATTERY_CLASS VolvoSpaBattery
|
#define SELECTED_BATTERY_CLASS VolvoSpaBattery
|
||||||
|
@ -15,6 +16,15 @@ class VolvoSpaBattery : public CanBattery {
|
||||||
virtual void update_values();
|
virtual void update_values();
|
||||||
virtual void transmit_can(unsigned long currentMillis);
|
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:
|
private:
|
||||||
void readCellVoltages();
|
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 "../include.h"
|
||||||
|
|
||||||
#include "CanBattery.h"
|
#include "CanBattery.h"
|
||||||
|
#include "VOLVO-SPA-HYBRID-HTML.h"
|
||||||
|
|
||||||
#define BATTERY_SELECTED
|
#define BATTERY_SELECTED
|
||||||
#define SELECTED_BATTERY_CLASS VolvoSpaHybridBattery
|
#define SELECTED_BATTERY_CLASS VolvoSpaHybridBattery
|
||||||
|
@ -15,7 +16,19 @@ class VolvoSpaHybridBattery : public CanBattery {
|
||||||
virtual void update_values();
|
virtual void update_values();
|
||||||
virtual void transmit_can(unsigned long currentMillis);
|
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:
|
private:
|
||||||
|
VolvoSpaHybridHtmlRenderer renderer;
|
||||||
void readCellVoltages();
|
void readCellVoltages();
|
||||||
|
|
||||||
static const int MAX_PACK_VOLTAGE_DV = 4294; //5000 = 500.0V
|
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 */
|
/** True if the inverter allows for the contactors to close */
|
||||||
bool inverter_allows_contactor_closing = true;
|
bool inverter_allows_contactor_closing = true;
|
||||||
#ifdef CONTACTOR_CONTROL
|
|
||||||
/** True if the contactor controlled by battery-emulator is closed */
|
/** True if the contactor controlled by battery-emulator is closed */
|
||||||
bool contactors_engaged = false;
|
bool contactors_engaged = false;
|
||||||
/** True if the contactor controlled by battery-emulator is closed. Determined by check_interconnect_available(); if voltage is OK */
|
/** True if the contactor controlled by battery-emulator is closed. Determined by check_interconnect_available(); if voltage is OK */
|
||||||
bool contactors_battery2_engaged = false;
|
bool contactors_battery2_engaged = false;
|
||||||
#endif
|
|
||||||
/** True if the BMS is being reset, by cutting power towards it */
|
/** True if the BMS is being reset, by cutting power towards it */
|
||||||
bool BMS_reset_in_progress = false;
|
bool BMS_reset_in_progress = false;
|
||||||
/** True if the BMS is starting up */
|
/** True if the BMS is starting up */
|
||||||
|
|
|
@ -241,65 +241,65 @@ void update_machineryprotection() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DOUBLE_BATTERY // Additional Double-Battery safeties are checked here
|
// 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
|
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
|
// Pause function is on
|
||||||
if (emulator_pause_request_ON) {
|
if (emulator_pause_request_ON) {
|
||||||
datalayer.battery2.status.max_discharge_power_W = 0;
|
datalayer.battery2.status.max_discharge_power_W = 0;
|
||||||
datalayer.battery2.status.max_charge_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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (soh_diff_pptt > MAX_SOH_DEVIATION_PPTT) {
|
if (!datalayer.battery2.status.CAN_battery_still_alive) {
|
||||||
set_event(EVENT_SOH_DIFFERENCE, (uint8_t)(MAX_SOH_DEVIATION_PPTT / 100));
|
set_event(EVENT_CAN_BATTERY2_MISSING, can_config.battery_double);
|
||||||
} else {
|
} 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
|
//Safeties verified, Zero charge/discharge ampere values incase any safety wrote the W to 0
|
||||||
if (datalayer.battery.status.max_discharge_power_W == 0) {
|
if (datalayer.battery.status.max_discharge_power_W == 0) {
|
||||||
datalayer.battery.status.max_discharge_current_dA = 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;
|
emulator_pause_status = PAUSING;
|
||||||
datalayer.battery.status.max_discharge_power_W = 0;
|
datalayer.battery.status.max_discharge_power_W = 0;
|
||||||
datalayer.battery.status.max_charge_power_W = 0;
|
datalayer.battery.status.max_charge_power_W = 0;
|
||||||
#ifdef DOUBLE_BATTERY
|
if (battery2) {
|
||||||
datalayer.battery2.status.max_discharge_power_W = 0;
|
datalayer.battery2.status.max_discharge_power_W = 0;
|
||||||
datalayer.battery2.status.max_charge_power_W = 0;
|
datalayer.battery2.status.max_charge_power_W = 0;
|
||||||
#endif
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
clear_event(EVENT_PAUSE_BEGIN);
|
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);
|
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
|
#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 += ".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 += ".low-voltage { color: red; }"; // Style for low voltage text
|
||||||
content += ".voltage-values { margin-bottom: 10px; }"; // Style for voltage values section
|
content += ".voltage-values { margin-bottom: 10px; }"; // Style for voltage values section
|
||||||
#ifdef DOUBLE_BATTERY
|
|
||||||
content +=
|
if (battery2) {
|
||||||
"#graph, #graph2 {display: flex;align-items: flex-end;height: 200px;border: 1px solid #ccc;position: "
|
content +=
|
||||||
"relative;}";
|
"#graph, #graph2 {display: flex;align-items: flex-end;height: 200px;border: 1px solid #ccc;position: "
|
||||||
#else
|
"relative;}";
|
||||||
content += "#graph {display: flex;align-items: flex-end;height: 200px;border: 1px solid #ccc;position: relative;}";
|
} else {
|
||||||
#endif
|
content +=
|
||||||
|
"#graph {display: flex;align-items: flex-end;height: 200px;border: 1px solid #ccc;position: relative;}";
|
||||||
|
}
|
||||||
content +=
|
content +=
|
||||||
".bar {margin: 0 0px;background-color: blue;display: inline-block;position: relative;cursor: pointer;border: "
|
".bar {margin: 0 0px;background-color: blue;display: inline-block;position: relative;cursor: pointer;border: "
|
||||||
"1px solid white; /* Add this line */}";
|
"1px solid white; /* Add this line */}";
|
||||||
#ifdef DOUBLE_BATTERY
|
|
||||||
content += "#valueDisplay, #valueDisplay2 {text-align: left;font-weight: bold;margin-top: 10px;}";
|
if (battery2) {
|
||||||
#else
|
content += "#valueDisplay, #valueDisplay2 {text-align: left;font-weight: bold;margin-top: 10px;}";
|
||||||
content += "#valueDisplay {text-align: left;font-weight: bold;margin-top: 10px;}";
|
} else {
|
||||||
#endif
|
content += "#valueDisplay {text-align: left;font-weight: bold;margin-top: 10px;}";
|
||||||
|
}
|
||||||
content += "</style>";
|
content += "</style>";
|
||||||
|
|
||||||
content += "<button onclick='home()'>Back to main page</button>";
|
content += "<button onclick='home()'>Back to main page</button>";
|
||||||
|
@ -68,43 +71,43 @@ String cellmonitor_processor(const String& var) {
|
||||||
// Close the block
|
// Close the block
|
||||||
content += "</div>";
|
content += "</div>";
|
||||||
|
|
||||||
#ifdef DOUBLE_BATTERY
|
if (battery2) {
|
||||||
// Start a new block with a specific background color
|
// Start a new block with a specific background color
|
||||||
content += "<div style='background-color: #303E41; padding: 10px; margin-bottom: 10px; border-radius: 50px'>";
|
content += "<div style='background-color: #303E41; padding: 10px; margin-bottom: 10px; border-radius: 50px'>";
|
||||||
|
|
||||||
// Display max, min, and deviation voltage values
|
// Display max, min, and deviation voltage values
|
||||||
content += "<div id='voltageValues2' class='voltage-values'></div>";
|
content += "<div id='voltageValues2' class='voltage-values'></div>";
|
||||||
// Display cells
|
// Display cells
|
||||||
content += "<div id='cellContainer2' class='container'></div>";
|
content += "<div id='cellContainer2' class='container'></div>";
|
||||||
// Display bars
|
// Display bars
|
||||||
content += "<div id='graph2'></div>";
|
content += "<div id='graph2'></div>";
|
||||||
// Display single hovered value
|
// Display single hovered value
|
||||||
content += "<div id='valueDisplay2'>Value: ...</div>";
|
content += "<div id='valueDisplay2'>Value: ...</div>";
|
||||||
//Legend for graph
|
//Legend for graph
|
||||||
content +=
|
|
||||||
"<span style='color: white; background-color: blue; font-weight: bold; padding: 2px 8px; border-radius: 4px; "
|
|
||||||
"margin-right: 15px;'>Idle</span>";
|
|
||||||
|
|
||||||
bool battery2_balancing = false;
|
|
||||||
for (uint8_t i = 0u; i < datalayer.battery2.info.number_of_cells; i++) {
|
|
||||||
battery2_balancing = datalayer.battery2.status.cell_balancing_status[i];
|
|
||||||
if (battery2_balancing)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (battery2_balancing) {
|
|
||||||
content +=
|
content +=
|
||||||
"<span style='color: black; background-color: #00FFFF; font-weight: bold; padding: 2px 8px; border-radius: "
|
"<span style='color: white; background-color: blue; font-weight: bold; padding: 2px 8px; border-radius: 4px; "
|
||||||
"4px; margin-right: 15px;'>Balancing</span>";
|
"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>";
|
content += "<script>";
|
||||||
// Populate cell data
|
// Populate cell data
|
||||||
|
@ -237,136 +240,136 @@ String cellmonitor_processor(const String& var) {
|
||||||
"available';";
|
"available';";
|
||||||
content += "}";
|
content += "}";
|
||||||
|
|
||||||
#ifdef DOUBLE_BATTERY
|
if (battery2) {
|
||||||
// Populate cell data
|
// Populate cell data
|
||||||
content += "const data2 = [";
|
content += "const data2 = [";
|
||||||
for (uint8_t i = 0u; i < datalayer.battery2.info.number_of_cells; i++) {
|
for (uint8_t i = 0u; i < datalayer.battery2.info.number_of_cells; i++) {
|
||||||
if (datalayer.battery2.status.cell_voltages_mV[i] == 0) {
|
if (datalayer.battery2.status.cell_voltages_mV[i] == 0) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
content += String(datalayer.battery2.status.cell_voltages_mV[i]) + ",";
|
||||||
}
|
}
|
||||||
content += String(datalayer.battery2.status.cell_voltages_mV[i]) + ",";
|
content += "];";
|
||||||
}
|
|
||||||
content += "];";
|
|
||||||
|
|
||||||
content += "const balancing2 = [";
|
content += "const balancing2 = [";
|
||||||
for (uint8_t i = 0u; i < datalayer.battery2.info.number_of_cells; i++) {
|
for (uint8_t i = 0u; i < datalayer.battery2.info.number_of_cells; i++) {
|
||||||
if (datalayer.battery2.status.cell_voltages_mV[i] == 0) {
|
if (datalayer.battery2.status.cell_voltages_mV[i] == 0) {
|
||||||
continue;
|
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
|
// Automatic refresh is nice
|
||||||
content += "setTimeout(function(){ location.reload(true); }, 20000);";
|
content += "setTimeout(function(){ location.reload(true); }, 20000);";
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "../../charger/CHARGERS.h"
|
#include "../../charger/CHARGERS.h"
|
||||||
#include "../../datalayer/datalayer.h"
|
#include "../../datalayer/datalayer.h"
|
||||||
|
#include "../../include.h"
|
||||||
|
|
||||||
String settings_processor(const String& var) {
|
String settings_processor(const String& var) {
|
||||||
if (var == "X") {
|
if (var == "X") {
|
||||||
|
@ -26,18 +27,13 @@ String settings_processor(const String& var) {
|
||||||
"<h4 style='color: white;'>Password: ######## <span id='Password'></span> <button "
|
"<h4 style='color: white;'>Password: ######## <span id='Password'></span> <button "
|
||||||
"onclick='editPassword()'>Edit</button></h4>";
|
"onclick='editPassword()'>Edit</button></h4>";
|
||||||
|
|
||||||
#ifndef RS485_BATTERY_SELECTED
|
content +=
|
||||||
content += "<h4 style='color: white;'>Battery interface: <span id='Battery'>" +
|
"<h4 style='color: white;'>Battery interface: <span id='Battery'>" + battery->interface_name() + "</span></h4>";
|
||||||
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
|
|
||||||
|
|
||||||
#ifdef DOUBLE_BATTERY
|
if (battery2) {
|
||||||
content += "<h4 style='color: white;'>Battery #2 interface: <span id='Battery'>" +
|
content += "<h4 style='color: white;'>Battery #2 interface: <span id='Battery'>" + battery->interface_name() +
|
||||||
String(getCANInterfaceName(can_config.battery_double)) + "</span></h4>";
|
"</span></h4>";
|
||||||
#endif // DOUBLE_BATTERY
|
}
|
||||||
|
|
||||||
if (inverter) {
|
if (inverter) {
|
||||||
content += "<h4 style='color: white;'>Inverter interface: <span id='Inverter'>" +
|
content += "<h4 style='color: white;'>Inverter interface: <span id='Inverter'>" +
|
||||||
|
@ -93,53 +89,47 @@ String settings_processor(const String& var) {
|
||||||
// Close the block
|
// Close the block
|
||||||
content += "</div>";
|
content += "</div>";
|
||||||
|
|
||||||
#ifdef TEST_FAKE_BATTERY
|
if (battery->supports_set_fake_voltage()) {
|
||||||
// Start a new block with blue background color
|
content += "<div style='background-color: #2E37AD; padding: 10px; margin-bottom: 10px;border-radius: 50px'>";
|
||||||
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) +
|
||||||
float voltageFloat =
|
" V </span> <button onclick='editFakeBatteryVoltage()'>Edit</button></h4>";
|
||||||
static_cast<float>(datalayer.battery.status.voltage_dV) / 10.0; // Convert to float and divide by 10
|
content += "</div>";
|
||||||
content += "<h4 style='color: white;'>Fake battery voltage: " + String(voltageFloat, 1) +
|
}
|
||||||
" V </span> <button onclick='editFakeBatteryVoltage()'>Edit</button></h4>";
|
|
||||||
|
|
||||||
// Close the block
|
if (battery->supports_manual_balancing()) {
|
||||||
content += "</div>";
|
// Start a new block with grey background color
|
||||||
#endif
|
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
|
// Close the block
|
||||||
content += "<div style='background-color: #303E47; padding: 10px; margin-bottom: 10px;border-radius: 50px'>";
|
content += "</div>";
|
||||||
|
}
|
||||||
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
|
|
||||||
|
|
||||||
if (charger) {
|
if (charger) {
|
||||||
// Start a new block with orange background color
|
// Start a new block with orange background color
|
||||||
|
@ -252,7 +242,6 @@ String settings_processor(const String& var) {
|
||||||
"between 0 "
|
"between 0 "
|
||||||
"and 1000.0');}}}";
|
"and 1000.0');}}}";
|
||||||
|
|
||||||
#ifdef TESLA_MODEL_3Y_BATTERY
|
|
||||||
content +=
|
content +=
|
||||||
"function editTeslaBalAct(){var value=prompt('Enable or disable forced LFP balancing. Makes the battery charge "
|
"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 "
|
"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','/"
|
"XMLHttpRequest();xhr.onload=editComplete;xhr.onerror=editError;xhr.open('GET','/"
|
||||||
"BalMaxDevCellV?value='+value,true);xhr.send();}else{alert('Invalid value. Please enter a value "
|
"BalMaxDevCellV?value='+value,true);xhr.send();}else{alert('Invalid value. Please enter a value "
|
||||||
"between 300 and 600');}}}";
|
"between 300 and 600');}}}";
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef TEST_FAKE_BATTERY
|
if (battery->supports_set_fake_voltage()) {
|
||||||
content +=
|
content +=
|
||||||
"function editFakeBatteryVoltage(){var value=prompt('Enter new fake battery "
|
"function editFakeBatteryVoltage(){var value=prompt('Enter new fake battery "
|
||||||
"voltage');if(value!==null){if(value>=0&&value<=5000){var xhr=new "
|
"voltage');if(value!==null){if(value>=0&&value<=5000){var xhr=new "
|
||||||
"XMLHttpRequest();xhr.onload=editComplete;xhr.onerror=editError;xhr.open('GET','/"
|
"XMLHttpRequest();xhr.onload=editComplete;xhr.onerror=editError;xhr.open('GET','/"
|
||||||
"updateFakeBatteryVoltage?value='+value,true);xhr.send();}else{alert('Invalid value. Please enter a value "
|
"updateFakeBatteryVoltage?value='+value,true);xhr.send();}else{alert('Invalid value. Please enter a value "
|
||||||
"between 0 and 1000');}}}";
|
"between 0 and 1000');}}}";
|
||||||
#endif
|
}
|
||||||
|
|
||||||
if (charger) {
|
if (charger) {
|
||||||
content +=
|
content +=
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
#include <Preferences.h>
|
#include <Preferences.h>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include "../../../USER_SECRETS.h"
|
#include "../../../USER_SECRETS.h"
|
||||||
|
#include "../../battery/BATTERIES.h"
|
||||||
|
#include "../../battery/Battery.h"
|
||||||
#include "../../datalayer/datalayer.h"
|
#include "../../datalayer/datalayer.h"
|
||||||
#include "../../datalayer/datalayer_extended.h"
|
#include "../../datalayer/datalayer_extended.h"
|
||||||
#include "../../lib/bblanchon-ArduinoJson/ArduinoJson.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
|
// Route for editing BATTERY_USE_VOLTAGE_LIMITS
|
||||||
server.on("/updateUseVoltageLimit", HTTP_GET, [](AsyncWebServerRequest* request) {
|
server.on("/updateUseVoltageLimit", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||||
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
|
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
|
// Route for editing FakeBatteryVoltage
|
||||||
server.on("/updateFakeBatteryVoltage", HTTP_GET, [](AsyncWebServerRequest* request) {
|
server.on("/updateFakeBatteryVoltage", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||||
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
|
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
|
||||||
|
@ -677,13 +611,10 @@ void init_webserver() {
|
||||||
String value = request->getParam("value")->value();
|
String value = request->getParam("value")->value();
|
||||||
float val = value.toFloat();
|
float val = value.toFloat();
|
||||||
|
|
||||||
datalayer.battery.status.voltage_dV = val * 10;
|
battery->set_fake_voltage(val);
|
||||||
|
|
||||||
request->send(200, "text/plain", "Updated successfully");
|
request->send(200, "text/plain", "Updated successfully");
|
||||||
});
|
});
|
||||||
#endif // TEST_FAKE_BATTERY
|
|
||||||
|
|
||||||
#ifdef TESLA_MODEL_3Y_BATTERY
|
|
||||||
|
|
||||||
// Route for editing balancing enabled
|
// Route for editing balancing enabled
|
||||||
server.on("/TeslaBalAct", HTTP_GET, [](AsyncWebServerRequest* request) {
|
server.on("/TeslaBalAct", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||||
|
@ -768,7 +699,6 @@ void init_webserver() {
|
||||||
request->send(400, "text/plain", "Bad Request");
|
request->send(400, "text/plain", "Bad Request");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
#endif
|
|
||||||
|
|
||||||
if (charger) {
|
if (charger) {
|
||||||
// Route for editing ChargerTargetV
|
// Route for editing ChargerTargetV
|
||||||
|
@ -1022,9 +952,9 @@ String processor(const String& var) {
|
||||||
content += "</h4>";
|
content += "</h4>";
|
||||||
content += "<h4 style='color: white;'>Battery protocol: ";
|
content += "<h4 style='color: white;'>Battery protocol: ";
|
||||||
content += datalayer.system.info.battery_protocol;
|
content += datalayer.system.info.battery_protocol;
|
||||||
#ifdef DOUBLE_BATTERY
|
if (battery2) {
|
||||||
content += " (Double battery)";
|
content += " (Double battery)";
|
||||||
#endif // DOUBLE_BATTERY
|
}
|
||||||
if (datalayer.battery.info.chemistry == battery_chemistry_enum::LFP) {
|
if (datalayer.battery.info.chemistry == battery_chemistry_enum::LFP) {
|
||||||
content += " (LFP)";
|
content += " (LFP)";
|
||||||
}
|
}
|
||||||
|
@ -1045,14 +975,14 @@ String processor(const String& var) {
|
||||||
// Close the block
|
// Close the block
|
||||||
content += "</div>";
|
content += "</div>";
|
||||||
|
|
||||||
#ifdef DOUBLE_BATTERY
|
if (battery2) {
|
||||||
// Start a new block with a specific background color. Color changes depending on BMS status
|
// 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='display: flex; width: 100%;'>";
|
||||||
content += "<div style='flex: 1; background-color: ";
|
content += "<div style='flex: 1; background-color: ";
|
||||||
#else
|
} else {
|
||||||
// Start a new block with a specific background color. Color changes depending on system status
|
// Start a new block with a specific background color. Color changes depending on system status
|
||||||
content += "<div style='background-color: ";
|
content += "<div style='background-color: ";
|
||||||
#endif // DOUBLE_BATTERY
|
}
|
||||||
|
|
||||||
switch (led_get_color()) {
|
switch (led_get_color()) {
|
||||||
case led_color::GREEN:
|
case led_color::GREEN:
|
||||||
|
@ -1176,27 +1106,27 @@ String processor(const String& var) {
|
||||||
}
|
}
|
||||||
content += "</h4>";
|
content += "</h4>";
|
||||||
|
|
||||||
#ifdef MEB_BATTERY
|
if (battery->supports_real_BMS_status()) {
|
||||||
content += "<h4>Battery BMS status: ";
|
content += "<h4>Battery BMS status: ";
|
||||||
switch (datalayer.battery.status.real_bms_status) {
|
switch (datalayer.battery.status.real_bms_status) {
|
||||||
case BMS_ACTIVE:
|
case BMS_ACTIVE:
|
||||||
content += String("OK");
|
content += String("OK");
|
||||||
break;
|
break;
|
||||||
case BMS_FAULT:
|
case BMS_FAULT:
|
||||||
content += String("FAULT");
|
content += String("FAULT");
|
||||||
break;
|
break;
|
||||||
case BMS_DISCONNECTED:
|
case BMS_DISCONNECTED:
|
||||||
content += String("DISCONNECTED");
|
content += String("DISCONNECTED");
|
||||||
break;
|
break;
|
||||||
case BMS_STANDBY:
|
case BMS_STANDBY:
|
||||||
content += String("STANDBY");
|
content += String("STANDBY");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
content += String("??");
|
content += String("??");
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
content += "</h4>";
|
||||||
}
|
}
|
||||||
content += "</h4>";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (datalayer.battery.status.current_dA == 0) {
|
if (datalayer.battery.status.current_dA == 0) {
|
||||||
content += "<h4>Battery idle</h4>";
|
content += "<h4>Battery idle</h4>";
|
||||||
|
@ -1286,159 +1216,161 @@ String processor(const String& var) {
|
||||||
// Close the block
|
// Close the block
|
||||||
content += "</div>";
|
content += "</div>";
|
||||||
|
|
||||||
#ifdef DOUBLE_BATTERY
|
if (battery2) {
|
||||||
content += "<div style='flex: 1; background-color: ";
|
content += "<div style='flex: 1; background-color: ";
|
||||||
switch (datalayer.battery.status.bms_status) {
|
switch (datalayer.battery.status.bms_status) {
|
||||||
case ACTIVE:
|
case ACTIVE:
|
||||||
content += "#2D3F2F;";
|
content += "#2D3F2F;";
|
||||||
break;
|
break;
|
||||||
case FAULT:
|
case FAULT:
|
||||||
content += "#A70107;";
|
content += "#A70107;";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
content += "#2D3F2F;";
|
content += "#2D3F2F;";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Add the common style properties
|
// Add the common style properties
|
||||||
content += "padding: 10px; margin-bottom: 10px; border-radius: 50px;'>";
|
content += "padding: 10px; margin-bottom: 10px; border-radius: 50px;'>";
|
||||||
|
|
||||||
// Display battery statistics within this block
|
// Display battery statistics within this block
|
||||||
socRealFloat =
|
socRealFloat =
|
||||||
static_cast<float>(datalayer.battery2.status.real_soc) / 100.0; // Convert to float and divide by 100
|
static_cast<float>(datalayer.battery2.status.real_soc) / 100.0; // Convert to float and divide by 100
|
||||||
//socScaledFloat; // Same value used for bat2
|
//socScaledFloat; // Same value used for bat2
|
||||||
sohFloat = static_cast<float>(datalayer.battery2.status.soh_pptt) / 100.0; // Convert to float and divide by 100
|
sohFloat = static_cast<float>(datalayer.battery2.status.soh_pptt) / 100.0; // Convert to float and divide by 100
|
||||||
voltageFloat =
|
voltageFloat =
|
||||||
static_cast<float>(datalayer.battery2.status.voltage_dV) / 10.0; // Convert to float and divide by 10
|
static_cast<float>(datalayer.battery2.status.voltage_dV) / 10.0; // Convert to float and divide by 10
|
||||||
currentFloat =
|
currentFloat =
|
||||||
static_cast<float>(datalayer.battery2.status.current_dA) / 10.0; // Convert to float and divide by 10
|
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
|
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
|
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
|
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;
|
cell_delta_mv = datalayer.battery2.status.cell_max_voltage_mV - datalayer.battery2.status.cell_min_voltage_mV;
|
||||||
|
|
||||||
if (datalayer.battery.settings.soc_scaling_active)
|
if (datalayer.battery.settings.soc_scaling_active)
|
||||||
content += "<h4 style='color: white;'>Scaled SOC: " + String(socScaledFloat, 2) +
|
content += "<h4 style='color: white;'>Scaled SOC: " + String(socScaledFloat, 2) +
|
||||||
"% (real: " + String(socRealFloat, 2) + "%)</h4>";
|
"% (real: " + String(socRealFloat, 2) + "%)</h4>";
|
||||||
else
|
else
|
||||||
content += "<h4 style='color: white;'>SOC: " + String(socRealFloat, 2) + "%</h4>";
|
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;'>SOH: " + String(sohFloat, 2) + "%</h4>";
|
||||||
content += "<h4 style='color: white;'>Voltage: " + String(voltageFloat, 1) +
|
content += "<h4 style='color: white;'>Voltage: " + String(voltageFloat, 1) +
|
||||||
" V Current: " + String(currentFloat, 1) + " A</h4>";
|
" V Current: " + String(currentFloat, 1) + " A</h4>";
|
||||||
content += formatPowerValue("Power", powerFloat, "", 1);
|
content += formatPowerValue("Power", powerFloat, "", 1);
|
||||||
|
|
||||||
if (datalayer.battery.settings.soc_scaling_active)
|
if (datalayer.battery.settings.soc_scaling_active)
|
||||||
content += "<h4 style='color: white;'>Scaled total capacity: " +
|
content += "<h4 style='color: white;'>Scaled total capacity: " +
|
||||||
formatPowerValue(datalayer.battery2.info.reported_total_capacity_Wh, "h", 1) +
|
formatPowerValue(datalayer.battery2.info.reported_total_capacity_Wh, "h", 1) +
|
||||||
" (real: " + formatPowerValue(datalayer.battery2.info.total_capacity_Wh, "h", 1) + ")</h4>";
|
" (real: " + formatPowerValue(datalayer.battery2.info.total_capacity_Wh, "h", 1) + ")</h4>";
|
||||||
else
|
else
|
||||||
content += formatPowerValue("Total capacity", datalayer.battery2.info.total_capacity_Wh, "h", 1);
|
content += formatPowerValue("Total capacity", datalayer.battery2.info.total_capacity_Wh, "h", 1);
|
||||||
|
|
||||||
if (datalayer.battery.settings.soc_scaling_active)
|
if (datalayer.battery.settings.soc_scaling_active)
|
||||||
content += "<h4 style='color: white;'>Scaled remaining capacity: " +
|
content += "<h4 style='color: white;'>Scaled remaining capacity: " +
|
||||||
formatPowerValue(datalayer.battery2.status.reported_remaining_capacity_Wh, "h", 1) +
|
formatPowerValue(datalayer.battery2.status.reported_remaining_capacity_Wh, "h", 1) +
|
||||||
" (real: " + formatPowerValue(datalayer.battery2.status.remaining_capacity_Wh, "h", 1) + ")</h4>";
|
" (real: " + formatPowerValue(datalayer.battery2.status.remaining_capacity_Wh, "h", 1) + ")</h4>";
|
||||||
else
|
else
|
||||||
content += formatPowerValue("Remaining capacity", datalayer.battery2.status.remaining_capacity_Wh, "h", 1);
|
content += formatPowerValue("Remaining capacity", datalayer.battery2.status.remaining_capacity_Wh, "h", 1);
|
||||||
|
|
||||||
if (datalayer.system.settings.equipment_stop_active) {
|
if (datalayer.system.settings.equipment_stop_active) {
|
||||||
content += formatPowerValue("Max discharge power", datalayer.battery2.status.max_discharge_power_W, "", 1, "red");
|
content +=
|
||||||
content += formatPowerValue("Max charge power", datalayer.battery2.status.max_charge_power_W, "", 1, "red");
|
formatPowerValue("Max discharge power", datalayer.battery2.status.max_discharge_power_W, "", 1, "red");
|
||||||
content += "<h4 style='color: red;'>Max discharge current: " + String(maxCurrentDischargeFloat, 1) + " A</h4>";
|
content += formatPowerValue("Max charge power", datalayer.battery2.status.max_charge_power_W, "", 1, "red");
|
||||||
content += "<h4 style='color: red;'>Max charge current: " + String(maxCurrentChargeFloat, 1) + " A</h4>";
|
content += "<h4 style='color: red;'>Max discharge current: " + String(maxCurrentDischargeFloat, 1) + " A</h4>";
|
||||||
} else {
|
content += "<h4 style='color: red;'>Max charge current: " + String(maxCurrentChargeFloat, 1) + " A</h4>";
|
||||||
content += formatPowerValue("Max discharge power", datalayer.battery2.status.max_discharge_power_W, "", 1);
|
} else {
|
||||||
content += formatPowerValue("Max charge power", datalayer.battery2.status.max_charge_power_W, "", 1);
|
content += formatPowerValue("Max discharge power", datalayer.battery2.status.max_discharge_power_W, "", 1);
|
||||||
content += "<h4 style='color: white;'>Max discharge current: " + String(maxCurrentDischargeFloat, 1) + " A</h4>";
|
content += formatPowerValue("Max charge power", datalayer.battery2.status.max_charge_power_W, "", 1);
|
||||||
content += "<h4 style='color: white;'>Max charge current: " + String(maxCurrentChargeFloat, 1) + " A</h4>";
|
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 / " +
|
content += "<h4>Cell min/max: " + String(datalayer.battery2.status.cell_min_voltage_mV) + " mV / " +
|
||||||
String(datalayer.battery2.status.cell_max_voltage_mV) + " mV</h4>";
|
String(datalayer.battery2.status.cell_max_voltage_mV) + " mV</h4>";
|
||||||
if (cell_delta_mv > datalayer.battery2.info.max_cell_voltage_deviation_mV) {
|
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>";
|
content += "<h4 style='color: red;'>Cell delta: " + String(cell_delta_mv) + " mV</h4>";
|
||||||
} else {
|
} else {
|
||||||
content += "<h4>Cell delta: " + String(cell_delta_mv) + " mV</h4>";
|
content += "<h4>Cell delta: " + String(cell_delta_mv) + " mV</h4>";
|
||||||
}
|
}
|
||||||
content +=
|
content += "<h4>Temperature min/max: " + String(tempMinFloat, 1) + " °C / " + String(tempMaxFloat, 1) +
|
||||||
"<h4>Temperature min/max: " + String(tempMinFloat, 1) + " °C / " + String(tempMaxFloat, 1) + " °C</h4>";
|
" °C</h4>";
|
||||||
if (datalayer.battery.status.bms_status == ACTIVE) {
|
if (datalayer.battery.status.bms_status == ACTIVE) {
|
||||||
content += "<h4>System status: OK </h4>";
|
content += "<h4>System status: OK </h4>";
|
||||||
} else if (datalayer.battery.status.bms_status == UPDATING) {
|
} else if (datalayer.battery.status.bms_status == UPDATING) {
|
||||||
content += "<h4>System status: UPDATING </h4>";
|
content += "<h4>System status: UPDATING </h4>";
|
||||||
} else {
|
} else {
|
||||||
content += "<h4>System status: FAULT </h4>";
|
content += "<h4>System status: FAULT </h4>";
|
||||||
}
|
}
|
||||||
if (datalayer.battery2.status.current_dA == 0) {
|
if (datalayer.battery2.status.current_dA == 0) {
|
||||||
content += "<h4>Battery idle</h4>";
|
content += "<h4>Battery idle</h4>";
|
||||||
} else if (datalayer.battery2.status.current_dA < 0) {
|
} else if (datalayer.battery2.status.current_dA < 0) {
|
||||||
content += "<h4>Battery discharging!</h4>";
|
content += "<h4>Battery discharging!</h4>";
|
||||||
} else { // > 0
|
} else { // > 0
|
||||||
content += "<h4>Battery charging!</h4>";
|
content += "<h4>Battery charging!</h4>";
|
||||||
}
|
}
|
||||||
|
|
||||||
content += "<h4>Automatic contactor closing allowed:</h4>";
|
content += "<h4>Automatic contactor closing allowed:</h4>";
|
||||||
content += "<h4>Battery: ";
|
content += "<h4>Battery: ";
|
||||||
if (datalayer.system.status.battery2_allowed_contactor_closing == true) {
|
if (datalayer.system.status.battery2_allowed_contactor_closing == true) {
|
||||||
content += "<span>✓</span>";
|
content += "<span>✓</span>";
|
||||||
} else {
|
} else {
|
||||||
content += "<span style='color: red;'>✕</span>";
|
content += "<span style='color: red;'>✕</span>";
|
||||||
}
|
}
|
||||||
|
|
||||||
content += " Inverter: ";
|
content += " Inverter: ";
|
||||||
if (datalayer.system.status.inverter_allows_contactor_closing == true) {
|
if (datalayer.system.status.inverter_allows_contactor_closing == true) {
|
||||||
content += "<span>✓</span></h4>";
|
content += "<span>✓</span></h4>";
|
||||||
} else {
|
} else {
|
||||||
content += "<span style='color: red;'>✕</span></h4>";
|
content += "<span style='color: red;'>✕</span></h4>";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (emulator_pause_status == NORMAL)
|
if (emulator_pause_status == NORMAL)
|
||||||
content += "<h4>Power status: " + String(get_emulator_pause_status().c_str()) + " </h4>";
|
content += "<h4>Power status: " + String(get_emulator_pause_status().c_str()) + " </h4>";
|
||||||
else
|
else
|
||||||
content += "<h4 style='color: red;'>Power status: " + String(get_emulator_pause_status().c_str()) + " </h4>";
|
content += "<h4 style='color: red;'>Power status: " + String(get_emulator_pause_status().c_str()) + " </h4>";
|
||||||
|
|
||||||
#ifdef CONTACTOR_CONTROL
|
#ifdef CONTACTOR_CONTROL
|
||||||
content += "<h4>Contactors controlled by emulator, state: ";
|
content += "<h4>Contactors controlled by emulator, state: ";
|
||||||
if (datalayer.system.status.contactors_battery2_engaged) {
|
if (datalayer.system.status.contactors_battery2_engaged) {
|
||||||
content += "<span style='color: green;'>ON</span>";
|
content += "<span style='color: green;'>ON</span>";
|
||||||
} else {
|
} else {
|
||||||
content += "<span style='color: red;'>OFF</span>";
|
content += "<span style='color: red;'>OFF</span>";
|
||||||
}
|
}
|
||||||
content += "</h4>";
|
content += "</h4>";
|
||||||
#ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY
|
#ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY
|
||||||
content += "<h4>Cont. Neg.: ";
|
content += "<h4>Cont. Neg.: ";
|
||||||
#ifdef PWM_CONTACTOR_CONTROL
|
#ifdef PWM_CONTACTOR_CONTROL
|
||||||
if (datalayer.system.status.contactors_battery2_engaged) {
|
if (datalayer.system.status.contactors_battery2_engaged) {
|
||||||
content += "<span style='color: green;'>Economized</span>";
|
content += "<span style='color: green;'>Economized</span>";
|
||||||
content += " Cont. Pos.: ";
|
content += " Cont. Pos.: ";
|
||||||
content += "<span style='color: green;'>Economized</span>";
|
content += "<span style='color: green;'>Economized</span>";
|
||||||
} else {
|
} else {
|
||||||
content += "<span style='color: red;'>✕</span>";
|
content += "<span style='color: red;'>✕</span>";
|
||||||
content += " Cont. Pos.: ";
|
content += " Cont. Pos.: ";
|
||||||
content += "<span style='color: red;'>✕</span>";
|
content += "<span style='color: red;'>✕</span>";
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // No PWM_CONTACTOR_CONTROL , we can read the pin and see feedback. Helpful if channel overloaded
|
#else // No PWM_CONTACTOR_CONTROL , we can read the pin and see feedback. Helpful if channel overloaded
|
||||||
if (digitalRead(SECOND_NEGATIVE_CONTACTOR_PIN) == HIGH) {
|
if (digitalRead(SECOND_NEGATIVE_CONTACTOR_PIN) == HIGH) {
|
||||||
content += "<span style='color: green;'>✓</span>";
|
content += "<span style='color: green;'>✓</span>";
|
||||||
} else {
|
} else {
|
||||||
content += "<span style='color: red;'>✕</span>";
|
content += "<span style='color: red;'>✕</span>";
|
||||||
}
|
}
|
||||||
|
|
||||||
content += " Cont. Pos.: ";
|
content += " Cont. Pos.: ";
|
||||||
if (digitalRead(SECOND_POSITIVE_CONTACTOR_PIN) == HIGH) {
|
if (digitalRead(SECOND_POSITIVE_CONTACTOR_PIN) == HIGH) {
|
||||||
content += "<span style='color: green;'>✓</span>";
|
content += "<span style='color: green;'>✓</span>";
|
||||||
} else {
|
} else {
|
||||||
content += "<span style='color: red;'>✕</span>";
|
content += "<span style='color: red;'>✕</span>";
|
||||||
}
|
}
|
||||||
#endif //no PWM_CONTACTOR_CONTROL
|
#endif //no PWM_CONTACTOR_CONTROL
|
||||||
content += "</h4>";
|
content += "</h4>";
|
||||||
#endif // CONTACTOR_CONTROL_DOUBLE_BATTERY
|
#endif // CONTACTOR_CONTROL_DOUBLE_BATTERY
|
||||||
#endif // CONTACTOR_CONTROL
|
#endif // CONTACTOR_CONTROL
|
||||||
|
|
||||||
content += "</div>";
|
content += "</div>";
|
||||||
content += "</div>";
|
content += "</div>";
|
||||||
#endif // DOUBLE_BATTERY
|
}
|
||||||
|
|
||||||
if (charger) {
|
if (charger) {
|
||||||
// Start a new block with orange background color
|
// Start a new block with orange background color
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue