mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-03 17:59:27 +02:00
Merge pull request #1208 from kyberias/can-receive
Generalize comm 'transmit' and introduce common class for shunts
This commit is contained in:
commit
30fc0f002a
32 changed files with 317 additions and 262 deletions
3
.github/workflows/compile-all-batteries.yml
vendored
3
.github/workflows/compile-all-batteries.yml
vendored
|
@ -115,4 +115,5 @@ jobs:
|
||||||
# in the build matrix, and using build flags to define the
|
# in the build matrix, and using build flags to define the
|
||||||
# battery and inverter set in the build matrix.
|
# battery and inverter set in the build matrix.
|
||||||
- name: Compile Sketch
|
- name: Compile Sketch
|
||||||
run: arduino-cli compile --fqbn ${{ matrix.fqbn }} --build-property "build.extra_flags=-Wall -Wextra -Wpedantic -Werror -DESP32 -D${{ matrix.battery}} -D${{ matrix.inverter}} -D${{ matrix.hardware}}" ./Software
|
run: arduino-cli compile --fqbn ${{ matrix.fqbn }} --build-property build.partitions=min_spiffs --build-property upload.maximum_size=1966080 --build-property "build.extra_flags=-Wall -Wextra -Wpedantic -Werror -DESP32 -D${{ matrix.battery}} -D${{ matrix.inverter}} -D${{ matrix.hardware}}" ./Software
|
||||||
|
|
|
@ -288,4 +288,4 @@ jobs:
|
||||||
# in the build matrix, and using build flags to define the
|
# in the build matrix, and using build flags to define the
|
||||||
# battery and inverter set in the build matrix.
|
# battery and inverter set in the build matrix.
|
||||||
- name: Compile Sketch
|
- name: Compile Sketch
|
||||||
run: arduino-cli compile --fqbn ${{ matrix.fqbn }} --build-property "build.extra_flags=-Wall -Wextra -Wpedantic -Werror -DESP32 -D${{ matrix.battery}} -D${{ matrix.inverter}} -D${{ matrix.hardware}}" ./Software
|
run: arduino-cli compile --fqbn ${{ matrix.fqbn }} --build-property build.partitions=min_spiffs --build-property upload.maximum_size=1966080 --build-property "build.extra_flags=-Wall -Wextra -Wpedantic -Werror -DESP32 -D${{ matrix.battery}} -D${{ matrix.inverter}} -D${{ matrix.hardware}}" ./Software
|
||||||
|
|
|
@ -90,4 +90,5 @@ jobs:
|
||||||
# in the build matrix, and using build flags to define the
|
# in the build matrix, and using build flags to define the
|
||||||
# battery and inverter set in the build matrix.
|
# battery and inverter set in the build matrix.
|
||||||
- name: Compile Sketch
|
- name: Compile Sketch
|
||||||
run: arduino-cli compile --fqbn ${{ matrix.fqbn }} --build-property "build.extra_flags=-Wall -Wextra -Wpedantic -Werror -DESP32 -DDOUBLE_BATTERY -D${{ matrix.battery}} -D${{ matrix.inverter}} -D${{ matrix.hardware}}" ./Software
|
run: arduino-cli compile --fqbn ${{ matrix.fqbn }} --build-property build.partitions=min_spiffs --build-property upload.maximum_size=1966080 --build-property "build.extra_flags=-Wall -Wextra -Wpedantic -Werror -DESP32 -DDOUBLE_BATTERY -D${{ matrix.battery}} -D${{ matrix.inverter}} -D${{ matrix.hardware}}" ./Software
|
||||||
|
|
||||||
|
|
2
.github/workflows/compile-all-hardware.yml
vendored
2
.github/workflows/compile-all-hardware.yml
vendored
|
@ -90,4 +90,4 @@ jobs:
|
||||||
# in the build matrix, and using build flags to define the
|
# in the build matrix, and using build flags to define the
|
||||||
# battery and inverter set in the build matrix.
|
# battery and inverter set in the build matrix.
|
||||||
- name: Compile Sketch
|
- name: Compile Sketch
|
||||||
run: arduino-cli compile --fqbn ${{ matrix.fqbn }} --build-property "build.extra_flags=-Wall -Wextra -Wpedantic -Werror -DESP32 -D${{ matrix.battery}} -D${{ matrix.inverter}} -D${{ matrix.hardware}}" ./Software
|
run: arduino-cli compile --fqbn ${{ matrix.fqbn }} --build-property build.partitions=min_spiffs --build-property upload.maximum_size=1966080 --build-property "build.extra_flags=-Wall -Wextra -Wpedantic -Werror -DESP32 -D${{ matrix.battery}} -D${{ matrix.inverter}} -D${{ matrix.hardware}}" ./Software
|
||||||
|
|
2
.github/workflows/compile-all-inverters.yml
vendored
2
.github/workflows/compile-all-inverters.yml
vendored
|
@ -105,4 +105,4 @@ jobs:
|
||||||
# in the build matrix, and using build flags to define the
|
# in the build matrix, and using build flags to define the
|
||||||
# battery and inverter set in the build matrix.
|
# battery and inverter set in the build matrix.
|
||||||
- name: Compile Sketch
|
- name: Compile Sketch
|
||||||
run: arduino-cli compile --fqbn ${{ matrix.fqbn }} --build-property "build.extra_flags=-Wall -Wextra -Wpedantic -Werror -DESP32 -D${{ matrix.battery}} -D${{ matrix.inverter}} -D${{ matrix.hardware}}" ./Software
|
run: arduino-cli compile --fqbn ${{ matrix.fqbn }} --build-property build.partitions=min_spiffs --build-property upload.maximum_size=1966080 --build-property "build.extra_flags=-Wall -Wextra -Wpedantic -Werror -DESP32 -D${{ matrix.battery}} -D${{ matrix.inverter}} -D${{ matrix.hardware}}" ./Software
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
|
|
||||||
|
#include "src/communication/Transmitter.h"
|
||||||
#include "src/communication/can/comm_can.h"
|
#include "src/communication/can/comm_can.h"
|
||||||
#include "src/communication/contactorcontrol/comm_contactorcontrol.h"
|
#include "src/communication/contactorcontrol/comm_contactorcontrol.h"
|
||||||
#include "src/communication/equipmentstopbutton/comm_equipmentstopbutton.h"
|
#include "src/communication/equipmentstopbutton/comm_equipmentstopbutton.h"
|
||||||
|
@ -88,11 +89,6 @@ void setup() {
|
||||||
&logging_loop_task, WIFI_CORE);
|
&logging_loop_task, WIFI_CORE);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef MQTT
|
|
||||||
xTaskCreatePinnedToCore((TaskFunction_t)&mqtt_loop, "mqtt_loop", 4096, NULL, TASK_MQTT_PRIO, &mqtt_loop_task,
|
|
||||||
WIFI_CORE);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
init_CAN();
|
init_CAN();
|
||||||
|
|
||||||
init_contactors();
|
init_contactors();
|
||||||
|
@ -110,9 +106,8 @@ void setup() {
|
||||||
#ifdef EQUIPMENT_STOP_BUTTON
|
#ifdef EQUIPMENT_STOP_BUTTON
|
||||||
init_equipment_stop_button();
|
init_equipment_stop_button();
|
||||||
#endif
|
#endif
|
||||||
#ifdef CAN_SHUNT_SELECTED
|
|
||||||
setup_can_shunt();
|
setup_can_shunt();
|
||||||
#endif
|
|
||||||
// BOOT button at runtime is used as an input for various things
|
// BOOT button at runtime is used as an input for various things
|
||||||
pinMode(0, INPUT_PULLUP);
|
pinMode(0, INPUT_PULLUP);
|
||||||
|
|
||||||
|
@ -126,6 +121,12 @@ void setup() {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Start tasks
|
// Start tasks
|
||||||
|
|
||||||
|
#ifdef MQTT
|
||||||
|
xTaskCreatePinnedToCore((TaskFunction_t)&mqtt_loop, "mqtt_loop", 4096, NULL, TASK_MQTT_PRIO, &mqtt_loop_task,
|
||||||
|
WIFI_CORE);
|
||||||
|
#endif
|
||||||
|
|
||||||
xTaskCreatePinnedToCore((TaskFunction_t)&core_loop, "core_loop", 4096, NULL, TASK_CORE_PRIO, &main_loop_task,
|
xTaskCreatePinnedToCore((TaskFunction_t)&core_loop, "core_loop", 4096, NULL, TASK_CORE_PRIO, &main_loop_task,
|
||||||
CORE_FUNCTION_CORE);
|
CORE_FUNCTION_CORE);
|
||||||
#ifdef PERIODIC_BMS_RESET_AT
|
#ifdef PERIODIC_BMS_RESET_AT
|
||||||
|
@ -202,6 +203,12 @@ void mqtt_loop(void*) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static std::list<Transmitter*> transmitters;
|
||||||
|
|
||||||
|
void register_transmitter(Transmitter* transmitter) {
|
||||||
|
transmitters.push_back(transmitter);
|
||||||
|
}
|
||||||
|
|
||||||
void core_loop(void*) {
|
void core_loop(void*) {
|
||||||
esp_task_wdt_add(NULL); // Register this task with WDT
|
esp_task_wdt_add(NULL); // Register this task with WDT
|
||||||
TickType_t xLastWakeTime = xTaskGetTickCount();
|
TickType_t xLastWakeTime = xTaskGetTickCount();
|
||||||
|
@ -254,14 +261,24 @@ void core_loop(void*) {
|
||||||
START_TIME_MEASUREMENT(time_values);
|
START_TIME_MEASUREMENT(time_values);
|
||||||
#endif
|
#endif
|
||||||
update_pause_state(); // Check if we are OK to send CAN or need to pause
|
update_pause_state(); // Check if we are OK to send CAN or need to pause
|
||||||
update_values_battery(); // Fetch battery values
|
|
||||||
#ifdef DOUBLE_BATTERY
|
// Fetch battery values
|
||||||
update_values_battery2();
|
if (battery) {
|
||||||
|
battery->update_values();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (battery2) {
|
||||||
|
battery2->update_values();
|
||||||
check_interconnect_available();
|
check_interconnect_available();
|
||||||
#endif // DOUBLE_BATTERY
|
}
|
||||||
update_calculated_values();
|
update_calculated_values();
|
||||||
update_machineryprotection(); // Check safeties
|
update_machineryprotection(); // Check safeties
|
||||||
update_values_inverter(); // Update values heading towards inverter
|
|
||||||
|
// Update values heading towards inverter
|
||||||
|
if (inverter) {
|
||||||
|
inverter->update_values();
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef FUNCTION_TIME_MEASUREMENT
|
#ifdef FUNCTION_TIME_MEASUREMENT
|
||||||
END_TIME_MEASUREMENT_MAX(time_values, datalayer.system.status.time_values_us);
|
END_TIME_MEASUREMENT_MAX(time_values, datalayer.system.status.time_values_us);
|
||||||
#endif
|
#endif
|
||||||
|
@ -269,12 +286,12 @@ void core_loop(void*) {
|
||||||
#ifdef FUNCTION_TIME_MEASUREMENT
|
#ifdef FUNCTION_TIME_MEASUREMENT
|
||||||
START_TIME_MEASUREMENT(cantx);
|
START_TIME_MEASUREMENT(cantx);
|
||||||
#endif
|
#endif
|
||||||
// Output
|
|
||||||
transmit_can(currentMillis); // Send CAN messages to all components
|
|
||||||
|
|
||||||
#ifdef RS485_BATTERY_SELECTED
|
// Let all transmitter objects send their messages
|
||||||
transmit_rs485(currentMillis);
|
for (auto& transmitter : transmitters) {
|
||||||
#endif // RS485_BATTERY_SELECTED
|
transmitter->transmit(currentMillis);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef FUNCTION_TIME_MEASUREMENT
|
#ifdef FUNCTION_TIME_MEASUREMENT
|
||||||
END_TIME_MEASUREMENT_MAX(cantx, datalayer.system.status.time_cantx_us);
|
END_TIME_MEASUREMENT_MAX(cantx, datalayer.system.status.time_cantx_us);
|
||||||
END_TIME_MEASUREMENT_MAX(all, datalayer.system.status.core_task_10s_max_us);
|
END_TIME_MEASUREMENT_MAX(all, datalayer.system.status.core_task_10s_max_us);
|
||||||
|
@ -506,12 +523,6 @@ void update_calculated_values() {
|
||||||
lastMillisOverflowCheck = currentMillis;
|
lastMillisOverflowCheck = currentMillis;
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_values_inverter() {
|
|
||||||
if (inverter) {
|
|
||||||
inverter->update_values();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void check_reset_reason() {
|
void check_reset_reason() {
|
||||||
esp_reset_reason_t reason = esp_reset_reason();
|
esp_reset_reason_t reason = esp_reset_reason();
|
||||||
switch (reason) {
|
switch (reason) {
|
||||||
|
|
|
@ -41,43 +41,21 @@ void setup_battery() {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_values_battery() {
|
|
||||||
battery->update_values();
|
|
||||||
}
|
|
||||||
|
|
||||||
// transmit_can_battery is called once and we need to
|
// transmit_can_battery is called once and we need to
|
||||||
// call both batteries.
|
// call both batteries.
|
||||||
void transmit_can_battery(unsigned long currentMillis) {
|
|
||||||
((CanBattery*)battery)->transmit_can(currentMillis);
|
|
||||||
|
|
||||||
#ifdef DOUBLE_BATTERY
|
|
||||||
((CanBattery*)battery2)->transmit_can(currentMillis);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
|
void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
|
||||||
((CanBattery*)battery)->handle_incoming_can_frame(rx_frame);
|
((CanBattery*)battery)->handle_incoming_can_frame(rx_frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DOUBLE_BATTERY
|
|
||||||
void update_values_battery2() {
|
|
||||||
battery2->update_values();
|
|
||||||
}
|
|
||||||
|
|
||||||
void handle_incoming_can_frame_battery2(CAN_frame rx_frame) {
|
void handle_incoming_can_frame_battery2(CAN_frame rx_frame) {
|
||||||
((CanBattery*)battery2)->handle_incoming_can_frame(rx_frame);
|
((CanBattery*)battery2)->handle_incoming_can_frame(rx_frame);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef RS485_BATTERY_SELECTED
|
#ifdef RS485_BATTERY_SELECTED
|
||||||
void transmit_rs485(unsigned long currentMillis) {
|
|
||||||
((RS485Battery*)battery)->transmit_rs485(currentMillis);
|
|
||||||
}
|
|
||||||
|
|
||||||
void receive_RS485() {
|
void receive_RS485() {
|
||||||
((RS485Battery*)battery)->receive_RS485();
|
((RS485Battery*)battery)->receive_RS485();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -9,11 +9,11 @@ class Battery;
|
||||||
extern Battery* battery;
|
extern Battery* battery;
|
||||||
extern Battery* battery2;
|
extern Battery* battery2;
|
||||||
|
|
||||||
|
void setup_can_shunt();
|
||||||
|
|
||||||
#ifdef BMW_SBOX
|
#ifdef BMW_SBOX
|
||||||
#include "BMW-SBOX.h"
|
#include "BMW-SBOX.h"
|
||||||
void handle_incoming_can_frame_shunt(CAN_frame rx_frame);
|
void handle_incoming_can_frame_shunt(CAN_frame rx_frame);
|
||||||
void transmit_can_shunt(unsigned long currentMillis);
|
|
||||||
void setup_can_shunt();
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef BMW_I3_BATTERY
|
#ifdef BMW_I3_BATTERY
|
||||||
|
@ -159,7 +159,6 @@ void setup_can_shunt();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void setup_battery(void);
|
void setup_battery(void);
|
||||||
void update_values_battery();
|
|
||||||
|
|
||||||
#ifdef RS485_BATTERY_SELECTED
|
#ifdef RS485_BATTERY_SELECTED
|
||||||
void transmit_rs485(unsigned long currentMillis);
|
void transmit_rs485(unsigned long currentMillis);
|
||||||
|
@ -169,7 +168,6 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame);
|
||||||
void transmit_can_battery(unsigned long currentMillis);
|
void transmit_can_battery(unsigned long currentMillis);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void update_values_battery2();
|
|
||||||
void handle_incoming_can_frame_battery2(CAN_frame rx_frame);
|
void handle_incoming_can_frame_battery2(CAN_frame rx_frame);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,41 +1,9 @@
|
||||||
#include "../include.h"
|
#include "../include.h"
|
||||||
#ifdef BMW_SBOX
|
#ifdef BMW_SBOX
|
||||||
|
#include "../communication/can/comm_can.h"
|
||||||
#include "../datalayer/datalayer.h"
|
#include "../datalayer/datalayer.h"
|
||||||
#include "BMW-SBOX.h"
|
#include "BMW-SBOX.h"
|
||||||
|
|
||||||
#define MAX_ALLOWED_FAULT_TICKS 1000
|
|
||||||
|
|
||||||
enum SboxState { DISCONNECTED, PRECHARGE, NEGATIVE, POSITIVE, PRECHARGE_OFF, COMPLETED, SHUTDOWN_REQUESTED };
|
|
||||||
SboxState contactorStatus = DISCONNECTED;
|
|
||||||
|
|
||||||
unsigned long prechargeStartTime = 0;
|
|
||||||
unsigned long negativeStartTime = 0;
|
|
||||||
unsigned long positiveStartTime = 0;
|
|
||||||
unsigned long timeSpentInFaultedMode = 0;
|
|
||||||
unsigned long LastMsgTime = 0; // will store last time a 20ms CAN Message was send
|
|
||||||
unsigned long LastAvgTime = 0; // Last current storage time
|
|
||||||
unsigned long ShuntLastSeen = 0;
|
|
||||||
|
|
||||||
uint32_t avg_mA_array[10];
|
|
||||||
uint32_t avg_sum;
|
|
||||||
|
|
||||||
uint8_t k; //avg array pointer
|
|
||||||
|
|
||||||
uint8_t CAN100_cnt = 0;
|
|
||||||
|
|
||||||
CAN_frame SBOX_100 = {.FD = false,
|
|
||||||
.ext_ID = false,
|
|
||||||
.DLC = 4,
|
|
||||||
.ID = 0x100,
|
|
||||||
.data = {0x55, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00}}; // Byte 0: relay control, Byte 1: counter 0-E, Byte 4: CRC
|
|
||||||
|
|
||||||
CAN_frame SBOX_300 = {.FD = false,
|
|
||||||
.ext_ID = false,
|
|
||||||
.DLC = 4,
|
|
||||||
.ID = 0x300,
|
|
||||||
.data = {0xFF, 0xFE, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}}; // Static frame
|
|
||||||
|
|
||||||
uint8_t reverse_bits(uint8_t byte) {
|
uint8_t reverse_bits(uint8_t byte) {
|
||||||
uint8_t reversed = 0;
|
uint8_t reversed = 0;
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
|
@ -64,7 +32,7 @@ uint8_t calculateCRC(CAN_frame CAN) {
|
||||||
return crc;
|
return crc;
|
||||||
}
|
}
|
||||||
|
|
||||||
void handle_incoming_can_frame_shunt(CAN_frame rx_frame) {
|
void BmwSbox::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||||
unsigned long currentTime = millis();
|
unsigned long currentTime = millis();
|
||||||
if (rx_frame.ID == 0x200) {
|
if (rx_frame.ID == 0x200) {
|
||||||
ShuntLastSeen = currentTime;
|
ShuntLastSeen = currentTime;
|
||||||
|
@ -100,7 +68,7 @@ void handle_incoming_can_frame_shunt(CAN_frame rx_frame) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void transmit_can_shunt(unsigned long currentMillis) {
|
void BmwSbox::transmit_can(unsigned long currentMillis) {
|
||||||
|
|
||||||
/** Shunt can frames seen? **/
|
/** Shunt can frames seen? **/
|
||||||
if (ShuntLastSeen + 1000 < currentMillis) {
|
if (ShuntLastSeen + 1000 < currentMillis) {
|
||||||
|
@ -210,7 +178,7 @@ void transmit_can_shunt(unsigned long currentMillis) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup_can_shunt() {
|
void BmwSbox::setup() {
|
||||||
strncpy(datalayer.system.info.shunt_protocol, "BMW SBOX", 63);
|
strncpy(datalayer.system.info.shunt_protocol, "BMW SBOX", 63);
|
||||||
datalayer.system.info.shunt_protocol[63] = '\0';
|
datalayer.system.info.shunt_protocol[63] = '\0';
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,67 @@
|
||||||
#ifndef BMW_SBOX_CONTROL_H
|
#ifndef BMW_SBOX_CONTROL_H
|
||||||
#define BMW_SBOX_CONTROL_H
|
#define BMW_SBOX_CONTROL_H
|
||||||
#include "../include.h"
|
#include "../include.h"
|
||||||
#define CAN_SHUNT_SELECTED
|
|
||||||
void transmit_can(CAN_frame* tx_frame, int interface);
|
|
||||||
|
|
||||||
|
#define CAN_SHUNT_SELECTED
|
||||||
|
#define SELECTED_SHUNT_CLASS BmwSbox
|
||||||
|
|
||||||
|
#include "Shunt.h"
|
||||||
|
|
||||||
|
class BmwSbox : public CanShunt {
|
||||||
|
public:
|
||||||
|
void setup();
|
||||||
|
void transmit_can(unsigned long currentMillis);
|
||||||
|
void handle_incoming_can_frame(CAN_frame rx_frame);
|
||||||
|
|
||||||
|
private:
|
||||||
/** Minimum input voltage required to enable relay control **/
|
/** Minimum input voltage required to enable relay control **/
|
||||||
#define MINIMUM_INPUT_VOLTAGE 250
|
static const int MINIMUM_INPUT_VOLTAGE = 250;
|
||||||
|
|
||||||
/** Minimum required percentage of input voltage at the output port to engage the positive relay. **/
|
/** Minimum required percentage of input voltage at the output port to engage the positive relay. **/
|
||||||
/** Prevents engagement of the positive contactor if the precharge resistor is faulty. **/
|
/** Prevents engagement of the positive contactor if the precharge resistor is faulty. **/
|
||||||
#define MAX_PRECHARGE_RESISTOR_VOLTAGE_PERCENT 0.99
|
static constexpr const double MAX_PRECHARGE_RESISTOR_VOLTAGE_PERCENT = 0.99;
|
||||||
|
|
||||||
/* NOTE: modify the T2 time constant below to account for the resistance and capacitance of the target system.
|
/* NOTE: modify the T2 time constant below to account for the resistance and capacitance of the target system.
|
||||||
* t=3RC at minimum, t=5RC ideally
|
* t=3RC at minimum, t=5RC ideally
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define CONTACTOR_CONTROL_T1 5000 // Time before negative contactor engages and precharging starts
|
static const int CONTACTOR_CONTROL_T1 = 5000; // Time before negative contactor engages and precharging starts
|
||||||
#define CONTACTOR_CONTROL_T2 5000 // Precharge time before precharge resistor is bypassed by positive contactor
|
static const int CONTACTOR_CONTROL_T2 =
|
||||||
#define CONTACTOR_CONTROL_T3 2000 // Precharge relay lead time after positive contactor has been engaged
|
5000; // Precharge time before precharge resistor is bypassed by positive contactor
|
||||||
|
static const int CONTACTOR_CONTROL_T3 = 2000; // Precharge relay lead time after positive contactor has been engaged
|
||||||
|
|
||||||
|
static const int MAX_ALLOWED_FAULT_TICKS = 1000;
|
||||||
|
|
||||||
|
enum SboxState { DISCONNECTED, PRECHARGE, NEGATIVE, POSITIVE, PRECHARGE_OFF, COMPLETED, SHUTDOWN_REQUESTED };
|
||||||
|
SboxState contactorStatus = DISCONNECTED;
|
||||||
|
|
||||||
|
unsigned long prechargeStartTime = 0;
|
||||||
|
unsigned long negativeStartTime = 0;
|
||||||
|
unsigned long positiveStartTime = 0;
|
||||||
|
unsigned long timeSpentInFaultedMode = 0;
|
||||||
|
unsigned long LastMsgTime = 0; // will store last time a 20ms CAN Message was send
|
||||||
|
unsigned long LastAvgTime = 0; // Last current storage time
|
||||||
|
unsigned long ShuntLastSeen = 0;
|
||||||
|
|
||||||
|
uint32_t avg_mA_array[10];
|
||||||
|
uint32_t avg_sum;
|
||||||
|
|
||||||
|
uint8_t k; //avg array pointer
|
||||||
|
|
||||||
|
uint8_t CAN100_cnt = 0;
|
||||||
|
|
||||||
|
CAN_frame SBOX_100 = {.FD = false,
|
||||||
|
.ext_ID = false,
|
||||||
|
.DLC = 4,
|
||||||
|
.ID = 0x100,
|
||||||
|
.data = {0x55, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00}}; // Byte 0: relay control, Byte 1: counter 0-E, Byte 4: CRC
|
||||||
|
|
||||||
|
CAN_frame SBOX_300 = {.FD = false,
|
||||||
|
.ext_ID = false,
|
||||||
|
.DLC = 4,
|
||||||
|
.ID = 0x300,
|
||||||
|
.data = {0xFF, 0xFE, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}}; // Static frame
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -47,6 +47,9 @@ class Battery {
|
||||||
// This allows for battery specific SOC plausibility calculations to be performed.
|
// This allows for battery specific SOC plausibility calculations to be performed.
|
||||||
virtual bool soc_plausible() { return true; }
|
virtual bool soc_plausible() { return true; }
|
||||||
|
|
||||||
|
// Battery reports total_charged_battery_Wh and total_discharged_battery_Wh
|
||||||
|
virtual bool supports_charged_energy() { return false; }
|
||||||
|
|
||||||
virtual BatteryHtmlRenderer& get_status_renderer() { return defaultRenderer; }
|
virtual BatteryHtmlRenderer& get_status_renderer() { return defaultRenderer; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -3,22 +3,31 @@
|
||||||
|
|
||||||
#include "Battery.h"
|
#include "Battery.h"
|
||||||
|
|
||||||
|
#include "src/communication/Transmitter.h"
|
||||||
#include "src/devboard/utils/types.h"
|
#include "src/devboard/utils/types.h"
|
||||||
|
|
||||||
// Abstract base class for batteries using the CAN bus
|
// Abstract base class for batteries using the CAN bus
|
||||||
class CanBattery : public Battery {
|
class CanBattery : public Battery, Transmitter {
|
||||||
public:
|
public:
|
||||||
virtual void handle_incoming_can_frame(CAN_frame rx_frame) = 0;
|
virtual void handle_incoming_can_frame(CAN_frame rx_frame) = 0;
|
||||||
virtual void transmit_can(unsigned long currentMillis) = 0;
|
virtual void transmit_can(unsigned long currentMillis) = 0;
|
||||||
|
|
||||||
String interface_name() { return getCANInterfaceName(can_interface); }
|
String interface_name() { return getCANInterfaceName(can_interface); }
|
||||||
|
|
||||||
|
void transmit(unsigned long currentMillis) { transmit_can(currentMillis); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
CAN_Interface can_interface;
|
CAN_Interface can_interface;
|
||||||
|
|
||||||
CanBattery() { can_interface = can_config.battery; }
|
CanBattery() {
|
||||||
|
can_interface = can_config.battery;
|
||||||
|
register_transmitter(this);
|
||||||
|
}
|
||||||
|
|
||||||
CanBattery(CAN_Interface interface) { can_interface = interface; }
|
CanBattery(CAN_Interface interface) {
|
||||||
|
can_interface = interface;
|
||||||
|
register_transmitter(this);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -15,6 +15,7 @@ class MebBattery : public CanBattery {
|
||||||
virtual void update_values();
|
virtual void update_values();
|
||||||
virtual void transmit_can(unsigned long currentMillis);
|
virtual void transmit_can(unsigned long currentMillis);
|
||||||
bool supports_real_BMS_status() { return true; }
|
bool supports_real_BMS_status() { return true; }
|
||||||
|
bool supports_charged_energy() { return true; }
|
||||||
|
|
||||||
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
|
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
|
||||||
|
|
||||||
|
|
|
@ -43,9 +43,14 @@ class NissanLeafBattery : public CanBattery {
|
||||||
virtual void transmit_can(unsigned long currentMillis);
|
virtual void transmit_can(unsigned long currentMillis);
|
||||||
|
|
||||||
bool supports_reset_SOH();
|
bool supports_reset_SOH();
|
||||||
|
|
||||||
void reset_SOH() { datalayer_extended.nissanleaf.UserRequestSOHreset = true; }
|
void reset_SOH() { datalayer_extended.nissanleaf.UserRequestSOHreset = true; }
|
||||||
|
|
||||||
|
bool soc_plausible() {
|
||||||
|
// When pack voltage is close to max, and SOC% is still low, SOC is not plausible
|
||||||
|
return !((datalayer.battery.status.voltage_dV > (datalayer.battery.info.max_design_voltage_dV - 100)) &&
|
||||||
|
(datalayer.battery.status.real_soc < 6500));
|
||||||
|
}
|
||||||
|
|
||||||
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
|
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -3,14 +3,20 @@
|
||||||
|
|
||||||
#include "Battery.h"
|
#include "Battery.h"
|
||||||
|
|
||||||
|
#include "src/communication/Transmitter.h"
|
||||||
#include "src/devboard/utils/types.h"
|
#include "src/devboard/utils/types.h"
|
||||||
|
|
||||||
// Abstract base class for batteries using the RS485 interface
|
// Abstract base class for batteries using the RS485 interface
|
||||||
class RS485Battery : public Battery {
|
class RS485Battery : public Battery, Transmitter {
|
||||||
public:
|
public:
|
||||||
virtual void receive_RS485() = 0;
|
virtual void receive_RS485() = 0;
|
||||||
virtual void transmit_rs485(unsigned long currentMillis) = 0;
|
virtual void transmit_rs485(unsigned long currentMillis) = 0;
|
||||||
|
|
||||||
String interface_name() { return "RS485"; }
|
String interface_name() { return "RS485"; }
|
||||||
|
|
||||||
|
void transmit(unsigned long currentMillis) { transmit_rs485(currentMillis); }
|
||||||
|
|
||||||
|
RS485Battery() { register_transmitter(this); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
31
Software/src/battery/Shunt.h
Normal file
31
Software/src/battery/Shunt.h
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#ifndef _SHUNT_H
|
||||||
|
#define _SHUNT_H
|
||||||
|
|
||||||
|
#include "../communication/can/comm_can.h"
|
||||||
|
#include "src/communication/Transmitter.h"
|
||||||
|
#include "src/devboard/utils/types.h"
|
||||||
|
|
||||||
|
class CanShunt : public Transmitter {
|
||||||
|
public:
|
||||||
|
virtual void setup() = 0;
|
||||||
|
virtual void transmit_can(unsigned long currentMillis) = 0;
|
||||||
|
virtual void handle_incoming_can_frame(CAN_frame rx_frame) = 0;
|
||||||
|
|
||||||
|
void transmit(unsigned long currentMillis) {
|
||||||
|
if (allowed_to_send_CAN) {
|
||||||
|
transmit_can(currentMillis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
CAN_Interface can_interface;
|
||||||
|
|
||||||
|
CanShunt() {
|
||||||
|
can_interface = can_config.battery;
|
||||||
|
register_transmitter(this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
extern CanShunt* shunt;
|
||||||
|
|
||||||
|
#endif
|
19
Software/src/battery/Shunts.cpp
Normal file
19
Software/src/battery/Shunts.cpp
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
#include "../include.h"
|
||||||
|
#include "Shunt.h"
|
||||||
|
|
||||||
|
CanShunt* shunt = nullptr;
|
||||||
|
|
||||||
|
void setup_can_shunt() {
|
||||||
|
#if defined(CAN_SHUNT_SELECTED) && defined(SELECTED_SHUNT_CLASS)
|
||||||
|
shunt = new SELECTED_SHUNT_CLASS();
|
||||||
|
if (shunt) {
|
||||||
|
shunt->setup();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle_incoming_can_frame_shunt(CAN_frame rx_frame) {
|
||||||
|
if (shunt) {
|
||||||
|
shunt->handle_incoming_can_frame(rx_frame);
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,6 +30,8 @@ class TeslaBattery : public CanBattery {
|
||||||
bool supports_reset_BMS() { return true; }
|
bool supports_reset_BMS() { return true; }
|
||||||
void reset_BMS() { datalayer.battery.settings.user_requests_tesla_bms_reset = true; }
|
void reset_BMS() { datalayer.battery.settings.user_requests_tesla_bms_reset = true; }
|
||||||
|
|
||||||
|
bool supports_charged_energy() { return true; }
|
||||||
|
|
||||||
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
|
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "src/devboard/utils/types.h"
|
#include "src/devboard/utils/types.h"
|
||||||
|
|
||||||
#include "../datalayer/datalayer.h"
|
#include "../datalayer/datalayer.h"
|
||||||
|
#include "src/communication/Transmitter.h"
|
||||||
|
|
||||||
enum class ChargerType { NissanLeaf, ChevyVolt };
|
enum class ChargerType { NissanLeaf, ChevyVolt };
|
||||||
|
|
||||||
|
@ -36,13 +37,19 @@ class Charger {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Base class for chargers on a CAN bus
|
// Base class for chargers on a CAN bus
|
||||||
class CanCharger : public Charger {
|
class CanCharger : public Charger, Transmitter {
|
||||||
public:
|
public:
|
||||||
virtual void map_can_frame_to_variable(CAN_frame rx_frame) = 0;
|
virtual void map_can_frame_to_variable(CAN_frame rx_frame) = 0;
|
||||||
virtual void transmit_can(unsigned long currentMillis) = 0;
|
virtual void transmit_can(unsigned long currentMillis) = 0;
|
||||||
|
|
||||||
|
void transmit(unsigned long currentMillis) {
|
||||||
|
if (allowed_to_send_CAN) {
|
||||||
|
transmit_can(currentMillis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
CanCharger(ChargerType type) : Charger(type) {}
|
CanCharger(ChargerType type) : Charger(type) { register_transmitter(this); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
11
Software/src/communication/Transmitter.h
Normal file
11
Software/src/communication/Transmitter.h
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#ifndef _TRANSMITTER_H
|
||||||
|
#define _TRANSMITTER_H
|
||||||
|
|
||||||
|
class Transmitter {
|
||||||
|
public:
|
||||||
|
virtual void transmit(unsigned long currentMillis) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
void register_transmitter(Transmitter* transmitter);
|
||||||
|
|
||||||
|
#endif
|
|
@ -104,30 +104,6 @@ void init_CAN() {
|
||||||
#endif // CANFD_ADDON
|
#endif // CANFD_ADDON
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transmit functions
|
|
||||||
void transmit_can(unsigned long currentMillis) {
|
|
||||||
|
|
||||||
if (!allowed_to_send_CAN) {
|
|
||||||
return; //Global block of CAN messages
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef RS485_BATTERY_SELECTED
|
|
||||||
transmit_can_battery(currentMillis);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CAN_INVERTER_SELECTED
|
|
||||||
transmit_can_inverter(currentMillis);
|
|
||||||
#endif // CAN_INVERTER_SELECTED
|
|
||||||
|
|
||||||
if (charger) {
|
|
||||||
charger->transmit_can(currentMillis);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CAN_SHUNT_SELECTED
|
|
||||||
transmit_can_shunt(currentMillis);
|
|
||||||
#endif // CAN_SHUNT_SELECTED
|
|
||||||
}
|
|
||||||
|
|
||||||
void transmit_can_frame(CAN_frame* tx_frame, int interface) {
|
void transmit_can_frame(CAN_frame* tx_frame, int interface) {
|
||||||
if (!allowed_to_send_CAN) {
|
if (!allowed_to_send_CAN) {
|
||||||
return;
|
return;
|
||||||
|
@ -325,10 +301,8 @@ void map_can_frame_to_variable(CAN_frame* rx_frame, int interface) {
|
||||||
map_can_frame_to_variable_inverter(*rx_frame);
|
map_can_frame_to_variable_inverter(*rx_frame);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
if (interface == can_config.battery_double) {
|
if (interface == can_config.battery_double && battery2) {
|
||||||
#ifdef DOUBLE_BATTERY
|
|
||||||
handle_incoming_can_frame_battery2(*rx_frame);
|
handle_incoming_can_frame_battery2(*rx_frame);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
if (interface == can_config.charger && charger) {
|
if (interface == can_config.charger && charger) {
|
||||||
charger->map_can_frame_to_variable(*rx_frame);
|
charger->map_can_frame_to_variable(*rx_frame);
|
||||||
|
|
|
@ -37,16 +37,6 @@ void init_CAN();
|
||||||
*/
|
*/
|
||||||
void transmit_can_frame();
|
void transmit_can_frame();
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Send CAN messages to all components
|
|
||||||
*
|
|
||||||
* @param[in] void
|
|
||||||
* @param[in] unsigned long currentMillis
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
void transmit_can(unsigned long currentMillis);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Receive CAN messages from all interfaces
|
* @brief Receive CAN messages from all interfaces
|
||||||
*
|
*
|
||||||
|
|
|
@ -79,12 +79,8 @@
|
||||||
#define EQUIPMENT_STOP_PIN 35
|
#define EQUIPMENT_STOP_PIN 35
|
||||||
|
|
||||||
// BMW_I3_BATTERY wake up pin
|
// BMW_I3_BATTERY wake up pin
|
||||||
#ifdef BMW_I3_BATTERY
|
|
||||||
#define WUP_PIN1 GPIO_NUM_25 // Wake up pin for battery 1
|
#define WUP_PIN1 GPIO_NUM_25 // Wake up pin for battery 1
|
||||||
#ifdef DOUBLE_BATTERY
|
|
||||||
#define WUP_PIN2 GPIO_NUM_32 // Wake up pin for battery 2
|
#define WUP_PIN2 GPIO_NUM_32 // Wake up pin for battery 2
|
||||||
#endif // DOUBLE_BATTERY
|
|
||||||
#endif // BMW_I3_BATTERY
|
|
||||||
|
|
||||||
/* ----- Error checks below, don't change (can't be moved to separate file) ----- */
|
/* ----- Error checks below, don't change (can't be moved to separate file) ----- */
|
||||||
#ifndef HW_CONFIGURED
|
#ifndef HW_CONFIGURED
|
||||||
|
|
|
@ -67,12 +67,8 @@ The pin layout below supports the following:
|
||||||
#define INVERTER_DISCONNECT_CONTACTOR_PIN GPIO_NUM_5
|
#define INVERTER_DISCONNECT_CONTACTOR_PIN GPIO_NUM_5
|
||||||
|
|
||||||
// BMW_I3_BATTERY wake up pin
|
// BMW_I3_BATTERY wake up pin
|
||||||
#ifdef BMW_I3_BATTERY
|
|
||||||
#define WUP_PIN1 GPIO_NUM_25 // Wake up pin for battery 1
|
#define WUP_PIN1 GPIO_NUM_25 // Wake up pin for battery 1
|
||||||
#ifdef DOUBLE_BATTERY
|
|
||||||
#define WUP_PIN2 GPIO_NUM_32 // Wake up pin for battery 2
|
#define WUP_PIN2 GPIO_NUM_32 // Wake up pin for battery 2
|
||||||
#endif // DOUBLE_BATTERY
|
|
||||||
#endif // BMW_I3_BATTERY
|
|
||||||
|
|
||||||
/* ----- Error checks below, don't change (can't be moved to separate file) ----- */
|
/* ----- Error checks below, don't change (can't be moved to separate file) ----- */
|
||||||
#ifndef HW_CONFIGURED
|
#ifndef HW_CONFIGURED
|
||||||
|
|
|
@ -74,12 +74,8 @@
|
||||||
#define EQUIPMENT_STOP_PIN 35
|
#define EQUIPMENT_STOP_PIN 35
|
||||||
|
|
||||||
// BMW_I3_BATTERY wake up pin
|
// BMW_I3_BATTERY wake up pin
|
||||||
#ifdef BMW_I3_BATTERY
|
|
||||||
#define WUP_PIN1 GPIO_NUM_25 // Wake up pin for battery 1
|
#define WUP_PIN1 GPIO_NUM_25 // Wake up pin for battery 1
|
||||||
#ifdef DOUBLE_BATTERY
|
|
||||||
#define WUP_PIN2 GPIO_NUM_32 // Wake up pin for battery 2
|
#define WUP_PIN2 GPIO_NUM_32 // Wake up pin for battery 2
|
||||||
#endif // DOUBLE_BATTERY
|
|
||||||
#endif // BMW_I3_BATTERY
|
|
||||||
|
|
||||||
/* ----- Error checks below, don't change (can't be moved to separate file) ----- */
|
/* ----- Error checks below, don't change (can't be moved to separate file) ----- */
|
||||||
#ifndef HW_CONFIGURED
|
#ifndef HW_CONFIGURED
|
||||||
|
|
|
@ -66,12 +66,8 @@ GPIOs on extra header
|
||||||
#define EQUIPMENT_STOP_PIN 2
|
#define EQUIPMENT_STOP_PIN 2
|
||||||
|
|
||||||
// BMW_I3_BATTERY wake up pin
|
// BMW_I3_BATTERY wake up pin
|
||||||
#ifdef BMW_I3_BATTERY
|
|
||||||
#define WUP_PIN1 GPIO_NUM_25 // Wake up pin for battery 1
|
#define WUP_PIN1 GPIO_NUM_25 // Wake up pin for battery 1
|
||||||
#ifdef DOUBLE_BATTERY
|
|
||||||
#define WUP_PIN2 GPIO_NUM_32 // Wake up pin for battery 2
|
#define WUP_PIN2 GPIO_NUM_32 // Wake up pin for battery 2
|
||||||
#endif // DOUBLE_BATTERY
|
|
||||||
#endif // BMW_I3_BATTERY
|
|
||||||
|
|
||||||
/* ----- Error checks below, don't change (can't be moved to separate file) ----- */
|
/* ----- Error checks below, don't change (can't be moved to separate file) ----- */
|
||||||
#ifndef HW_CONFIGURED
|
#ifndef HW_CONFIGURED
|
||||||
|
|
|
@ -62,46 +62,53 @@ struct SensorConfig {
|
||||||
const char* value_template;
|
const char* value_template;
|
||||||
const char* unit;
|
const char* unit;
|
||||||
const char* device_class;
|
const char* device_class;
|
||||||
|
|
||||||
|
// A function that returns true for the battery if it supports this config
|
||||||
|
std::function<bool(Battery*)> condition;
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::function<bool(Battery*)> always = [](Battery* b) {
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
static std::function<bool(Battery*)> supports_charged = [](Battery* b) {
|
||||||
|
return b->supports_charged_energy();
|
||||||
};
|
};
|
||||||
|
|
||||||
SensorConfig sensorConfigTemplate[] = {
|
SensorConfig sensorConfigTemplate[] = {
|
||||||
{"SOC", "SOC (Scaled)", "", "%", "battery"},
|
{"SOC", "SOC (Scaled)", "", "%", "battery", always},
|
||||||
{"SOC_real", "SOC (real)", "", "%", "battery"},
|
{"SOC_real", "SOC (real)", "", "%", "battery", always},
|
||||||
{"state_of_health", "State Of Health", "", "%", "battery"},
|
{"state_of_health", "State Of Health", "", "%", "battery", always},
|
||||||
{"temperature_min", "Temperature Min", "", "°C", "temperature"},
|
{"temperature_min", "Temperature Min", "", "°C", "temperature", always},
|
||||||
{"temperature_max", "Temperature Max", "", "°C", "temperature"},
|
{"temperature_max", "Temperature Max", "", "°C", "temperature", always},
|
||||||
{"cpu_temp", "CPU Temperature", "", "°C", "temperature"},
|
{"cpu_temp", "CPU Temperature", "", "°C", "temperature", always},
|
||||||
{"stat_batt_power", "Stat Batt Power", "", "W", "power"},
|
{"stat_batt_power", "Stat Batt Power", "", "W", "power", always},
|
||||||
{"battery_current", "Battery Current", "", "A", "current"},
|
{"battery_current", "Battery Current", "", "A", "current", always},
|
||||||
{"cell_max_voltage", "Cell Max Voltage", "", "V", "voltage"},
|
{"cell_max_voltage", "Cell Max Voltage", "", "V", "voltage", always},
|
||||||
{"cell_min_voltage", "Cell Min Voltage", "", "V", "voltage"},
|
{"cell_min_voltage", "Cell Min Voltage", "", "V", "voltage", always},
|
||||||
{"cell_voltage_delta", "Cell Voltage Delta", "", "mV", "voltage"},
|
{"cell_voltage_delta", "Cell Voltage Delta", "", "mV", "voltage", always},
|
||||||
{"battery_voltage", "Battery Voltage", "", "V", "voltage"},
|
{"battery_voltage", "Battery Voltage", "", "V", "voltage", always},
|
||||||
{"total_capacity", "Battery Total Capacity", "", "Wh", "energy"},
|
{"total_capacity", "Battery Total Capacity", "", "Wh", "energy", always},
|
||||||
{"remaining_capacity", "Battery Remaining Capacity (scaled)", "", "Wh", "energy"},
|
{"remaining_capacity", "Battery Remaining Capacity (scaled)", "", "Wh", "energy", always},
|
||||||
{"remaining_capacity_real", "Battery Remaining Capacity (real)", "", "Wh", "energy"},
|
{"remaining_capacity_real", "Battery Remaining Capacity (real)", "", "Wh", "energy", always},
|
||||||
{"max_discharge_power", "Battery Max Discharge Power", "", "W", "power"},
|
{"max_discharge_power", "Battery Max Discharge Power", "", "W", "power", always},
|
||||||
{"max_charge_power", "Battery Max Charge Power", "", "W", "power"},
|
{"max_charge_power", "Battery Max Charge Power", "", "W", "power", always},
|
||||||
#if defined(MEB_BATTERY) || defined(TESLA_BATTERY)
|
{"charged_energy", "Battery Charged Energy", "", "Wh", "energy", supports_charged},
|
||||||
{"charged_energy", "Battery Charged Energy", "", "Wh", "energy"},
|
{"discharged_energy", "Battery Discharged Energy", "", "Wh", "energy", supports_charged},
|
||||||
{"discharged_energy", "Battery Discharged Energy", "", "Wh", "energy"},
|
{"bms_status", "BMS Status", "", "", "", always},
|
||||||
#endif
|
{"pause_status", "Pause Status", "", "", "", always}};
|
||||||
{"bms_status", "BMS Status", "", "", ""},
|
|
||||||
{"pause_status", "Pause Status", "", "", ""}};
|
|
||||||
|
|
||||||
#ifdef DOUBLE_BATTERY
|
// Enough space for two batteries
|
||||||
SensorConfig sensorConfigs[((sizeof(sensorConfigTemplate) / sizeof(sensorConfigTemplate[0])) * 2) - 2];
|
SensorConfig sensorConfigs[((sizeof(sensorConfigTemplate) / sizeof(sensorConfigTemplate[0])) * 2) - 2];
|
||||||
#else
|
|
||||||
SensorConfig sensorConfigs[sizeof(sensorConfigTemplate) / sizeof(sensorConfigTemplate[0])];
|
|
||||||
#endif // DOUBLE_BATTERY
|
|
||||||
|
|
||||||
void create_sensor_configs() {
|
void create_sensor_configs() {
|
||||||
int number_of_templates = sizeof(sensorConfigTemplate) / sizeof(sensorConfigTemplate[0]);
|
int number_of_templates = sizeof(sensorConfigTemplate) / sizeof(sensorConfigTemplate[0]);
|
||||||
|
|
||||||
for (int i = 0; i < number_of_templates; i++) {
|
for (int i = 0; i < number_of_templates; i++) {
|
||||||
SensorConfig config = sensorConfigTemplate[i];
|
SensorConfig config = sensorConfigTemplate[i];
|
||||||
config.value_template = strdup(("{{ value_json." + std::string(config.object_id) + " }}").c_str());
|
config.value_template = strdup(("{{ value_json." + std::string(config.object_id) + " }}").c_str());
|
||||||
sensorConfigs[i] = config;
|
sensorConfigs[i] = config;
|
||||||
#ifdef DOUBLE_BATTERY
|
|
||||||
|
if (battery2) {
|
||||||
if (config.object_id == "pause_status" || config.object_id == "bms_status") {
|
if (config.object_id == "pause_status" || config.object_id == "bms_status") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -110,7 +117,7 @@ void create_sensor_configs() {
|
||||||
sensorConfigs[i + number_of_templates].object_id = strdup(String(config.object_id + String("_2")).c_str());
|
sensorConfigs[i + number_of_templates].object_id = strdup(String(config.object_id + String("_2")).c_str());
|
||||||
sensorConfigs[i + number_of_templates].value_template =
|
sensorConfigs[i + number_of_templates].value_template =
|
||||||
strdup(("{{ value_json." + std::string(config.object_id) + "_2 }}").c_str());
|
strdup(("{{ value_json." + std::string(config.object_id) + "_2 }}").c_str());
|
||||||
#endif // DOUBLE_BATTERY
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,7 +171,8 @@ static String generateButtonTopic(const char* subtype) {
|
||||||
return topic_name + "/command/" + String(subtype);
|
return topic_name + "/command/" + String(subtype);
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_battery_attributes(JsonDocument& doc, const DATALAYER_BATTERY_TYPE& battery, const String& suffix) {
|
void set_battery_attributes(JsonDocument& doc, const DATALAYER_BATTERY_TYPE& battery, const String& suffix,
|
||||||
|
bool supports_charged) {
|
||||||
doc["SOC" + suffix] = ((float)battery.status.reported_soc) / 100.0;
|
doc["SOC" + suffix] = ((float)battery.status.reported_soc) / 100.0;
|
||||||
doc["SOC_real" + suffix] = ((float)battery.status.real_soc) / 100.0;
|
doc["SOC_real" + suffix] = ((float)battery.status.real_soc) / 100.0;
|
||||||
doc["state_of_health" + suffix] = ((float)battery.status.soh_pptt) / 100.0;
|
doc["state_of_health" + suffix] = ((float)battery.status.soh_pptt) / 100.0;
|
||||||
|
@ -185,13 +193,14 @@ void set_battery_attributes(JsonDocument& doc, const DATALAYER_BATTERY_TYPE& bat
|
||||||
doc["remaining_capacity" + suffix] = ((float)battery.status.reported_remaining_capacity_Wh);
|
doc["remaining_capacity" + suffix] = ((float)battery.status.reported_remaining_capacity_Wh);
|
||||||
doc["max_discharge_power" + suffix] = ((float)battery.status.max_discharge_power_W);
|
doc["max_discharge_power" + suffix] = ((float)battery.status.max_discharge_power_W);
|
||||||
doc["max_charge_power" + suffix] = ((float)battery.status.max_charge_power_W);
|
doc["max_charge_power" + suffix] = ((float)battery.status.max_charge_power_W);
|
||||||
#if defined(MEB_BATTERY) || defined(TESLA_BATTERY)
|
|
||||||
|
if (supports_charged) {
|
||||||
if (datalayer.battery.status.total_charged_battery_Wh != 0 &&
|
if (datalayer.battery.status.total_charged_battery_Wh != 0 &&
|
||||||
datalayer.battery.status.total_discharged_battery_Wh != 0) {
|
datalayer.battery.status.total_discharged_battery_Wh != 0) {
|
||||||
doc["charged_energy" + suffix] = ((float)datalayer.battery.status.total_charged_battery_Wh);
|
doc["charged_energy" + suffix] = ((float)datalayer.battery.status.total_charged_battery_Wh);
|
||||||
doc["discharged_energy" + suffix] = ((float)datalayer.battery.status.total_discharged_battery_Wh);
|
doc["discharged_energy" + suffix] = ((float)datalayer.battery.status.total_discharged_battery_Wh);
|
||||||
}
|
}
|
||||||
#endif
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<EventData> order_events;
|
static std::vector<EventData> order_events;
|
||||||
|
@ -203,13 +212,19 @@ static bool publish_common_info(void) {
|
||||||
if (ha_common_info_published == false) {
|
if (ha_common_info_published == false) {
|
||||||
for (int i = 0; i < sizeof(sensorConfigs) / sizeof(sensorConfigs[0]); i++) {
|
for (int i = 0; i < sizeof(sensorConfigs) / sizeof(sensorConfigs[0]); i++) {
|
||||||
SensorConfig& config = sensorConfigs[i];
|
SensorConfig& config = sensorConfigs[i];
|
||||||
|
|
||||||
|
if (!config.condition(battery)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
doc["name"] = config.name;
|
doc["name"] = config.name;
|
||||||
doc["state_topic"] = state_topic;
|
doc["state_topic"] = state_topic;
|
||||||
doc["unique_id"] = topic_name + "_" + String(config.object_id);
|
doc["unique_id"] = topic_name + "_" + String(config.object_id);
|
||||||
doc["object_id"] = object_id_prefix + String(config.object_id);
|
doc["object_id"] = object_id_prefix + String(config.object_id);
|
||||||
doc["value_template"] = config.value_template;
|
doc["value_template"] = config.value_template;
|
||||||
if (config.unit != nullptr && strlen(config.unit) > 0)
|
if (config.unit != nullptr && strlen(config.unit) > 0) {
|
||||||
doc["unit_of_measurement"] = config.unit;
|
doc["unit_of_measurement"] = config.unit;
|
||||||
|
}
|
||||||
if (config.device_class != nullptr && strlen(config.device_class) > 0) {
|
if (config.device_class != nullptr && strlen(config.device_class) > 0) {
|
||||||
doc["device_class"] = config.device_class;
|
doc["device_class"] = config.device_class;
|
||||||
doc["state_class"] = "measurement";
|
doc["state_class"] = "measurement";
|
||||||
|
@ -231,14 +246,15 @@ static bool publish_common_info(void) {
|
||||||
|
|
||||||
//only publish these values if BMS is active and we are comunication with the battery (can send CAN messages to the battery)
|
//only publish these values if BMS is active and we are comunication with the battery (can send CAN messages to the battery)
|
||||||
if (datalayer.battery.status.CAN_battery_still_alive && allowed_to_send_CAN && millis() > BOOTUP_TIME) {
|
if (datalayer.battery.status.CAN_battery_still_alive && allowed_to_send_CAN && millis() > BOOTUP_TIME) {
|
||||||
set_battery_attributes(doc, datalayer.battery, "");
|
set_battery_attributes(doc, datalayer.battery, "", battery->supports_charged_energy());
|
||||||
}
|
}
|
||||||
#ifdef DOUBLE_BATTERY
|
|
||||||
|
if (battery2) {
|
||||||
//only publish these values if BMS is active and we are comunication with the battery (can send CAN messages to the battery)
|
//only publish these values if BMS is active and we are comunication with the battery (can send CAN messages to the battery)
|
||||||
if (datalayer.battery2.status.CAN_battery_still_alive && allowed_to_send_CAN && millis() > BOOTUP_TIME) {
|
if (datalayer.battery2.status.CAN_battery_still_alive && allowed_to_send_CAN && millis() > BOOTUP_TIME) {
|
||||||
set_battery_attributes(doc, datalayer.battery2, "_2");
|
set_battery_attributes(doc, datalayer.battery2, "_2", battery2->supports_charged_energy());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif // DOUBLE_BATTERY
|
|
||||||
serializeJson(doc, mqtt_msg);
|
serializeJson(doc, mqtt_msg);
|
||||||
if (mqtt_publish(state_topic.c_str(), mqtt_msg, false) == false) {
|
if (mqtt_publish(state_topic.c_str(), mqtt_msg, false) == false) {
|
||||||
#ifdef DEBUG_LOG
|
#ifdef DEBUG_LOG
|
||||||
|
@ -256,9 +272,7 @@ static bool publish_common_info(void) {
|
||||||
static bool publish_cell_voltages(void) {
|
static bool publish_cell_voltages(void) {
|
||||||
static JsonDocument doc;
|
static JsonDocument doc;
|
||||||
static String state_topic = topic_name + "/spec_data";
|
static String state_topic = topic_name + "/spec_data";
|
||||||
#ifdef DOUBLE_BATTERY
|
|
||||||
static String state_topic_2 = topic_name + "/spec_data_2";
|
static String state_topic_2 = topic_name + "/spec_data_2";
|
||||||
#endif // DOUBLE_BATTERY
|
|
||||||
|
|
||||||
#ifdef HA_AUTODISCOVERY
|
#ifdef HA_AUTODISCOVERY
|
||||||
bool failed_to_publish = false;
|
bool failed_to_publish = false;
|
||||||
|
@ -280,7 +294,9 @@ static bool publish_cell_voltages(void) {
|
||||||
}
|
}
|
||||||
doc.clear(); // clear after sending autoconfig
|
doc.clear(); // clear after sending autoconfig
|
||||||
}
|
}
|
||||||
#ifdef DOUBLE_BATTERY
|
|
||||||
|
if (battery2) {
|
||||||
|
// TODO: Combine this identical block with the previous one.
|
||||||
// If the cell voltage number isn't initialized...
|
// If the cell voltage number isn't initialized...
|
||||||
if (datalayer.battery2.info.number_of_cells != 0u) {
|
if (datalayer.battery2.info.number_of_cells != 0u) {
|
||||||
|
|
||||||
|
@ -297,7 +313,7 @@ static bool publish_cell_voltages(void) {
|
||||||
}
|
}
|
||||||
doc.clear(); // clear after sending autoconfig
|
doc.clear(); // clear after sending autoconfig
|
||||||
}
|
}
|
||||||
#endif // DOUBLE_BATTERY
|
}
|
||||||
}
|
}
|
||||||
if (failed_to_publish == false) {
|
if (failed_to_publish == false) {
|
||||||
ha_cell_voltages_published = true;
|
ha_cell_voltages_published = true;
|
||||||
|
@ -324,7 +340,7 @@ static bool publish_cell_voltages(void) {
|
||||||
doc.clear();
|
doc.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DOUBLE_BATTERY
|
if (battery2) {
|
||||||
// If cell voltages have been populated...
|
// If cell voltages have been populated...
|
||||||
if (datalayer.battery2.info.number_of_cells != 0u &&
|
if (datalayer.battery2.info.number_of_cells != 0u &&
|
||||||
datalayer.battery2.status.cell_voltages_mV[datalayer.battery2.info.number_of_cells - 1] != 0u) {
|
datalayer.battery2.status.cell_voltages_mV[datalayer.battery2.info.number_of_cells - 1] != 0u) {
|
||||||
|
@ -344,7 +360,7 @@ static bool publish_cell_voltages(void) {
|
||||||
}
|
}
|
||||||
doc.clear();
|
doc.clear();
|
||||||
}
|
}
|
||||||
#endif // DOUBLE_BATTERY
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -156,18 +156,9 @@ void update_machineryprotection() {
|
||||||
clear_event(EVENT_SOH_LOW);
|
clear_event(EVENT_SOH_LOW);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef NISSAN_LEAF_BATTERY
|
if (!battery->soc_plausible()) {
|
||||||
// Check if SOC% is plausible
|
|
||||||
if (datalayer.battery.status.voltage_dV >
|
|
||||||
(datalayer.battery.info.max_design_voltage_dV -
|
|
||||||
100)) { // When pack voltage is close to max, and SOC% is still low, raise event
|
|
||||||
if (datalayer.battery.status.real_soc < 6500) { // 65.00%
|
|
||||||
set_event(EVENT_SOC_PLAUSIBILITY_ERROR, datalayer.battery.status.real_soc);
|
set_event(EVENT_SOC_PLAUSIBILITY_ERROR, datalayer.battery.status.real_soc);
|
||||||
} else {
|
|
||||||
clear_event(EVENT_SOC_PLAUSIBILITY_ERROR);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
#endif //NISSAN_LEAF_BATTERY
|
|
||||||
|
|
||||||
// Check diff between highest and lowest cell
|
// Check diff between highest and lowest cell
|
||||||
cell_deviation_mV =
|
cell_deviation_mV =
|
||||||
|
|
|
@ -5,11 +5,20 @@
|
||||||
|
|
||||||
#include "src/devboard/utils/types.h"
|
#include "src/devboard/utils/types.h"
|
||||||
|
|
||||||
class CanInverterProtocol : public InverterProtocol {
|
class CanInverterProtocol : public InverterProtocol, Transmitter {
|
||||||
public:
|
public:
|
||||||
virtual const char* interface_name() { return getCANInterfaceName(can_config.inverter); }
|
virtual const char* interface_name() { return getCANInterfaceName(can_config.inverter); }
|
||||||
virtual void transmit_can(unsigned long currentMillis) = 0;
|
virtual void transmit_can(unsigned long currentMillis) = 0;
|
||||||
virtual void map_can_frame_to_variable(CAN_frame rx_frame) = 0;
|
virtual void map_can_frame_to_variable(CAN_frame rx_frame) = 0;
|
||||||
|
|
||||||
|
void transmit(unsigned long currentMillis) {
|
||||||
|
if (allowed_to_send_CAN) {
|
||||||
|
transmit_can(currentMillis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
CanInverterProtocol() { register_transmitter(this); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -34,17 +34,10 @@ void setup_inverter() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CAN_INVERTER_SELECTED
|
#ifdef CAN_INVERTER_SELECTED
|
||||||
void update_values_can_inverter() {
|
|
||||||
can_inverter->update_values();
|
|
||||||
}
|
|
||||||
|
|
||||||
void map_can_frame_to_variable_inverter(CAN_frame rx_frame) {
|
void map_can_frame_to_variable_inverter(CAN_frame rx_frame) {
|
||||||
can_inverter->map_can_frame_to_variable(rx_frame);
|
can_inverter->map_can_frame_to_variable(rx_frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
void transmit_can_inverter(unsigned long currentMillis) {
|
|
||||||
can_inverter->transmit_can(currentMillis);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef RS485_INVERTER_SELECTED
|
#ifdef RS485_INVERTER_SELECTED
|
||||||
|
|
|
@ -34,7 +34,6 @@ extern InverterProtocol* inverter;
|
||||||
void setup_inverter();
|
void setup_inverter();
|
||||||
|
|
||||||
#ifdef CAN_INVERTER_SELECTED
|
#ifdef CAN_INVERTER_SELECTED
|
||||||
void update_values_can_inverter();
|
|
||||||
void map_can_frame_to_variable_inverter(CAN_frame rx_frame);
|
void map_can_frame_to_variable_inverter(CAN_frame rx_frame);
|
||||||
void transmit_can_inverter(unsigned long currentMillis);
|
void transmit_can_inverter(unsigned long currentMillis);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -251,7 +251,10 @@ void KostalInverterProtocol::receive_RS485() // Runs as fast as possible to han
|
||||||
int code = RS485_RXFRAME[6] + RS485_RXFRAME[7] * 0x100;
|
int code = RS485_RXFRAME[6] + RS485_RXFRAME[7] * 0x100;
|
||||||
if (code == 0x44a) {
|
if (code == 0x44a) {
|
||||||
//Send cyclic data
|
//Send cyclic data
|
||||||
update_values_battery();
|
// TODO: Probably not a good idea to use the battery object here like this.
|
||||||
|
if (battery) {
|
||||||
|
battery->update_values();
|
||||||
|
}
|
||||||
update_values();
|
update_values();
|
||||||
if (f2_startup_count < 15) {
|
if (f2_startup_count < 15) {
|
||||||
f2_startup_count++;
|
f2_startup_count++;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue