mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-03 17:59:27 +02:00
Compare commits
91 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d8153c48bb | ||
![]() |
f3a6157080 | ||
![]() |
867512eecd | ||
![]() |
f9109d0348 | ||
![]() |
9943406836 | ||
![]() |
18ee9d6c27 | ||
![]() |
31ea1f0928 | ||
![]() |
dad97b36e1 | ||
![]() |
19a1634f4e | ||
![]() |
aa375dc36a | ||
![]() |
a91f8ab4d4 | ||
![]() |
7fbc9ffcc6 | ||
![]() |
579e0e0bcc | ||
![]() |
b26c451eaf | ||
![]() |
ee9e78e80b | ||
![]() |
706b4a7cea | ||
![]() |
f5ba607fa6 | ||
![]() |
572867b7ad | ||
![]() |
47e715ffe5 | ||
![]() |
c445bd1869 | ||
![]() |
28c0267cae | ||
![]() |
4058050423 | ||
![]() |
04d9a36292 | ||
![]() |
29129037b0 | ||
![]() |
c6b7ff82c0 | ||
![]() |
95ee6ff9ae | ||
![]() |
8cc10a1b71 | ||
![]() |
3ef3279527 | ||
![]() |
f48b4235c1 | ||
![]() |
cdf6314bff | ||
![]() |
48411680c6 | ||
![]() |
132029169d | ||
![]() |
483d4300b1 | ||
![]() |
397e8d03a1 | ||
![]() |
d336b75f56 | ||
![]() |
a7af7cf938 | ||
![]() |
2ba937bd32 | ||
![]() |
48d416b5c4 | ||
![]() |
ed2ebb00fd | ||
![]() |
0458267634 | ||
![]() |
a0de6b092b | ||
![]() |
0f2cbe8f04 | ||
![]() |
983105ab6a | ||
![]() |
5aa657e0b5 | ||
![]() |
69ae0385fb | ||
![]() |
a8d74f5885 | ||
![]() |
20b6ea5e85 | ||
![]() |
b8bbb02b20 | ||
![]() |
4896f3061a | ||
![]() |
0aaab6d4b7 | ||
![]() |
a12ea4b112 | ||
![]() |
e023962bfc | ||
![]() |
4df42e10b4 | ||
![]() |
ee4981b6e1 | ||
![]() |
172e260167 | ||
![]() |
fd52c0e5e7 | ||
![]() |
6982476e89 | ||
![]() |
24c1ce73ae | ||
![]() |
d8530a2b8b | ||
![]() |
7a5cd36e6d | ||
![]() |
3bfc350b65 | ||
![]() |
b52bc7bfd4 | ||
![]() |
89abc6a964 | ||
![]() |
45643237e4 | ||
![]() |
e66161176b | ||
![]() |
269c655dc5 | ||
![]() |
15143d1384 | ||
![]() |
0244468624 | ||
![]() |
3ead4d12d4 | ||
![]() |
5277665dd1 | ||
![]() |
79964a0601 | ||
![]() |
e11843e4a0 | ||
![]() |
df52d067e7 | ||
![]() |
1ea663c0b9 | ||
![]() |
73c6821a9a | ||
![]() |
26126bae1a | ||
![]() |
6094a2c85b | ||
![]() |
b5d14edb94 | ||
![]() |
bf14553d77 | ||
![]() |
fdc1fb61ba | ||
![]() |
2546b6da21 | ||
![]() |
7178e0376e | ||
![]() |
0aad11d9bc | ||
![]() |
29e6f52c4c | ||
![]() |
25393106b8 | ||
![]() |
fd2e5f2e52 | ||
![]() |
30a7b9d90e | ||
![]() |
e02cb61efc | ||
![]() |
64a3d5f5aa | ||
![]() |
cfdc1a3130 | ||
![]() |
54f9cb49f3 |
37 changed files with 828 additions and 541 deletions
|
@ -106,3 +106,20 @@ Navigate to Battery-Emulator/test folder
|
||||||
sudo cmake CMakeLists.txt
|
sudo cmake CMakeLists.txt
|
||||||
sudo make
|
sudo make
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Downloading a pull request build to test locally 🛜
|
||||||
|
If you want to test a pull request, you can download the precompiled binaries from the build system. To do this,start by clicking on the "**Checks**" tab
|
||||||
|
|
||||||
|
<img width="779" height="312" alt="image" src="https://github.com/user-attachments/assets/fc7783c1-ba61-440e-ab09-b53d2b49f1bb" />
|
||||||
|
|
||||||
|
Then select which hardware you need the binaries for. Currently we build for these hardwares:
|
||||||
|
- LilyGo T-CAN485
|
||||||
|
- Stark CMR
|
||||||
|
- LilyGo T-2CAN
|
||||||
|
|
||||||
|
<img width="647" height="476" alt="image" src="https://github.com/user-attachments/assets/fbb97719-0155-4792-9d91-c51e6052fa57" />
|
||||||
|
|
||||||
|
After selecting the hardware you need, click the "**Upload Artifact**", and there will be a download link. Download the file, and [OTA Update](https://github.com/dalathegreat/Battery-Emulator/wiki/OTA-Update) your device with this file!
|
||||||
|
|
||||||
|
<img width="1714" height="697" alt="image" src="https://github.com/user-attachments/assets/2e17f90f-cc7d-4265-b7bc-7aa5cc6b6ec8" />
|
||||||
|
|
||||||
|
|
54
README.md
54
README.md
|
@ -33,52 +33,18 @@ At the same time, EV manufacturers have been putting high capacity battery packs
|
||||||
|
|
||||||
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)
|
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)
|
||||||
|
|
||||||
## How to compile the software 💻
|
## How to install the software 💻
|
||||||
|
|
||||||
Start by watching this [quickstart guide](https://www.youtube.com/watch?v=hcl2GdHc0Y0)
|
Start by watching this [quickstart guide](https://www.youtube.com/watch?v=sR3t7j0R9Z0)
|
||||||
|
|
||||||
[](https://www.youtube.com/watch?v=hcl2GdHc0Y0)
|
[](https://www.youtube.com/watch?v=sR3t7j0R9Z0)
|
||||||
|
|
||||||
1. Download the Arduino IDE: https://www.arduino.cc/en/software
|
1. Open the [webinstaller page](https://dalathegreat.github.io/BE-Web-Installer/)
|
||||||
2. Open the Arduino IDE.
|
2. Follow the instructions on that page to install the software
|
||||||
3. Click `File` menu -> `Preferences` -> `Additional Development` -> `Additional Board Manager URLs` -> Enter the URL in the input box: `https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json` and click OK.
|
3. After successful installation, connect to the wireless network (Battery-Emulator , password: 123456789)
|
||||||
4. Click `Tools` menu -> `Board: "...."` -> `Boards Manager...`, install the `esp32` package by `Espressif Systems` (not `Arduino ESP32 Boards`), then press `Close`.
|
4. Go to setup page and configure component selection
|
||||||
|
5. (OPTIONAL, connect the board to your home Wifi)
|
||||||
**NOTE: The ESP32 version depends on which release of Battery-Emulator you are running! See the [Release Notes](https://github.com/dalathegreat/Battery-Emulator/releases) for more info on which version to use with the current version (Suggested ESP32 version X.Y.Z)**
|
6. Connect your battery and inverter to the board and you are done! 🔋⚡
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
5. The Arduino board should be set to `ESP32 Dev Module` and `Partition Scheme` to `Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS)` (under `Tools` -> `Board` -> `ESP32 Arduino`) with the following settings:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
6. Select which battery type you will use, along with other optional settings. This is done in the `USER_SETTINGS.h` file.
|
|
||||||
7. Copy the `USER_SECRETS.TEMPLATE.h` file to `USER_SECRETS.h` and update connectivity settings inside this file.
|
|
||||||
8. Press `Verify` and `Upload` to send the sketch to the board.
|
|
||||||
NOTE: In some cases, the LilyGo must be powered through the main power connector instead of USB-C
|
|
||||||
when performing the initial firmware upload.
|
|
||||||
NOTE: On Mac, the following USB driver may need to be installed: https://github.com/WCHSoftGroup/ch34xser_macos
|
|
||||||
|
|
||||||
NOTE: If you see garbled messages on the serial console, change the serial console to match the baud rate to the code, currently 115200.
|
|
||||||
|
|
||||||
NOTE: If you see the error `Sketch too big` then check you set the Partition Scheme above correctly.
|
|
||||||
|
|
||||||
This video explains all the above mentioned steps:
|
|
||||||
<https://youtu.be/_mH2AjnAjDk>
|
|
||||||
|
|
||||||
|
|
||||||
### Linux Development Environment Setup
|
|
||||||
In addition to the steps above, ESP32 requires a dependency for a Python module, pyserial install using the cli.\
|
|
||||||
```python3 -m pip install pyserial```
|
|
||||||
|
|
||||||
If you're using Ubuntu , use apt to manage the dependencies of arduino:\
|
|
||||||
pyserial install: ```sudo apt install python3-serial```
|
|
||||||
|
|
||||||
Arduino AppImage must be set as executable after downloading to run correctly\
|
|
||||||
example: ```chmod 775 arduino-ide_2.3.3_Linux_64bit.AppImage```
|
|
||||||
|
|
||||||
Also you might need to install FUSE to run appimages
|
|
||||||
```sudo apt install libfuse2```
|
|
||||||
|
|
||||||
## Dependencies 📖
|
## Dependencies 📖
|
||||||
This code uses the following excellent libraries:
|
This code uses the following excellent libraries:
|
||||||
|
@ -88,7 +54,7 @@ This code uses the following excellent libraries:
|
||||||
- [eModbus/eModbus](https://github.com/eModbus/eModbus) MIT-License
|
- [eModbus/eModbus](https://github.com/eModbus/eModbus) MIT-License
|
||||||
- [ESP32Async/AsyncTCP](https://github.com/ESP32Async/AsyncTCP) LGPL-3.0 license
|
- [ESP32Async/AsyncTCP](https://github.com/ESP32Async/AsyncTCP) LGPL-3.0 license
|
||||||
- [ESP32Async/ESPAsyncWebServer](https://github.com/ESP32Async/ESPAsyncWebServer) LGPL-3.0 license
|
- [ESP32Async/ESPAsyncWebServer](https://github.com/ESP32Async/ESPAsyncWebServer) LGPL-3.0 license
|
||||||
- [miwagner/ESP32-Arduino-CAN](https://github.com/miwagner/ESP32-Arduino-CAN/) MIT-License
|
- [pierremolinaro/acan-esp32](https://github.com/pierremolinaro/acan-esp32) MIT-License
|
||||||
- [pierremolinaro/acan2515](https://github.com/pierremolinaro/acan2515) MIT-License
|
- [pierremolinaro/acan2515](https://github.com/pierremolinaro/acan2515) MIT-License
|
||||||
- [pierremolinaro/acan2517FD](https://github.com/pierremolinaro/acan2517FD) MIT-License
|
- [pierremolinaro/acan2517FD](https://github.com/pierremolinaro/acan2517FD) MIT-License
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// The current software version, shown on webserver
|
// The current software version, shown on webserver
|
||||||
const char* version_number = "9.0.0";
|
const char* version_number = "9.2.dev";
|
||||||
|
|
||||||
// Interval timers
|
// Interval timers
|
||||||
volatile unsigned long currentMillis = 0;
|
volatile unsigned long currentMillis = 0;
|
||||||
|
@ -86,9 +86,7 @@ void connectivity_loop(void*) {
|
||||||
// Init wifi
|
// Init wifi
|
||||||
init_WiFi();
|
init_WiFi();
|
||||||
|
|
||||||
if (webserver_enabled) {
|
|
||||||
init_webserver();
|
init_webserver();
|
||||||
}
|
|
||||||
|
|
||||||
if (mdns_enabled) {
|
if (mdns_enabled) {
|
||||||
init_mDNS();
|
init_mDNS();
|
||||||
|
@ -98,9 +96,7 @@ void connectivity_loop(void*) {
|
||||||
START_TIME_MEASUREMENT(wifi);
|
START_TIME_MEASUREMENT(wifi);
|
||||||
wifi_monitor();
|
wifi_monitor();
|
||||||
|
|
||||||
if (webserver_enabled) {
|
|
||||||
ota_monitor();
|
ota_monitor();
|
||||||
}
|
|
||||||
|
|
||||||
END_TIME_MEASUREMENT_MAX(wifi, datalayer.system.status.wifi_task_10s_max_us);
|
END_TIME_MEASUREMENT_MAX(wifi, datalayer.system.status.wifi_task_10s_max_us);
|
||||||
|
|
||||||
|
@ -388,11 +384,9 @@ void core_loop(void*) {
|
||||||
|
|
||||||
END_TIME_MEASUREMENT_MAX(comm, datalayer.system.status.time_comm_us);
|
END_TIME_MEASUREMENT_MAX(comm, datalayer.system.status.time_comm_us);
|
||||||
|
|
||||||
if (webserver_enabled) {
|
|
||||||
START_TIME_MEASUREMENT(ota);
|
START_TIME_MEASUREMENT(ota);
|
||||||
ElegantOTA.loop();
|
ElegantOTA.loop();
|
||||||
END_TIME_MEASUREMENT_MAX(ota, datalayer.system.status.time_ota_us);
|
END_TIME_MEASUREMENT_MAX(ota, datalayer.system.status.time_ota_us);
|
||||||
}
|
|
||||||
|
|
||||||
// Process
|
// Process
|
||||||
currentMillis = millis();
|
currentMillis = millis();
|
||||||
|
|
|
@ -296,6 +296,8 @@ bool user_selected_tesla_GTW_rightHandDrive = true;
|
||||||
uint16_t user_selected_tesla_GTW_mapRegion = 2;
|
uint16_t user_selected_tesla_GTW_mapRegion = 2;
|
||||||
uint16_t user_selected_tesla_GTW_chassisType = 2;
|
uint16_t user_selected_tesla_GTW_chassisType = 2;
|
||||||
uint16_t user_selected_tesla_GTW_packEnergy = 1;
|
uint16_t user_selected_tesla_GTW_packEnergy = 1;
|
||||||
|
/* User-selected EGMP+others settings */
|
||||||
|
bool user_selected_use_estimated_SOC = false;
|
||||||
|
|
||||||
// Use 0V for user selected cell/pack voltage defaults (On boot will be replaced with saved values from NVM)
|
// Use 0V for user selected cell/pack voltage defaults (On boot will be replaced with saved values from NVM)
|
||||||
uint16_t user_selected_max_pack_voltage_dV = 0;
|
uint16_t user_selected_max_pack_voltage_dV = 0;
|
||||||
|
|
|
@ -62,7 +62,7 @@ extern uint16_t user_selected_max_pack_voltage_dV;
|
||||||
extern uint16_t user_selected_min_pack_voltage_dV;
|
extern uint16_t user_selected_min_pack_voltage_dV;
|
||||||
extern uint16_t user_selected_max_cell_voltage_mV;
|
extern uint16_t user_selected_max_cell_voltage_mV;
|
||||||
extern uint16_t user_selected_min_cell_voltage_mV;
|
extern uint16_t user_selected_min_cell_voltage_mV;
|
||||||
|
extern bool user_selected_use_estimated_SOC;
|
||||||
extern bool user_selected_LEAF_interlock_mandatory;
|
extern bool user_selected_LEAF_interlock_mandatory;
|
||||||
extern bool user_selected_tesla_digital_HVIL;
|
extern bool user_selected_tesla_digital_HVIL;
|
||||||
extern uint16_t user_selected_tesla_GTW_country;
|
extern uint16_t user_selected_tesla_GTW_country;
|
||||||
|
|
|
@ -5,8 +5,27 @@
|
||||||
#include "../devboard/utils/events.h"
|
#include "../devboard/utils/events.h"
|
||||||
|
|
||||||
/* TODO:
|
/* TODO:
|
||||||
This integration is still ongoing. Here is what still needs to be done in order to use this battery type
|
This integration is still ongoing. The same integration can be used on multiple variants of the Stellantis platforms
|
||||||
- Disable the isolation resistance requirement that opens contactors after 30s under load. Factory mode?
|
- eCMP: Disable the isolation resistance requirement that opens contactors after 30s under load. Factory mode?
|
||||||
|
|
||||||
|
- MysteryVan: Map more values from constantly transmitted instead of PID
|
||||||
|
- ADD CAN sending towards the battery (CAN-logs of full vehicle wanted!)
|
||||||
|
- Following CAN messages need to be sent towards it:
|
||||||
|
- VCU: 4C9 , 565 , 398, 448, 458, 4F1 , 342, 3E2 , 402 , 422 , 482 4D1
|
||||||
|
- CMM: 478 , 558, 1A8, 4B8 1F8 498 4E8
|
||||||
|
- OBC: 531 441 541 551 3C1
|
||||||
|
- BSIInfo_382
|
||||||
|
- VCU_BSI_Wakeup_27A
|
||||||
|
- V2_BSI_552
|
||||||
|
- CRASH_4C8
|
||||||
|
- EVSE plug in (optional): 108, 109, 119
|
||||||
|
- CRASH_4C8
|
||||||
|
- CRNT_SENS_095
|
||||||
|
- MCU 526
|
||||||
|
- JDD 55F NEW
|
||||||
|
|
||||||
|
- STLA medium: Everything missing
|
||||||
|
- ADD CAN sending towards the battery (CAN-logs of full vehicle wanted!)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Do not change code below unless you are sure what you are doing */
|
/* Do not change code below unless you are sure what you are doing */
|
||||||
|
@ -95,6 +114,8 @@ void EcmpBattery::update_values() {
|
||||||
datalayer.battery.status.cell_max_voltage_mV = pid_high_cell_voltage;
|
datalayer.battery.status.cell_max_voltage_mV = pid_high_cell_voltage;
|
||||||
datalayer.battery.status.cell_min_voltage_mV = pid_low_cell_voltage;
|
datalayer.battery.status.cell_min_voltage_mV = pid_low_cell_voltage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
datalayer.battery.info.number_of_cells = NUMBER_OF_CELL_MEASUREMENTS_IN_BATTERY; //50/75kWh sends valid cellcount
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update extended datalayer (More Battery Info page)
|
// Update extended datalayer (More Battery Info page)
|
||||||
|
@ -170,6 +191,28 @@ void EcmpBattery::update_values() {
|
||||||
datalayer_extended.stellantisECMP.pid_contactor_closing_counter = pid_contactor_closing_counter;
|
datalayer_extended.stellantisECMP.pid_contactor_closing_counter = pid_contactor_closing_counter;
|
||||||
datalayer_extended.stellantisECMP.pid_date_of_manufacture = pid_date_of_manufacture;
|
datalayer_extended.stellantisECMP.pid_date_of_manufacture = pid_date_of_manufacture;
|
||||||
datalayer_extended.stellantisECMP.pid_SOH_cell_1 = pid_SOH_cell_1;
|
datalayer_extended.stellantisECMP.pid_SOH_cell_1 = pid_SOH_cell_1;
|
||||||
|
// Update extended datalayer for MysteryVan
|
||||||
|
datalayer_extended.stellantisECMP.MysteryVan = MysteryVan;
|
||||||
|
datalayer_extended.stellantisECMP.CONTACTORS_STATE = CONTACTORS_STATE;
|
||||||
|
datalayer_extended.stellantisECMP.CrashMemorized = HV_BATT_CRASH_MEMORIZED;
|
||||||
|
datalayer_extended.stellantisECMP.CONTACTOR_OPENING_REASON = CONTACTOR_OPENING_REASON;
|
||||||
|
datalayer_extended.stellantisECMP.TBMU_FAULT_TYPE = TBMU_FAULT_TYPE;
|
||||||
|
datalayer_extended.stellantisECMP.HV_BATT_FC_INSU_MINUS_RES = HV_BATT_FC_INSU_MINUS_RES;
|
||||||
|
datalayer_extended.stellantisECMP.HV_BATT_FC_INSU_PLUS_RES = HV_BATT_FC_INSU_PLUS_RES;
|
||||||
|
datalayer_extended.stellantisECMP.HV_BATT_FC_VHL_INSU_PLUS_RES = HV_BATT_FC_VHL_INSU_PLUS_RES;
|
||||||
|
datalayer_extended.stellantisECMP.HV_BATT_ONLY_INSU_MINUS_RES = HV_BATT_ONLY_INSU_MINUS_RES;
|
||||||
|
datalayer_extended.stellantisECMP.HV_BATT_ONLY_INSU_MINUS_RES = HV_BATT_ONLY_INSU_MINUS_RES;
|
||||||
|
datalayer_extended.stellantisECMP.ALERT_CELL_POOR_CONSIST = ALERT_CELL_POOR_CONSIST;
|
||||||
|
datalayer_extended.stellantisECMP.ALERT_OVERCHARGE = ALERT_OVERCHARGE;
|
||||||
|
datalayer_extended.stellantisECMP.ALERT_BATT = ALERT_BATT;
|
||||||
|
datalayer_extended.stellantisECMP.ALERT_LOW_SOC = ALERT_LOW_SOC;
|
||||||
|
datalayer_extended.stellantisECMP.ALERT_HIGH_SOC = ALERT_HIGH_SOC;
|
||||||
|
datalayer_extended.stellantisECMP.ALERT_SOC_JUMP = ALERT_SOC_JUMP;
|
||||||
|
datalayer_extended.stellantisECMP.ALERT_TEMP_DIFF = ALERT_TEMP_DIFF;
|
||||||
|
datalayer_extended.stellantisECMP.ALERT_HIGH_TEMP = ALERT_HIGH_TEMP;
|
||||||
|
datalayer_extended.stellantisECMP.ALERT_OVERVOLTAGE = ALERT_OVERVOLTAGE;
|
||||||
|
datalayer_extended.stellantisECMP.ALERT_CELL_OVERVOLTAGE = ALERT_CELL_OVERVOLTAGE;
|
||||||
|
datalayer_extended.stellantisECMP.ALERT_CELL_UNDERVOLTAGE = ALERT_CELL_UNDERVOLTAGE;
|
||||||
|
|
||||||
if (battery_InterlockOpen) {
|
if (battery_InterlockOpen) {
|
||||||
set_event(EVENT_HVIL_FAILURE, 0);
|
set_event(EVENT_HVIL_FAILURE, 0);
|
||||||
|
@ -192,56 +235,221 @@ void EcmpBattery::update_values() {
|
||||||
|
|
||||||
void EcmpBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
void EcmpBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||||
switch (rx_frame.ID) {
|
switch (rx_frame.ID) {
|
||||||
case 0x2D4: //MysteryVan 50/75kWh platform
|
case 0x2D4: //MysteryVan 50/75kWh platform (TBMU 100ms periodic)
|
||||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
MysteryVan = true;
|
MysteryVan = true;
|
||||||
|
SOE_MAX_CURRENT_TEMP = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; // (Wh, 0-200000)
|
||||||
|
FRONT_MACHINE_POWER_LIMIT = (rx_frame.data.u8[4] << 6) | ((rx_frame.data.u8[5] & 0xFC) >> 2); // (W 0-1000000)
|
||||||
|
REAR_MACHINE_POWER_LIMIT = ((rx_frame.data.u8[5] & 0x03) << 12) | (rx_frame.data.u8[6] << 4) |
|
||||||
|
((rx_frame.data.u8[7] & 0xF0) >> 4); // (W 0-1000000)
|
||||||
break;
|
break;
|
||||||
case 0x3B4: //MysteryVan 50/75kWh platform
|
case 0x3B4: //MysteryVan 50/75kWh platform (TBMU 100ms periodic)
|
||||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
|
EVSE_INSTANT_DC_HV_CURRENT =
|
||||||
|
((rx_frame.data.u8[2] & 0x03) << 12) | (rx_frame.data.u8[3] << 2) | ((rx_frame.data.u8[4] & 0xC0) >> 6);
|
||||||
|
EVSE_STATE = ((rx_frame.data.u8[4] & 0x38) >> 3); /*Enumeration below
|
||||||
|
000: NOT CONNECTED
|
||||||
|
001: CONNECTED
|
||||||
|
010: INITIALISATION
|
||||||
|
011: READY
|
||||||
|
100: PRECHARGE IN PROGRESS
|
||||||
|
101: TRANSFER IN PROGRESS
|
||||||
|
110: NOT READY
|
||||||
|
111: Reserved */
|
||||||
|
HV_BATT_SOE_HD = ((rx_frame.data.u8[4] & 0x03) << 12) | (rx_frame.data.u8[5] << 4) |
|
||||||
|
((rx_frame.data.u8[6] & 0xF0) >> 4); // (Wh, 0-200000)
|
||||||
|
HV_BATT_SOE_MAX = ((rx_frame.data.u8[6] & 0x03) << 8) | rx_frame.data.u8[7]; // (Wh, 0-200000)
|
||||||
|
CHECKSUM_FRAME_3B4 = (rx_frame.data.u8[0] & 0xF0) >> 4;
|
||||||
|
COUNTER_3B4 = (rx_frame.data.u8[0] & 0x0F);
|
||||||
break;
|
break;
|
||||||
case 0x2F4: //MysteryVan 50/75kWh platform
|
case 0x2F4: //MysteryVan 50/75kWh platform (Event triggered when charging)
|
||||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
|
//TBMU_EVSE_DC_MES_VOLTAGE = (rx_frame.data.u8[0] << 6) | (rx_frame.data.u8[1] >> 2); //V 0-1000 //Fastcharger info, not needed for BE
|
||||||
|
//TBMU_EVSE_DC_MIN_VOLTAGE = ((rx_frame.data.u8[1] & 0x03) << 8) | rx_frame.data.u8[2]; //V 0-1000 //Fastcharger info, not needed for BE
|
||||||
|
//TBMU_EVSE_DC_MES_CURRENT = (rx_frame.data.u8[3] << 4) | ((rx_frame.data.u8[4] & 0xF0) >> 4); //A -2000 - 2000 //Fastcharger info, not needed for BE
|
||||||
|
//TBMU_EVSE_CHRG_REQ = (rx_frame.data.u8[4] & 0x0C) >> 2; //00 No request, 01 Stop request //Fastcharger info, not needed for BE
|
||||||
|
//HV_STORAGE_MAX_I = ((rx_frame.data.u8[4] & 0x03) << 12) | (rx_frame.data.u8[5] << 2) | //Fastcharger info, not needed for BE
|
||||||
|
//((rx_frame.data.u8[6] & 0xC0) >> 6); //A -2000 - 2000
|
||||||
|
//TBMU_EVSE_DC_MAX_POWER = ((rx_frame.data.u8[6] & 0x3F) << 8) | rx_frame.data.u8[7]; //W -1000000 - 0 //Fastcharger info, not needed for BE
|
||||||
break;
|
break;
|
||||||
case 0x3F4: //MysteryVan 50/75kWh platform
|
case 0x3F4: //MysteryVan 50/75kWh platform (Temperature sensors)
|
||||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
|
switch (((rx_frame.data.u8[0] & 0xE0) >> 5)) //Mux resides in top 3 bits of frame0
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
BMS_PROBETEMP[0] = (rx_frame.data.u8[1] - 40);
|
||||||
|
BMS_PROBETEMP[1] = (rx_frame.data.u8[2] - 40);
|
||||||
|
BMS_PROBETEMP[2] = (rx_frame.data.u8[3] - 40);
|
||||||
|
BMS_PROBETEMP[3] = (rx_frame.data.u8[4] - 40);
|
||||||
|
BMS_PROBETEMP[4] = (rx_frame.data.u8[5] - 40);
|
||||||
|
BMS_PROBETEMP[5] = (rx_frame.data.u8[6] - 40);
|
||||||
|
BMS_PROBETEMP[6] = (rx_frame.data.u8[7] - 40);
|
||||||
break;
|
break;
|
||||||
case 0x554: //MysteryVan 50/75kWh platform
|
default: //There are in total 64 temperature measurements in the BMS. We do not need to sample them all.
|
||||||
|
break; //For future, we could read them all if we want to.
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x554: //MysteryVan 50/75kWh platform (Discharge/Charge limits)
|
||||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
|
HV_BATT_PEAK_DISCH_POWER_HD = (rx_frame.data.u8[1] << 6) | (rx_frame.data.u8[2] >> 2); //0-1000000 W
|
||||||
|
HV_BATT_PEAK_CH_POWER_HD = ((rx_frame.data.u8[2] & 0x03) << 12) | (rx_frame.data.u8[3] << 4) |
|
||||||
|
((rx_frame.data.u8[4] & 0xF0) >> 4); // -1000000 - 0 W
|
||||||
|
HV_BATT_NOM_CH_POWER_HD = ((rx_frame.data.u8[4] & 0x0F) << 12) | (rx_frame.data.u8[5] << 6) |
|
||||||
|
((rx_frame.data.u8[6] & 0xC0) >> 6); // -1000000 - 0 W
|
||||||
|
MAX_ALLOW_CHRG_CURRENT = ((rx_frame.data.u8[6] & 0x3F) << 8) | rx_frame.data.u8[7];
|
||||||
|
CHECKSUM_FRAME_554 = (rx_frame.data.u8[0] & 0xF0) >> 4; //Frame checksum 0xE
|
||||||
|
COUNTER_554 = (rx_frame.data.u8[0] & 0x0F);
|
||||||
break;
|
break;
|
||||||
case 0x373: //MysteryVan 50/75kWh platform
|
case 0x373: //MysteryVan 50/75kWh platform
|
||||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
|
REQ_CLEAR_DTC_TBMU = ((rx_frame.data.u8[3] & 0x40) >> 7);
|
||||||
|
TBCU_48V_WAKEUP = (rx_frame.data.u8[3] >> 7);
|
||||||
|
HV_BATT_MAX_REAL_CURR = (rx_frame.data.u8[5] << 7) | (rx_frame.data.u8[6] >> 1); //A -2000 - 2000 0.1 scaling
|
||||||
|
TBMU_FAULT_TYPE = (rx_frame.data.u8[7] & 0xE0) >> 5;
|
||||||
|
/*000: No fault
|
||||||
|
001: FirstLevelFault: Warning Lamp
|
||||||
|
010: SecondLevelFault: Stop Lamp
|
||||||
|
011: ThirdLevelFault: Stop Lamp + contactor opening (EPS shutdown)
|
||||||
|
100: FourthLevelFault: Stop Lamp + Active Discharge
|
||||||
|
101: Inhibition of powertrain activation
|
||||||
|
110: Reserved
|
||||||
|
111: Invalid*/
|
||||||
|
HV_BATT_REAL_VOLT_HD = ((rx_frame.data.u8[3] & 0x3F) << 8) | (rx_frame.data.u8[4]); //V 0-1000 * 0.1 scaling
|
||||||
|
HV_BATT_REAL_CURR_HD = (rx_frame.data.u8[1] << 8) | (rx_frame.data.u8[2]); //A -2000 - 2000 0.1 scaling
|
||||||
|
CHECKSUM_FRAME_373 = (rx_frame.data.u8[0] & 0xF0) >> 4; //Frame checksum 0xD
|
||||||
|
COUNTER_373 = (rx_frame.data.u8[0] & 0x0F);
|
||||||
break;
|
break;
|
||||||
case 0x4F4: //MysteryVan 50/75kWh platform
|
case 0x4F4: //MysteryVan 50/75kWh platform
|
||||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
|
HV_BATT_CRASH_MEMORIZED = ((rx_frame.data.u8[2] & 0x08) >> 3);
|
||||||
|
HV_BATT_COLD_CRANK_ACK = ((rx_frame.data.u8[2] & 0x04) >> 2);
|
||||||
|
HV_BATT_CHARGE_NEEDED_STATE = ((rx_frame.data.u8[2] & 0x02) >> 1);
|
||||||
|
HV_BATT_NOM_CH_VOLTAGE = ((rx_frame.data.u8[2] & 0x01) << 8) | (rx_frame.data.u8[3]); //V 0 - 500
|
||||||
|
HV_BATT_NOM_CH_CURRENT = rx_frame.data.u8[4]; // -120 - 0 0.5scaling
|
||||||
|
HV_BATT_GENERATED_HEAT_RATE = (rx_frame.data.u8[5] << 1) | (rx_frame.data.u8[6] >> 7); //W 0-50000
|
||||||
|
REQ_MIL_LAMP_CONTINOUS = (rx_frame.data.u8[7] & 0x04) >> 2;
|
||||||
|
REQ_BLINK_STOP_AND_SERVICE_LAMP = (rx_frame.data.u8[7] & 0x02) >> 1;
|
||||||
|
CMD_RESET_MIL = (rx_frame.data.u8[7] & 0x01);
|
||||||
|
HV_BATT_SOC = (rx_frame.data.u8[1] << 2) | (rx_frame.data.u8[2] >> 6);
|
||||||
|
CONTACTORS_STATE =
|
||||||
|
(rx_frame.data.u8[2] & 0x30) >> 4; //00 : contactor open 01 : pre-load contactor 10 : contactor close
|
||||||
|
HV_BATT_DISCONT_WARNING_OPEN = (rx_frame.data.u8[7] & 0x08) >> 3;
|
||||||
|
CHECKSUM_FRAME_4F4 = (rx_frame.data.u8[0] & 0xF0) >> 4;
|
||||||
|
COUNTER_4F4 = (rx_frame.data.u8[0] & 0x0F);
|
||||||
break;
|
break;
|
||||||
case 0x414: //MysteryVan 50/75kWh platform
|
case 0x414: //MysteryVan 50/75kWh platform
|
||||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
|
HV_BATT_REAL_POWER_HD = (rx_frame.data.u8[1] << 7) | (rx_frame.data.u8[2] >> 1);
|
||||||
|
MAX_ALLOW_CHRG_POWER =
|
||||||
|
((rx_frame.data.u8[2] & 0x01) << 13) | (rx_frame.data.u8[3] << 5) | ((rx_frame.data.u8[4] & 0xF8) >> 3);
|
||||||
|
MAX_ALLOW_DISCHRG_POWER =
|
||||||
|
((rx_frame.data.u8[5] & 0x07) << 11) | (rx_frame.data.u8[6] << 3) | ((rx_frame.data.u8[7] & 0xE0) >> 5);
|
||||||
|
CHECKSUM_FRAME_414 = (rx_frame.data.u8[0] & 0xF0) >> 4; //Frame checksum 0x9
|
||||||
|
COUNTER_414 = (rx_frame.data.u8[0] & 0x0F);
|
||||||
break;
|
break;
|
||||||
case 0x353: //MysteryVan 50/75kWh platform
|
case 0x353: //MysteryVan 50/75kWh platform
|
||||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
|
HV_BATT_COP_VOLTAGE =
|
||||||
|
(rx_frame.data.u8[1] << 5) | (rx_frame.data.u8[2] >> 3); //Real voltage HV battery (dV, 0-5000)
|
||||||
|
HV_BATT_COP_CURRENT =
|
||||||
|
(rx_frame.data.u8[3] << 5) | (rx_frame.data.u8[4] >> 3); //High resolution battery current (dA, -4000 - 4000)
|
||||||
|
CHECKSUM_FRAME_353 = (rx_frame.data.u8[0] & 0xF0) >> 4; //Frame checksum 0xB
|
||||||
|
COUNTER_353 = (rx_frame.data.u8[0] & 0x0F);
|
||||||
break;
|
break;
|
||||||
case 0x474: //MysteryVan 50/75kWh platform
|
case 0x474: //MysteryVan 50/75kWh platform
|
||||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
|
BMS_DC_RELAY_MES_EVSE_VOLTAGE = (rx_frame.data.u8[1] << 6) | (rx_frame.data.u8[2] >> 2); //V 0-1000
|
||||||
|
FAST_CHARGE_CONTACTOR_STATE = (rx_frame.data.u8[2] & 0x03);
|
||||||
|
/*00: Contactors Opened
|
||||||
|
01: Contactors Closed
|
||||||
|
10: No Request
|
||||||
|
11: WELDING TEST*/
|
||||||
|
BMS_FASTCHARGE_STATUS = (rx_frame.data.u8[4] & 0x03);
|
||||||
|
/*00 : not charging
|
||||||
|
01 : charging
|
||||||
|
10 : charging fault
|
||||||
|
11 : charging finished*/
|
||||||
|
CHECKSUM_FRAME_474 = (rx_frame.data.u8[0] & 0xF0) >> 4; //Frame checksum 0xF
|
||||||
|
COUNTER_474 = (rx_frame.data.u8[0] & 0x0F);
|
||||||
break;
|
break;
|
||||||
case 0x574: //MysteryVan 50/75kWh platform
|
case 0x574: //MysteryVan 50/75kWh platform
|
||||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
|
HV_BATT_FC_INSU_MINUS_RES = (rx_frame.data.u8[0] << 5) | (rx_frame.data.u8[1] >> 3); //kOhm (0-60000)
|
||||||
|
HV_BATT_FC_VHL_INSU_PLUS_RES =
|
||||||
|
((rx_frame.data.u8[1] & 0x07) << 10) | (rx_frame.data.u8[2] << 2) | ((rx_frame.data.u8[3] & 0xC0) >> 6);
|
||||||
|
HV_BATT_FC_INSU_PLUS_RES = (rx_frame.data.u8[5] << 4) | (rx_frame.data.u8[6] >> 4);
|
||||||
|
HV_BATT_ONLY_INSU_MINUS_RES = ((rx_frame.data.u8[3] & 0x3F) << 7) | (rx_frame.data.u8[4] >> 1);
|
||||||
break;
|
break;
|
||||||
case 0x583: //MysteryVan 50/75kWh platform
|
case 0x583: //MysteryVan 50/75kWh platform (CAN-FD also?)
|
||||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
|
ALERT_OVERCHARGE = (rx_frame.data.u8[4] & 0x20) >> 5;
|
||||||
|
NUMBER_PROBE_TEMP_MAX = rx_frame.data.u8[0];
|
||||||
|
NUMBER_PROBE_TEMP_MIN = rx_frame.data.u8[1];
|
||||||
|
TEMPERATURE_MINIMUM_C = rx_frame.data.u8[2] - 40;
|
||||||
|
ALERT_BATT = (rx_frame.data.u8[3] & 0x80) >> 7;
|
||||||
|
ALERT_TEMP_DIFF = (rx_frame.data.u8[3] & 0x40) >> 6;
|
||||||
|
ALERT_HIGH_TEMP = (rx_frame.data.u8[3] & 0x20) >> 5;
|
||||||
|
ALERT_OVERVOLTAGE = (rx_frame.data.u8[3] & 0x10) >> 4;
|
||||||
|
ALERT_LOW_SOC = (rx_frame.data.u8[3] & 0x08) >> 3;
|
||||||
|
ALERT_HIGH_SOC = (rx_frame.data.u8[3] & 0x04) >> 2;
|
||||||
|
ALERT_CELL_OVERVOLTAGE = (rx_frame.data.u8[3] & 0x02) >> 1;
|
||||||
|
ALERT_CELL_UNDERVOLTAGE = (rx_frame.data.u8[3] & 0x01);
|
||||||
|
ALERT_SOC_JUMP = (rx_frame.data.u8[4] & 0x80) >> 7;
|
||||||
|
ALERT_CELL_POOR_CONSIST = (rx_frame.data.u8[4] & 0x40) >> 6;
|
||||||
|
CONTACTOR_OPENING_REASON = (rx_frame.data.u8[4] & 0x1C) >> 2;
|
||||||
|
/*
|
||||||
|
000 : Not error
|
||||||
|
001 : Crash
|
||||||
|
010 : 12V supply source undervoltage
|
||||||
|
011 : 12V supply source overvoltage
|
||||||
|
100 : Battery temperature
|
||||||
|
101 : interlock line open
|
||||||
|
110 : e-Service plug disconnected
|
||||||
|
111 : Not valid
|
||||||
|
*/
|
||||||
|
NUMBER_OF_TEMPERATURE_SENSORS_IN_BATTERY = rx_frame.data.u8[5];
|
||||||
|
NUMBER_OF_CELL_MEASUREMENTS_IN_BATTERY = rx_frame.data.u8[6];
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case 0x314: //MysteryVan 50/75kWh platform
|
case 0x314: //MysteryVan 50/75kWh platform
|
||||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
|
MIN_ALLOW_DISCHRG_VOLTAGE = (rx_frame.data.u8[1] << 3) | (rx_frame.data.u8[2] >> 5); //V (0-1000)
|
||||||
|
//EVSE_DC_MAX_CURRENT = ((rx_frame.data.u8[2] & 0x1F) << 5) | (rx_frame.data.u8[3] >> 3); //Fastcharger info, not needed for BE
|
||||||
|
//TBMU_EVSE_DC_MAX_VOLTAGE //Fastcharger info, not needed for BE
|
||||||
|
//TBMU_MAX_CHRG_SCKT_TEMP //Fastcharger info, not needed for BE
|
||||||
|
//DC_CHARGE_MODE_AVAIL //Fastcharger info, not needed for BE
|
||||||
|
//BIDIR_V2HG_MODE_AVAIL //Fastcharger info, not needed for BE
|
||||||
|
//TBMU_CHRG_CONN_CONF //Fastcharger info, not needed for BE
|
||||||
|
//EVSE_GRID_FAULT //Fastcharger info, not needed for BE
|
||||||
|
CHECKSUM_FRAME_314 = (rx_frame.data.u8[0] & 0xF0) >> 4; //Frame checksum 0x8
|
||||||
|
COUNTER_314 = (rx_frame.data.u8[0] & 0x0F);
|
||||||
break;
|
break;
|
||||||
case 0x254: //MysteryVan 50/75kWh platform
|
case 0x254: //MysteryVan 50/75kWh platform
|
||||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
|
//HV_BATT_SOE_MAX_HR = frame6 & frame7 //Only on FD-CAN variant of the message. FD has length 7, non-fd 5
|
||||||
|
HV_BATT_NOMINAL_DISCH_CURR_HD = (rx_frame.data.u8[0] << 7) | (rx_frame.data.u8[1] >> 1); //dA (0-20000)
|
||||||
|
HV_BATT_PEAK_DISCH_CURR_HD = (rx_frame.data.u8[2] << 7) | (rx_frame.data.u8[3] >> 1); //dA (0-20000)
|
||||||
|
HV_BATT_STABLE_DISCH_CURR_HD = (rx_frame.data.u8[4] << 7) | (rx_frame.data.u8[5] >> 1); //dA (0-20000)
|
||||||
break;
|
break;
|
||||||
case 0x2B4: //MysteryVan 50/75kWh platform
|
case 0x2B4: //MysteryVan 50/75kWh platform
|
||||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
|
HV_BATT_NOMINAL_CHARGE_CURR_HD = (rx_frame.data.u8[0] << 7) | (rx_frame.data.u8[1] >> 1);
|
||||||
|
HV_BATT_PEAK_CHARGE_CURR_HD = (rx_frame.data.u8[2] << 7) | (rx_frame.data.u8[3] >> 1);
|
||||||
|
HV_BATT_STABLE_CHARGE_CURR_HD = (rx_frame.data.u8[4] << 7) | (rx_frame.data.u8[5] >> 1);
|
||||||
break;
|
break;
|
||||||
case 0x4D4: //MysteryVan 50/75kWh platform
|
case 0x4D4: //MysteryVan 50/75kWh platform
|
||||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
|
HV_BATT_STABLE_CHARGE_POWER_HD = (rx_frame.data.u8[0] << 6) | (rx_frame.data.u8[1] >> 2);
|
||||||
|
HV_BATT_STABLE_DISCH_POWER_HD =
|
||||||
|
((rx_frame.data.u8[2] & 0x03) << 12) | (rx_frame.data.u8[3] << 4) | ((rx_frame.data.u8[4] & 0xF0) >> 4);
|
||||||
|
HV_BATT_NOMINAL_DISCH_POWER_HD =
|
||||||
|
((rx_frame.data.u8[4] & 0x0F) << 10) | (rx_frame.data.u8[5] << 2) | ((rx_frame.data.u8[6] & 0xC0) >> 6);
|
||||||
|
MAX_ALLOW_DISCHRG_CURRENT = ((rx_frame.data.u8[6] & 0x3F) << 5) | (rx_frame.data.u8[7] >> 3);
|
||||||
|
RC01_PERM_SYNTH_TBMU = (rx_frame.data.u8[7] & 0x04) >> 2; //TBMU Readiness Code synthesis
|
||||||
|
CHECKSUM_FRAME_4D4 = (rx_frame.data.u8[0] & 0xF0) >> 4; //Frame checksum 0x5
|
||||||
|
COUNTER_4D4 = (rx_frame.data.u8[0] & 0x0F);
|
||||||
break;
|
break;
|
||||||
case 0x125: //Common
|
case 0x125: //Common eCMP
|
||||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
battery_soc = (rx_frame.data.u8[0] << 2) |
|
battery_soc = (rx_frame.data.u8[0] << 2) |
|
||||||
(rx_frame.data.u8[1] >> 6); // Byte1, bit 7 length 10 (0x3FE when abnormal) (0-1000 ppt)
|
(rx_frame.data.u8[1] >> 6); // Byte1, bit 7 length 10 (0x3FE when abnormal) (0-1000 ppt)
|
||||||
|
|
|
@ -133,7 +133,68 @@ class EcmpBattery : public CanBattery {
|
||||||
uint32_t pid_date_of_manufacture = NOT_SAMPLED_YET;
|
uint32_t pid_date_of_manufacture = NOT_SAMPLED_YET;
|
||||||
uint16_t pid_SOH_cell_1 = NOT_SAMPLED_YET;
|
uint16_t pid_SOH_cell_1 = NOT_SAMPLED_YET;
|
||||||
|
|
||||||
unsigned long previousMillis10 = 0; // will store last time a 10ms CAN Message was sent
|
//MysteryVan platform (allcaps to make code easer to co-exist)
|
||||||
|
uint16_t SOE_MAX_CURRENT_TEMP = 0;
|
||||||
|
uint16_t FRONT_MACHINE_POWER_LIMIT = 0;
|
||||||
|
uint16_t REAR_MACHINE_POWER_LIMIT = 0;
|
||||||
|
uint16_t EVSE_INSTANT_DC_HV_CURRENT = 0;
|
||||||
|
uint8_t EVSE_STATE = 0;
|
||||||
|
uint16_t HV_BATT_SOE_HD = 0;
|
||||||
|
uint16_t HV_BATT_SOE_MAX = 0;
|
||||||
|
uint8_t CHECKSUM_FRAME_314, CHECKSUM_FRAME_3B4, CHECKSUM_FRAME_554, CHECKSUM_FRAME_373, CHECKSUM_FRAME_4F4,
|
||||||
|
CHECKSUM_FRAME_414, CHECKSUM_FRAME_353, CHECKSUM_FRAME_474, CHECKSUM_FRAME_4D4 = 0;
|
||||||
|
uint16_t HV_STORAGE_MAX_I = 0;
|
||||||
|
int8_t BMS_PROBETEMP[7] = {0};
|
||||||
|
uint8_t COUNTER_314, COUNTER_554, COUNTER_373, COUNTER_3B4, COUNTER_4F4, COUNTER_414, COUNTER_353, COUNTER_474,
|
||||||
|
COUNTER_4D4 = 0;
|
||||||
|
uint16_t HV_BATT_PEAK_DISCH_POWER_HD = 0;
|
||||||
|
uint16_t HV_BATT_PEAK_CH_POWER_HD = 0;
|
||||||
|
uint16_t HV_BATT_NOM_CH_POWER_HD = 0;
|
||||||
|
uint16_t MAX_ALLOW_CHRG_CURRENT = 0;
|
||||||
|
int16_t HV_BATT_REAL_CURR_HD = 0;
|
||||||
|
uint16_t HV_BATT_REAL_VOLT_HD = 0;
|
||||||
|
uint8_t TBMU_FAULT_TYPE = 0;
|
||||||
|
int16_t HV_BATT_MAX_REAL_CURR = 0;
|
||||||
|
bool TBCU_48V_WAKEUP = false;
|
||||||
|
bool REQ_CLEAR_DTC_TBMU = false;
|
||||||
|
bool HV_BATT_DISCONT_WARNING_OPEN = false;
|
||||||
|
uint8_t CONTACTORS_STATE = 0;
|
||||||
|
uint16_t HV_BATT_SOC = 0;
|
||||||
|
bool CMD_RESET_MIL = 0;
|
||||||
|
bool REQ_BLINK_STOP_AND_SERVICE_LAMP = false;
|
||||||
|
bool REQ_MIL_LAMP_CONTINOUS = false;
|
||||||
|
uint16_t HV_BATT_GENERATED_HEAT_RATE = 0;
|
||||||
|
bool HV_BATT_CRASH_MEMORIZED = false;
|
||||||
|
bool HV_BATT_COLD_CRANK_ACK = false;
|
||||||
|
bool HV_BATT_CHARGE_NEEDED_STATE = false;
|
||||||
|
uint8_t HV_BATT_NOM_CH_CURRENT = 0;
|
||||||
|
uint16_t HV_BATT_NOM_CH_VOLTAGE = 0;
|
||||||
|
uint16_t HV_BATT_REAL_POWER_HD = 0;
|
||||||
|
uint16_t MAX_ALLOW_CHRG_POWER = 0;
|
||||||
|
uint16_t MAX_ALLOW_DISCHRG_POWER = 0;
|
||||||
|
bool ALERT_CELL_POOR_CONSIST, ALERT_OVERCHARGE, ALERT_BATT, ALERT_LOW_SOC, ALERT_HIGH_SOC, ALERT_SOC_JUMP,
|
||||||
|
ALERT_TEMP_DIFF, ALERT_HIGH_TEMP, ALERT_OVERVOLTAGE, ALERT_CELL_OVERVOLTAGE, ALERT_CELL_UNDERVOLTAGE = false;
|
||||||
|
uint8_t NUMBER_PROBE_TEMP_MAX, NUMBER_PROBE_TEMP_MIN = 0;
|
||||||
|
int8_t TEMPERATURE_MINIMUM_C = 0;
|
||||||
|
uint8_t CONTACTOR_OPENING_REASON = 0;
|
||||||
|
uint8_t NUMBER_OF_TEMPERATURE_SENSORS_IN_BATTERY, NUMBER_OF_CELL_MEASUREMENTS_IN_BATTERY = 0;
|
||||||
|
uint16_t HV_BATT_COP_VOLTAGE = 0;
|
||||||
|
int16_t HV_BATT_COP_CURRENT = 0;
|
||||||
|
uint16_t BMS_DC_RELAY_MES_EVSE_VOLTAGE = 0;
|
||||||
|
uint8_t FAST_CHARGE_CONTACTOR_STATE = 0;
|
||||||
|
uint8_t BMS_FASTCHARGE_STATUS = 0;
|
||||||
|
uint16_t HV_BATT_FC_INSU_MINUS_RES, HV_BATT_FC_INSU_PLUS_RES, HV_BATT_FC_VHL_INSU_PLUS_RES,
|
||||||
|
HV_BATT_ONLY_INSU_MINUS_RES = 0;
|
||||||
|
uint16_t MIN_ALLOW_DISCHRG_VOLTAGE = 0;
|
||||||
|
uint16_t HV_BATT_NOMINAL_DISCH_CURR_HD, HV_BATT_PEAK_DISCH_CURR_HD, HV_BATT_STABLE_DISCH_CURR_HD = 0;
|
||||||
|
uint16_t HV_BATT_NOMINAL_CHARGE_CURR_HD, HV_BATT_PEAK_CHARGE_CURR_HD, HV_BATT_STABLE_CHARGE_CURR_HD = 0;
|
||||||
|
bool RC01_PERM_SYNTH_TBMU = false;
|
||||||
|
uint16_t HV_BATT_STABLE_CHARGE_POWER_HD = 0;
|
||||||
|
uint16_t HV_BATT_STABLE_DISCH_POWER_HD = 0;
|
||||||
|
uint16_t HV_BATT_NOMINAL_DISCH_POWER_HD = 0;
|
||||||
|
uint16_t MAX_ALLOW_DISCHRG_CURRENT = 0;
|
||||||
|
|
||||||
|
unsigned long previousMillis10 = 0; //- will store last time a 10ms CAN Message was sent
|
||||||
unsigned long previousMillis20 = 0; // will store last time a 20ms CAN Message was sent
|
unsigned long previousMillis20 = 0; // will store last time a 20ms CAN Message was sent
|
||||||
unsigned long previousMillis50 = 0; // will store last time a 50ms CAN Message was sent
|
unsigned long previousMillis50 = 0; // will store last time a 50ms CAN Message was sent
|
||||||
unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was sent
|
unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was sent
|
||||||
|
|
|
@ -383,6 +383,132 @@ class EcmpHtmlRenderer : public BatteryHtmlRenderer {
|
||||||
: String(datalayer_extended.stellantisECMP.pid_SOH_cell_1)) +
|
: String(datalayer_extended.stellantisECMP.pid_SOH_cell_1)) +
|
||||||
"</h4>";
|
"</h4>";
|
||||||
|
|
||||||
|
if (datalayer_extended.stellantisECMP.MysteryVan) {
|
||||||
|
content += "<h3>MysteryVan platform detected!</h3>";
|
||||||
|
content += "<h4>Contactor State: ";
|
||||||
|
if (datalayer_extended.stellantisECMP.CONTACTORS_STATE == 0) {
|
||||||
|
content += "Open";
|
||||||
|
} else if (datalayer_extended.stellantisECMP.CONTACTORS_STATE == 1) {
|
||||||
|
content += "Precharge";
|
||||||
|
} else if (datalayer_extended.stellantisECMP.CONTACTORS_STATE == 2) {
|
||||||
|
content += "Closed";
|
||||||
|
}
|
||||||
|
content += "</h4>";
|
||||||
|
content += "<h4>Crash Memorized: ";
|
||||||
|
if (datalayer_extended.stellantisECMP.CrashMemorized) {
|
||||||
|
content += "Yes</h4>";
|
||||||
|
} else {
|
||||||
|
content += "No</h4>";
|
||||||
|
}
|
||||||
|
content += "<h4>Contactor Opening Reason: ";
|
||||||
|
if (datalayer_extended.stellantisECMP.CONTACTOR_OPENING_REASON == 0) {
|
||||||
|
content += "No error";
|
||||||
|
} else if (datalayer_extended.stellantisECMP.CONTACTOR_OPENING_REASON == 1) {
|
||||||
|
content += "Crash!";
|
||||||
|
} else if (datalayer_extended.stellantisECMP.CONTACTOR_OPENING_REASON == 2) {
|
||||||
|
content += "12V supply source undervoltage";
|
||||||
|
} else if (datalayer_extended.stellantisECMP.CONTACTOR_OPENING_REASON == 3) {
|
||||||
|
content += "12V supply source overvoltage";
|
||||||
|
} else if (datalayer_extended.stellantisECMP.CONTACTOR_OPENING_REASON == 4) {
|
||||||
|
content += "Battery temperature";
|
||||||
|
} else if (datalayer_extended.stellantisECMP.CONTACTOR_OPENING_REASON == 5) {
|
||||||
|
content += "Interlock line open";
|
||||||
|
} else if (datalayer_extended.stellantisECMP.CONTACTOR_OPENING_REASON == 6) {
|
||||||
|
content += "e-Service plug disconnected";
|
||||||
|
}
|
||||||
|
content += "</h4>";
|
||||||
|
content += "<h4>Battery fault type: ";
|
||||||
|
if (datalayer_extended.stellantisECMP.TBMU_FAULT_TYPE == 0) {
|
||||||
|
content += "No fault";
|
||||||
|
} else if (datalayer_extended.stellantisECMP.TBMU_FAULT_TYPE == 1) {
|
||||||
|
content += "FirstLevelFault: Warning Lamp";
|
||||||
|
} else if (datalayer_extended.stellantisECMP.TBMU_FAULT_TYPE == 2) {
|
||||||
|
content += "SecondLevelFault: Stop Lamp";
|
||||||
|
} else if (datalayer_extended.stellantisECMP.TBMU_FAULT_TYPE == 3) {
|
||||||
|
content += "ThirdLevelFault: Stop Lamp + contactor opening (EPS shutdown)";
|
||||||
|
} else if (datalayer_extended.stellantisECMP.TBMU_FAULT_TYPE == 4) {
|
||||||
|
content += "FourthLevelFault: Stop Lamp + Active Discharge";
|
||||||
|
} else if (datalayer_extended.stellantisECMP.TBMU_FAULT_TYPE == 5) {
|
||||||
|
content += "Inhibition of powertrain activation";
|
||||||
|
} else if (datalayer_extended.stellantisECMP.TBMU_FAULT_TYPE == 6) {
|
||||||
|
content += "Reserved";
|
||||||
|
}
|
||||||
|
content += "</h4>";
|
||||||
|
content += "<h4>FC insulation minus resistance " +
|
||||||
|
String(datalayer_extended.stellantisECMP.HV_BATT_FC_INSU_MINUS_RES) + " kOhm</h4>";
|
||||||
|
content += "<h4>FC insulation plus resistance " +
|
||||||
|
String(datalayer_extended.stellantisECMP.HV_BATT_FC_INSU_PLUS_RES) + " kOhm</h4>";
|
||||||
|
content += "<h4>FC vehicle insulation plus resistance " +
|
||||||
|
String(datalayer_extended.stellantisECMP.HV_BATT_FC_VHL_INSU_PLUS_RES) + " kOhm</h4>";
|
||||||
|
content += "<h4>FC vehicle insulation plus resistance " +
|
||||||
|
String(datalayer_extended.stellantisECMP.HV_BATT_ONLY_INSU_MINUS_RES) + " kOhm</h4>";
|
||||||
|
}
|
||||||
|
content += "<h4>Alert Battery: ";
|
||||||
|
if (datalayer_extended.stellantisECMP.ALERT_BATT) {
|
||||||
|
content += "Yes</h4>";
|
||||||
|
} else {
|
||||||
|
content += "No</h4>";
|
||||||
|
}
|
||||||
|
content += "<h4>Alert Low SOC: ";
|
||||||
|
if (datalayer_extended.stellantisECMP.ALERT_LOW_SOC) {
|
||||||
|
content += "Yes</h4>";
|
||||||
|
} else {
|
||||||
|
content += "No</h4>";
|
||||||
|
}
|
||||||
|
content += "<h4>Alert High SOC: ";
|
||||||
|
if (datalayer_extended.stellantisECMP.ALERT_HIGH_SOC) {
|
||||||
|
content += "Yes</h4>";
|
||||||
|
} else {
|
||||||
|
content += "No</h4>";
|
||||||
|
}
|
||||||
|
content += "<h4>Alert SOC Jump: ";
|
||||||
|
if (datalayer_extended.stellantisECMP.ALERT_SOC_JUMP) {
|
||||||
|
content += "Yes</h4>";
|
||||||
|
} else {
|
||||||
|
content += "No</h4>";
|
||||||
|
}
|
||||||
|
content += "<h4>Alert Overcharge: ";
|
||||||
|
if (datalayer_extended.stellantisECMP.ALERT_OVERCHARGE) {
|
||||||
|
content += "Yes</h4>";
|
||||||
|
} else {
|
||||||
|
content += "No</h4>";
|
||||||
|
}
|
||||||
|
content += "<h4>Alert Temp Diff: ";
|
||||||
|
if (datalayer_extended.stellantisECMP.ALERT_TEMP_DIFF) {
|
||||||
|
content += "Yes</h4>";
|
||||||
|
} else {
|
||||||
|
content += "No</h4>";
|
||||||
|
}
|
||||||
|
content += "<h4>Alert Temp High: ";
|
||||||
|
if (datalayer_extended.stellantisECMP.ALERT_HIGH_TEMP) {
|
||||||
|
content += "Yes</h4>";
|
||||||
|
} else {
|
||||||
|
content += "No</h4>";
|
||||||
|
}
|
||||||
|
content += "<h4>Alert Overvoltage: ";
|
||||||
|
if (datalayer_extended.stellantisECMP.ALERT_OVERVOLTAGE) {
|
||||||
|
content += "Yes</h4>";
|
||||||
|
} else {
|
||||||
|
content += "No</h4>";
|
||||||
|
}
|
||||||
|
content += "<h4>Alert Cell Overvoltage: ";
|
||||||
|
if (datalayer_extended.stellantisECMP.ALERT_CELL_OVERVOLTAGE) {
|
||||||
|
content += "Yes</h4>";
|
||||||
|
} else {
|
||||||
|
content += "No</h4>";
|
||||||
|
}
|
||||||
|
content += "<h4>Alert Cell Undervoltage: ";
|
||||||
|
if (datalayer_extended.stellantisECMP.ALERT_CELL_UNDERVOLTAGE) {
|
||||||
|
content += "Yes</h4>";
|
||||||
|
} else {
|
||||||
|
content += "No</h4>";
|
||||||
|
}
|
||||||
|
content += "<h4>Alert Cell Poor Consistency: ";
|
||||||
|
if (datalayer_extended.stellantisECMP.ALERT_CELL_POOR_CONSIST) {
|
||||||
|
content += "Yes</h4>";
|
||||||
|
} else {
|
||||||
|
content += "No</h4>";
|
||||||
|
}
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -72,7 +72,7 @@ uint16_t KiaEGmpBattery::selectSOC(uint16_t SOC_low, uint16_t SOC_high) {
|
||||||
|
|
||||||
void KiaEGmpBattery::set_cell_voltages(CAN_frame rx_frame, int start, int length, int startCell) {
|
void KiaEGmpBattery::set_cell_voltages(CAN_frame rx_frame, int start, int length, int startCell) {
|
||||||
for (size_t i = 0; i < length; i++) {
|
for (size_t i = 0; i < length; i++) {
|
||||||
if ((rx_frame.data.u8[start + i] * 20) > 1000) {
|
if ((rx_frame.data.u8[start + i] * 20) > 2600) {
|
||||||
datalayer.battery.status.cell_voltages_mV[startCell + i] = (rx_frame.data.u8[start + i] * 20);
|
datalayer.battery.status.cell_voltages_mV[startCell + i] = (rx_frame.data.u8[start + i] * 20);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,16 +114,17 @@ uint8_t KiaEGmpBattery::calculateCRC(CAN_frame rx_frame, uint8_t length, uint8_t
|
||||||
|
|
||||||
void KiaEGmpBattery::update_values() {
|
void KiaEGmpBattery::update_values() {
|
||||||
|
|
||||||
#ifdef ESTIMATE_SOC_FROM_CELLVOLTAGE
|
if (user_selected_use_estimated_SOC) {
|
||||||
// Use the simplified pack-based SOC estimation with proper compensation
|
// Use the simplified pack-based SOC estimation with proper compensation
|
||||||
datalayer.battery.status.real_soc = estimateSOC(batteryVoltage, datalayer.battery.info.number_of_cells, batteryAmps);
|
datalayer.battery.status.real_soc =
|
||||||
|
estimateSOC(batteryVoltage, datalayer.battery.info.number_of_cells, batteryAmps);
|
||||||
|
|
||||||
// For comparison or fallback, we can still calculate from min/max cell voltages
|
// For comparison or fallback, we can still calculate from min/max cell voltages
|
||||||
SOC_estimated_lowest = estimateSOCFromCell(CellVoltMin_mV);
|
SOC_estimated_lowest = estimateSOCFromCell(CellVoltMin_mV);
|
||||||
SOC_estimated_highest = estimateSOCFromCell(CellVoltMax_mV);
|
SOC_estimated_highest = estimateSOCFromCell(CellVoltMax_mV);
|
||||||
#else
|
} else {
|
||||||
datalayer.battery.status.real_soc = (SOC_Display * 10); //increase SOC range from 0-100.0 -> 100.00
|
datalayer.battery.status.real_soc = (SOC_Display * 10); //increase SOC range from 0-100.0 -> 100.00
|
||||||
#endif
|
}
|
||||||
|
|
||||||
datalayer.battery.status.soh_pptt = (batterySOH * 10); //Increase decimals from 100.0% -> 100.00%
|
datalayer.battery.status.soh_pptt = (batterySOH * 10); //Increase decimals from 100.0% -> 100.00%
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
#include "CanBattery.h"
|
#include "CanBattery.h"
|
||||||
#include "KIA-E-GMP-HTML.h"
|
#include "KIA-E-GMP-HTML.h"
|
||||||
|
|
||||||
#define ESTIMATE_SOC_FROM_CELLVOLTAGE
|
extern bool user_selected_use_estimated_SOC;
|
||||||
|
|
||||||
class KiaEGmpBattery : public CanBattery {
|
class KiaEGmpBattery : public CanBattery {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -201,15 +201,21 @@ void KiaHyundaiHybridBattery::transmit_can(unsigned long currentMillis) {
|
||||||
}
|
}
|
||||||
poll_data_pid++;
|
poll_data_pid++;
|
||||||
if (poll_data_pid == 1) {
|
if (poll_data_pid == 1) {
|
||||||
transmit_can_frame(&KIA_7E4_id1);
|
KIA_7E4.data.u8[2] = 0x01;
|
||||||
|
KIA_7E4.data.u8[3] = 0x00;
|
||||||
|
transmit_can_frame(&KIA_7E4);
|
||||||
} else if (poll_data_pid == 2) {
|
} else if (poll_data_pid == 2) {
|
||||||
transmit_can_frame(&KIA_7E4_id2);
|
KIA_7E4.data.u8[2] = 0x02;
|
||||||
|
transmit_can_frame(&KIA_7E4);
|
||||||
} else if (poll_data_pid == 3) {
|
} else if (poll_data_pid == 3) {
|
||||||
transmit_can_frame(&KIA_7E4_id3);
|
KIA_7E4.data.u8[2] = 0x03;
|
||||||
|
transmit_can_frame(&KIA_7E4);
|
||||||
} else if (poll_data_pid == 4) {
|
} else if (poll_data_pid == 4) {
|
||||||
|
//Group 4 not polled
|
||||||
} else if (poll_data_pid == 5) {
|
} else if (poll_data_pid == 5) {
|
||||||
transmit_can_frame(&KIA_7E4_id5);
|
KIA_7E4.data.u8[2] = 0x05;
|
||||||
|
KIA_7E4.data.u8[3] = 0x04;
|
||||||
|
transmit_can_frame(&KIA_7E4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,26 +34,11 @@ class KiaHyundaiHybridBattery : public CanBattery {
|
||||||
uint16_t min_cell_voltage_mv = 3700;
|
uint16_t min_cell_voltage_mv = 3700;
|
||||||
uint16_t max_cell_voltage_mv = 3700;
|
uint16_t max_cell_voltage_mv = 3700;
|
||||||
|
|
||||||
CAN_frame KIA_7E4_id1 = {.FD = false,
|
CAN_frame KIA_7E4 = {.FD = false,
|
||||||
.ext_ID = false,
|
.ext_ID = false,
|
||||||
.DLC = 8,
|
.DLC = 8,
|
||||||
.ID = 0x7E4,
|
.ID = 0x7E4,
|
||||||
.data = {0x02, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
.data = {0x02, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
CAN_frame KIA_7E4_id2 = {.FD = false,
|
|
||||||
.ext_ID = false,
|
|
||||||
.DLC = 8,
|
|
||||||
.ID = 0x7E4,
|
|
||||||
.data = {0x02, 0x21, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
|
||||||
CAN_frame KIA_7E4_id3 = {.FD = false,
|
|
||||||
.ext_ID = false,
|
|
||||||
.DLC = 8,
|
|
||||||
.ID = 0x7E4,
|
|
||||||
.data = {0x02, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
|
||||||
CAN_frame KIA_7E4_id5 = {.FD = false,
|
|
||||||
.ext_ID = false,
|
|
||||||
.DLC = 8,
|
|
||||||
.ID = 0x7E4,
|
|
||||||
.data = {0x02, 0x21, 0x05, 0x04, 0x00, 0x00, 0x00, 0x00}};
|
|
||||||
CAN_frame KIA_7E4_ack = {.FD = false,
|
CAN_frame KIA_7E4_ack = {.FD = false,
|
||||||
.ext_ID = false,
|
.ext_ID = false,
|
||||||
.DLC = 8,
|
.DLC = 8,
|
||||||
|
|
|
@ -34,10 +34,10 @@ class RenaultZoeGen1Battery : public CanBattery {
|
||||||
private:
|
private:
|
||||||
RenaultZoeGen1HtmlRenderer renderer;
|
RenaultZoeGen1HtmlRenderer renderer;
|
||||||
|
|
||||||
static const int MAX_PACK_VOLTAGE_DV = 4200; //5000 = 500.0V
|
static const int MAX_PACK_VOLTAGE_DV = 4040; //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;
|
||||||
static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value
|
static const int MAX_CELL_VOLTAGE_MV = 4220; //Battery is put into emergency stop if one cell goes over this value
|
||||||
static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value
|
static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value
|
||||||
|
|
||||||
DATALAYER_BATTERY_TYPE* datalayer_battery;
|
DATALAYER_BATTERY_TYPE* datalayer_battery;
|
||||||
|
|
|
@ -113,33 +113,6 @@ inline const char* getHvilStatusState(int index) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const char* getBMSState(int index) {
|
|
||||||
switch (index) {
|
|
||||||
case 0:
|
|
||||||
return "STANDBY";
|
|
||||||
case 1:
|
|
||||||
return "DRIVE";
|
|
||||||
case 2:
|
|
||||||
return "SUPPORT";
|
|
||||||
case 3:
|
|
||||||
return "CHARGE";
|
|
||||||
case 4:
|
|
||||||
return "FEIM";
|
|
||||||
case 5:
|
|
||||||
return "CLEAR_FAULT";
|
|
||||||
case 6:
|
|
||||||
return "FAULT";
|
|
||||||
case 7:
|
|
||||||
return "WELD";
|
|
||||||
case 8:
|
|
||||||
return "TEST";
|
|
||||||
case 9:
|
|
||||||
return "SNA";
|
|
||||||
default:
|
|
||||||
return "UNKNOWN";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline const char* getBMSContactorState(int index) {
|
inline const char* getBMSContactorState(int index) {
|
||||||
switch (index) {
|
switch (index) {
|
||||||
case 0:
|
case 0:
|
||||||
|
@ -161,174 +134,10 @@ inline const char* getBMSContactorState(int index) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const char* getBMSHvState(int index) {
|
|
||||||
switch (index) {
|
|
||||||
case 0:
|
|
||||||
return "DOWN";
|
|
||||||
case 1:
|
|
||||||
return "COMING_UP";
|
|
||||||
case 2:
|
|
||||||
return "GOING_DOWN";
|
|
||||||
case 3:
|
|
||||||
return "UP_FOR_DRIVE";
|
|
||||||
case 4:
|
|
||||||
return "UP_FOR_CHARGE";
|
|
||||||
case 5:
|
|
||||||
return "UP_FOR_DC_CHARGE";
|
|
||||||
case 6:
|
|
||||||
return "UP";
|
|
||||||
default:
|
|
||||||
return "UNKNOWN";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline const char* getBMSUiChargeStatus(int index) {
|
|
||||||
switch (index) {
|
|
||||||
case 0:
|
|
||||||
return "DISCONNECTED";
|
|
||||||
case 1:
|
|
||||||
return "NO_POWER";
|
|
||||||
case 2:
|
|
||||||
return "ABOUT_TO_CHARGE";
|
|
||||||
case 3:
|
|
||||||
return "CHARGING";
|
|
||||||
case 4:
|
|
||||||
return "CHARGE_COMPLETE";
|
|
||||||
case 5:
|
|
||||||
return "CHARGE_STOPPED";
|
|
||||||
default:
|
|
||||||
return "UNKNOWN";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline const char* getPCS_DcdcStatus(int index) {
|
|
||||||
switch (index) {
|
|
||||||
case 0:
|
|
||||||
return "IDLE";
|
|
||||||
case 1:
|
|
||||||
return "ACTIVE";
|
|
||||||
case 2:
|
|
||||||
return "FAULTED";
|
|
||||||
default:
|
|
||||||
return "UNKNOWN";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline const char* getPCS_DcdcMainState(int index) {
|
|
||||||
switch (index) {
|
|
||||||
case 0:
|
|
||||||
return "STANDBY";
|
|
||||||
case 1:
|
|
||||||
return "12V_SUPPORT_ACTIVE";
|
|
||||||
case 2:
|
|
||||||
return "PRECHARGE_STARTUP";
|
|
||||||
case 3:
|
|
||||||
return "PRECHARGE_ACTIVE";
|
|
||||||
case 4:
|
|
||||||
return "DIS_HVBUS_ACTIVE";
|
|
||||||
case 5:
|
|
||||||
return "SHUTDOWN";
|
|
||||||
case 6:
|
|
||||||
return "FAULTED";
|
|
||||||
default:
|
|
||||||
return "UNKNOWN";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline const char* getPCS_DcdcSubState(int index) {
|
|
||||||
switch (index) {
|
|
||||||
case 0:
|
|
||||||
return "PWR_UP_INIT";
|
|
||||||
case 1:
|
|
||||||
return "STANDBY";
|
|
||||||
case 2:
|
|
||||||
return "12V_SUPPORT_ACTIVE";
|
|
||||||
case 3:
|
|
||||||
return "DIS_HVBUS";
|
|
||||||
case 4:
|
|
||||||
return "PCHG_FAST_DIS_HVBUS";
|
|
||||||
case 5:
|
|
||||||
return "PCHG_SLOW_DIS_HVBUS";
|
|
||||||
case 6:
|
|
||||||
return "PCHG_DWELL_CHARGE";
|
|
||||||
case 7:
|
|
||||||
return "PCHG_DWELL_WAIT";
|
|
||||||
case 8:
|
|
||||||
return "PCHG_DI_RECOVERY_WAIT";
|
|
||||||
case 9:
|
|
||||||
return "PCHG_ACTIVE";
|
|
||||||
case 10:
|
|
||||||
return "PCHG_FLT_FAST_DIS_HVBUS";
|
|
||||||
case 11:
|
|
||||||
return "SHUTDOWN";
|
|
||||||
case 12:
|
|
||||||
return "12V_SUPPORT_FAULTED";
|
|
||||||
case 13:
|
|
||||||
return "DIS_HVBUS_FAULTED";
|
|
||||||
case 14:
|
|
||||||
return "PCHG_FAULTED";
|
|
||||||
case 15:
|
|
||||||
return "CLEAR_FAULTS";
|
|
||||||
case 16:
|
|
||||||
return "FAULTED";
|
|
||||||
case 17:
|
|
||||||
return "NUM";
|
|
||||||
default:
|
|
||||||
return "UNKNOWN";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline const char* getBMSPowerLimitState(int index) {
|
|
||||||
switch (index) {
|
|
||||||
case 0:
|
|
||||||
return "NOT_CALCULATED_FOR_DRIVE";
|
|
||||||
case 1:
|
|
||||||
return "CALCULATED_FOR_DRIVE";
|
|
||||||
default:
|
|
||||||
return "UNKNOWN";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline const char* getHVPStatus(int index) {
|
|
||||||
switch (index) {
|
|
||||||
case 0:
|
|
||||||
return "INVALID";
|
|
||||||
case 1:
|
|
||||||
return "NOT_AVAILABLE";
|
|
||||||
case 2:
|
|
||||||
return "STALE";
|
|
||||||
case 3:
|
|
||||||
return "VALID";
|
|
||||||
default:
|
|
||||||
return "UNKNOWN";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline const char* getHVPContactor(int index) {
|
|
||||||
switch (index) {
|
|
||||||
case 0:
|
|
||||||
return "NOT_ACTIVE";
|
|
||||||
case 1:
|
|
||||||
return "ACTIVE";
|
|
||||||
case 2:
|
|
||||||
return "COMPLETED";
|
|
||||||
default:
|
|
||||||
return "UNKNOWN";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline const char* getFalseTrue(bool value) {
|
|
||||||
return value ? "True" : "False";
|
|
||||||
}
|
|
||||||
|
|
||||||
inline const char* getNoYes(bool value) {
|
inline const char* getNoYes(bool value) {
|
||||||
return value ? "Yes" : "No";
|
return value ? "Yes" : "No";
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const char* getFault(bool value) {
|
|
||||||
return value ? "ACTIVE" : "NOT_ACTIVE";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clamp DLC to 0–8 bytes for classic CAN
|
// Clamp DLC to 0–8 bytes for classic CAN
|
||||||
inline int getDataLen(uint8_t dlc) {
|
inline int getDataLen(uint8_t dlc) {
|
||||||
return std::min<int>(dlc, 8);
|
return std::min<int>(dlc, 8);
|
||||||
|
@ -660,8 +469,6 @@ void TeslaBattery::
|
||||||
|
|
||||||
datalayer.battery.status.cell_min_voltage_mV = battery_cell_min_v;
|
datalayer.battery.status.cell_min_voltage_mV = battery_cell_min_v;
|
||||||
|
|
||||||
battery_cell_deviation_mV = (battery_cell_max_v - battery_cell_min_v);
|
|
||||||
|
|
||||||
/* Value mapping is completed. Start to check all safeties */
|
/* Value mapping is completed. Start to check all safeties */
|
||||||
|
|
||||||
//INTERNAL_OPEN_FAULT - Someone disconnected a high voltage cable while battery was in use
|
//INTERNAL_OPEN_FAULT - Someone disconnected a high voltage cable while battery was in use
|
||||||
|
@ -676,9 +483,22 @@ void TeslaBattery::
|
||||||
} else {
|
} else {
|
||||||
clear_event(EVENT_BATTERY_FUSE);
|
clear_event(EVENT_BATTERY_FUSE);
|
||||||
}
|
}
|
||||||
|
// Raise any Tesla BMS events in BE
|
||||||
|
// Events: Informational
|
||||||
|
if (BMS_a145_SW_SOC_Change) { // BMS has newly recalibrated pack SOC
|
||||||
|
set_event_latched(EVENT_BATTERY_SOC_RECALIBRATION, 0); // Latcched as BMS_a145 can be active for a while
|
||||||
|
} else if (!BMS_a145_SW_SOC_Change) {
|
||||||
|
clear_event(EVENT_BATTERY_SOC_RECALIBRATION);
|
||||||
|
}
|
||||||
|
// Events: Warning
|
||||||
|
if (BMS_contactorState == 5) { // BMS has detected welded contactor(s)
|
||||||
|
set_event_latched(EVENT_CONTACTOR_WELDED, 0);
|
||||||
|
} else if (BMS_contactorState != 5) {
|
||||||
|
clear_event(EVENT_CONTACTOR_WELDED);
|
||||||
|
}
|
||||||
|
|
||||||
if (user_selected_tesla_GTW_chassisType > 1) { //{{0, "Model S"}, {1, "Model X"}, {2, "Model 3"}, {3, "Model Y"}};
|
if (user_selected_tesla_GTW_chassisType > 1) { //{{0, "Model S"}, {1, "Model X"}, {2, "Model 3"}, {3, "Model Y"}};
|
||||||
// Autodetect algoritm for chemistry on 3/Y packs.
|
// Autodetect algorithm for chemistry on 3/Y packs.
|
||||||
// NCM/A batteries have 96s, LFP has 102-108s
|
// NCM/A batteries have 96s, LFP has 102-108s
|
||||||
// Drawback with this check is that it takes 3-5 minutes before all cells have been counted!
|
// Drawback with this check is that it takes 3-5 minutes before all cells have been counted!
|
||||||
if (datalayer.battery.info.number_of_cells > 101) {
|
if (datalayer.battery.info.number_of_cells > 101) {
|
||||||
|
@ -721,23 +541,28 @@ void TeslaBattery::
|
||||||
//Start the BMS ECU reset statemachine, only if contactors are OPEN and BMS ECU allows it
|
//Start the BMS ECU reset statemachine, only if contactors are OPEN and BMS ECU allows it
|
||||||
stateMachineBMSReset = 0;
|
stateMachineBMSReset = 0;
|
||||||
datalayer.battery.settings.user_requests_tesla_bms_reset = false;
|
datalayer.battery.settings.user_requests_tesla_bms_reset = false;
|
||||||
logging.println("BMS reset requested");
|
logging.println("INFO: BMS reset requested");
|
||||||
} else {
|
} else {
|
||||||
logging.println("ERROR: BMS reset failed due to contactors not being open, or BMS ECU not allowing it");
|
logging.println("ERROR: BMS reset failed due to contactors not being open, or BMS ECU not allowing it");
|
||||||
stateMachineBMSReset = 0xFF;
|
stateMachineBMSReset = 0xFF;
|
||||||
datalayer.battery.settings.user_requests_tesla_bms_reset = false;
|
datalayer.battery.settings.user_requests_tesla_bms_reset = false;
|
||||||
|
set_event(EVENT_BMS_RESET_REQ_FAIL, 0);
|
||||||
|
clear_event(EVENT_BMS_RESET_REQ_FAIL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (datalayer.battery.settings.user_requests_tesla_soc_reset) {
|
if (datalayer.battery.settings.user_requests_tesla_soc_reset) {
|
||||||
if (datalayer.battery.status.real_soc < 1500 || datalayer.battery.status.real_soc > 9000) {
|
if ((datalayer.battery.status.real_soc < 1500 || datalayer.battery.status.real_soc > 9000) &&
|
||||||
//Start the SOC reset statemachine, only if SOC < 15% or > 90%
|
battery_contactor == 1) {
|
||||||
|
//Start the SOC reset statemachine, only if SOC less than 15% or greater than 90%, and contactors open
|
||||||
stateMachineSOCReset = 0;
|
stateMachineSOCReset = 0;
|
||||||
datalayer.battery.settings.user_requests_tesla_soc_reset = false;
|
datalayer.battery.settings.user_requests_tesla_soc_reset = false;
|
||||||
logging.println("SOC reset requested");
|
logging.println("INFO: SOC reset requested");
|
||||||
} else {
|
} else {
|
||||||
logging.println("ERROR: SOC reset failed due to SOC not being less than 15 or greater than 90");
|
logging.println("ERROR: SOC reset failed, SOC not < 15 or > 90, or contactors not open");
|
||||||
stateMachineSOCReset = 0xFF;
|
stateMachineSOCReset = 0xFF;
|
||||||
datalayer.battery.settings.user_requests_tesla_soc_reset = false;
|
datalayer.battery.settings.user_requests_tesla_soc_reset = false;
|
||||||
|
set_event(EVENT_BATTERY_SOC_RESET_FAIL, 0);
|
||||||
|
clear_event(EVENT_BATTERY_SOC_RESET_FAIL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -972,42 +797,42 @@ void TeslaBattery::
|
||||||
datalayer_extended.tesla.HVP_shuntBarTempStatus = HVP_shuntBarTempStatus;
|
datalayer_extended.tesla.HVP_shuntBarTempStatus = HVP_shuntBarTempStatus;
|
||||||
datalayer_extended.tesla.HVP_shuntAsicTempStatus = HVP_shuntAsicTempStatus;
|
datalayer_extended.tesla.HVP_shuntAsicTempStatus = HVP_shuntAsicTempStatus;
|
||||||
|
|
||||||
//Safety checks for CAN message sesnding
|
//Safety checks for CAN message sending
|
||||||
if ((datalayer.system.status.inverter_allows_contactor_closing == true) &&
|
if ((datalayer.system.status.inverter_allows_contactor_closing == true) &&
|
||||||
(datalayer.battery.status.bms_status != FAULT) && (!datalayer.system.settings.equipment_stop_active)) {
|
(datalayer.battery.status.bms_status != FAULT) && (!datalayer.system.settings.equipment_stop_active)) {
|
||||||
// Carry on: 0x221 DRIVE state & reset power down timer
|
// Carry on: 0x221 DRIVE state & reset power down timer
|
||||||
vehicleState = 1;
|
vehicleState = CAR_DRIVE;
|
||||||
powerDownTimer = 180; //0x221 50ms cyclic, 20 calls/second
|
powerDownSeconds = 9;
|
||||||
} else {
|
} else {
|
||||||
// Faulted state, or inverter blocks contactor closing
|
// Faulted state, or inverter blocks contactor closing
|
||||||
// Shut down: 0x221 ACCESSORY state for 3 seconds, followed by GOING_DOWN, then OFF
|
// Shut down: 0x221 ACCESSORY state for 3 seconds, followed by GOING_DOWN, then OFF
|
||||||
if (powerDownTimer <= 180 && powerDownTimer > 120) {
|
if (powerDownSeconds <= 9 && powerDownSeconds > 6) {
|
||||||
vehicleState = 2; //ACCESSORY
|
vehicleState = ACCESSORY;
|
||||||
powerDownTimer--;
|
powerDownSeconds--;
|
||||||
}
|
}
|
||||||
if (powerDownTimer <= 120 && powerDownTimer > 60) {
|
if (powerDownSeconds <= 6 && powerDownSeconds > 3) {
|
||||||
vehicleState = 3; //GOING_DOWN
|
vehicleState = GOING_DOWN;
|
||||||
powerDownTimer--;
|
powerDownSeconds--;
|
||||||
}
|
}
|
||||||
if (powerDownTimer <= 60 && powerDownTimer > 0) {
|
if (powerDownSeconds <= 3 && powerDownSeconds > 0) {
|
||||||
vehicleState = 0; //OFF
|
vehicleState = CAR_OFF;
|
||||||
powerDownTimer--;
|
powerDownSeconds--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
printFaultCodesIfActive();
|
printFaultCodesIfActive();
|
||||||
logging.printf("BMS Contactors State: ");
|
logging.printf("Contactor State: ");
|
||||||
logging.printf(getBMSContactorState(battery_contactor)); // Display what state the BMS thinks the contactors are in
|
logging.printf(getBMSContactorState(battery_contactor)); // Display what state the BMS thinks the contactors are in
|
||||||
logging.printf(", HVIL: ");
|
logging.printf(" HVIL: ");
|
||||||
logging.printf(getHvilStatusState(battery_hvil_status));
|
logging.printf(getHvilStatusState(battery_hvil_status));
|
||||||
logging.printf(", NegativeState: ");
|
logging.printf(" NegState: ");
|
||||||
logging.printf(getContactorState(battery_packContNegativeState));
|
logging.printf(getContactorState(battery_packContNegativeState));
|
||||||
logging.printf(", PositiveState: ");
|
logging.printf(" PosState: ");
|
||||||
logging.println(getContactorState(battery_packContPositiveState));
|
logging.println(getContactorState(battery_packContPositiveState));
|
||||||
logging.printf("HVP Contactors setState: ");
|
logging.printf("Cont. setState: ");
|
||||||
logging.printf(
|
logging.printf(
|
||||||
getContactorText(battery_packContactorSetState)); // Display what state the HVP has set the contactors to be in
|
getContactorText(battery_packContactorSetState)); // Display what state the HVP has set the contactors to be in
|
||||||
logging.printf(", Closing blocked: ");
|
logging.printf(" Closing blocked: ");
|
||||||
logging.printf(getNoYes(battery_packCtrsClosingBlocked));
|
logging.printf(getNoYes(battery_packCtrsClosingBlocked));
|
||||||
if (battery_packContactorSetState == 5) {
|
if (battery_packContactorSetState == 5) {
|
||||||
logging.printf(" (already CLOSED)");
|
logging.printf(" (already CLOSED)");
|
||||||
|
@ -1015,43 +840,8 @@ void TeslaBattery::
|
||||||
logging.printf(", Pyrotest: ");
|
logging.printf(", Pyrotest: ");
|
||||||
logging.println(getNoYes(battery_pyroTestInProgress));
|
logging.println(getNoYes(battery_pyroTestInProgress));
|
||||||
|
|
||||||
logging.printf("Battery values: ");
|
logging.printf("HV: %.2f V, 12V: %.2f V, 12V current: %.2f A.\n", (battery_dcdcHvBusVolt * 0.146484),
|
||||||
logging.printf("Real SOC: ");
|
(battery_dcdcLvBusVolt * 0.0390625), (battery_dcdcLvOutputCurrent * 0.1));
|
||||||
logging.print(battery_soc_ui / 10.0, 1);
|
|
||||||
logging.printf(", Battery voltage: ");
|
|
||||||
logging.print(battery_volts / 10.0, 1);
|
|
||||||
logging.printf("V");
|
|
||||||
logging.printf(", Battery HV current: ");
|
|
||||||
logging.print(battery_amps / 10.0, 1);
|
|
||||||
logging.printf("A");
|
|
||||||
logging.printf(", Fully charged?: ");
|
|
||||||
if (battery_full_charge_complete)
|
|
||||||
logging.printf("YES, ");
|
|
||||||
else
|
|
||||||
logging.printf("NO, ");
|
|
||||||
if (datalayer.battery.info.chemistry == battery_chemistry_enum::LFP) {
|
|
||||||
logging.printf("LFP chemistry detected!");
|
|
||||||
}
|
|
||||||
logging.println("");
|
|
||||||
logging.printf("Cellstats, Max: ");
|
|
||||||
logging.print(battery_cell_max_v);
|
|
||||||
logging.printf("mV (cell ");
|
|
||||||
logging.print(battery_BrickVoltageMaxNum);
|
|
||||||
logging.printf("), Min: ");
|
|
||||||
logging.print(battery_cell_min_v);
|
|
||||||
logging.printf("mV (cell ");
|
|
||||||
logging.print(battery_BrickVoltageMinNum);
|
|
||||||
logging.printf("), Imbalance: ");
|
|
||||||
logging.print(battery_cell_deviation_mV);
|
|
||||||
logging.println("mV.");
|
|
||||||
|
|
||||||
logging.printf("High Voltage Output Pins: %.2f V, Low Voltage: %.2f V, DC/DC 12V current: %.2f A.\n",
|
|
||||||
(battery_dcdcHvBusVolt * 0.146484), (battery_dcdcLvBusVolt * 0.0390625),
|
|
||||||
(battery_dcdcLvOutputCurrent * 0.1));
|
|
||||||
|
|
||||||
logging.printf("PCS_ambientTemp: %.2f°C, DCDC_Temp: %.2f°C, ChgPhA: %.2f°C, ChgPhB: %.2f°C, ChgPhC: %.2f°C.\n",
|
|
||||||
PCS_ambientTemp * 0.1 + 40, PCS_dcdcTemp * 0.1 + 40, PCS_chgPhATemp * 0.1 + 40,
|
|
||||||
PCS_chgPhBTemp * 0.1 + 40, PCS_chgPhCTemp * 0.1 + 40);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TeslaBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
void TeslaBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||||
|
@ -1895,17 +1685,17 @@ void TeslaBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
break;
|
break;
|
||||||
case 0x612: // CAN UDSs for BMS
|
case 0x612: // CAN UDS responses for BMS
|
||||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
//BMS Query
|
//BMS Query
|
||||||
if (stateMachineBMSQuery != 0xFF && stateMachineBMSReset == 0xFF) {
|
if (stateMachineBMSQuery != 0xFF && stateMachineBMSReset == 0xFF && stateMachineSOCReset == 0xFF) {
|
||||||
if (memcmp(rx_frame.data.u8, "\x02\x50\x03\xAA\xAA\xAA\xAA\xAA", 8) == 0) {
|
if (memcmp(rx_frame.data.u8, "\x02\x50\x03\xAA\xAA\xAA\xAA\xAA", 8) == 0) {
|
||||||
//Received initial response, proceed to actual query
|
//Received initial response, proceed to actual query
|
||||||
logging.println("CAN UDS: Received BMS query initial handshake reply");
|
logging.println("CAN UDS: Received BMS query initial handshake reply");
|
||||||
stateMachineBMSQuery = 1;
|
stateMachineBMSQuery = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (memcmp(&rx_frame.data.u8[0], "\x10", 1) == 0) {
|
if (rx_frame.data.u8[0] == 0x10) {
|
||||||
//Received first data frame
|
//Received first data frame
|
||||||
battery_partNumber[0] = rx_frame.data.u8[5];
|
battery_partNumber[0] = rx_frame.data.u8[5];
|
||||||
battery_partNumber[1] = rx_frame.data.u8[6];
|
battery_partNumber[1] = rx_frame.data.u8[6];
|
||||||
|
@ -1914,7 +1704,7 @@ void TeslaBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||||
stateMachineBMSQuery = 2;
|
stateMachineBMSQuery = 2;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (memcmp(&rx_frame.data.u8[0], "\x21", 1) == 0) {
|
if (rx_frame.data.u8[0] == 0x21) {
|
||||||
//Second part of part number after flow control
|
//Second part of part number after flow control
|
||||||
battery_partNumber[3] = rx_frame.data.u8[1];
|
battery_partNumber[3] = rx_frame.data.u8[1];
|
||||||
battery_partNumber[4] = rx_frame.data.u8[2];
|
battery_partNumber[4] = rx_frame.data.u8[2];
|
||||||
|
@ -1926,7 +1716,7 @@ void TeslaBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||||
logging.println("CAN UDS: Received BMS query second data frame");
|
logging.println("CAN UDS: Received BMS query second data frame");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (memcmp(&rx_frame.data.u8[0], "\x22", 1) == 0) {
|
if (rx_frame.data.u8[0] == 0x22) {
|
||||||
//Final part of part number
|
//Final part of part number
|
||||||
battery_partNumber[10] = rx_frame.data.u8[1];
|
battery_partNumber[10] = rx_frame.data.u8[1];
|
||||||
battery_partNumber[11] = rx_frame.data.u8[2];
|
battery_partNumber[11] = rx_frame.data.u8[2];
|
||||||
|
@ -1941,15 +1731,28 @@ void TeslaBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//BMS Reset
|
//BMS ECU responses
|
||||||
if (stateMachineBMSQuery == 0xFF) { // Make sure this is reset request not query
|
|
||||||
if (memcmp(rx_frame.data.u8, "\x02\x67\x06\xAA\xAA\xAA\xAA\xAA", 8) == 0) {
|
if (memcmp(rx_frame.data.u8, "\x02\x67\x06\xAA\xAA\xAA\xAA\xAA", 8) == 0) {
|
||||||
logging.println("CAN UDS: ECU unlocked");
|
logging.println("CAN UDS: BMS ECU unlocked");
|
||||||
} else if (memcmp(rx_frame.data.u8, "\x03\x7F\x11\x78\xAA\xAA\xAA\xAA", 8) == 0) {
|
|
||||||
logging.println("CAN UDS: ECU reset request successful but ECU busy, response pending");
|
|
||||||
} else if (memcmp(rx_frame.data.u8, "\x02\x51\x01\xAA\xAA\xAA\xAA\xAA", 8) == 0) {
|
|
||||||
logging.println("CAN UDS: ECU reset positive response, 1 second downtime");
|
|
||||||
}
|
}
|
||||||
|
if (memcmp(rx_frame.data.u8, "\x03\x7F\x11\x78\xAA\xAA\xAA\xAA", 8) == 0) {
|
||||||
|
logging.println("CAN UDS: BMS ECU reset request successful but ECU busy, response pending");
|
||||||
|
}
|
||||||
|
if (memcmp(rx_frame.data.u8, "\x02\x51\x01\xAA\xAA\xAA\xAA\xAA", 8) == 0) {
|
||||||
|
logging.println("CAN UDS: BMS ECU reset positive response, 1 second downtime");
|
||||||
|
set_event(EVENT_BMS_RESET_REQ_SUCCESS, 0);
|
||||||
|
clear_event(EVENT_BMS_RESET_REQ_SUCCESS);
|
||||||
|
}
|
||||||
|
if (memcmp(rx_frame.data.u8, "\x05\x71\x01\x04\x07\x01\xAA\xAA", 8) == 0) {
|
||||||
|
logging.println("CAN UDS: BMS SOC reset accepted, resetting BMS ECU");
|
||||||
|
set_event(EVENT_BATTERY_SOC_RESET_SUCCESS, 0);
|
||||||
|
clear_event(EVENT_BATTERY_SOC_RESET_SUCCESS);
|
||||||
|
stateMachineBMSReset = 6; // BMS ECU already unlocked etc. so we jump straight to reset
|
||||||
|
}
|
||||||
|
if (memcmp(rx_frame.data.u8, "\x05\x71\x01\x04\x07\x00\xAA\xAA", 8) == 0) {
|
||||||
|
logging.println("CAN UDS: BMS SOC reset failed");
|
||||||
|
set_event(EVENT_BATTERY_SOC_RESET_FAIL, 0);
|
||||||
|
clear_event(EVENT_BATTERY_SOC_RESET_FAIL);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -1985,41 +1788,26 @@ CAN_frame can_msg_118[] = {
|
||||||
{.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x118, .data = {0x6F, 0x8E, 0x30, 0x10, 0x00, 0x08, 0x00, 0x80}},
|
{.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x118, .data = {0x6F, 0x8E, 0x30, 0x10, 0x00, 0x08, 0x00, 0x80}},
|
||||||
{.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x118, .data = {0x70, 0x8F, 0x30, 0x10, 0x00, 0x08, 0x00, 0x80}}};
|
{.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x118, .data = {0x70, 0x8F, 0x30, 0x10, 0x00, 0x08, 0x00, 0x80}}};
|
||||||
|
|
||||||
unsigned long lastSend1CF = 0;
|
|
||||||
unsigned long lastSend118 = 0;
|
|
||||||
|
|
||||||
int index_1CF = 0;
|
|
||||||
int index_118 = 0;
|
|
||||||
|
|
||||||
void TeslaBattery::transmit_can(unsigned long currentMillis) {
|
void TeslaBattery::transmit_can(unsigned long currentMillis) {
|
||||||
|
|
||||||
if (user_selected_tesla_digital_HVIL) { //Special S/X? mode for 2024+ batteries
|
|
||||||
if ((datalayer.system.status.inverter_allows_contactor_closing) && (datalayer.battery.status.bms_status != FAULT)) {
|
|
||||||
if (currentMillis - lastSend1CF >= 10) {
|
|
||||||
transmit_can_frame(&can_msg_1CF[index_1CF]);
|
|
||||||
|
|
||||||
index_1CF = (index_1CF + 1) % 8;
|
|
||||||
lastSend1CF = currentMillis;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentMillis - lastSend118 >= 10) {
|
|
||||||
transmit_can_frame(&can_msg_118[index_118]);
|
|
||||||
|
|
||||||
index_118 = (index_118 + 1) % 16;
|
|
||||||
lastSend118 = currentMillis;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
index_1CF = 0;
|
|
||||||
index_118 = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Send 10ms messages
|
//Send 10ms messages
|
||||||
if (currentMillis - previousMillis10 >= INTERVAL_10_MS) {
|
if (currentMillis - previousMillis10 >= INTERVAL_10_MS) {
|
||||||
previousMillis10 = currentMillis;
|
previousMillis10 = currentMillis;
|
||||||
|
|
||||||
|
if (user_selected_tesla_digital_HVIL) { //Special Digital HVIL mode for S/X 2024+ batteries
|
||||||
|
if ((datalayer.system.status.inverter_allows_contactor_closing) &&
|
||||||
|
(datalayer.battery.status.bms_status != FAULT)) {
|
||||||
|
transmit_can_frame(&can_msg_1CF[index_1CF]);
|
||||||
|
index_1CF = (index_1CF + 1) % 8;
|
||||||
|
transmit_can_frame(&can_msg_118[index_118]);
|
||||||
|
index_118 = (index_118 + 1) % 16;
|
||||||
|
}
|
||||||
|
} else { //Normal handling of 118 message (Non digital HVIL version)
|
||||||
//0x118 DI_systemStatus
|
//0x118 DI_systemStatus
|
||||||
transmit_can_frame(&TESLA_118);
|
transmit_can_frame(&TESLA_118);
|
||||||
|
index_1CF = 0; //Stop broadcasting Digital HVIL 1CF and 118 to keep contactors open
|
||||||
|
index_118 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
//0x2E1 VCFRONT_status
|
//0x2E1 VCFRONT_status
|
||||||
switch (muxNumber_TESLA_2E1) {
|
switch (muxNumber_TESLA_2E1) {
|
||||||
|
@ -2059,7 +1847,7 @@ void TeslaBattery::transmit_can(unsigned long currentMillis) {
|
||||||
previousMillis50 = currentMillis;
|
previousMillis50 = currentMillis;
|
||||||
|
|
||||||
//0x221 VCFRONT_LVPowerState
|
//0x221 VCFRONT_LVPowerState
|
||||||
if (vehicleState == 1) { // Drive
|
if (vehicleState == CAR_DRIVE) {
|
||||||
switch (muxNumber_TESLA_221) {
|
switch (muxNumber_TESLA_221) {
|
||||||
case 0:
|
case 0:
|
||||||
generateMuxFrameCounterChecksum(TESLA_221_DRIVE_Mux0, frameCounter_TESLA_221, 52, 4, 56, 8);
|
generateMuxFrameCounterChecksum(TESLA_221_DRIVE_Mux0, frameCounter_TESLA_221, 52, 4, 56, 8);
|
||||||
|
@ -2074,10 +1862,8 @@ void TeslaBattery::transmit_can(unsigned long currentMillis) {
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
//Generate next new frame
|
|
||||||
frameCounter_TESLA_221 = (frameCounter_TESLA_221 + 1) % 16;
|
|
||||||
}
|
}
|
||||||
if (vehicleState == 2) { // Accessory
|
if (vehicleState == ACCESSORY) {
|
||||||
switch (muxNumber_TESLA_221) {
|
switch (muxNumber_TESLA_221) {
|
||||||
case 0:
|
case 0:
|
||||||
generateMuxFrameCounterChecksum(TESLA_221_ACCESSORY_Mux0, frameCounter_TESLA_221, 52, 4, 56, 8);
|
generateMuxFrameCounterChecksum(TESLA_221_ACCESSORY_Mux0, frameCounter_TESLA_221, 52, 4, 56, 8);
|
||||||
|
@ -2092,10 +1878,8 @@ void TeslaBattery::transmit_can(unsigned long currentMillis) {
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
//Generate next new frame
|
|
||||||
frameCounter_TESLA_221 = (frameCounter_TESLA_221 + 1) % 16;
|
|
||||||
}
|
}
|
||||||
if (vehicleState == 3) { // Going down
|
if (vehicleState == GOING_DOWN) {
|
||||||
switch (muxNumber_TESLA_221) {
|
switch (muxNumber_TESLA_221) {
|
||||||
case 0:
|
case 0:
|
||||||
generateMuxFrameCounterChecksum(TESLA_221_GOING_DOWN_Mux0, frameCounter_TESLA_221, 52, 4, 56, 8);
|
generateMuxFrameCounterChecksum(TESLA_221_GOING_DOWN_Mux0, frameCounter_TESLA_221, 52, 4, 56, 8);
|
||||||
|
@ -2110,10 +1894,8 @@ void TeslaBattery::transmit_can(unsigned long currentMillis) {
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
//Generate next new frame
|
|
||||||
frameCounter_TESLA_221 = (frameCounter_TESLA_221 + 1) % 16;
|
|
||||||
}
|
}
|
||||||
if (vehicleState == 0) { // Off
|
if (vehicleState == CAR_OFF) {
|
||||||
switch (muxNumber_TESLA_221) {
|
switch (muxNumber_TESLA_221) {
|
||||||
case 0:
|
case 0:
|
||||||
generateMuxFrameCounterChecksum(TESLA_221_OFF_Mux0, frameCounter_TESLA_221, 52, 4, 56, 8);
|
generateMuxFrameCounterChecksum(TESLA_221_OFF_Mux0, frameCounter_TESLA_221, 52, 4, 56, 8);
|
||||||
|
@ -2128,23 +1910,13 @@ void TeslaBattery::transmit_can(unsigned long currentMillis) {
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
//Generate next new frame
|
//Generate next new frame
|
||||||
frameCounter_TESLA_221 = (frameCounter_TESLA_221 + 1) % 16;
|
frameCounter_TESLA_221 = (frameCounter_TESLA_221 + 1) % 16;
|
||||||
}
|
|
||||||
|
|
||||||
//0x3C2 VCLEFT_switchStatus
|
//0x3C2 VCLEFT_switchStatus
|
||||||
switch (muxNumber_TESLA_3C2) {
|
transmit_can_frame(muxNumber_TESLA_3C2 == 0 ? &TESLA_3C2_Mux0 : &TESLA_3C2_Mux1);
|
||||||
case 0:
|
muxNumber_TESLA_3C2 = !muxNumber_TESLA_3C2; // Flip between sending Mux0 and Mux1 on each pass
|
||||||
transmit_can_frame(&TESLA_3C2_Mux0);
|
|
||||||
muxNumber_TESLA_3C2++;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
transmit_can_frame(&TESLA_3C2_Mux1);
|
|
||||||
muxNumber_TESLA_3C2 = 0;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
//0x39D IBST_status
|
//0x39D IBST_status
|
||||||
transmit_can_frame(&TESLA_39D);
|
transmit_can_frame(&TESLA_39D);
|
||||||
|
@ -2567,7 +2339,7 @@ void TeslaBattery::printFaultCodesIfActive() {
|
||||||
printDebugIfActive(BMS_a139_SW_DC_Link_V_Irrational, "ERROR: BMS_a139_SW_DC_Link_V_Irrational");
|
printDebugIfActive(BMS_a139_SW_DC_Link_V_Irrational, "ERROR: BMS_a139_SW_DC_Link_V_Irrational");
|
||||||
printDebugIfActive(BMS_a141_SW_BMB_Status_Warning, "ERROR: BMS_a141_SW_BMB_Status_Warning");
|
printDebugIfActive(BMS_a141_SW_BMB_Status_Warning, "ERROR: BMS_a141_SW_BMB_Status_Warning");
|
||||||
printDebugIfActive(BMS_a144_Hvp_Config_Mismatch, "ERROR: BMS_a144_Hvp_Config_Mismatch");
|
printDebugIfActive(BMS_a144_Hvp_Config_Mismatch, "ERROR: BMS_a144_Hvp_Config_Mismatch");
|
||||||
printDebugIfActive(BMS_a145_SW_SOC_Change, "ERROR: BMS_a145_SW_SOC_Change");
|
printDebugIfActive(BMS_a145_SW_SOC_Change, "INFO: BMS_a145_SW_SOC_Change");
|
||||||
printDebugIfActive(BMS_a146_SW_Brick_Overdischarged, "ERROR: BMS_a146_SW_Brick_Overdischarged");
|
printDebugIfActive(BMS_a146_SW_Brick_Overdischarged, "ERROR: BMS_a146_SW_Brick_Overdischarged");
|
||||||
printDebugIfActive(BMS_a149_SW_Missing_Config_Block, "ERROR: BMS_a149_SW_Missing_Config_Block");
|
printDebugIfActive(BMS_a149_SW_Missing_Config_Block, "ERROR: BMS_a149_SW_Missing_Config_Block");
|
||||||
printDebugIfActive(BMS_a151_SW_external_isolation, "ERROR: BMS_a151_SW_external_isolation");
|
printDebugIfActive(BMS_a151_SW_external_isolation, "ERROR: BMS_a151_SW_external_isolation");
|
||||||
|
@ -2601,7 +2373,7 @@ void TeslaModel3YBattery::setup(void) { // Performs one time setup at startup
|
||||||
//0x7FF GTW CAN frame values
|
//0x7FF GTW CAN frame values
|
||||||
//Mux1
|
//Mux1
|
||||||
write_signal_value(&TESLA_7FF_Mux1, 16, 16, user_selected_tesla_GTW_country, false);
|
write_signal_value(&TESLA_7FF_Mux1, 16, 16, user_selected_tesla_GTW_country, false);
|
||||||
write_signal_value(&TESLA_7FF_Mux1, 11, 1, user_selected_tesla_GTW_country, false);
|
write_signal_value(&TESLA_7FF_Mux1, 11, 1, user_selected_tesla_GTW_rightHandDrive, false);
|
||||||
//Mux3
|
//Mux3
|
||||||
write_signal_value(&TESLA_7FF_Mux3, 8, 4, user_selected_tesla_GTW_mapRegion, false);
|
write_signal_value(&TESLA_7FF_Mux3, 8, 4, user_selected_tesla_GTW_mapRegion, false);
|
||||||
write_signal_value(&TESLA_7FF_Mux3, 18, 3, user_selected_tesla_GTW_chassisType, false);
|
write_signal_value(&TESLA_7FF_Mux3, 18, 3, user_selected_tesla_GTW_chassisType, false);
|
||||||
|
|
|
@ -78,7 +78,11 @@ class TeslaBattery : public CanBattery {
|
||||||
uint8_t muxNumber_TESLA_221 = 0;
|
uint8_t muxNumber_TESLA_221 = 0;
|
||||||
uint8_t frameCounter_TESLA_221 = 15; // Start at 15 for Mux 0
|
uint8_t frameCounter_TESLA_221 = 15; // Start at 15 for Mux 0
|
||||||
uint8_t vehicleState = 1; // "OFF": 0, "DRIVE": 1, "ACCESSORY": 2, "GOING_DOWN": 3
|
uint8_t vehicleState = 1; // "OFF": 0, "DRIVE": 1, "ACCESSORY": 2, "GOING_DOWN": 3
|
||||||
uint16_t powerDownTimer = 180; // Car power down (i.e. contactor open) tracking timer, 3 seconds per sendingState
|
static const uint8_t CAR_OFF = 0;
|
||||||
|
static const uint8_t CAR_DRIVE = 1;
|
||||||
|
static const uint8_t ACCESSORY = 2;
|
||||||
|
static const uint8_t GOING_DOWN = 3;
|
||||||
|
uint8_t powerDownSeconds = 9; // Car power down (i.e. contactor open) tracking timer, 3 seconds per sendingState
|
||||||
//0x2E1 VCFRONT_status, 6 mux tracker
|
//0x2E1 VCFRONT_status, 6 mux tracker
|
||||||
uint8_t muxNumber_TESLA_2E1 = 0;
|
uint8_t muxNumber_TESLA_2E1 = 0;
|
||||||
//0x334 UI
|
//0x334 UI
|
||||||
|
@ -457,15 +461,14 @@ class TeslaBattery : public CanBattery {
|
||||||
.DLC = 8,
|
.DLC = 8,
|
||||||
.ID = 0x610,
|
.ID = 0x610,
|
||||||
.data = {0x02, 0x10, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}}; // Define initial UDS request
|
.data = {0x02, 0x10, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}}; // Define initial UDS request
|
||||||
|
uint8_t index_1CF = 0;
|
||||||
|
uint8_t index_118 = 0;
|
||||||
uint8_t stateMachineClearIsolationFault = 0xFF;
|
uint8_t stateMachineClearIsolationFault = 0xFF;
|
||||||
uint8_t stateMachineBMSReset = 0xFF;
|
uint8_t stateMachineBMSReset = 0xFF;
|
||||||
uint8_t stateMachineSOCReset = 0xFF;
|
uint8_t stateMachineSOCReset = 0xFF;
|
||||||
uint8_t stateMachineBMSQuery = 0xFF;
|
uint8_t stateMachineBMSQuery = 0xFF;
|
||||||
uint16_t sendContactorClosingMessagesStill = 300;
|
|
||||||
uint16_t battery_cell_max_v = 3300;
|
uint16_t battery_cell_max_v = 3300;
|
||||||
uint16_t battery_cell_min_v = 3300;
|
uint16_t battery_cell_min_v = 3300;
|
||||||
uint16_t battery_cell_deviation_mV = 0; //contains the deviation between highest and lowest cell in mV
|
|
||||||
bool cellvoltagesRead = false;
|
bool cellvoltagesRead = false;
|
||||||
//0x3d2: 978 BMS_kwhCounter
|
//0x3d2: 978 BMS_kwhCounter
|
||||||
uint32_t battery_total_discharge = 0;
|
uint32_t battery_total_discharge = 0;
|
||||||
|
|
|
@ -86,6 +86,13 @@ void VolvoSpaBattery::
|
||||||
datalayer.battery.info.total_capacity_Wh = 69511;
|
datalayer.battery.info.total_capacity_Wh = 69511;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Check safeties
|
||||||
|
if (datalayer_extended.VolvoPolestar.BECMsupplyVoltage < 10700) { //10.7V,
|
||||||
|
//If 12V voltage goes under this, latch battery OFF to prevent contactors from swinging between on/off
|
||||||
|
set_event(EVENT_12V_LOW, (datalayer_extended.VolvoPolestar.BECMsupplyVoltage / 100));
|
||||||
|
set_event(EVENT_BATTERY_CHG_DISCHG_STOP_REQ, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VolvoSpaBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
void VolvoSpaBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||||
|
|
|
@ -12,7 +12,7 @@ class VolvoSpaHtmlRenderer : public BatteryHtmlRenderer {
|
||||||
content += "</h4><h4>BECM reported number of DTCs: " + String(datalayer_extended.VolvoPolestar.DTCcount) + "</h4>";
|
content += "</h4><h4>BECM reported number of DTCs: " + String(datalayer_extended.VolvoPolestar.DTCcount) + "</h4>";
|
||||||
content += "<h4>BECM reported SOC: " + String(datalayer_extended.VolvoPolestar.soc_bms / 10.0) + " %</h4>";
|
content += "<h4>BECM reported SOC: " + String(datalayer_extended.VolvoPolestar.soc_bms / 10.0) + " %</h4>";
|
||||||
content += "<h4>Calculated SOC: " + String(datalayer_extended.VolvoPolestar.soc_calc / 10.0) + " %</h4>";
|
content += "<h4>Calculated SOC: " + String(datalayer_extended.VolvoPolestar.soc_calc / 10.0) + " %</h4>";
|
||||||
content += "<h4>Rescaled SOC: " + String(datalayer_extended.VolvoPolestar.soc_rescaled / 10.0) + " %</h4>";
|
content += "<h4>Rescaled SOC: " + String(datalayer_extended.VolvoPolestar.soc_rescaled / 100.0) + " %</h4>";
|
||||||
content += "<h4>BECM reported SOH: " + String(datalayer_extended.VolvoPolestar.soh_bms / 100.0) + " %</h4>";
|
content += "<h4>BECM reported SOH: " + String(datalayer_extended.VolvoPolestar.soh_bms / 100.0) + " %</h4>";
|
||||||
content += "<h4>BECM supply voltage: " + String(datalayer_extended.VolvoPolestar.BECMsupplyVoltage) + " mV</h4>";
|
content += "<h4>BECM supply voltage: " + String(datalayer_extended.VolvoPolestar.BECMsupplyVoltage) + " mV</h4>";
|
||||||
|
|
||||||
|
|
|
@ -104,6 +104,7 @@ void init_stored_settings() {
|
||||||
user_selected_can_addon_crystal_frequency_mhz = settings.getUInt("CANFREQ", 8);
|
user_selected_can_addon_crystal_frequency_mhz = settings.getUInt("CANFREQ", 8);
|
||||||
user_selected_canfd_addon_crystal_frequency_mhz = settings.getUInt("CANFDFREQ", 40);
|
user_selected_canfd_addon_crystal_frequency_mhz = settings.getUInt("CANFDFREQ", 40);
|
||||||
user_selected_LEAF_interlock_mandatory = settings.getBool("INTERLOCKREQ", false);
|
user_selected_LEAF_interlock_mandatory = settings.getBool("INTERLOCKREQ", false);
|
||||||
|
user_selected_use_estimated_SOC = settings.getBool("SOCESTIMATED", false);
|
||||||
user_selected_tesla_digital_HVIL = settings.getBool("DIGITALHVIL", false);
|
user_selected_tesla_digital_HVIL = settings.getBool("DIGITALHVIL", false);
|
||||||
user_selected_tesla_GTW_country = settings.getUInt("GTWCOUNTRY", 0);
|
user_selected_tesla_GTW_country = settings.getUInt("GTWCOUNTRY", 0);
|
||||||
user_selected_tesla_GTW_rightHandDrive = settings.getBool("GTWRHD", false);
|
user_selected_tesla_GTW_rightHandDrive = settings.getBool("GTWRHD", false);
|
||||||
|
@ -164,6 +165,7 @@ void init_stored_settings() {
|
||||||
// WIFI AP is enabled by default unless disabled in the settings
|
// WIFI AP is enabled by default unless disabled in the settings
|
||||||
wifiap_enabled = settings.getBool("WIFIAPENABLED", true);
|
wifiap_enabled = settings.getBool("WIFIAPENABLED", true);
|
||||||
wifi_channel = settings.getUInt("WIFICHANNEL", 0);
|
wifi_channel = settings.getUInt("WIFICHANNEL", 0);
|
||||||
|
ssidAP = settings.getString("APNAME", "BatteryEmulator").c_str();
|
||||||
passwordAP = settings.getString("APPASSWORD", "123456789").c_str();
|
passwordAP = settings.getString("APPASSWORD", "123456789").c_str();
|
||||||
mqtt_enabled = settings.getBool("MQTTENABLED", false);
|
mqtt_enabled = settings.getBool("MQTTENABLED", false);
|
||||||
mqtt_timeout_ms = settings.getUInt("MQTTTIMEOUT", 2000);
|
mqtt_timeout_ms = settings.getUInt("MQTTTIMEOUT", 2000);
|
||||||
|
@ -207,9 +209,11 @@ void store_settings() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!settings.putString("SSID", String(ssid.c_str()))) {
|
if (!settings.putString("SSID", String(ssid.c_str()))) {
|
||||||
|
if (ssid != "")
|
||||||
set_event(EVENT_PERSISTENT_SAVE_INFO, 1);
|
set_event(EVENT_PERSISTENT_SAVE_INFO, 1);
|
||||||
}
|
}
|
||||||
if (!settings.putString("PASSWORD", String(password.c_str()))) {
|
if (!settings.putString("PASSWORD", String(password.c_str()))) {
|
||||||
|
if (password != "")
|
||||||
set_event(EVENT_PERSISTENT_SAVE_INFO, 2);
|
set_event(EVENT_PERSISTENT_SAVE_INFO, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -235,6 +235,17 @@ struct DATALAYER_INFO_CMFAEV {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DATALAYER_INFO_ECMP {
|
struct DATALAYER_INFO_ECMP {
|
||||||
|
//mysteryvan parameters
|
||||||
|
bool MysteryVan = false;
|
||||||
|
bool CrashMemorized = false;
|
||||||
|
uint8_t CONTACTOR_OPENING_REASON = 0;
|
||||||
|
uint8_t TBMU_FAULT_TYPE = 0;
|
||||||
|
uint8_t CONTACTORS_STATE = 0;
|
||||||
|
uint16_t HV_BATT_FC_INSU_MINUS_RES, HV_BATT_FC_INSU_PLUS_RES, HV_BATT_FC_VHL_INSU_PLUS_RES,
|
||||||
|
HV_BATT_ONLY_INSU_MINUS_RES = 0;
|
||||||
|
bool ALERT_CELL_POOR_CONSIST, ALERT_OVERCHARGE, ALERT_BATT, ALERT_LOW_SOC, ALERT_HIGH_SOC, ALERT_SOC_JUMP,
|
||||||
|
ALERT_TEMP_DIFF, ALERT_HIGH_TEMP, ALERT_OVERVOLTAGE, ALERT_CELL_OVERVOLTAGE, ALERT_CELL_UNDERVOLTAGE = false;
|
||||||
|
//ecmp below
|
||||||
uint8_t MainConnectorState = 0;
|
uint8_t MainConnectorState = 0;
|
||||||
uint16_t InsulationResistance = 0;
|
uint16_t InsulationResistance = 0;
|
||||||
uint8_t InsulationDiag = 0;
|
uint8_t InsulationDiag = 0;
|
||||||
|
@ -766,7 +777,7 @@ struct DATALAYER_INFO_VOLVO_POLESTAR {
|
||||||
uint16_t soc_calc = 0;
|
uint16_t soc_calc = 0;
|
||||||
uint16_t soc_rescaled = 0;
|
uint16_t soc_rescaled = 0;
|
||||||
uint16_t soh_bms = 0;
|
uint16_t soh_bms = 0;
|
||||||
uint16_t BECMsupplyVoltage = 0;
|
uint16_t BECMsupplyVoltage = 12000;
|
||||||
|
|
||||||
uint16_t BECMBatteryVoltage = 0;
|
uint16_t BECMBatteryVoltage = 0;
|
||||||
int16_t BECMBatteryCurrent = 0;
|
int16_t BECMBatteryCurrent = 0;
|
||||||
|
|
|
@ -21,6 +21,9 @@ class StarkHal : public Esp32Hal {
|
||||||
public:
|
public:
|
||||||
const char* name() { return "Stark CMR Module"; }
|
const char* name() { return "Stark CMR Module"; }
|
||||||
|
|
||||||
|
//Always enable BMS power on Stark CMR, it does not collide with any pin definitions
|
||||||
|
virtual bool always_enable_bms_power() { return true; }
|
||||||
|
|
||||||
// Not needed, GPIO 16 has hardware pullup for PSRAM compatibility
|
// Not needed, GPIO 16 has hardware pullup for PSRAM compatibility
|
||||||
virtual gpio_num_t PIN_5V_EN() { return GPIO_NUM_NC; }
|
virtual gpio_num_t PIN_5V_EN() { return GPIO_NUM_NC; }
|
||||||
|
|
||||||
|
|
|
@ -67,6 +67,9 @@ void init_events(void) {
|
||||||
events.entries[EVENT_BATTERY_UNDERVOLTAGE].level = EVENT_LEVEL_WARNING;
|
events.entries[EVENT_BATTERY_UNDERVOLTAGE].level = EVENT_LEVEL_WARNING;
|
||||||
events.entries[EVENT_BATTERY_VALUE_UNAVAILABLE].level = EVENT_LEVEL_WARNING;
|
events.entries[EVENT_BATTERY_VALUE_UNAVAILABLE].level = EVENT_LEVEL_WARNING;
|
||||||
events.entries[EVENT_BATTERY_ISOLATION].level = EVENT_LEVEL_WARNING;
|
events.entries[EVENT_BATTERY_ISOLATION].level = EVENT_LEVEL_WARNING;
|
||||||
|
events.entries[EVENT_BATTERY_SOC_RECALIBRATION].level = EVENT_LEVEL_INFO;
|
||||||
|
events.entries[EVENT_BATTERY_SOC_RESET_SUCCESS].level = EVENT_LEVEL_INFO;
|
||||||
|
events.entries[EVENT_BATTERY_SOC_RESET_FAIL].level = EVENT_LEVEL_INFO;
|
||||||
events.entries[EVENT_VOLTAGE_DIFFERENCE].level = EVENT_LEVEL_INFO;
|
events.entries[EVENT_VOLTAGE_DIFFERENCE].level = EVENT_LEVEL_INFO;
|
||||||
events.entries[EVENT_SOH_DIFFERENCE].level = EVENT_LEVEL_WARNING;
|
events.entries[EVENT_SOH_DIFFERENCE].level = EVENT_LEVEL_WARNING;
|
||||||
events.entries[EVENT_SOH_LOW].level = EVENT_LEVEL_ERROR;
|
events.entries[EVENT_SOH_LOW].level = EVENT_LEVEL_ERROR;
|
||||||
|
@ -124,6 +127,8 @@ void init_events(void) {
|
||||||
events.entries[EVENT_EQUIPMENT_STOP].level = EVENT_LEVEL_ERROR;
|
events.entries[EVENT_EQUIPMENT_STOP].level = EVENT_LEVEL_ERROR;
|
||||||
events.entries[EVENT_SD_INIT_FAILED].level = EVENT_LEVEL_WARNING;
|
events.entries[EVENT_SD_INIT_FAILED].level = EVENT_LEVEL_WARNING;
|
||||||
events.entries[EVENT_PERIODIC_BMS_RESET].level = EVENT_LEVEL_INFO;
|
events.entries[EVENT_PERIODIC_BMS_RESET].level = EVENT_LEVEL_INFO;
|
||||||
|
events.entries[EVENT_BMS_RESET_REQ_SUCCESS].level = EVENT_LEVEL_INFO;
|
||||||
|
events.entries[EVENT_BMS_RESET_REQ_FAIL].level = EVENT_LEVEL_INFO;
|
||||||
events.entries[EVENT_BATTERY_TEMP_DEVIATION_HIGH].level = EVENT_LEVEL_WARNING;
|
events.entries[EVENT_BATTERY_TEMP_DEVIATION_HIGH].level = EVENT_LEVEL_WARNING;
|
||||||
events.entries[EVENT_GPIO_CONFLICT].level = EVENT_LEVEL_ERROR;
|
events.entries[EVENT_GPIO_CONFLICT].level = EVENT_LEVEL_ERROR;
|
||||||
events.entries[EVENT_GPIO_NOT_DEFINED].level = EVENT_LEVEL_ERROR;
|
events.entries[EVENT_GPIO_NOT_DEFINED].level = EVENT_LEVEL_ERROR;
|
||||||
|
@ -244,6 +249,12 @@ String get_event_message_string(EVENTS_ENUM_TYPE event) {
|
||||||
return "Battery measurement unavailable. Check 12V power supply and battery wiring!";
|
return "Battery measurement unavailable. Check 12V power supply and battery wiring!";
|
||||||
case EVENT_BATTERY_ISOLATION:
|
case EVENT_BATTERY_ISOLATION:
|
||||||
return "Battery reports isolation error. High voltage might be leaking to ground. Check battery!";
|
return "Battery reports isolation error. High voltage might be leaking to ground. Check battery!";
|
||||||
|
case EVENT_BATTERY_SOC_RECALIBRATION:
|
||||||
|
return "The BMS updated the HV battery State of Charge (SOC) by more than 3pct based on SocByOcv.";
|
||||||
|
case EVENT_BATTERY_SOC_RESET_SUCCESS:
|
||||||
|
return "SOC reset routine was successful.";
|
||||||
|
case EVENT_BATTERY_SOC_RESET_FAIL:
|
||||||
|
return "SOC reset routine failed - check SOC is < 15 or > 90, and contactors are open.";
|
||||||
case EVENT_VOLTAGE_DIFFERENCE:
|
case EVENT_VOLTAGE_DIFFERENCE:
|
||||||
return "Too large voltage diff between the batteries. Second battery cannot join the DC-link";
|
return "Too large voltage diff between the batteries. Second battery cannot join the DC-link";
|
||||||
case EVENT_SOH_DIFFERENCE:
|
case EVENT_SOH_DIFFERENCE:
|
||||||
|
@ -361,7 +372,11 @@ String get_event_message_string(EVENTS_ENUM_TYPE event) {
|
||||||
case EVENT_SD_INIT_FAILED:
|
case EVENT_SD_INIT_FAILED:
|
||||||
return "SD card initialization failed, check hardware. Power must be removed to reset the SD card.";
|
return "SD card initialization failed, check hardware. Power must be removed to reset the SD card.";
|
||||||
case EVENT_PERIODIC_BMS_RESET:
|
case EVENT_PERIODIC_BMS_RESET:
|
||||||
return "BMS Reset Event Completed.";
|
return "BMS reset event completed.";
|
||||||
|
case EVENT_BMS_RESET_REQ_SUCCESS:
|
||||||
|
return "BMS reset request completed successfully.";
|
||||||
|
case EVENT_BMS_RESET_REQ_FAIL:
|
||||||
|
return "BMS reset request failed - check contactors are open.";
|
||||||
case EVENT_GPIO_CONFLICT:
|
case EVENT_GPIO_CONFLICT:
|
||||||
return "GPIO Pin Conflict: The pin used by '" + esp32hal->failed_allocator() + "' is already allocated by '" +
|
return "GPIO Pin Conflict: The pin used by '" + esp32hal->failed_allocator() + "' is already allocated by '" +
|
||||||
esp32hal->conflicting_allocator() + "'. Please check your configuration and assign different pins.";
|
esp32hal->conflicting_allocator() + "'. Please check your configuration and assign different pins.";
|
||||||
|
|
|
@ -49,6 +49,9 @@
|
||||||
XX(EVENT_BATTERY_ISOLATION) \
|
XX(EVENT_BATTERY_ISOLATION) \
|
||||||
XX(EVENT_BATTERY_REQUESTS_HEAT) \
|
XX(EVENT_BATTERY_REQUESTS_HEAT) \
|
||||||
XX(EVENT_BATTERY_WARMED_UP) \
|
XX(EVENT_BATTERY_WARMED_UP) \
|
||||||
|
XX(EVENT_BATTERY_SOC_RECALIBRATION) \
|
||||||
|
XX(EVENT_BATTERY_SOC_RESET_SUCCESS) \
|
||||||
|
XX(EVENT_BATTERY_SOC_RESET_FAIL) \
|
||||||
XX(EVENT_VOLTAGE_DIFFERENCE) \
|
XX(EVENT_VOLTAGE_DIFFERENCE) \
|
||||||
XX(EVENT_SOH_DIFFERENCE) \
|
XX(EVENT_SOH_DIFFERENCE) \
|
||||||
XX(EVENT_SOH_LOW) \
|
XX(EVENT_SOH_LOW) \
|
||||||
|
@ -107,6 +110,8 @@
|
||||||
XX(EVENT_AUTOMATIC_PRECHARGE_FAILURE) \
|
XX(EVENT_AUTOMATIC_PRECHARGE_FAILURE) \
|
||||||
XX(EVENT_SD_INIT_FAILED) \
|
XX(EVENT_SD_INIT_FAILED) \
|
||||||
XX(EVENT_PERIODIC_BMS_RESET) \
|
XX(EVENT_PERIODIC_BMS_RESET) \
|
||||||
|
XX(EVENT_BMS_RESET_REQ_SUCCESS) \
|
||||||
|
XX(EVENT_BMS_RESET_REQ_FAIL) \
|
||||||
XX(EVENT_BATTERY_TEMP_DEVIATION_HIGH) \
|
XX(EVENT_BATTERY_TEMP_DEVIATION_HIGH) \
|
||||||
XX(EVENT_GPIO_NOT_DEFINED) \
|
XX(EVENT_GPIO_NOT_DEFINED) \
|
||||||
XX(EVENT_GPIO_CONFLICT) \
|
XX(EVENT_GPIO_CONFLICT) \
|
||||||
|
|
|
@ -251,6 +251,10 @@ String settings_processor(const String& var, BatteryEmulatorSettingsStore& setti
|
||||||
return settings.getBool("DBLBTR") ? "checked" : "";
|
return settings.getBool("DBLBTR") ? "checked" : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (var == "SOCESTIMATED") {
|
||||||
|
return settings.getBool("SOCESTIMATED") ? "checked" : "";
|
||||||
|
}
|
||||||
|
|
||||||
if (var == "CNTCTRL") {
|
if (var == "CNTCTRL") {
|
||||||
return settings.getBool("CNTCTRL") ? "checked" : "";
|
return settings.getBool("CNTCTRL") ? "checked" : "";
|
||||||
}
|
}
|
||||||
|
@ -295,6 +299,10 @@ String settings_processor(const String& var, BatteryEmulatorSettingsStore& setti
|
||||||
return settings.getString("APPASSWORD", "123456789");
|
return settings.getString("APPASSWORD", "123456789");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (var == "APNAME") {
|
||||||
|
return settings.getString("APNAME", "BatteryEmulator");
|
||||||
|
}
|
||||||
|
|
||||||
if (var == "STATICIP") {
|
if (var == "STATICIP") {
|
||||||
return settings.getBool("STATICIP") ? "checked" : "";
|
return settings.getBool("STATICIP") ? "checked" : "";
|
||||||
}
|
}
|
||||||
|
@ -742,7 +750,7 @@ const char* getCANInterfaceName(CAN_Interface interface) {
|
||||||
|
|
||||||
function editError(){alert('Invalid input');}
|
function editError(){alert('Invalid input');}
|
||||||
|
|
||||||
function editSSID(){var value=prompt('Enter new SSID:');if(value!==null){var xhr=new
|
function editSSID(){var value=prompt('Which SSID to connect to. Enter new SSID:');if(value!==null){var xhr=new
|
||||||
XMLHttpRequest();xhr.onload=editComplete;xhr.onerror=editError;xhr.open('GET','/updateSSID?value='+encodeURIComponent(value),true);xhr.send();}}
|
XMLHttpRequest();xhr.onload=editComplete;xhr.onerror=editError;xhr.open('GET','/updateSSID?value='+encodeURIComponent(value),true);xhr.send();}}
|
||||||
|
|
||||||
function editPassword(){var value=prompt('Enter new password:');if(value!==null){var xhr=new
|
function editPassword(){var value=prompt('Enter new password:');if(value!==null){var xhr=new
|
||||||
|
@ -912,6 +920,11 @@ const char* getCANInterfaceName(CAN_Interface interface) {
|
||||||
display: contents;
|
display: contents;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
form .if-socestimated { display: none; } /* Integrations where you can turn on SOC estimation */
|
||||||
|
form[data-battery="16"] .if-socestimated {
|
||||||
|
display: contents;
|
||||||
|
}
|
||||||
|
|
||||||
form .if-dblbtr { display: none; }
|
form .if-dblbtr { display: none; }
|
||||||
form[data-dblbtr="true"] .if-dblbtr {
|
form[data-dblbtr="true"] .if-dblbtr {
|
||||||
display: contents;
|
display: contents;
|
||||||
|
@ -968,6 +981,7 @@ const char* getCANInterfaceName(CAN_Interface interface) {
|
||||||
#define SETTINGS_HTML_BODY \
|
#define SETTINGS_HTML_BODY \
|
||||||
R"rawliteral(
|
R"rawliteral(
|
||||||
<button onclick='goToMainPage()'>Back to main page</button>
|
<button onclick='goToMainPage()'>Back to main page</button>
|
||||||
|
<button onclick="askFactoryReset()">Factory reset</button>
|
||||||
|
|
||||||
<div style='background-color: #303E47; padding: 10px; margin-bottom: 10px; border-radius: 50px'>
|
<div style='background-color: #303E47; padding: 10px; margin-bottom: 10px; border-radius: 50px'>
|
||||||
<h4 style='color: white;'>SSID: <span id='SSID'>%SSID%</span><button onclick='editSSID()'>Edit</button></h4>
|
<h4 style='color: white;'>SSID: <span id='SSID'>%SSID%</span><button onclick='editSSID()'>Edit</button></h4>
|
||||||
|
@ -1016,10 +1030,20 @@ const char* getCANInterfaceName(CAN_Interface interface) {
|
||||||
|
|
||||||
<div class="if-estimated">
|
<div class="if-estimated">
|
||||||
<label>Manual charging power, watt: </label>
|
<label>Manual charging power, watt: </label>
|
||||||
<input name='CHGPOWER' pattern="^[0-9]+$" type='text' value='%CHGPOWER%' />
|
<input type='number' name='CHGPOWER' value="%CHGPOWER%"
|
||||||
|
min="0" max="65000" step="1"
|
||||||
|
title="Continous max charge power. Used since CAN data not valid for this integration. Do not set too high!" />
|
||||||
|
|
||||||
<label>Manual discharge power, watt: </label>
|
<label>Manual discharge power, watt: </label>
|
||||||
<input name='DCHGPOWER' pattern="^[0-9]+$" type='text' value='%DCHGPOWER%' />
|
<input type='number' name='DCHGPOWER' value="%DCHGPOWER%"
|
||||||
|
min="0" max="65000" step="1"
|
||||||
|
title="Continous max discharge power. Used since CAN data not valid for this integration. Do not set too high!" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="if-socestimated">
|
||||||
|
<label>Use estimated SOC: </label>
|
||||||
|
<input type='checkbox' name='SOCESTIMATED' value='on' %SOCESTIMATED%
|
||||||
|
title="Switch to estimated State of Charge when accurate SOC data is not available from the battery" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="if-battery">
|
<div class="if-battery">
|
||||||
|
@ -1034,20 +1058,25 @@ const char* getCANInterfaceName(CAN_Interface interface) {
|
||||||
|
|
||||||
<div class="if-cbms">
|
<div class="if-cbms">
|
||||||
<label>Battery max design voltage (V): </label>
|
<label>Battery max design voltage (V): </label>
|
||||||
<input name='BATTPVMAX' pattern="^[0-9]+(\.[0-9]+)?$" type='text' value='%BATTPVMAX%' />
|
<input name='BATTPVMAX' pattern="[0-9]+(\.[0-9]+)?" type='text' value='%BATTPVMAX%'
|
||||||
|
title="Maximum safe voltage for the entire battery pack in volts. Used as charge target and protection limits." />
|
||||||
|
|
||||||
<label>Battery min design voltage (V): </label>
|
<label>Battery min design voltage (V): </label>
|
||||||
<input name='BATTPVMIN' pattern="^[0-9]+(\.[0-9]+)?$" type='text' value='%BATTPVMIN%' />
|
<input name='BATTPVMIN' pattern="[0-9]+(\.[0-9]+)?" type='text' value='%BATTPVMIN%'
|
||||||
|
title="Minimum safe voltage for the entire battery pack in volts. Further discharge not possible below this limit." />
|
||||||
|
|
||||||
<label>Cell max design voltage (mV): </label>
|
<label>Cell max design voltage (mV): </label>
|
||||||
<input name='BATTCVMAX' pattern="^[0-9]+$" type='text' value='%BATTCVMAX%' />
|
<input name='BATTCVMAX' pattern="[0-9]+" type='text' value='%BATTCVMAX%'
|
||||||
|
title="Maximum voltage per individual cell in millivolts. Charging stops if one cell reaches this voltage." />
|
||||||
|
|
||||||
<label>Cell min design voltage (mV): </label>
|
<label>Cell min design voltage (mV): </label>
|
||||||
<input name='BATTCVMIN' pattern="^[0-9]+$" type='text' value='%BATTCVMIN%' />
|
<input name='BATTCVMIN' pattern="[0-9]+$" type='text' value='%BATTCVMIN%'
|
||||||
|
title="Minimum voltage per individual cell in millivolts. Discharge stops if one cell drops to this voltage." />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label>Double battery: </label>
|
<label>Double battery: </label>
|
||||||
<input type='checkbox' name='DBLBTR' value='on' %DBLBTR% />
|
<input type='checkbox' name='DBLBTR' value='on' %DBLBTR%
|
||||||
|
title="Enable this option if you intend to run two batteries in parallel" />
|
||||||
|
|
||||||
<div class="if-dblbtr">
|
<div class="if-dblbtr">
|
||||||
<label>Battery 2 interface: </label>
|
<label>Battery 2 interface: </label>
|
||||||
|
@ -1075,33 +1104,33 @@ const char* getCANInterfaceName(CAN_Interface interface) {
|
||||||
|
|
||||||
<div class="if-sofar">
|
<div class="if-sofar">
|
||||||
<label>Sofar Battery ID (0-15): </label>
|
<label>Sofar Battery ID (0-15): </label>
|
||||||
<input name='SOFAR_ID' type='text' value="%SOFAR_ID%" pattern="^[0-9]{1,2}$" />
|
<input name='SOFAR_ID' type='text' value="%SOFAR_ID%" pattern="[0-9]{1,2}" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="if-pylonish">
|
<div class="if-pylonish">
|
||||||
<label>Reported cell count (0 for default): </label>
|
<label>Reported cell count (0 for default): </label>
|
||||||
<input name='INVCELLS' type='text' value="%INVCELLS%" pattern="^[0-9]+$" />
|
<input name='INVCELLS' type='text' value="%INVCELLS%" pattern="[0-9]+" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="if-pylonish if-solax">
|
<div class="if-pylonish if-solax">
|
||||||
<label>Reported module count (0 for default): </label>
|
<label>Reported module count (0 for default): </label>
|
||||||
<input name='INVMODULES' type='text' value="%INVMODULES%" pattern="^[0-9]+$" />
|
<input name='INVMODULES' type='text' value="%INVMODULES%" pattern="[0-9]+" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="if-pylonish">
|
<div class="if-pylonish">
|
||||||
<label>Reported cells per module (0 for default): </label>
|
<label>Reported cells per module (0 for default): </label>
|
||||||
<input name='INVCELLSPER' type='text' value="%INVCELLSPER%" pattern="^[0-9]+$" />
|
<input name='INVCELLSPER' type='text' value="%INVCELLSPER%" pattern="[0-9]+" />
|
||||||
|
|
||||||
<label>Reported voltage level (0 for default): </label>
|
<label>Reported voltage level (0 for default): </label>
|
||||||
<input name='INVVLEVEL' type='text' value="%INVVLEVEL%" pattern="^[0-9]+$" />
|
<input name='INVVLEVEL' type='text' value="%INVVLEVEL%" pattern="[0-9]+" />
|
||||||
|
|
||||||
<label>Reported Ah capacity (0 for default): </label>
|
<label>Reported Ah capacity (0 for default): </label>
|
||||||
<input name='INVCAPACITY' type='text' value="%INVCAPACITY%" pattern="^[0-9]+$" />
|
<input name='INVCAPACITY' type='text' value="%INVCAPACITY%" pattern="[0-9]+" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="if-solax">
|
<div class="if-solax">
|
||||||
<label>Reported battery type (in decimal): </label>
|
<label>Reported battery type (in decimal): </label>
|
||||||
<input name='INVBTYPE' type='text' value="%INVBTYPE%" pattern="^[0-9]+$" />
|
<input name='INVBTYPE' type='text' value="%INVBTYPE%" pattern="[0-9]+" />
|
||||||
|
|
||||||
<label>Inverter should ignore contactors: </label>
|
<label>Inverter should ignore contactors: </label>
|
||||||
<input type='checkbox' name='INVICNT' value='on' %INVICNT% />
|
<input type='checkbox' name='INVICNT' value='on' %INVICNT% />
|
||||||
|
@ -1142,13 +1171,18 @@ const char* getCANInterfaceName(CAN_Interface interface) {
|
||||||
<div style='display: grid; grid-template-columns: 1fr 1.5fr; gap: 10px; align-items: center;'>
|
<div style='display: grid; grid-template-columns: 1fr 1.5fr; gap: 10px; align-items: center;'>
|
||||||
|
|
||||||
<label>Use CanFD as classic CAN: </label>
|
<label>Use CanFD as classic CAN: </label>
|
||||||
<input type='checkbox' name='CANFDASCAN' value='on' %CANFDASCAN% />
|
<input type='checkbox' name='CANFDASCAN' value='on' %CANFDASCAN%
|
||||||
|
title="When enabled, CAN-FD channel will operate as normal 500kbps CAN" />
|
||||||
|
|
||||||
<label>CAN addon crystal (Mhz): </label>
|
<label>CAN addon crystal (Mhz): </label>
|
||||||
<input name='CANFREQ' type='text' value="%CANFREQ%" pattern="^[0-9]+$" />
|
<input type='number' name='CANFREQ' value="%CANFREQ%"
|
||||||
|
min="0" max="1000" step="1"
|
||||||
|
title="Configure this if you are using a custom add-on CAN board. Integers only" />
|
||||||
|
|
||||||
<label>CAN-FD-addon crystal (Mhz): </label>
|
<label>CAN-FD-addon crystal (Mhz): </label>
|
||||||
<input name='CANFDFREQ' type='text' value="%CANFDFREQ%" pattern="^[0-9]+$" />
|
<input type='number' name='CANFDFREQ' value="%CANFDFREQ%"
|
||||||
|
min="0" max="1000" step="1"
|
||||||
|
title="Configure this if you are using a custom add-on CAN board. Integers only" />
|
||||||
|
|
||||||
<label>Equipment stop button: </label><select name='EQSTOP'>
|
<label>Equipment stop button: </label><select name='EQSTOP'>
|
||||||
%EQSTOP%
|
%EQSTOP%
|
||||||
|
@ -1164,17 +1198,23 @@ const char* getCANInterfaceName(CAN_Interface interface) {
|
||||||
|
|
||||||
<div class="if-cntctrl">
|
<div class="if-cntctrl">
|
||||||
<label>Precharge time ms: </label>
|
<label>Precharge time ms: </label>
|
||||||
<input name='PRECHGMS' type='text' value="%PRECHGMS%" pattern="^[0-9]+$" />
|
<input type='number' name='PRECHGMS' value="%PRECHGMS%"
|
||||||
|
min="1" max="65000" step="1"
|
||||||
|
title="Time in milliseconds the precharge should be active" />
|
||||||
|
|
||||||
<label>PWM contactor control: </label>
|
<label>PWM contactor control: </label>
|
||||||
<input type='checkbox' name='PWMCNTCTRL' value='on' %PWMCNTCTRL% />
|
<input type='checkbox' name='PWMCNTCTRL' value='on' %PWMCNTCTRL% />
|
||||||
|
|
||||||
<div class="if-pwmcntctrl">
|
<div class="if-pwmcntctrl">
|
||||||
<label>PWM Frequency Hz: </label>
|
<label>PWM Frequency Hz: </label>
|
||||||
<input name='PWMFREQ' type='text' value="%PWMFREQ%" pattern="^[0-9]+$" />
|
<input name='PWMFREQ' type='text' value="%PWMFREQ%"
|
||||||
|
min="1" max="65000" step="1"
|
||||||
|
title="Frequency in Hz used for PWM" />
|
||||||
|
|
||||||
<label>PWM Hold 0-1023: </label>
|
<label>PWM Hold 1-1023: </label>
|
||||||
<input name='PWMHOLD' type='text' value="%PWMHOLD%" pattern="^[0-9]+$" />
|
<input type='number' name='PWMHOLD' value="%PWMHOLD%"
|
||||||
|
min="1" max="1023" step="1"
|
||||||
|
title="1-1023 , lower value = lower power consumption" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -1187,7 +1227,7 @@ const char* getCANInterfaceName(CAN_Interface interface) {
|
||||||
|
|
||||||
<div class="if-extprecharge">
|
<div class="if-extprecharge">
|
||||||
<label>Precharge, maximum ms before fault: </label>
|
<label>Precharge, maximum ms before fault: </label>
|
||||||
<input name='MAXPRETIME' type='text' value="%MAXPRETIME%" pattern="^[0-9]+$" />
|
<input name='MAXPRETIME' type='text' value="%MAXPRETIME%" pattern="[0-9]+" />
|
||||||
|
|
||||||
<label>Normally Open (NO) inverter disconnect contactor: </label>
|
<label>Normally Open (NO) inverter disconnect contactor: </label>
|
||||||
<input type='checkbox' name='NOINVDISC' value='on' %NOINVDISC% />
|
<input type='checkbox' name='NOINVDISC' value='on' %NOINVDISC% />
|
||||||
|
@ -1207,14 +1247,27 @@ const char* getCANInterfaceName(CAN_Interface interface) {
|
||||||
<label>Broadcast Wifi access point: </label>
|
<label>Broadcast Wifi access point: </label>
|
||||||
<input type='checkbox' name='WIFIAPENABLED' value='on' %WIFIAPENABLED% />
|
<input type='checkbox' name='WIFIAPENABLED' value='on' %WIFIAPENABLED% />
|
||||||
|
|
||||||
|
<label>Access point name: </label>
|
||||||
|
<input type='text' name='APNAME' value="%APNAME%"
|
||||||
|
pattern="[A-Za-z0-9!#*-]{8,63}"
|
||||||
|
title="Name must be 8-63 characters long and may only contain letters, numbers and some special characters: !#*-"
|
||||||
|
required />
|
||||||
|
|
||||||
<label>Access point password: </label>
|
<label>Access point password: </label>
|
||||||
<input type='text' name='APPASSWORD' value="%APPASSWORD%" />
|
<input type='text' name='APPASSWORD' value="%APPASSWORD%"
|
||||||
|
pattern="[A-Za-z0-9!#*-]{8,63}"
|
||||||
|
title="Password must be 8-63 characters long and may only contain letters, numbers and some special characters: !#*-"
|
||||||
|
required />
|
||||||
|
|
||||||
<label>Wifi channel 0-14: </label>
|
<label>Wifi channel 0-14: </label>
|
||||||
<input name='WIFICHANNEL' type='text' value="%WIFICHANNEL%" pattern="^[0-9]+$" />
|
<input type='number' name='WIFICHANNEL' value="%WIFICHANNEL%"
|
||||||
|
min="0" max="14" step="1"
|
||||||
|
title="Force specific channel. Set to 0 for autodetect" required />
|
||||||
|
|
||||||
<label>Custom Wifi hostname: </label>
|
<label>Custom Wifi hostname: </label>
|
||||||
<input type='text' name='HOSTNAME' value="%HOSTNAME%" />
|
<input type='text' name='HOSTNAME' value="%HOSTNAME%"
|
||||||
|
pattern="[A-Za-z0-9!#*-]+"
|
||||||
|
title="Optional: Hostname may only contain letters, numbers and some special characters: !#*-" />
|
||||||
|
|
||||||
<label>Use static IP address: </label>
|
<label>Use static IP address: </label>
|
||||||
<input type='checkbox' name='STATICIP' value='on' %STATICIP% />
|
<input type='checkbox' name='STATICIP' value='on' %STATICIP% />
|
||||||
|
@ -1250,11 +1303,24 @@ const char* getCANInterfaceName(CAN_Interface interface) {
|
||||||
<input type='checkbox' name='MQTTENABLED' value='on' %MQTTENABLED% />
|
<input type='checkbox' name='MQTTENABLED' value='on' %MQTTENABLED% />
|
||||||
|
|
||||||
<div class='if-mqtt'>
|
<div class='if-mqtt'>
|
||||||
<label>MQTT server: </label><input type='text' name='MQTTSERVER' value="%MQTTSERVER%" />
|
<label>MQTT server: </label>
|
||||||
<label>MQTT port: </label><input type='text' name='MQTTPORT' value="%MQTTPORT%" />
|
<input type='text' name='MQTTSERVER' value="%MQTTSERVER%"
|
||||||
<label>MQTT user: </label><input type='text' name='MQTTUSER' value="%MQTTUSER%" />
|
pattern="[A-Za-z0-9.-]+"
|
||||||
<label>MQTT password: </label><input type='password' name='MQTTPASSWORD' value="%MQTTPASSWORD%" />
|
title="Hostname (letters, numbers, dots, hyphens)" />
|
||||||
<label>MQTT timeout ms: </label><input name='MQTTTIMEOUT' type='text' value="%MQTTTIMEOUT%" pattern="^[0-9]+$" />
|
<label>MQTT port: </label>
|
||||||
|
<input type='number' name='MQTTPORT' value="%MQTTPORT%"
|
||||||
|
min="1" max="65535" step="1"
|
||||||
|
title="Port number (1-65535)" />
|
||||||
|
<label>MQTT user: </label><input type='text' name='MQTTUSER' value="%MQTTUSER%"
|
||||||
|
pattern="[A-Za-z0-9!#*-]+"
|
||||||
|
title="MQTT username can only contain letters, numbers and some special characters: !#*-" />
|
||||||
|
<label>MQTT password: </label><input type='password' name='MQTTPASSWORD' value="%MQTTPASSWORD%"
|
||||||
|
pattern="[A-Za-z0-9!#*-]+"
|
||||||
|
title="MQTT password can only contain letters, numbers and some special characters: !#*-" />
|
||||||
|
<label>MQTT timeout ms: </label>
|
||||||
|
<input name='MQTTTIMEOUT' type='number' value="%MQTTTIMEOUT%"
|
||||||
|
min="1" max="60000" step="1"
|
||||||
|
title="Timeout in milliseconds (1-60000)" />
|
||||||
<label>Send all cellvoltages via MQTT: </label><input type='checkbox' name='MQTTCELLV' value='on' %MQTTCELLV% />
|
<label>Send all cellvoltages via MQTT: </label><input type='checkbox' name='MQTTCELLV' value='on' %MQTTCELLV% />
|
||||||
<label>Remote BMS reset via MQTT allowed: </label>
|
<label>Remote BMS reset via MQTT allowed: </label>
|
||||||
<input type='checkbox' name='REMBMSRESET' value='on' %REMBMSRESET% />
|
<input type='checkbox' name='REMBMSRESET' value='on' %REMBMSRESET% />
|
||||||
|
@ -1283,22 +1349,46 @@ const char* getCANInterfaceName(CAN_Interface interface) {
|
||||||
<div style='display: grid; grid-template-columns: 1fr 1.5fr; gap: 10px; align-items: center;'>
|
<div style='display: grid; grid-template-columns: 1fr 1.5fr; gap: 10px; align-items: center;'>
|
||||||
|
|
||||||
<label>Enable performance profiling on main page: </label>
|
<label>Enable performance profiling on main page: </label>
|
||||||
<input type='checkbox' name='PERFPROFILE' value='on' %PERFPROFILE% />
|
<input type='checkbox' name='PERFPROFILE' value='on' %PERFPROFILE%
|
||||||
|
title="For developers. Enable this to get detailed performance metrics on the front page" />
|
||||||
|
|
||||||
<label>Enable CAN message logging via USB serial: </label>
|
<label>Enable CAN message logging via USB serial: </label>
|
||||||
<input type='checkbox' name='CANLOGUSB' value='on' %CANLOGUSB% />
|
<input type='checkbox' name='CANLOGUSB' value='on' %CANLOGUSB%
|
||||||
|
title="WARNING: Causes performance issues. Enable this to get incoming/outgoing CAN messages logged via USB cable. Avoid if possible" />
|
||||||
|
<script> //Make sure user only uses one general logging method, improves performance
|
||||||
|
function handleCheckboxSelection(clickedCheckbox) {
|
||||||
|
const usbCheckbox = document.querySelector('input[name="USBENABLED"]');
|
||||||
|
const webCheckbox = document.querySelector('input[name="WEBENABLED"]');
|
||||||
|
|
||||||
|
if (clickedCheckbox.checked) {
|
||||||
|
// If the clicked checkbox is being checked, uncheck the other one
|
||||||
|
if (clickedCheckbox.name === 'USBENABLED') {
|
||||||
|
webCheckbox.checked = false;
|
||||||
|
} else {
|
||||||
|
usbCheckbox.checked = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If unchecking, do nothing (allow both to be unchecked)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<label>Enable general logging via USB serial: </label>
|
<label>Enable general logging via USB serial: </label>
|
||||||
<input type='checkbox' name='USBENABLED' value='on' %USBENABLED% />
|
<input type='checkbox' name='USBENABLED' value='on' %USBENABLED%
|
||||||
|
onclick="handleCheckboxSelection(this)"
|
||||||
|
title="WARNING: Causes performance issues. Enable this to get general logging via USB cable. Avoid if possible" />
|
||||||
|
|
||||||
<label>Enable general logging via Webserver: </label>
|
<label>Enable general logging via Webserver: </label>
|
||||||
<input type='checkbox' name='WEBENABLED' value='on' %WEBENABLED% />
|
<input type='checkbox' name='WEBENABLED' value='on' %WEBENABLED%
|
||||||
|
onclick="handleCheckboxSelection(this)"
|
||||||
|
title="Enable this if you want general logging available in the Webserver" />
|
||||||
|
|
||||||
<label>Enable CAN message logging via SD card: </label>
|
<label>Enable CAN message logging via SD card: </label>
|
||||||
<input type='checkbox' name='CANLOGSD' value='on' %CANLOGSD% />
|
<input type='checkbox' name='CANLOGSD' value='on' %CANLOGSD%
|
||||||
|
title="Enable this if you want incoming/outgoing CAN messages to be stored to an SD card. Only works on select hardware with SD-card slot" />
|
||||||
|
|
||||||
<label>Enable general logging via SD card: </label>
|
<label>Enable general logging via SD card: </label>
|
||||||
<input type='checkbox' name='SDLOGENABLED' value='on' %SDLOGENABLED% />
|
<input type='checkbox' name='SDLOGENABLED' value='on' %SDLOGENABLED%
|
||||||
|
title="Enable this if you want general logging to be stored to an SD card. Only works on select hardware with SD-card slot" />
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1391,8 +1481,6 @@ const char* getCANInterfaceName(CAN_Interface interface) {
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button onclick="askFactoryReset()">Factory reset</button>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
)rawliteral"
|
)rawliteral"
|
||||||
|
|
|
@ -23,9 +23,6 @@
|
||||||
extern std::string http_username;
|
extern std::string http_username;
|
||||||
extern std::string http_password;
|
extern std::string http_password;
|
||||||
|
|
||||||
bool webserver_enabled =
|
|
||||||
true; // Global flag to enable or disable the webserver //Old method to disable was with #ifdef WEBSERVER
|
|
||||||
|
|
||||||
bool webserver_auth = false;
|
bool webserver_auth = false;
|
||||||
|
|
||||||
// Create AsyncWebServer object on port 80
|
// Create AsyncWebServer object on port 80
|
||||||
|
@ -400,7 +397,7 @@ void init_webserver() {
|
||||||
"DBLBTR", "CNTCTRL", "CNTCTRLDBL", "PWMCNTCTRL", "PERBMSRESET", "SDLOGENABLED", "STATICIP",
|
"DBLBTR", "CNTCTRL", "CNTCTRLDBL", "PWMCNTCTRL", "PERBMSRESET", "SDLOGENABLED", "STATICIP",
|
||||||
"REMBMSRESET", "EXTPRECHARGE", "USBENABLED", "CANLOGUSB", "WEBENABLED", "CANFDASCAN", "CANLOGSD",
|
"REMBMSRESET", "EXTPRECHARGE", "USBENABLED", "CANLOGUSB", "WEBENABLED", "CANFDASCAN", "CANLOGSD",
|
||||||
"WIFIAPENABLED", "MQTTENABLED", "NOINVDISC", "HADISC", "MQTTTOPICS", "MQTTCELLV", "INVICNT",
|
"WIFIAPENABLED", "MQTTENABLED", "NOINVDISC", "HADISC", "MQTTTOPICS", "MQTTCELLV", "INVICNT",
|
||||||
"GTWRHD", "DIGITALHVIL", "PERFPROFILE", "INTERLOCKREQ",
|
"GTWRHD", "DIGITALHVIL", "PERFPROFILE", "INTERLOCKREQ", "SOCESTIMATED",
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handles the form POST from UI to save settings of the common image
|
// Handles the form POST from UI to save settings of the common image
|
||||||
|
@ -509,6 +506,8 @@ void init_webserver() {
|
||||||
} else if (p->name() == "SUBNET4") {
|
} else if (p->name() == "SUBNET4") {
|
||||||
auto type = atoi(p->value().c_str());
|
auto type = atoi(p->value().c_str());
|
||||||
settings.saveUInt("SUBNET4", type);
|
settings.saveUInt("SUBNET4", type);
|
||||||
|
} else if (p->name() == "APNAME") {
|
||||||
|
settings.saveString("APNAME", p->value().c_str());
|
||||||
} else if (p->name() == "APPASSWORD") {
|
} else if (p->name() == "APPASSWORD") {
|
||||||
settings.saveString("APPASSWORD", p->value().c_str());
|
settings.saveString("APPASSWORD", p->value().c_str());
|
||||||
} else if (p->name() == "HOSTNAME") {
|
} else if (p->name() == "HOSTNAME") {
|
||||||
|
@ -525,7 +524,8 @@ void init_webserver() {
|
||||||
} else if (p->name() == "MQTTTOPIC") {
|
} else if (p->name() == "MQTTTOPIC") {
|
||||||
settings.saveString("MQTTTOPIC", p->value().c_str());
|
settings.saveString("MQTTTOPIC", p->value().c_str());
|
||||||
} else if (p->name() == "MQTTTIMEOUT") {
|
} else if (p->name() == "MQTTTIMEOUT") {
|
||||||
settings.saveString("MQTTTIMEOUT", p->value().c_str());
|
auto port = atoi(p->value().c_str());
|
||||||
|
settings.saveUInt("MQTTTIMEOUT", port);
|
||||||
} else if (p->name() == "MQTTOBJIDPREFIX") {
|
} else if (p->name() == "MQTTOBJIDPREFIX") {
|
||||||
settings.saveString("MQTTOBJIDPREFIX", p->value().c_str());
|
settings.saveString("MQTTOBJIDPREFIX", p->value().c_str());
|
||||||
} else if (p->name() == "MQTTDEVICENAME") {
|
} else if (p->name() == "MQTTDEVICENAME") {
|
||||||
|
@ -621,7 +621,7 @@ void init_webserver() {
|
||||||
def_route_with_auth("/updatePassword", server, HTTP_GET, [](AsyncWebServerRequest* request) {
|
def_route_with_auth("/updatePassword", server, HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||||
if (request->hasParam("value")) {
|
if (request->hasParam("value")) {
|
||||||
String value = request->getParam("value")->value();
|
String value = request->getParam("value")->value();
|
||||||
if (value.length() > 8) { // Check if password is within the allowable length
|
if (value.length() >= 8) { // Password must be 8 characters or longer
|
||||||
password = value.c_str();
|
password = value.c_str();
|
||||||
store_settings();
|
store_settings();
|
||||||
request->send(200, "text/plain", "Updated successfully");
|
request->send(200, "text/plain", "Updated successfully");
|
||||||
|
|
|
@ -7,8 +7,6 @@
|
||||||
#include "../../lib/ayushsharma82-ElegantOTA/src/ElegantOTA.h"
|
#include "../../lib/ayushsharma82-ElegantOTA/src/ElegantOTA.h"
|
||||||
#include "../../lib/mathieucarbou-AsyncTCPSock/src/AsyncTCP.h"
|
#include "../../lib/mathieucarbou-AsyncTCPSock/src/AsyncTCP.h"
|
||||||
|
|
||||||
extern bool webserver_enabled;
|
|
||||||
|
|
||||||
extern const char* version_number; // The current software version, shown on webserver
|
extern const char* version_number; // The current software version, shown on webserver
|
||||||
|
|
||||||
// Common charger parameters
|
// Common charger parameters
|
||||||
|
|
|
@ -54,7 +54,7 @@ static uint16_t current_check_interval = WIFI_CHECK_INTERVAL;
|
||||||
static bool connected_once = false;
|
static bool connected_once = false;
|
||||||
|
|
||||||
void init_WiFi() {
|
void init_WiFi() {
|
||||||
DEBUG_PRINTF("init_Wifi enabled=%d, apå=%d, ssid=%s, password=%s\n", wifi_enabled, wifiap_enabled, ssid.c_str(),
|
DEBUG_PRINTF("init_Wifi enabled=%d, ap=%d, ssid=%s, password=%s\n", wifi_enabled, wifiap_enabled, ssid.c_str(),
|
||||||
password.c_str());
|
password.c_str());
|
||||||
|
|
||||||
if (!custom_hostname.empty()) {
|
if (!custom_hostname.empty()) {
|
||||||
|
@ -108,7 +108,7 @@ void wifi_monitor() {
|
||||||
if ((hasConnectedBefore && (currentMillis - lastWiFiCheck > current_check_interval)) ||
|
if ((hasConnectedBefore && (currentMillis - lastWiFiCheck > current_check_interval)) ||
|
||||||
(!hasConnectedBefore && (currentMillis - lastWiFiCheck > INIT_WIFI_FULL_RECONNECT_INTERVAL))) {
|
(!hasConnectedBefore && (currentMillis - lastWiFiCheck > INIT_WIFI_FULL_RECONNECT_INTERVAL))) {
|
||||||
|
|
||||||
DEBUG_PRINTF("Time to monitor Wi-Fi status: %d, %d, %d, %d, %d\n", hasConnectedBefore, currentMillis, lastWiFiCheck,
|
DEBUG_PRINTF("Wi-Fi status: %d, %d, %d, %d, %d\n", hasConnectedBefore, currentMillis, lastWiFiCheck,
|
||||||
current_check_interval, INIT_WIFI_FULL_RECONNECT_INTERVAL);
|
current_check_interval, INIT_WIFI_FULL_RECONNECT_INTERVAL);
|
||||||
|
|
||||||
lastWiFiCheck = currentMillis;
|
lastWiFiCheck = currentMillis;
|
||||||
|
@ -240,7 +240,6 @@ void init_mDNS() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void init_WiFi_AP() {
|
void init_WiFi_AP() {
|
||||||
ssidAP = std::string("BatteryEmulator") + WiFi.macAddress().c_str();
|
|
||||||
|
|
||||||
DEBUG_PRINTF("Creating Access Point: %s\n", ssidAP.c_str());
|
DEBUG_PRINTF("Creating Access Point: %s\n", ssidAP.c_str());
|
||||||
DEBUG_PRINTF("With password: %s\n", passwordAP.c_str());
|
DEBUG_PRINTF("With password: %s\n", passwordAP.c_str());
|
||||||
|
|
|
@ -139,11 +139,10 @@ void AforeCanInverter::map_can_frame_to_variable(CAN_frame rx_frame) {
|
||||||
switch (rx_frame.ID) {
|
switch (rx_frame.ID) {
|
||||||
case 0x305: // Every 1s from inverter
|
case 0x305: // Every 1s from inverter
|
||||||
datalayer.system.status.CAN_inverter_still_alive = CAN_STILL_ALIVE;
|
datalayer.system.status.CAN_inverter_still_alive = CAN_STILL_ALIVE;
|
||||||
char0 = rx_frame.data.u8[0]; // A
|
for (uint8_t i = 0; i < 5; i++) {
|
||||||
char1 = rx_frame.data.u8[0]; // F
|
datalayer.system.info.inverter_brand[i] = rx_frame.data.u8[i];
|
||||||
char2 = rx_frame.data.u8[0]; // O
|
}
|
||||||
char3 = rx_frame.data.u8[0]; // R
|
datalayer.system.info.inverter_brand[7] = '\0';
|
||||||
char4 = rx_frame.data.u8[0]; // E
|
|
||||||
inverter_status = rx_frame.data.u8[7];
|
inverter_status = rx_frame.data.u8[7];
|
||||||
time_to_send_info = true;
|
time_to_send_info = true;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -16,11 +16,6 @@ class AforeCanInverter : public CanInverterProtocol {
|
||||||
uint8_t inverter_status =
|
uint8_t inverter_status =
|
||||||
0; //0 = init, 1 = standby, 2 = starting, 3 = grid connected, 4 off-grid, 5 diesel generator, 6 grid connected, but disconnected, 7off grid and disconnected, 8 = power failure processing, 9 = power off, 10 = Failure
|
0; //0 = init, 1 = standby, 2 = starting, 3 = grid connected, 4 off-grid, 5 diesel generator, 6 grid connected, but disconnected, 7off grid and disconnected, 8 = power failure processing, 9 = power off, 10 = Failure
|
||||||
bool time_to_send_info = false;
|
bool time_to_send_info = false;
|
||||||
uint8_t char0 = 0;
|
|
||||||
uint8_t char1 = 0;
|
|
||||||
uint8_t char2 = 0;
|
|
||||||
uint8_t char3 = 0;
|
|
||||||
uint8_t char4 = 0;
|
|
||||||
//Actual content messages
|
//Actual content messages
|
||||||
CAN_frame AFORE_350 = {.FD = false, // Operation information
|
CAN_frame AFORE_350 = {.FD = false, // Operation information
|
||||||
.ext_ID = false,
|
.ext_ID = false,
|
||||||
|
|
|
@ -69,6 +69,10 @@ void BydCanInverter::
|
||||||
BYD_150.data.u8[6] = (fully_charged_capacity_ah >> 8);
|
BYD_150.data.u8[6] = (fully_charged_capacity_ah >> 8);
|
||||||
BYD_150.data.u8[7] = (fully_charged_capacity_ah & 0x00FF);
|
BYD_150.data.u8[7] = (fully_charged_capacity_ah & 0x00FF);
|
||||||
|
|
||||||
|
//Alarms
|
||||||
|
//TODO: BYD Alarms are not implemented yet. Investigation needed on the bits in this message
|
||||||
|
//BYD_190.data.u8[0] =
|
||||||
|
|
||||||
//Voltage (ex 370.0)
|
//Voltage (ex 370.0)
|
||||||
BYD_1D0.data.u8[0] = (datalayer.battery.status.voltage_dV >> 8);
|
BYD_1D0.data.u8[0] = (datalayer.battery.status.voltage_dV >> 8);
|
||||||
BYD_1D0.data.u8[1] = (datalayer.battery.status.voltage_dV & 0x00FF);
|
BYD_1D0.data.u8[1] = (datalayer.battery.status.voltage_dV & 0x00FF);
|
||||||
|
@ -144,21 +148,21 @@ void BydCanInverter::transmit_can(unsigned long currentMillis) {
|
||||||
if (currentMillis - previousMillis2s >= INTERVAL_2_S) {
|
if (currentMillis - previousMillis2s >= INTERVAL_2_S) {
|
||||||
previousMillis2s = currentMillis;
|
previousMillis2s = currentMillis;
|
||||||
|
|
||||||
transmit_can_frame(&BYD_110);
|
transmit_can_frame(&BYD_110); //Send Limits
|
||||||
}
|
}
|
||||||
// Send 10s CAN Message
|
// Send 10s CAN Message
|
||||||
if (currentMillis - previousMillis10s >= INTERVAL_10_S) {
|
if (currentMillis - previousMillis10s >= INTERVAL_10_S) {
|
||||||
previousMillis10s = currentMillis;
|
previousMillis10s = currentMillis;
|
||||||
|
|
||||||
transmit_can_frame(&BYD_150);
|
transmit_can_frame(&BYD_150); //Send States
|
||||||
transmit_can_frame(&BYD_1D0);
|
transmit_can_frame(&BYD_1D0); //Send Battery Info
|
||||||
transmit_can_frame(&BYD_210);
|
transmit_can_frame(&BYD_210); //Send Cell Info
|
||||||
}
|
}
|
||||||
//Send 60s message
|
//Send 60s message
|
||||||
if (currentMillis - previousMillis60s >= INTERVAL_60_S) {
|
if (currentMillis - previousMillis60s >= INTERVAL_60_S) {
|
||||||
previousMillis60s = currentMillis;
|
previousMillis60s = currentMillis;
|
||||||
|
|
||||||
transmit_can_frame(&BYD_190);
|
transmit_can_frame(&BYD_190); //Send Alarm
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,8 +170,12 @@ void BydCanInverter::send_initial_data() {
|
||||||
transmit_can_frame(&BYD_250);
|
transmit_can_frame(&BYD_250);
|
||||||
transmit_can_frame(&BYD_290);
|
transmit_can_frame(&BYD_290);
|
||||||
transmit_can_frame(&BYD_2D0);
|
transmit_can_frame(&BYD_2D0);
|
||||||
transmit_can_frame(&BYD_3D0_0);
|
BYD_3D0.data = {0x00, 0x42, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79}; //Battery
|
||||||
transmit_can_frame(&BYD_3D0_1);
|
transmit_can_frame(&BYD_3D0);
|
||||||
transmit_can_frame(&BYD_3D0_2);
|
BYD_3D0.data = {0x01, 0x2D, 0x42, 0x6F, 0x78, 0x20, 0x50, 0x72}; //-Box Pr
|
||||||
transmit_can_frame(&BYD_3D0_3);
|
transmit_can_frame(&BYD_3D0);
|
||||||
|
BYD_3D0.data = {0x02, 0x65, 0x6D, 0x69, 0x75, 0x6D, 0x20, 0x48}; //emium H
|
||||||
|
transmit_can_frame(&BYD_3D0);
|
||||||
|
BYD_3D0.data = {0x03, 0x56, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00}; //VS
|
||||||
|
transmit_can_frame(&BYD_3D0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,48 +40,33 @@ class BydCanInverter : public CanInverterProtocol {
|
||||||
.DLC = 8,
|
.DLC = 8,
|
||||||
.ID = 0x2D0,
|
.ID = 0x2D0,
|
||||||
.data = {0x00, 0x42, 0x59, 0x44, 0x00, 0x00, 0x00, 0x00}}; //BYD
|
.data = {0x00, 0x42, 0x59, 0x44, 0x00, 0x00, 0x00, 0x00}}; //BYD
|
||||||
CAN_frame BYD_3D0_0 = {.FD = false,
|
CAN_frame BYD_3D0 = {.FD = false,
|
||||||
.ext_ID = false,
|
.ext_ID = false,
|
||||||
.DLC = 8,
|
.DLC = 8,
|
||||||
.ID = 0x3D0,
|
.ID = 0x3D0,
|
||||||
.data = {0x00, 0x42, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79}}; //Battery
|
.data = {0x00, 0x42, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79}}; //Battery
|
||||||
CAN_frame BYD_3D0_1 = {.FD = false,
|
|
||||||
.ext_ID = false,
|
|
||||||
.DLC = 8,
|
|
||||||
.ID = 0x3D0,
|
|
||||||
.data = {0x01, 0x2D, 0x42, 0x6F, 0x78, 0x20, 0x50, 0x72}}; //-Box Pr
|
|
||||||
CAN_frame BYD_3D0_2 = {.FD = false,
|
|
||||||
.ext_ID = false,
|
|
||||||
.DLC = 8,
|
|
||||||
.ID = 0x3D0,
|
|
||||||
.data = {0x02, 0x65, 0x6D, 0x69, 0x75, 0x6D, 0x20, 0x48}}; //emium H
|
|
||||||
CAN_frame BYD_3D0_3 = {.FD = false,
|
|
||||||
.ext_ID = false,
|
|
||||||
.DLC = 8,
|
|
||||||
.ID = 0x3D0,
|
|
||||||
.data = {0x03, 0x56, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00}}; //VS
|
|
||||||
//Actual content messages
|
//Actual content messages
|
||||||
CAN_frame BYD_110 = {.FD = false,
|
CAN_frame BYD_110 = {.FD = false, //Limits
|
||||||
.ext_ID = false,
|
.ext_ID = false,
|
||||||
.DLC = 8,
|
.DLC = 8,
|
||||||
.ID = 0x110,
|
.ID = 0x110,
|
||||||
.data = {0x01, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
.data = {0x01, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
CAN_frame BYD_150 = {.FD = false,
|
CAN_frame BYD_150 = {.FD = false, //States
|
||||||
.ext_ID = false,
|
.ext_ID = false,
|
||||||
.DLC = 8,
|
.DLC = 8,
|
||||||
.ID = 0x150,
|
.ID = 0x150,
|
||||||
.data = {0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00}};
|
.data = {0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00}};
|
||||||
CAN_frame BYD_190 = {.FD = false,
|
CAN_frame BYD_190 = {.FD = false, //Alarm
|
||||||
.ext_ID = false,
|
.ext_ID = false,
|
||||||
.DLC = 8,
|
.DLC = 8,
|
||||||
.ID = 0x190,
|
.ID = 0x190,
|
||||||
.data = {0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
.data = {0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
CAN_frame BYD_1D0 = {.FD = false,
|
CAN_frame BYD_1D0 = {.FD = false, //Battery Info
|
||||||
.ext_ID = false,
|
.ext_ID = false,
|
||||||
.DLC = 8,
|
.DLC = 8,
|
||||||
.ID = 0x1D0,
|
.ID = 0x1D0,
|
||||||
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x08}};
|
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x08}};
|
||||||
CAN_frame BYD_210 = {.FD = false,
|
CAN_frame BYD_210 = {.FD = false, //Cell info
|
||||||
.ext_ID = false,
|
.ext_ID = false,
|
||||||
.DLC = 8,
|
.DLC = 8,
|
||||||
.ID = 0x210,
|
.ID = 0x210,
|
||||||
|
|
|
@ -136,8 +136,8 @@ void KostalInverterProtocol::update_values() {
|
||||||
float2frame(CYCLIC_DATA, (float)datalayer.battery.status.current_dA / 10, 18); // Last current
|
float2frame(CYCLIC_DATA, (float)datalayer.battery.status.current_dA / 10, 18); // Last current
|
||||||
float2frame(CYCLIC_DATA, (float)datalayer.battery.status.current_dA / 10, 22); // Should be Avg current(1s)
|
float2frame(CYCLIC_DATA, (float)datalayer.battery.status.current_dA / 10, 22); // Should be Avg current(1s)
|
||||||
|
|
||||||
// Close contactors after 20 battery info frames requested
|
// Close contactors after 7 battery info frames requested
|
||||||
if (f2_startup_count > 20) {
|
if (f2_startup_count > 7) {
|
||||||
datalayer.system.status.inverter_allows_contactor_closing = true;
|
datalayer.system.status.inverter_allows_contactor_closing = true;
|
||||||
dbg_message("inverter_allows_contactor_closing -> true (info frame)");
|
dbg_message("inverter_allows_contactor_closing -> true (info frame)");
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ class SmaBydHInverter : public SmaInverterBase {
|
||||||
void update_values();
|
void update_values();
|
||||||
void transmit_can(unsigned long currentMillis);
|
void transmit_can(unsigned long currentMillis);
|
||||||
void map_can_frame_to_variable(CAN_frame rx_frame);
|
void map_can_frame_to_variable(CAN_frame rx_frame);
|
||||||
static constexpr const char* Name = "BYD over SMA CAN";
|
static constexpr const char* Name = "SMA compatible BYD H";
|
||||||
|
|
||||||
virtual bool controls_contactor() { return true; }
|
virtual bool controls_contactor() { return true; }
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ class SmaBydHvsInverter : public SmaInverterBase {
|
||||||
void update_values();
|
void update_values();
|
||||||
void transmit_can(unsigned long currentMillis);
|
void transmit_can(unsigned long currentMillis);
|
||||||
void map_can_frame_to_variable(CAN_frame rx_frame);
|
void map_can_frame_to_variable(CAN_frame rx_frame);
|
||||||
static constexpr const char* Name = "BYD Battery-Box HVS over SMA CAN";
|
static constexpr const char* Name = "SMA compatible BYD Battery-Box HVS";
|
||||||
|
|
||||||
virtual bool controls_contactor() { return true; }
|
virtual bool controls_contactor() { return true; }
|
||||||
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -3,6 +3,6 @@
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
extern const uint8_t ELEGANT_HTML[11640];
|
extern const uint8_t ELEGANT_HTML[10615];
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
23
Software/src/lib/update_ota_html_gzip.py
Normal file
23
Software/src/lib/update_ota_html_gzip.py
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
libpath = Path("ayushsharma82-ElegantOTA")
|
||||||
|
gzipped=libpath/"CurrentPlainHTML.txt.gz"
|
||||||
|
header=libpath/"src/elop.h"
|
||||||
|
cpp=libpath/"src/elop.cpp"
|
||||||
|
if not gzipped.exists():
|
||||||
|
print(f"Please create {gzipped.resolve()} to replace OTA file.")
|
||||||
|
print(f"Example: zopfli -v --i10000 {libpath.resolve()}/CurrentPlainHTML.txt")
|
||||||
|
raise SystemExit(1)
|
||||||
|
gzipbytes=gzipped.read_bytes()
|
||||||
|
intlist = [int(one) for one in gzipbytes]
|
||||||
|
content = json.dumps(intlist).replace("[","{").replace("]","}").replace(" ", "")
|
||||||
|
headertext = header.read_text()
|
||||||
|
header.write_text(headertext[:1+headertext.find("[")]+str(len(gzipbytes))+headertext[headertext.find("]"):])
|
||||||
|
cpptext = cpp.read_text()
|
||||||
|
first_bracket = cpptext.find("[")
|
||||||
|
second_bracket = cpptext.find("]")
|
||||||
|
corrected_bytes = cpptext[:1+first_bracket]+str(len(gzipbytes))+cpptext[second_bracket:]
|
||||||
|
cppout = corrected_bytes[:corrected_bytes.find("{")]+content+corrected_bytes[corrected_bytes.find(";"):]+"\n"
|
||||||
|
cpp.write_text(cppout)
|
||||||
|
print("File content updated from", gzipped.resolve())
|
||||||
|
print("Bytes fixed:", cpptext[1+first_bracket:second_bracket], "to", len(gzipbytes))
|
|
@ -26,6 +26,9 @@ platform = https://github.com/pioarduino/platform-espressif32/releases/download/
|
||||||
board = esp32dev
|
board = esp32dev
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
monitor_filters = default, time, log2file
|
monitor_filters = default, time, log2file
|
||||||
|
board_build.flash_mode = qio
|
||||||
|
board_build.f_flash = 80000000
|
||||||
|
board_build.arduino.memory_type = qio_qspi
|
||||||
board_build.partitions = min_spiffs.csv
|
board_build.partitions = min_spiffs.csv
|
||||||
framework = arduino
|
framework = arduino
|
||||||
build_flags = -I include -DHW_LILYGO
|
build_flags = -I include -DHW_LILYGO
|
||||||
|
@ -36,6 +39,9 @@ platform = https://github.com/pioarduino/platform-espressif32/releases/download/
|
||||||
board = esp32dev
|
board = esp32dev
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
monitor_filters = default, time, log2file, esp32_exception_decoder
|
monitor_filters = default, time, log2file, esp32_exception_decoder
|
||||||
|
board_build.flash_mode = qio
|
||||||
|
board_build.f_flash = 80000000
|
||||||
|
board_build.arduino.memory_type = qio_qspi
|
||||||
board_build.partitions = min_spiffs.csv
|
board_build.partitions = min_spiffs.csv
|
||||||
framework = arduino
|
framework = arduino
|
||||||
build_flags = -I include -DHW_STARK
|
build_flags = -I include -DHW_STARK
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue