Make tests pass

This commit is contained in:
Daniel Öster 2025-09-01 21:27:09 +03:00
parent 024ff58965
commit 12cc542a42
44 changed files with 362 additions and 259 deletions

View file

@ -37,7 +37,7 @@ uint8_t BmwIXBattery::increment_alive_counter(uint8_t counter) {
return counter;
}
static byte increment_C0_counter(byte counter) {
static uint8_t increment_C0_counter(uint8_t counter) {
counter++;
// Reset to 0xF0 if it exceeds 0xFE
if (counter > 0xFE) {
@ -459,10 +459,12 @@ void BmwIXBattery::setup(void) { // Performs one time setup at startup
void BmwIXBattery::HandleIncomingUserRequest(void) {
// Debug user request to open or close the contactors
logging.print("User request: contactor close: ");
logging.print(userRequestContactorClose);
logging.print(" User request: contactor open: ");
logging.println(userRequestContactorOpen);
if (userRequestContactorClose) {
logging.printf("User request: contactor close");
}
if (userRequestContactorOpen) {
logging.printf("User request: contactor open");
}
if ((userRequestContactorClose == false) && (userRequestContactorOpen == false)) {
// do nothing
} else if ((userRequestContactorClose == true) && (userRequestContactorOpen == false)) {

View file

@ -1,5 +1,6 @@
#include "BMW-PHEV-BATTERY.h"
#include <Arduino.h>
#include <cstring> //For unit test
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
@ -149,15 +150,6 @@ uint8_t BmwPhevBattery::increment_alive_counter(uint8_t counter) {
return counter;
}
static byte increment_0C0_counter(byte counter) {
counter++;
// Reset to 0xF0 if it exceeds 0xFE
if (counter > 0xFE) {
counter = 0xF0;
}
return counter;
}
void BmwPhevBattery::processCellVoltages() {
const int startByte = 3; // Start reading at byte 3
const int numVoltages = 96; // Number of cell voltage values to process
@ -469,7 +461,7 @@ void BmwPhevBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
#endif // DEBUG_LOG && UDS_LOG
transmit_can_frame(&BMW_6F1_REQUEST_CONTINUE_MULTIFRAME);
gUDSContext.receivedInBatch = 0; // Reset batch count
Serial.println("Sent FC for next batch of 3 frames.");
logging.println("Sent FC for next batch of 3 frames.");
}
break;
@ -513,18 +505,18 @@ void BmwPhevBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
battery_current = ((int32_t)((gUDSContext.UDS_buffer[3] << 24) | (gUDSContext.UDS_buffer[4] << 16) |
(gUDSContext.UDS_buffer[5] << 8) | gUDSContext.UDS_buffer[6])) *
0.1;
logging.print("Received current/amps measurement data: ");
logging.printf("Received current/amps measurement data: ");
logging.print(battery_current);
logging.print(" - ");
logging.printf(" - ");
for (uint16_t i = 0; i < gUDSContext.UDS_bytesReceived; i++) {
// Optional leading zero for single-digit hex
if (gUDSContext.UDS_buffer[i] < 0x10) {
logging.print("0");
logging.printf("0");
}
logging.print(gUDSContext.UDS_buffer[i], HEX);
logging.print(" ");
logging.print(gUDSContext.UDS_buffer[i]);
logging.printf(" ");
}
logging.println(); // new line at the end
logging.println(""); // new line at the end
}
//Cell Min/Max
@ -540,14 +532,14 @@ void BmwPhevBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
max_cell_voltage = (gUDSContext.UDS_buffer[11] << 8 | gUDSContext.UDS_buffer[12]) / 10;
} else {
logging.println("Cell Min Max Invalid 65535 or 0...");
logging.print("Received data: ");
logging.printf("Received data: ");
for (uint16_t i = 0; i < gUDSContext.UDS_bytesReceived; i++) {
// Optional leading zero for single-digit hex
if (gUDSContext.UDS_buffer[i] < 0x10) {
logging.print("0");
logging.printf("0");
}
logging.print(gUDSContext.UDS_buffer[i], HEX);
logging.print(" ");
logging.print(gUDSContext.UDS_buffer[i]);
logging.printf(" ");
}
logging.println(); // new line at the end
}
@ -678,7 +670,7 @@ void BmwPhevBattery::transmit_can(unsigned long currentMillis) {
}
void BmwPhevBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "BMW PHEV Battery", 63);
strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0';
//Wakeup the SME
wake_battery_via_canbus();

View file

@ -10,6 +10,8 @@ class BmwPhevBattery : public CanBattery {
virtual void update_values();
virtual void transmit_can(unsigned long currentMillis);
static constexpr const char* Name = "BMW PHEV Battery";
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
private:

View file

@ -1,4 +1,5 @@
#include "BOLT-AMPERA-BATTERY.h"
#include <cstring> //For unit test
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"

View file

@ -1,4 +1,5 @@
#include "BYD-ATTO-3-BATTERY.h"
#include <cstring> //For unit test
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"

View file

@ -120,7 +120,7 @@ void ChademoBattery::process_vehicle_charging_session(CAN_frame rx_frame) {
uint8_t chargingrate = 0;
if (x100_chg_lim.ConstantOfChargingRateIndication > 0) {
chargingrate = x102_chg_session.StateOfCharge / x100_chg_lim.ConstantOfChargingRateIndication * 100;
logging.print("Charge Rate (kW): ");
logging.printf("Charge Rate (kW): ");
logging.println(chargingrate);
}
@ -220,9 +220,9 @@ void ChademoBattery::process_vehicle_charging_limits(CAN_frame rx_frame) {
if (get_measured_voltage() <= x200_discharge_limits.MinimumDischargeVoltage && CHADEMO_Status > CHADEMO_NEGOTIATE) {
logging.println("x200 minimum discharge voltage met or exceeded, stopping.");
logging.print("Measured: ");
logging.printf("Measured: ");
logging.print(get_measured_voltage());
logging.print("Minimum voltage: ");
logging.printf("Minimum voltage: ");
logging.print(x200_discharge_limits.MinimumDischargeVoltage);
CHADEMO_Status = CHADEMO_STOP;
}
@ -240,9 +240,9 @@ void ChademoBattery::process_vehicle_discharge_estimate(CAN_frame rx_frame) {
if (currentMillis - previousMillis5000 >= INTERVAL_5_S) {
previousMillis5000 = currentMillis;
logging.print("x201 availabile vehicle energy, completion time: ");
logging.printf("x201 availabile vehicle energy, completion time: ");
logging.println(x201_discharge_estimate.AvailableVehicleEnergy);
logging.print("x201 approx vehicle completion time: ");
logging.printf("x201 approx vehicle completion time: ");
logging.println(x201_discharge_estimate.ApproxDischargeCompletionTime);
}
}
@ -766,7 +766,7 @@ void ChademoBattery::handle_chademo_sequence() {
/* check whether contactors ready, because externally dependent upon inverter allow during discharge */
if (contactors_ready) {
logging.println("Contactors ready");
logging.print("Voltage: ");
logging.printf("Voltage: ");
logging.println(get_measured_voltage());
/* transition to POWERFLOW state if discharge compatible on both sides */
if (x109_evse_state.discharge_compatible && x102_chg_session.s.status.StatusVehicleDischargeCompatible &&

View file

@ -22,7 +22,6 @@
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "CHADEMO-BATTERY.h"
#include "src/devboard/utils/logging.h"
/* Initial frames received from ISA shunts provide invalid during initialization */
static int framecount = 0;
@ -87,17 +86,6 @@ void ISA_handleFrame(CAN_frame* frame) {
case 0x510:
case 0x511:
logging.print(millis()); // Example printout, time, ID, length, data: 7553 1DB 8 FF C0 B9 EA 0 0 2 5D
logging.print(" ");
logging.print(frame->ID, HEX);
logging.print(" ");
logging.print(frame->DLC);
logging.print(" ");
for (int i = 0; i < frame->DLC; ++i) {
logging.print(frame->data.u8[i], HEX);
logging.print(" ");
}
logging.println("");
break;
case 0x521:
@ -245,7 +233,7 @@ void ISA_initialize() {
ISA_STOP();
delay(500);
for (int i = 0; i < 8; i++) {
logging.print("ISA Initialization ");
logging.printf("ISA Initialization ");
logging.println(i);
outframe.data.u8[0] = (0x20 + i);
@ -382,7 +370,7 @@ void ISA_initCurrent() {
}
void ISA_getCONFIG(uint8_t i) {
logging.print("ISA Get Config ");
logging.printf("ISA Get Config ");
logging.println(i);
outframe.data.u8[0] = (0x60 + i);
@ -398,7 +386,7 @@ void ISA_getCONFIG(uint8_t i) {
}
void ISA_getCAN_ID(uint8_t i) {
logging.print("ISA Get CAN ID ");
logging.printf("ISA Get CAN ID ");
logging.println(i);
outframe.data.u8[0] = (0x50 + i);
@ -418,8 +406,8 @@ void ISA_getCAN_ID(uint8_t i) {
}
void ISA_getINFO(uint8_t i) {
logging.print("ISA Get INFO ");
logging.println(i, HEX);
logging.printf("ISA Get INFO ");
logging.println(i);
outframe.data.u8[0] = (0x70 + i);
outframe.data.u8[1] = 0x00;

View file

@ -1,4 +1,5 @@
#include "CMFA-EV-BATTERY.h"
#include <cstring> //unit tests memcpy
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"

View file

@ -16,7 +16,7 @@ static uint16_t voltage_dV = 0;
static uint32_t remaining_capacity_mAh = 0;
static uint16_t cellvoltages_mV[48] = {0};
static uint16_t cellvoltage_min_mV = 3700;
static uint16_t cellvoltage_max_mV = 3700;
static uint16_t cellvoltage_max_mV = 0;
static uint16_t cell_count = 0;
static uint16_t SOC = 0;
static bool has_fault = false;
@ -109,12 +109,12 @@ uint32_t decode_uint32be(uint8_t data[8], uint8_t offset) {
}
void dump_buff(const char* msg, uint8_t* buff, uint8_t len) {
logging.print("[DALY-BMS] ");
logging.print(msg);
logging.printf("[DALY-BMS] ");
logging.printf(msg);
for (int i = 0; i < len; i++) {
logging.print(buff[i] >> 4, HEX);
logging.print(buff[i] & 0xf, HEX);
logging.print(" ");
logging.print(buff[i] >> 4);
logging.print(buff[i] & 0xf);
logging.printf(" ");
}
logging.println();
}

View file

@ -1,4 +1,5 @@
#include "FOXESS-BATTERY.h"
#include <cstring> //For unit test
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"

View file

@ -1,4 +1,5 @@
#include "GEELY-GEOMETRY-C-BATTERY.h"
#include <cstring> //For unit test
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"

View file

@ -1,6 +1,7 @@
#ifndef _GEELY_GEOMETRY_C_HTML_H
#define _GEELY_GEOMETRY_C_HTML_H
#include <cstring> //For unit test
#include "../datalayer/datalayer_extended.h"
#include "../devboard/webserver/BatteryHtmlRenderer.h"

View file

@ -1,4 +1,5 @@
#include "IMIEV-CZERO-ION-BATTERY.h"
#include <cstring> //for unit tests
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
@ -76,17 +77,8 @@ void ImievCZeroIonBattery::
if (!BMU_Detected) {
logging.println("BMU not detected, check wiring!");
//TODO: Raise event
}
logging.println("Battery Values");
logging.print("BMU SOC: ");
logging.print(BMU_SOC);
logging.print(" BMU Current: ");
logging.print(BMU_Current);
logging.print(" BMU Battery Voltage: ");
logging.print(BMU_PackVoltage);
logging.print(" BMU_Power: ");
logging.print(BMU_Power);
}
void ImievCZeroIonBattery::handle_incoming_can_frame(CAN_frame rx_frame) {

View file

@ -1,4 +1,5 @@
#include "JAGUAR-IPACE-BATTERY.h"
#include <cstring> //for unit tests
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
@ -57,9 +58,9 @@ CAN_frame ipace_keep_alive = {.FD = false,
.data = {0x9E, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}};*/
static void print_units(const char* header, int value, const char* units) {
logging.print(header);
logging.print(value);
logging.print(units);
logging.printf(header);
logging.printf("%d", value);
logging.printf(units);
}
void JaguarIpaceBattery::update_values() {

View file

@ -55,18 +55,6 @@ uint16_t KiaEGmpBattery::estimateSOC(uint16_t packVoltage, uint16_t cellCount, i
// Calculate average cell voltage in millivolts
uint16_t avgCellVoltage = compensatedPackVoltageMv / cellCount;
logging.print("Pack: ");
logging.print(packVoltage / 10.0);
logging.print("V, Current: ");
logging.print(currentAmps / 10.0);
logging.print("A, Drop: ");
logging.print(voltageDrop / 1000.0);
logging.print("V, Comp Pack: ");
logging.print(compensatedPackVoltageMv / 1000.0);
logging.print("V, Avg Cell: ");
logging.print(avgCellVoltage);
logging.println("mV");
// Use the cell voltage lookup table to estimate SOC
return estimateSOCFromCell(avgCellVoltage);
}
@ -182,65 +170,6 @@ void KiaEGmpBattery::update_values() {
if (leadAcidBatteryVoltage < 110) {
set_event(EVENT_12V_LOW, leadAcidBatteryVoltage);
}
/* Safeties verified. Perform USB serial printout if configured to do so */
logging.println(); //sepatator
logging.println("Values from battery: ");
logging.print("SOC BMS: ");
logging.print((uint16_t)SOC_BMS / 10.0, 1);
logging.print("% | SOC Display: ");
logging.print((uint16_t)SOC_Display / 10.0, 1);
logging.print("% | SOH ");
logging.print((uint16_t)batterySOH / 10.0, 1);
logging.println("%");
logging.print((int16_t)batteryAmps / 10.0, 1);
logging.print(" Amps | ");
logging.print((uint16_t)batteryVoltage / 10.0, 1);
logging.print(" Volts | ");
logging.print((int16_t)datalayer.battery.status.active_power_W);
logging.println(" Watts");
logging.print("Allowed Charge ");
logging.print((uint16_t)allowedChargePower * 10);
logging.print(" W | Allowed Discharge ");
logging.print((uint16_t)allowedDischargePower * 10);
logging.println(" W");
logging.print("MaxCellVolt ");
logging.print(CellVoltMax_mV);
logging.print(" mV No ");
logging.print(CellVmaxNo);
logging.print(" | MinCellVolt ");
logging.print(CellVoltMin_mV);
logging.print(" mV No ");
logging.println(CellVminNo);
logging.print("TempHi ");
logging.print((int16_t)temperatureMax);
logging.print("°C TempLo ");
logging.print((int16_t)temperatureMin);
logging.print("°C WaterInlet ");
logging.print((int8_t)temperature_water_inlet);
logging.print("°C PowerRelay ");
logging.print((int8_t)powerRelayTemperature * 2);
logging.println("°C");
logging.print("Aux12volt: ");
logging.print((int16_t)leadAcidBatteryVoltage / 10.0, 1);
logging.println("V | ");
logging.print("BmsManagementMode ");
logging.print((uint8_t)batteryManagementMode, BIN);
if (bitRead((uint8_t)BMS_ign, 2) == 1) {
logging.print(" | BmsIgnition ON");
} else {
logging.print(" | BmsIgnition OFF");
}
if (bitRead((uint8_t)batteryRelay, 0) == 1) {
logging.print(" | PowerRelay ON");
} else {
logging.print(" | PowerRelay OFF");
}
logging.print(" | Inverter ");
logging.print(inverterVoltage);
logging.println(" Volts");
}
// Getter implementations for HTML renderer
@ -321,14 +250,10 @@ void KiaEGmpBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
break;
case 0x7EC:
// print_canfd_frame(frame);
switch (rx_frame.data.u8[0]) {
case 0x10: //"PID Header"
// logging.println ("Send ack");
poll_data_pid = rx_frame.data.u8[4];
// if (rx_frame.data.u8[4] == poll_data_pid) {
transmit_can_frame(&EGMP_7E4_ack); //Send ack to BMS if the same frame is sent as polled
// }
transmit_can_frame(&EGMP_7E4_ack); //Send ack to BMS
break;
case 0x21: //First frame in PID group
if (poll_data_pid == 1) {

View file

@ -1,4 +1,5 @@
#include "KIA-HYUNDAI-64-BATTERY.h"
#include <cstring> //For unit test
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"

View file

@ -1,6 +1,7 @@
#ifndef _KIA_HYUNDAI_64_HTML_H
#define _KIA_HYUNDAI_64_HTML_H
#include <cstring> //For unit test
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "../devboard/webserver/BatteryHtmlRenderer.h"

View file

@ -1,4 +1,5 @@
#include "KIA-HYUNDAI-HYBRID-BATTERY.h"
#include <cstring> //For unit test
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"

View file

@ -1,6 +1,7 @@
#include "MEB-BATTERY.h"
#include <Arduino.h>
#include <algorithm> // For std::min and std::max
#include <cstring> //For unit test
#include "../communication/can/comm_can.h"
#include "../communication/can/obd.h"
#include "../datalayer/datalayer.h"
@ -1262,8 +1263,6 @@ void MebBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
handle_obd_frame(rx_frame);
break;
default:
logging.printf("Unknown CAN frame received:\n");
dump_can_frame(rx_frame, MSG_RX);
break;
}
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;

View file

@ -1,4 +1,5 @@
#include "MG-5-BATTERY.h"
#include <cstring> //For unit test
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
@ -112,7 +113,7 @@ void Mg5Battery::transmit_can(unsigned long currentMillis) {
}
void Mg5Battery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "MG 5 battery", 63);
strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.system.status.battery_allows_contactor_closing = true;
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;

View file

@ -1,10 +1,11 @@
#include "MG-HS-PHEV-BATTERY.h"
#include <cmath> //For unit test
#include <cstring> //For unit test
#include "../communication/can/comm_can.h"
#include "../communication/contactorcontrol/comm_contactorcontrol.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "../devboard/utils/logging.h"
/*
MG HS PHEV 16.6kWh battery integration

View file

@ -1,13 +1,13 @@
#include "NISSAN-LEAF-BATTERY.h"
#include <cstring> //For unit test
#include "../charger/CHARGERS.h"
#include "../charger/CanCharger.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" //For "More battery info" webpage
#include "../devboard/utils/events.h"
#include "../devboard/utils/logging.h"
#include "../charger/CHARGERS.h"
#include "../charger/CanCharger.h"
uint16_t Temp_fromRAW_to_F(uint16_t temperature);
//Cryptographic functions
void decodeChallengeData(unsigned int SeedInput, unsigned char* Crypt_Output_Buffer);

View file

@ -1,8 +1,8 @@
#include "RANGE-ROVER-PHEV-BATTERY.h"
#include <cstring> //For unit test
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
/* TODO
- LOG files from vehicle needed to determine CAN content needed to send towards battery!
- BCCM_PMZ_A (0x18B 50ms)

View file

@ -1,5 +1,6 @@
#include "RENAULT-TWIZY.h"
#include <cstdint>
#include <cstring> //For unit test
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"

View file

@ -1,10 +1,9 @@
#include "RENAULT-ZOE-GEN1-BATTERY.h"
#include <cstring> //For unit test
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "../devboard/utils/events.h"
void transmit_can_frame(CAN_frame* tx_frame, int interface);
/* Information in this file is based of the OVMS V3 vehicle_renaultzoe.cpp component
https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/blob/master/vehicle/OVMS.V3/components/vehicle_renaultzoe/src/vehicle_renaultzoe.cpp
The Zoe BMS apparently does not send total pack voltage, so we use the polled 96x cellvoltages summed up as total voltage

View file

@ -1,8 +1,8 @@
#include "SANTA-FE-PHEV-BATTERY.h"
#include <cstring> //For unit test
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
/* Credits go to maciek16c for these findings!
https://github.com/maciek16c/hyundai-santa-fe-phev-battery
https://openinverter.org/forum/viewtopic.php?p=62256

View file

@ -1,8 +1,8 @@
#include "SIMPBMS-BATTERY.h"
#include <cstring> //For unit test
#include "../battery/BATTERIES.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
void SimpBmsBattery::update_values() {
datalayer.battery.status.real_soc = (SOC * 100); //increase SOC range from 0-100 -> 100.00

View file

@ -1,4 +1,5 @@
#include "SONO-BATTERY.h"
#include <cstring> //For unit test
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"

View file

@ -1,4 +1,5 @@
#include "TESLA-BATTERY.h"
#include <cstring> //For unit test
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" //For Advanced Battery Insights webpage
@ -995,52 +996,52 @@ void TeslaBattery::
}
printFaultCodesIfActive();
logging.print("BMS Contactors State: ");
logging.print(getBMSContactorState(battery_contactor)); // Display what state the BMS thinks the contactors are in
logging.print(", HVIL: ");
logging.print(getHvilStatusState(battery_hvil_status));
logging.print(", NegativeState: ");
logging.print(getContactorState(battery_packContNegativeState));
logging.print(", PositiveState: ");
logging.printf("BMS Contactors State: ");
logging.printf(getBMSContactorState(battery_contactor)); // Display what state the BMS thinks the contactors are in
logging.printf(", HVIL: ");
logging.printf(getHvilStatusState(battery_hvil_status));
logging.printf(", NegativeState: ");
logging.printf(getContactorState(battery_packContNegativeState));
logging.printf(", PositiveState: ");
logging.println(getContactorState(battery_packContPositiveState));
logging.print("HVP Contactors setState: ");
logging.print(
logging.printf("HVP Contactors setState: ");
logging.printf(
getContactorText(battery_packContactorSetState)); // Display what state the HVP has set the contactors to be in
logging.print(", Closing blocked: ");
logging.print(getNoYes(battery_packCtrsClosingBlocked));
logging.printf(", Closing blocked: ");
logging.printf(getNoYes(battery_packCtrsClosingBlocked));
if (battery_packContactorSetState == 5) {
logging.print(" (already CLOSED)");
logging.printf(" (already CLOSED)");
}
logging.print(", Pyrotest: ");
logging.printf(", Pyrotest: ");
logging.println(getNoYes(battery_pyroTestInProgress));
logging.print("Battery values: ");
logging.print("Real SOC: ");
logging.printf("Battery values: ");
logging.printf("Real SOC: ");
logging.print(battery_soc_ui / 10.0, 1);
logging.print(", Battery voltage: ");
logging.printf(", Battery voltage: ");
logging.print(battery_volts / 10.0, 1);
logging.print("V");
logging.print(", Battery HV current: ");
logging.printf("V");
logging.printf(", Battery HV current: ");
logging.print(battery_amps / 10.0, 1);
logging.print("A");
logging.print(", Fully charged?: ");
logging.printf("A");
logging.printf(", Fully charged?: ");
if (battery_full_charge_complete)
logging.print("YES, ");
logging.printf("YES, ");
else
logging.print("NO, ");
logging.printf("NO, ");
if (datalayer.battery.info.chemistry == battery_chemistry_enum::LFP) {
logging.print("LFP chemistry detected!");
logging.printf("LFP chemistry detected!");
}
logging.println("");
logging.print("Cellstats, Max: ");
logging.printf("Cellstats, Max: ");
logging.print(battery_cell_max_v);
logging.print("mV (cell ");
logging.printf("mV (cell ");
logging.print(battery_BrickVoltageMaxNum);
logging.print("), Min: ");
logging.printf("), Min: ");
logging.print(battery_cell_min_v);
logging.print("mV (cell ");
logging.printf("mV (cell ");
logging.print(battery_BrickVoltageMinNum);
logging.print("), Imbalance: ");
logging.printf("), Imbalance: ");
logging.print(battery_cell_deviation_mV);
logging.println("mV.");

View file

@ -3,12 +3,6 @@
#include "../datalayer/datalayer.h"
#include "../devboard/utils/logging.h"
static void print_units(const char* header, int value, const char* units) {
logging.print(header);
logging.print(value);
logging.print(units);
}
void TestFakeBattery::
update_values() { /* This function puts fake values onto the parameters sent towards the inverter */

View file

@ -1,10 +1,10 @@
#include "VOLVO-SPA-BATTERY.h"
#include <cstring> //For unit test
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" //For "More battery info" webpage
#include "../devboard/utils/events.h"
#include "../devboard/utils/logging.h"
void VolvoSpaBattery::
update_values() { //This function maps all the values fetched via CAN to the correct parameters used for the inverter

View file

@ -1,10 +1,10 @@
#include "VOLVO-SPA-HYBRID-BATTERY.h"
#include <cstring> //For unit test
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" //For "More battery info" webpage
#include "../devboard/utils/events.h"
#include "../devboard/utils/logging.h"
void VolvoSpaHybridBattery::
update_values() { //This function maps all the values fetched via CAN to the correct parameters used for the inverter
uint8_t cnt = 0;

View file

@ -8,7 +8,6 @@
#ifndef UNIT_TEST
// Real implementation for production
#include <Print.h>
class Logging : public Print {
void add_timestamp(size_t size);
@ -51,12 +50,61 @@ class Logging {
(void)fmt;
}
// Overloaded print methods for different data types
static void print(const char* str) { (void)str; }
static void print(char c) { (void)c; }
static void print(int8_t num) { (void)num; }
static void print(uint8_t num) { (void)num; }
static void print(int16_t num) { (void)num; }
static void print(uint16_t num) { (void)num; }
static void print(int32_t num) { (void)num; }
static void print(uint32_t num) { (void)num; }
static void print(int64_t num) { (void)num; }
static void print(uint64_t num) { (void)num; }
static void print(float num) { (void)num; }
static void print(double num) { (void)num; }
static void print(bool b) { (void)b; }
static void print(double num, int precision) {
(void)num;
(void)precision;
}
static void print(float num, int precision) {
(void)num;
(void)precision;
}
static void print(int32_t num, int base) {
(void)num;
(void)base;
}
static void print(uint32_t num, int base) {
(void)num;
(void)base;
}
static void println(const char* str) { (void)str; }
static void println(char c) { (void)c; }
static void println(int8_t num) { (void)num; }
static void println(uint8_t num) { (void)num; }
static void println(int16_t num) { (void)num; }
static void println(uint16_t num) { (void)num; }
static void println(int32_t num) { (void)num; }
static void println(uint32_t num) { (void)num; }
static void println(int64_t num) { (void)num; }
static void println(uint64_t num) { (void)num; }
static void println(float num) { (void)num; }
static void println(double num) { (void)num; }
static void println(bool b) { (void)b; }
static void println() {} // Empty println
Logging() {}
};
// Test macros - empty implementations
#define DEBUG_PRINT(fmt, ...) ((void)0)
#define DEBUG_PRINTF(fmt, ...) ((void)0)
#define DEBUG_PRINTLN(str) ((void)0)

View file

@ -14,18 +14,18 @@ void KostalInverterProtocol::float2frame(uint8_t* arr, float value, uint8_t fram
}
static void dbg_timestamp(void) {
logging.print("[");
logging.printf("[");
logging.print(millis());
logging.print(" ms] ");
logging.printf(" ms] ");
}
static void dbg_frame(uint8_t* frame, int len, const char* prefix) {
dbg_timestamp();
logging.print(prefix);
logging.print(": ");
logging.printf(": ");
for (uint8_t i = 0; i < len; i++) {
if (frame[i] < 0x10) {
logging.print("0");
logging.printf("0");
}
logging.print(frame[i], HEX);
logging.print(" ");

View file

@ -20,7 +20,6 @@ class KostalInverterProtocol : public Rs485InverterProtocol {
int baud_rate() { return 57600; }
void float2frame(uint8_t* arr, float value, uint8_t framepointer);
bool check_kostal_frame_crc(int len);
// How many value updates we can go without inverter gets reported as missing \
// e.g. value set to 12, 12*5sec=60seconds without comm before event is raised
const int RS485_HEALTHY = 12;

View file

@ -137,17 +137,6 @@ void SmaTripowerInverter::map_can_frame_to_variable(CAN_frame rx_frame) {
}
}
void SmaTripowerInverter::pushFrame(CAN_frame* frame, std::function<void(void)> callback) {
if (listLength >= 20) {
return; //TODO: scream.
}
framesToSend[listLength] = {
.frame = frame,
.callback = callback,
};
listLength++;
}
void SmaTripowerInverter::transmit_can(unsigned long currentMillis) {
// Send CAN Message only if we're enabled by inverter
@ -155,18 +144,6 @@ void SmaTripowerInverter::transmit_can(unsigned long currentMillis) {
return;
}
if (listLength > 0 && currentMillis - previousMillis250ms >= INTERVAL_250_MS) {
previousMillis250ms = currentMillis;
// Send next frame.
Frame frame = framesToSend[0];
transmit_can_frame(frame.frame);
frame.callback();
for (int i = 0; i < listLength - 1; i++) {
framesToSend[i] = framesToSend[i + 1];
}
listLength--;
}
if (!pairing_completed) {
return;
}
@ -174,19 +151,19 @@ void SmaTripowerInverter::transmit_can(unsigned long currentMillis) {
// Send CAN Message every 2s
if (currentMillis - previousMillis2s >= INTERVAL_2_S) {
previousMillis2s = currentMillis;
pushFrame(&SMA_358);
transmit_can_frame(&SMA_358);
}
// Send CAN Message every 10s
if (currentMillis - previousMillis10s >= INTERVAL_10_S) {
previousMillis10s = currentMillis;
pushFrame(&SMA_518);
pushFrame(&SMA_4D8);
pushFrame(&SMA_3D8);
transmit_can_frame(&SMA_518);
transmit_can_frame(&SMA_4D8);
transmit_can_frame(&SMA_3D8);
}
// Send CAN Message every 60s (potentially SMA_458 is not required for stable operation)
if (currentMillis - previousMillis60s >= INTERVAL_60_S) {
previousMillis60s = currentMillis;
pushFrame(&SMA_458);
transmit_can_frame(&SMA_458);
}
}
@ -195,18 +172,17 @@ void SmaTripowerInverter::completePairing() {
}
void SmaTripowerInverter::transmit_can_init() {
listLength = 0; // clear all frames
pushFrame(&SMA_558); //Pairing start - Vendor
pushFrame(&SMA_598); //Serial
pushFrame(&SMA_5D8); //BYD
pushFrame(&SMA_618_0); //BATTERY
pushFrame(&SMA_618_1); //-Box Pr
pushFrame(&SMA_618_2); //emium H
pushFrame(&SMA_618_3); //VS
pushFrame(&SMA_358);
pushFrame(&SMA_3D8);
pushFrame(&SMA_458);
pushFrame(&SMA_4D8);
pushFrame(&SMA_518, [this]() { this->completePairing(); });
transmit_can_frame(&SMA_558); //Pairing start - Vendor
transmit_can_frame(&SMA_598); //Serial
transmit_can_frame(&SMA_5D8); //BYD
transmit_can_frame(&SMA_618_0); //BATTERY
transmit_can_frame(&SMA_618_1); //-Box Pr
transmit_can_frame(&SMA_618_2); //emium H
transmit_can_frame(&SMA_618_3); //VS
transmit_can_frame(&SMA_358);
transmit_can_frame(&SMA_3D8);
transmit_can_frame(&SMA_458);
transmit_can_frame(&SMA_4D8);
transmit_can_frame(&SMA_518);
}

View file

@ -24,7 +24,6 @@ class SmaTripowerInverter : public SmaInverterBase {
const int THIRTY_MINUTES = 1200;
void transmit_can_init();
void pushFrame(CAN_frame* frame, std::function<void(void)> callback = []() {});
void completePairing();
unsigned long previousMillis250ms = 0; // will store last time a 250ms CAN Message was send
@ -33,14 +32,6 @@ class SmaTripowerInverter : public SmaInverterBase {
unsigned long previousMillis10s = 0; // will store last time a 10s CAN Message was send
unsigned long previousMillis60s = 0; // will store last time a 60s CAN Message was send
typedef struct {
CAN_frame* frame;
std::function<void(void)> callback;
} Frame;
unsigned short listLength = 0;
Frame framesToSend[20];
uint32_t inverter_time = 0;
uint16_t inverter_voltage = 0;
int16_t inverter_current = 0;

View file

@ -292,9 +292,8 @@ bool SofarInverter::setup() { // Performs one time setup at startup over CAN bu
init_frame(SOFAR_783, 0x783);
init_frame(SOFAR_784, 0x784);
String tempStr(datalayer.battery.settings.sofar_user_specified_battery_id);
strncpy(datalayer.system.info.inverter_brand, tempStr.c_str(), 7);
datalayer.system.info.inverter_brand[7] = '\0';
snprintf(datalayer.system.info.inverter_brand, sizeof(datalayer.system.info.inverter_brand), "%s",
datalayer.battery.settings.sofar_user_specified_battery_id);
return true;
}

View file

@ -85,11 +85,80 @@ add_executable(tests
../Software/USER_SETTINGS.cpp
../Software/src/battery/BATTERIES.cpp
../Software/src/battery/Battery.cpp
../Software/src/battery/BMW-I3-BATTERY.cpp
../Software/src/battery/BMW-I3-HTML.cpp
../Software/src/battery/BMW-IX-BATTERY.cpp
../Software/src/battery/BMW-IX-HTML.cpp
../Software/src/battery/BMW-PHEV-BATTERY.cpp
../Software/src/battery/BMW-SBOX.cpp
../Software/src/battery/BOLT-AMPERA-BATTERY.cpp
../Software/src/battery/BYD-ATTO-3-BATTERY.cpp
../Software/src/battery/CanBattery.cpp
../Software/src/battery/CELLPOWER-BMS.cpp
../Software/src/battery/CHADEMO-BATTERY.cpp
../Software/src/battery/CHADEMO-SHUNTS.cpp
../Software/src/battery/CMFA-EV-BATTERY.cpp
../Software/src/battery/DALY-BMS.cpp
../Software/src/battery/ECMP-BATTERY.cpp
../Software/src/battery/FOXESS-BATTERY.cpp
../Software/src/battery/GEELY-GEOMETRY-C-BATTERY.cpp
../Software/src/battery/HYUNDAI-IONIQ-28-BATTERY-HTML.cpp
../Software/src/battery/HYUNDAI-IONIQ-28-BATTERY.cpp
../Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp
../Software/src/battery/JAGUAR-IPACE-BATTERY.cpp
../Software/src/battery/KIA-E-GMP-BATTERY.cpp
../Software/src/battery/KIA-E-GMP-HTML.cpp
../Software/src/battery/KIA-64FD-BATTERY.cpp
../Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp
../Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp
../Software/src/battery/MEB-BATTERY.cpp
../Software/src/battery/MG-5-BATTERY.cpp
../Software/src/battery/MG-HS-PHEV-BATTERY.cpp
../Software/src/battery/NISSAN-LEAF-BATTERY.cpp
../Software/src/inverter/INVERTERS.cpp
../Software/src/battery/ORION-BMS.cpp
../Software/src/battery/PYLON-BATTERY.cpp
../Software/src/battery/RANGE-ROVER-PHEV-BATTERY.cpp
../Software/src/battery/RELION-LV-BATTERY.cpp
../Software/src/battery/RENAULT-KANGOO-BATTERY.cpp
../Software/src/battery/RENAULT-TWIZY.cpp
../Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp
../Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp
../Software/src/battery/RJXZS-BMS.cpp
../Software/src/battery/SAMSUNG-SDI-LV-BATTERY.cpp
../Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp
../Software/src/battery/Shunts.cpp
../Software/src/battery/SIMPBMS-BATTERY.cpp
../Software/src/battery/SONO-BATTERY.cpp
../Software/src/battery/TESLA-BATTERY.cpp
../Software/src/battery/TEST-FAKE-BATTERY.cpp
../Software/src/battery/VOLVO-SPA-BATTERY.cpp
../Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.cpp
../Software/src/inverter/AFORE-CAN.cpp
../Software/src/inverter/BYD-CAN.cpp
../Software/src/inverter/BYD-MODBUS.cpp
../Software/src/inverter/FERROAMP-CAN.cpp
../Software/src/inverter/FOXESS-CAN.cpp
../Software/src/inverter/GROWATT-HV-CAN.cpp
../Software/src/inverter/GROWATT-LV-CAN.cpp
../Software/src/inverter/GROWATT-WIT-CAN.cpp
../Software/src/inverter/INVERTERS.cpp
../Software/src/inverter/KOSTAL-RS485.cpp
../Software/src/inverter/ModbusInverterProtocol.cpp
../Software/src/inverter/PYLON-CAN.cpp
../Software/src/inverter/PYLON-LV-CAN.cpp
../Software/src/inverter/SCHNEIDER-CAN.cpp
../Software/src/inverter/SMA-BYD-H-CAN.cpp
../Software/src/inverter/SMA-BYD-HVS-CAN.cpp
../Software/src/inverter/SMA-LV-CAN.cpp
../Software/src/inverter/SMA-TRIPOWER-CAN.cpp
../Software/src/inverter/SOFAR-CAN.cpp
../Software/src/inverter/SOL-ARK-LV-CAN.cpp
../Software/src/inverter/SOLAX-CAN.cpp
../Software/src/inverter/SOLXPOW-CAN.cpp
../Software/src/inverter/SUNGROW-CAN.cpp
../Software/src/charger/CHARGERS.cpp
../Software/src/charger/CHEVY-VOLT-CHARGER.cpp
../Software/src/charger/NISSAN-LEAF-CHARGER.cpp
emul/can.cpp
emul/time.cpp
emul/serial.cpp

View file

@ -15,4 +15,22 @@ int max(int a, int b) {
return (a > b) ? a : b;
}
// Mock implementation for OBD
#include "../../Software/src/communication/can/obd.h"
void handle_obd_frame(CAN_frame& frame) {
(void)frame;
}
void transmit_obd_can_frame(unsigned int address, int interface, bool canFD) {
(void)interface;
}
void start_bms_reset() {}
#include "../../Software/src/communication/rs485/comm_rs485.h"
// Mock implementation
void register_receiver(Rs485Receiver* receiver) {
(void)receiver; // Silence unused parameter warning
}
ESPClass ESP;

View file

@ -6,15 +6,97 @@
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include "HardwareSerial.h"
#include "Logging.h"
#include "Print.h"
#include "esp-hal-gpio.h"
// Arduino base constants for print formatting
constexpr int BIN = 2;
constexpr int OCT = 8;
constexpr int DEC = 10;
constexpr int HEX = 16;
// Arduino type aliases
using byte = uint8_t;
#define boolean bool
// Arduino random functions
inline long random(long max) {
(void)max;
return 0; // Return a predictable value for testing
}
inline long random(long min, long max) {
(void)min;
(void)max;
return min; // Return the minimum value for predictability
}
// Also add randomSeed for completeness
inline void randomSeed(unsigned long seed) {
(void)seed;
}
inline uint16_t word(uint8_t highByte, uint8_t lowByte) {
return (static_cast<uint16_t>(highByte) << 8) | lowByte;
}
inline uint16_t word(uint16_t w) {
return w;
}
// Bit manipulation functions
inline uint8_t bitRead(uint8_t value, uint8_t bit) {
return (value >> bit) & 0x01;
}
inline void bitSet(uint8_t& value, uint8_t bit) {
value |= (1UL << bit);
}
inline void bitClear(uint8_t& value, uint8_t bit) {
value &= ~(1UL << bit);
}
inline void bitWrite(uint8_t& value, uint8_t bit, uint8_t bitvalue) {
if (bitvalue) {
bitSet(value, bit);
} else {
bitClear(value, bit);
}
}
// Byte extraction functions
inline uint8_t lowByte(uint16_t w) {
return static_cast<uint8_t>(w & 0xFF);
}
inline uint8_t highByte(uint16_t w) {
return static_cast<uint8_t>(w >> 8);
}
template <typename T>
inline const T& min(const T& a, const T& b) {
return (a < b) ? a : b;
}
template <typename T>
inline const T& max(const T& a, const T& b) {
return (a > b) ? a : b;
}
void pinMode(uint8_t pin, uint8_t mode);
void digitalWrite(uint8_t pin, uint8_t val);
int digitalRead(uint8_t pin);
inline int analogRead(uint8_t pin) {
(void)pin;
return 0; // Return 0 for predictable tests
}
unsigned long micros();
// Can be previously declared as a macro in stupid eModbus

View file

@ -35,17 +35,28 @@ enum SerialConfig {
class HardwareSerial : public Stream {
public:
int available() { return 0; }
// Implement ALL pure virtual functions from base classes
int available() override { return 0; }
int read() override { return -1; }
int peek() override { return -1; }
void flush() override {} // Implement flush from Print
size_t write(uint8_t) override { return 0; } // Implement write from Print
// Your existing methods
uint32_t baudRate() { return 9600; }
void begin(unsigned long baud, uint32_t config = SERIAL_8N1, int8_t rxPin = -1, int8_t txPin = -1,
bool invert = false, unsigned long timeout_ms = 20000UL, uint8_t rxfifo_full_thrhd = 120) {}
int read() { return 0; }
void setTxBufferSize(uint16_t size) {}
void setRxBufferSize(uint16_t size) {}
bool setRxFIFOFull(uint8_t fifoBytes) { return false; }
size_t write(uint8_t) { return 0; }
};
// Add the buffer write method
size_t write(const uint8_t* buffer, size_t size) override {
(void)buffer;
(void)size;
return 0;
}
};
extern HardwareSerial Serial;
extern HardwareSerial Serial1;
extern HardwareSerial Serial2;

View file

@ -5,7 +5,7 @@ class Print {
public:
virtual void flush() {}
void printf(const char* format, ...) {}
virtual size_t write(uint8_t) = 0;
virtual size_t write(uint8_t) { return 0; }
virtual size_t write(const char* s) { return 0; }
virtual size_t write(const uint8_t* buffer, size_t size) { return 0; }
};

View file

@ -5,8 +5,10 @@
class Stream : public Print {
public:
virtual int available() = 0;
virtual int read() = 0;
virtual int available() { return 0; }
virtual int read() { return -1; }
virtual int peek() { return -1; }
// flush() is already inherited from Print
};
#endif