Add working unit tests

This commit is contained in:
Jaakko Haakana 2025-07-26 15:19:49 +03:00
parent f609d54a14
commit 995cc7784c
106 changed files with 589 additions and 582 deletions

View file

@ -13,6 +13,7 @@ jobs:
- name: Configure and build with CMake
run: |
cd test
mkdir build
cd build
cmake ..
@ -20,11 +21,4 @@ jobs:
- name: Run unit tests
run: |
set -e # Exit immediately on non-zero exit code
cd build/test
dir -s
for test_executable in *; do
if [ -f "$test_executable" ] && [ -x "$test_executable" ]; then
./"$test_executable"
fi
done
ctest

View file

@ -1,10 +0,0 @@
cmake_minimum_required(VERSION 3.10)
# Set the C++ standard to C++20
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
project(BatteryEmulator)
# add_subdirectory(Software/src/devboard/utils)
add_subdirectory(test)

View file

@ -1,4 +1,5 @@
#include "BMW-I3-BATTERY.h"
#include <Arduino.h>
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"

View file

@ -2,9 +2,9 @@
#define BMW_I3_BATTERY_H
#include "../datalayer/datalayer.h"
#include "../devboard/hal/hal.h"
#include "BMW-I3-HTML.h"
#include "CanBattery.h"
#include "src/devboard/hal/hal.h"
#ifdef BMW_I3_BATTERY
#define SELECTED_BATTERY_CLASS BmwI3Battery

View file

@ -1,9 +1,9 @@
#ifndef _BMW_I3_HTML_H
#define _BMW_I3_HTML_H
#include "../../src/devboard/webserver/BatteryHtmlRenderer.h"
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
class BmwI3Battery;

View file

@ -2,7 +2,7 @@
#define _BMW_IX_HTML_H
#include "../datalayer/datalayer.h"
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
#include "../devboard/webserver/BatteryHtmlRenderer.h"
class BmwIXBattery;

View file

@ -1,4 +1,5 @@
#include "BMW-PHEV-BATTERY.h"
#include <Arduino.h>
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"

View file

@ -1,6 +1,5 @@
#ifndef BMW_PHEV_BATTERY_H
#define BMW_PHEV_BATTERY_H
#include <Arduino.h>
#include "BMW-PHEV-HTML.h"
#include "CanBattery.h"
@ -407,7 +406,7 @@ class BmwPhevBattery : public CanBattery {
const unsigned long STALE_PERIOD =
STALE_PERIOD_CONFIG; // Time in milliseconds to check for staleness (e.g., 5000 ms = 5 seconds)
byte iX_0C0_counter = 0xF0; // Initialize to 0xF0
uint8_t iX_0C0_counter = 0xF0; // Initialize to 0xF0
//End iX Intermediate vars

View file

@ -3,7 +3,7 @@
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
#include "../devboard/webserver/BatteryHtmlRenderer.h"
class BmwPhevHtmlRenderer : public BatteryHtmlRenderer {
public:

View file

@ -1,4 +1,5 @@
#include "BMW-SBOX.h"
#include <Arduino.h>
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"

View file

@ -1,7 +1,5 @@
#ifndef BOLT_AMPERA_BATTERY_H
#define BOLT_AMPERA_BATTERY_H
#include <Arduino.h>
#include "BOLT-AMPERA-HTML.h"
#include "CanBattery.h"

View file

@ -2,7 +2,7 @@
#define _BOLT_AMPERA_HTML_H
#include "../datalayer/datalayer_extended.h"
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
#include "../devboard/webserver/BatteryHtmlRenderer.h"
class BoltAmperaHtmlRenderer : public BatteryHtmlRenderer {
public:

View file

@ -3,7 +3,7 @@
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
#include "../devboard/webserver/BatteryHtmlRenderer.h"
class BydAtto3HtmlRenderer : public BatteryHtmlRenderer {
public:

View file

@ -2,8 +2,8 @@
#define BATTERY_H
#include <vector>
#include "src/devboard/utils/types.h"
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
#include "../../src/devboard/utils/types.h"
#include "../../src/devboard/webserver/BatteryHtmlRenderer.h"
enum class BatteryType {
None = 0,

View file

@ -1,6 +1,5 @@
#ifndef CELLPOWER_BMS_H
#define CELLPOWER_BMS_H
#include <Arduino.h>
#include "CELLPOWER-HTML.h"
#include "CanBattery.h"

View file

@ -3,7 +3,7 @@
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
#include "../devboard/webserver/BatteryHtmlRenderer.h"
class CellpowerHtmlRenderer : public BatteryHtmlRenderer {
public:

View file

@ -3,7 +3,7 @@
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
#include "../devboard/webserver/BatteryHtmlRenderer.h"
class ChademoBatteryHtmlRenderer : public BatteryHtmlRenderer {
public:

View file

@ -3,9 +3,9 @@
#include <Arduino.h>
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "../devboard/hal/hal.h"
#include "CHADEMO-BATTERY-HTML.h"
#include "CanBattery.h"
#include "src/devboard/hal/hal.h"
#ifdef CHADEMO_BATTERY
#define SELECTED_BATTERY_CLASS ChademoBattery

View file

@ -3,7 +3,7 @@
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
#include "../devboard/webserver/BatteryHtmlRenderer.h"
class CmfaEvHtmlRenderer : public BatteryHtmlRenderer {
public:

View file

@ -3,11 +3,11 @@
#include "Battery.h"
#include "USER_SETTINGS.h"
#include "src/communication/Transmitter.h"
#include "src/communication/can/CanReceiver.h"
#include "src/communication/can/comm_can.h"
#include "src/devboard/utils/types.h"
#include "../../USER_SETTINGS.h"
#include "../../src/communication/Transmitter.h"
#include "../../src/communication/can/CanReceiver.h"
#include "../../src/communication/can/comm_can.h"
#include "../../src/devboard/utils/types.h"
// Abstract base class for batteries using the CAN bus
class CanBattery : public Battery, Transmitter, CanReceiver {

View file

@ -1,4 +1,5 @@
#include "DALY-BMS.h"
#include <Arduino.h>
#include <cstdint>
#include "../datalayer/datalayer.h"
#include "../devboard/hal/hal.h"

View file

@ -1,7 +1,5 @@
#ifndef STELLANTIS_ECMP_BATTERY_H
#define STELLANTIS_ECMP_BATTERY_H
#include <Arduino.h>
#include "CanBattery.h"
#include "ECMP-HTML.h"

View file

@ -3,7 +3,7 @@
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
#include "../devboard/webserver/BatteryHtmlRenderer.h"
class EcmpHtmlRenderer : public BatteryHtmlRenderer {
public:

View file

@ -1,7 +1,5 @@
#ifndef FOXESS_BATTERY_H
#define FOXESS_BATTERY_H
#include <Arduino.h>
#include "CanBattery.h"
#ifdef FOXESS_BATTERY

View file

@ -3,7 +3,7 @@
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
#include "../devboard/webserver/BatteryHtmlRenderer.h"
class GeelyGeometryCHtmlRenderer : public BatteryHtmlRenderer {
public:

View file

@ -1,7 +1,5 @@
#ifndef IMIEV_CZERO_ION_BATTERY_H
#define IMIEV_CZERO_ION_BATTERY_H
#include <Arduino.h>
#include "CanBattery.h"
#ifdef IMIEV_CZERO_ION_BATTERY

View file

@ -1,4 +1,5 @@
#include "KIA-E-GMP-BATTERY.h"
#include <Arduino.h>
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"

View file

@ -1,6 +1,5 @@
#ifndef KIA_E_GMP_BATTERY_H
#define KIA_E_GMP_BATTERY_H
#include <Arduino.h>
#include "CanBattery.h"
#define ESTIMATE_SOC_FROM_CELLVOLTAGE

View file

@ -1,6 +1,5 @@
#ifndef KIA_HYUNDAI_64_BATTERY_H
#define KIA_HYUNDAI_64_BATTERY_H
#include <Arduino.h>
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "CanBattery.h"

View file

@ -3,7 +3,7 @@
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
#include "../devboard/webserver/BatteryHtmlRenderer.h"
class KiaHyundai64HtmlRenderer : public BatteryHtmlRenderer {
public:

View file

@ -1,7 +1,5 @@
#ifndef KIA_HYUNDAI_HYBRID_BATTERY_H
#define KIA_HYUNDAI_HYBRID_BATTERY_H
#include <Arduino.h>
#include "CanBattery.h"
#ifdef KIA_HYUNDAI_HYBRID_BATTERY

View file

@ -1,4 +1,5 @@
#include "MEB-BATTERY.h"
#include <Arduino.h>
#include <algorithm> // For std::min and std::max
#include "../communication/can/comm_can.h"
#include "../communication/can/obd.h"

View file

@ -1,6 +1,5 @@
#ifndef MEB_BATTERY_H
#define MEB_BATTERY_H
#include <Arduino.h>
#include "CanBattery.h"
#include "MEB-HTML.h"

View file

@ -3,7 +3,7 @@
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
#include "../devboard/webserver/BatteryHtmlRenderer.h"
class MebHtmlRenderer : public BatteryHtmlRenderer {
public:

View file

@ -1,7 +1,5 @@
#ifndef MG_5_BATTERY_H
#define MG_5_BATTERY_H
#include <Arduino.h>
#include "CanBattery.h"
#ifdef MG_5_BATTERY

View file

@ -1,6 +1,5 @@
#ifndef MG_HS_PHEV_BATTERY_H
#define MG_HS_PHEV_BATTERY_H
#include <Arduino.h>
#include "CanBattery.h"

View file

@ -747,11 +747,16 @@ void NissanLeafBattery::transmit_can(unsigned long currentMillis) {
}
}
bool NissanLeafBattery::is_message_corrupt(CAN_frame rx_frame) {
uint8_t NissanLeafBattery::calculate_crc(CAN_frame& rx_frame) {
uint8_t crc = 0;
for (uint8_t j = 0; j < 7; j++) {
crc = crctable[(crc ^ static_cast<uint8_t>(rx_frame.data.u8[j])) % 256];
}
return crc;
}
bool NissanLeafBattery::is_message_corrupt(CAN_frame rx_frame) {
uint8_t crc = calculate_crc(rx_frame);
return crc != rx_frame.data.u8[7];
}

View file

@ -47,6 +47,8 @@ class NissanLeafBattery : public CanBattery {
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
static constexpr const char* Name = "Nissan LEAF battery";
uint8_t calculate_crc(CAN_frame& frame);
private:
static const int MAX_PACK_VOLTAGE_DV = 4040; //5000 = 500.0V
static const int MIN_PACK_VOLTAGE_DV = 2600;

View file

@ -3,7 +3,7 @@
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
#include "../devboard/webserver/BatteryHtmlRenderer.h"
class NissanLeafHtmlRenderer : public BatteryHtmlRenderer {
public:

View file

@ -1,6 +1,5 @@
#ifndef ORION_BMS_H
#define ORION_BMS_H
#include <Arduino.h>
#include "../system_settings.h"
#include "CanBattery.h"

View file

@ -1,6 +1,5 @@
#ifndef PYLON_BATTERY_H
#define PYLON_BATTERY_H
#include <Arduino.h>
#include "../datalayer/datalayer.h"
#include "CanBattery.h"

View file

@ -1,6 +1,5 @@
#ifndef RANGE_ROVER_PHEV_BATTERY_H
#define RANGE_ROVER_PHEV_BATTERY_H
#include <Arduino.h>
#include "CanBattery.h"

View file

@ -1,4 +1,5 @@
#include "RENAULT-KANGOO-BATTERY.h"
#include <Arduino.h>
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"

View file

@ -1,6 +1,5 @@
#ifndef RENAULT_KANGOO_BATTERY_H
#define RENAULT_KANGOO_BATTERY_H
#include <Arduino.h>
#include "CanBattery.h"

View file

@ -3,7 +3,7 @@
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
#include "../devboard/webserver/BatteryHtmlRenderer.h"
class RenaultZoeGen1HtmlRenderer : public BatteryHtmlRenderer {
public:

View file

@ -1,4 +1,5 @@
#include "RENAULT-ZOE-GEN2-BATTERY.h"
#include <Arduino.h>
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" //For "More battery info" webpage

View file

@ -3,7 +3,7 @@
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
#include "../devboard/webserver/BatteryHtmlRenderer.h"
class RenaultZoeGen2HtmlRenderer : public BatteryHtmlRenderer {
public:

View file

@ -1,6 +1,5 @@
#ifndef RJXZS_BMS_H
#define RJXZS_BMS_H
#include <Arduino.h>
#include "../system_settings.h"
#include "CanBattery.h"

View file

@ -3,10 +3,9 @@
#include "Battery.h"
#include "src/communication/Transmitter.h"
#include "src/devboard/utils/types.h"
#include "src/communication/rs485/comm_rs485.h"
#include "../communication/Transmitter.h"
#include "../communication/rs485/comm_rs485.h"
#include "../devboard/utils/types.h"
// Abstract base class for batteries using the RS485 interface
class RS485Battery : public Battery, Transmitter, Rs485Receiver {

View file

@ -1,6 +1,5 @@
#ifndef SANTA_FE_PHEV_BATTERY_H
#define SANTA_FE_PHEV_BATTERY_H
#include <Arduino.h>
#include "../datalayer/datalayer.h"
#include "CanBattery.h"

View file

@ -1,6 +1,5 @@
#ifndef SIMPBMS_BATTERY_H
#define SIMPBMS_BATTERY_H
#include <Arduino.h>
#include "CanBattery.h"

View file

@ -1,6 +1,5 @@
#ifndef SONO_BATTERY_H
#define SONO_BATTERY_H
#include <Arduino.h>
#include "CanBattery.h"

View file

@ -1,12 +1,14 @@
#ifndef _SHUNT_H
#define _SHUNT_H
#include "USER_SETTINGS.h"
#include "src/communication/Transmitter.h"
#include "src/communication/can/CanReceiver.h"
#include "src/communication/can/comm_can.h"
#include "src/devboard/safety/safety.h"
#include "src/devboard/utils/types.h"
#include "../../USER_SETTINGS.h"
#include "../../src/communication/Transmitter.h"
#include "../../src/communication/can/CanReceiver.h"
#include "../../src/communication/can/comm_can.h"
#include "../../src/devboard/safety/safety.h"
#include "../../src/devboard/utils/types.h"
#include <vector>
enum class ShuntType { None = 0, BmwSbox = 1, Highest };

View file

@ -3,7 +3,7 @@
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
#include "../devboard/webserver/BatteryHtmlRenderer.h"
class TeslaHtmlRenderer : public BatteryHtmlRenderer {
public:

View file

@ -1,4 +1,5 @@
#include "TEST-FAKE-BATTERY.h"
#include <Arduino.h>
#include "../datalayer/datalayer.h"
#include "../devboard/utils/logging.h"

View file

@ -1,6 +1,5 @@
#ifndef VOLVO_SPA_BATTERY_H
#define VOLVO_SPA_BATTERY_H
#include <Arduino.h>
#include "CanBattery.h"
#include "VOLVO-SPA-HTML.h"

View file

@ -3,7 +3,7 @@
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
#include "../devboard/webserver/BatteryHtmlRenderer.h"
class VolvoSpaHtmlRenderer : public BatteryHtmlRenderer {
public:

View file

@ -1,7 +1,5 @@
#ifndef VOLVO_SPA_HYBRID_BATTERY_H
#define VOLVO_SPA_HYBRID_BATTERY_H
#include <Arduino.h>
#include "CanBattery.h"
#include "VOLVO-SPA-HYBRID-HTML.h"

View file

@ -3,7 +3,7 @@
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
#include "../devboard/webserver/BatteryHtmlRenderer.h"
class VolvoSpaHybridHtmlRenderer : public BatteryHtmlRenderer {
public:

View file

@ -1,4 +1,5 @@
#include "CHEVY-VOLT-CHARGER.h"
#include <Arduino.h>
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"

View file

@ -1,6 +1,5 @@
#ifndef CHEVYVOLT_CHARGER_H
#define CHEVYVOLT_CHARGER_H
#include <Arduino.h>
#include "../datalayer/datalayer.h"
#include "CanCharger.h"

View file

@ -1,13 +1,12 @@
#ifndef CAN_CHARGER_H
#define CAN_CHARGER_H
#include "src/devboard/utils/types.h"
#include "../communication/Transmitter.h"
#include "../communication/can/CanReceiver.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "src/communication/Transmitter.h"
#include "src/communication/can/CanReceiver.h"
#include "src/communication/can/comm_can.h"
#include "src/devboard/safety/safety.h"
#include "../devboard/safety/safety.h"
#include "../devboard/utils/types.h"
enum class ChargerType { None, NissanLeaf, ChevyVolt, Highest };

View file

@ -1,7 +1,5 @@
#ifndef NISSANLEAF_CHARGER_H
#define NISSANLEAF_CHARGER_H
#include <Arduino.h>
#include "CanCharger.h"
#ifdef NISSANLEAF_CHARGER

View file

@ -1,7 +1,7 @@
#ifndef _CANRECEIVER_H
#define _CANRECEIVER_H
#include "src/devboard/utils/types.h"
#include "../../devboard/utils/types.h"
class CanReceiver {
public:

View file

@ -1,6 +1,7 @@
#include "comm_equipmentstopbutton.h"
#include "../../devboard/hal/hal.h"
#include "../../devboard/safety/safety.h"
#include "../../devboard/utils/debounce_button.h"
#include "USER_SETTINGS.h"
STOP_BUTTON_BEHAVIOR equipment_stop_behavior = stop_button_default_behavior;

View file

@ -1,8 +1,6 @@
#ifndef _COMM_EQUIPMENTSTOPBUTTON_H_
#define _COMM_EQUIPMENTSTOPBUTTON_H_
#include "../../devboard/utils/debounce_button.h"
/**
* @brief Initialization of equipment stop button
*

View file

@ -1,4 +1,5 @@
#include "precharge_control.h"
#include <Arduino.h>
#include "../../datalayer/datalayer.h"
#include "../../datalayer/datalayer_extended.h"
#include "../../devboard/hal/hal.h"

View file

@ -1,7 +1,7 @@
#include "comm_rs485.h"
#include <Arduino.h>
#include "../../devboard/hal/hal.h"
#include <Arduino.h>
#include <list>
bool init_rs485() {

View file

@ -5,7 +5,7 @@
#include "../devboard/utils/types.h"
#include "../system_settings.h"
typedef struct {
struct DATALAYER_BATTERY_INFO_TYPE {
/** uint32_t */
/** Total energy capacity in Watt-hours */
uint32_t total_capacity_Wh = BATTERY_WH_MAX;
@ -30,9 +30,9 @@ typedef struct {
/** Other */
/** Chemistry of the pack. NCA, NMC or LFP (so far) */
battery_chemistry_enum chemistry = battery_chemistry_enum::NCA;
} DATALAYER_BATTERY_INFO_TYPE;
};
typedef struct {
struct DATALAYER_BATTERY_STATUS_TYPE {
/** int32_t */
/** Instantaneous battery power in Watts. Calculated based on voltage_dV and current_dA */
/* Positive value = Battery Charging */
@ -111,10 +111,9 @@ typedef struct {
/** LED mode, customizable by user */
led_mode_enum led_mode = LED_MODE;
};
} DATALAYER_BATTERY_STATUS_TYPE;
typedef struct {
struct DATALAYER_BATTERY_SETTINGS_TYPE {
/** SOC scaling setting. Set to true to use SOC scaling */
bool soc_scaling_active = BATTERY_USE_SCALED_SOC;
/** Minimum percentage setting. Set this value to the lowest real SOC
@ -164,8 +163,7 @@ typedef struct {
/** Sofar CAN Battery ID (0-15) used to parallel multiple packs */
uint8_t sofar_user_specified_battery_id = 0;
} DATALAYER_BATTERY_SETTINGS_TYPE;
};
typedef struct {
DATALAYER_BATTERY_INFO_TYPE info;
@ -173,7 +171,7 @@ typedef struct {
DATALAYER_BATTERY_SETTINGS_TYPE settings;
} DATALAYER_BATTERY_TYPE;
typedef struct {
struct DATALAYER_CHARGER_TYPE {
/** Charger setpoint voltage */
float charger_setpoint_HV_VDC = 0;
/** Charger setpoint current */
@ -202,9 +200,9 @@ typedef struct {
* we report the battery as missing entirely on the CAN bus.
*/
uint8_t CAN_charger_still_alive = CAN_STILL_ALIVE;
} DATALAYER_CHARGER_TYPE;
};
typedef struct {
struct DATALAYER_SHUNT_TYPE {
/** measured voltage in deciVolts. 4200 = 420.0 V */
uint16_t measured_voltage_dV = 0;
/** measured amperage in deciAmperes. 300 = 30.0 A */
@ -223,9 +221,9 @@ typedef struct {
bool contactors_engaged = false;
/** True if shunt communication ok **/
bool available = false;
} DATALAYER_SHUNT_TYPE;
};
typedef struct {
struct DATALAYER_SYSTEM_INFO_TYPE {
/** ESP32 main CPU temperature, for displaying on webserver and for safeties */
float CPU_temperature = 0;
/** array with type of battery used, for displaying on webserver */
@ -249,10 +247,9 @@ typedef struct {
bool can_2515_send_fail = false;
/** uint16_t, MCP2518 CANFD failed to send flag */
bool can_2518_send_fail = false;
};
} DATALAYER_SYSTEM_INFO_TYPE;
typedef struct {
struct DATALAYER_SYSTEM_STATUS_TYPE {
/** Millis rollover count. Increments every 49.7 days. Used for keeping track on events */
uint8_t millisrolloverCount = 0;
#ifdef FUNCTION_TIME_MEASUREMENT
@ -324,18 +321,18 @@ typedef struct {
/** State of automatic precharge sequence */
PrechargeState precharge_status = AUTO_PRECHARGE_IDLE;
} DATALAYER_SYSTEM_STATUS_TYPE;
};
typedef struct {
struct DATALAYER_SYSTEM_SETTINGS_TYPE {
bool equipment_stop_active = false;
bool start_precharging = false;
} DATALAYER_SYSTEM_SETTINGS_TYPE;
};
typedef struct {
struct DATALAYER_SYSTEM_TYPE {
DATALAYER_SYSTEM_INFO_TYPE info;
DATALAYER_SYSTEM_STATUS_TYPE status;
DATALAYER_SYSTEM_SETTINGS_TYPE settings;
} DATALAYER_SYSTEM_TYPE;
};
class DataLayer {
public:

View file

@ -4,7 +4,7 @@
#include <stdint.h>
#include "../../USER_SETTINGS.h"
typedef struct {
struct DATALAYER_INFO_BOLTAMPERA {
/** uint16_t */
/** PID polling parameters */
uint16_t battery_5V_ref = 0;
@ -38,9 +38,9 @@ typedef struct {
uint16_t battery_HVIL = 0;
uint16_t battery_HVIL_status = 0;
int16_t battery_current_7E4 = 0;
} DATALAYER_INFO_BOLTAMPERA;
};
typedef struct {
struct DATALAYER_INFO_BMWPHEV {
/** uint8_t */
/** Status isolation external, 0 not evaluated, 1 OK, 2 error active, 3 Invalid signal*/
uint8_t ST_iso_ext = 0;
@ -93,10 +93,9 @@ typedef struct {
int32_t iso_safety_trg_plausible = 0;
int32_t iso_safety_kohm = 0; //STAT_R_ISO_ROH_01_WERT
int32_t iso_safety_kohm_quality = 0; //STAT_R_ISO_ROH_QAL_01_INFO Quality of measurement 0-21 (higher better)
};
} DATALAYER_INFO_BMWPHEV;
typedef struct {
struct DATALAYER_INFO_BYDATTO3 {
/** bool */
/** User requesting crash reset via WebUI*/
bool UserRequestCrashReset = false;
@ -136,9 +135,9 @@ typedef struct {
uint8_t unknown11 = 0;
uint8_t unknown12 = 0;
uint8_t unknown13 = 0;
} DATALAYER_INFO_BYDATTO3;
};
typedef struct {
struct DATALAYER_INFO_CELLPOWER {
/** bool */
/** All values either True or false */
bool system_state_discharge = false;
@ -202,9 +201,9 @@ typedef struct {
bool warning_Low_SOC = false;
bool warning_Balancing_required_OCV_model = false;
bool warning_Charger_not_responding = false;
} DATALAYER_INFO_CELLPOWER;
};
typedef struct {
struct DATALAYER_INFO_CHADEMO {
bool UserRequestRestart = false;
bool UserRequestStop = false;
bool FaultBatteryVoltageDeviation = false;
@ -214,10 +213,9 @@ typedef struct {
bool FaultBatteryOverVoltage = false;
uint8_t CHADEMO_Status = 0;
uint8_t ControlProtocolNumberEV = 0;
};
} DATALAYER_INFO_CHADEMO;
typedef struct {
struct DATALAYER_INFO_CMFAEV {
uint16_t soc_z = 0;
uint16_t soc_u = 0;
uint16_t soh_average = 0;
@ -235,9 +233,9 @@ typedef struct {
uint64_t cumulative_energy_when_discharging = 0;
uint64_t cumulative_energy_when_charging = 0;
uint64_t cumulative_energy_in_regen = 0;
} DATALAYER_INFO_CMFAEV;
};
typedef struct {
struct DATALAYER_INFO_ECMP {
uint8_t MainConnectorState = 0;
uint16_t InsulationResistance = 0;
uint8_t InsulationDiag = 0;
@ -312,9 +310,9 @@ typedef struct {
uint32_t pid_time_spent_over_55c = 0;
uint32_t pid_contactor_closing_counter = 0;
uint32_t pid_date_of_manufacture = 0;
} DATALAYER_INFO_ECMP;
};
typedef struct {
struct DATALAYER_INFO_GEELY_GEOMETRY_C {
/** uint8_t */
/** Battery software/hardware/serial versions, stores raw HEX values for ASCII chars */
uint8_t BatterySoftwareVersion[16] = {0};
@ -339,9 +337,9 @@ typedef struct {
uint16_t capModMin = 0;
uint16_t unknown7 = 0;
uint16_t unknown8 = 0;
} DATALAYER_INFO_GEELY_GEOMETRY_C;
};
typedef struct {
struct DATALAYER_INFO_KIAHYUNDAI64 {
uint8_t total_cell_count = 0;
int16_t battery_12V = 0;
uint8_t waterleakageSensor = 0;
@ -350,9 +348,9 @@ typedef struct {
uint8_t batteryManagementMode = 0;
uint8_t BMS_ign = 0;
uint8_t batteryRelay = 0;
} DATALAYER_INFO_KIAHYUNDAI64;
};
typedef struct {
struct DATALAYER_INFO_TESLA {
/** uint8_t */
/** Contactor status */
//uint8_t status_contactor = 0;
@ -584,9 +582,9 @@ typedef struct {
uint8_t HVP_shuntAuxCurrentStatus = 0;
uint8_t HVP_shuntBarTempStatus = 0;
uint8_t HVP_shuntAsicTempStatus = 0;
} DATALAYER_INFO_TESLA;
};
typedef struct {
struct DATALAYER_INFO_NISSAN_LEAF {
/** uint8_t */
/** Battery info, stores raw HEX values for ASCII chars */
uint8_t BatterySerialNumber[15] = {0};
@ -652,10 +650,9 @@ typedef struct {
/** uint32_t */
/** Solution for crypto challenge, LSBs */
uint32_t SolvedChallengeLSB = 0;
};
} DATALAYER_INFO_NISSAN_LEAF;
typedef struct {
struct DATALAYER_INFO_MEB {
/** uint8_t */
/** Service disconnect switch status */
bool SDSW = 0;
@ -740,9 +737,9 @@ typedef struct {
uint16_t celltemperature_dC[56] = {0};
uint16_t battery_temperature_dC = 0;
uint8_t BMS_welded_contactors_status = 0;
} DATALAYER_INFO_MEB;
};
typedef struct {
struct DATALAYER_INFO_VOLVO_POLESTAR {
uint16_t soc_bms = 0;
uint16_t soc_calc = 0;
uint16_t soc_rescaled = 0;
@ -769,10 +766,9 @@ typedef struct {
bool UserRequestDTCreadout = false;
/** User requesting BECM reset via WebUI*/
bool UserRequestBECMecuReset = false;
};
} DATALAYER_INFO_VOLVO_POLESTAR;
typedef struct {
struct DATALAYER_INFO_VOLVO_HYBRID {
uint16_t soc_bms = 0;
uint16_t soc_calc = 0;
uint16_t soc_rescaled = 0;
@ -799,10 +795,9 @@ typedef struct {
bool UserRequestDTCreadout = false;
/** User requesting BECM reset via WebUI*/
bool UserRequestBECMecuReset = false;
};
} DATALAYER_INFO_VOLVO_HYBRID;
typedef struct {
struct DATALAYER_INFO_ZOE {
/** uint8_t */
uint8_t CUV = 0;
uint8_t HVBIR = 0;
@ -814,9 +809,9 @@ typedef struct {
uint8_t COV = 0;
uint16_t mileage_km = 0;
uint16_t alltime_kWh = 0;
} DATALAYER_INFO_ZOE;
};
typedef struct {
struct DATALAYER_INFO_ZOE_PH2 {
/** User requesting NVROL reset via WebUI*/
bool UserRequestNVROLReset = false;
/** uint16_t */
@ -861,7 +856,7 @@ typedef struct {
uint16_t battery_pack_time = 0;
uint16_t battery_soc_min = 0;
uint16_t battery_soc_max = 0;
} DATALAYER_INFO_ZOE_PH2;
};
class DataLayerExtended {
public:

View file

@ -2,6 +2,7 @@
#include "../../../USER_SETTINGS.h"
#include <Arduino.h>
#include "hw_3LB.h"
#include "hw_devkit.h"
#include "hw_lilygo.h"
@ -23,8 +24,6 @@ void init_hal() {
#endif
}
unsigned long millis();
bool Esp32Hal::system_booted_up() {
return milliseconds(millis()) > BOOTUP_TIME();
}

View file

@ -1,4 +1,5 @@
#include "events.h"
#include <Arduino.h>
#include "../../../USER_SETTINGS.h"
#include "../../datalayer/datalayer.h"
#include "../../devboard/hal/hal.h"

View file

@ -2,8 +2,8 @@
#define __EVENTS_H__
#include <WString.h>
#include <src/devboard/utils/types.h>
#include <stdint.h>
#include "types.h"
#define GENERATE_ENUM(ENUM) ENUM,
#define GENERATE_STRING(STRING) #STRING,

View file

@ -1,8 +1,8 @@
#ifndef __LOGGING_H__
#define __LOGGING_H__
#include <Print.h>
#include <inttypes.h>
#include "Print.h"
#include "types.h"
class Logging : public Print {

View file

@ -2,6 +2,7 @@
#include "../datalayer/datalayer.h"
#include "../devboard/hal/hal.h"
#include "../devboard/utils/events.h"
#include "../lib/eModbus-eModbus/RTUutils.h"
#include "../lib/eModbus-eModbus/scripts/mbServerFCs.h"
// For modbus register definitions, see https://gitlab.com/pelle8/inverter_resources/-/blob/main/byd_registers_modbus_rtu.md
@ -149,8 +150,10 @@ bool BydModbusInverter::setup(void) { // Performs one time setup at startup ove
// Init Static data to the RTU Modbus
handle_static_data();
#if HAS_FREERTOS
// Init Serial2 connected to the RTU Modbus
RTUutils::prepareHardwareSerial(Serial2);
#endif
auto rx_pin = esp32hal->RS485_RX_PIN();
auto tx_pin = esp32hal->RS485_TX_PIN();
@ -160,14 +163,16 @@ bool BydModbusInverter::setup(void) { // Performs one time setup at startup ove
}
Serial2.begin(9600, SERIAL_8N1, rx_pin, tx_pin);
#if HAS_FREERTOS
// Register served function code worker for server
MBserver.registerWorker(MBTCP_ID, READ_HOLD_REGISTER, &FC03);
MBserver.registerWorker(MBTCP_ID, WRITE_HOLD_REGISTER, &FC06);
MBserver.registerWorker(MBTCP_ID, WRITE_MULT_REGISTERS, &FC16);
MBserver.registerWorker(MBTCP_ID, R_W_MULT_REGISTERS, &FC23);
MBserver->registerWorker(MBTCP_ID, READ_HOLD_REGISTER, &FC03);
MBserver->registerWorker(MBTCP_ID, WRITE_HOLD_REGISTER, &FC06);
MBserver->registerWorker(MBTCP_ID, WRITE_MULT_REGISTERS, &FC16);
MBserver->registerWorker(MBTCP_ID, R_W_MULT_REGISTERS, &FC23);
// Start ModbusRTU background task
MBserver.begin(Serial2, esp32hal->MODBUS_CORE());
((ModbusServerRTU*)MBserver)->begin(Serial2, esp32hal->MODBUS_CORE());
#endif
return true;
}

View file

@ -1,14 +1,14 @@
#ifndef CANINVERTER_PROTOCOL_H
#define CANINVERTER_PROTOCOL_H
#include "../../USER_SETTINGS.h"
#include "InverterProtocol.h"
#include "USER_SETTINGS.h"
#include "src/communication/Transmitter.h"
#include "src/communication/can/CanReceiver.h"
#include "src/communication/can/comm_can.h"
#include "src/devboard/safety/safety.h"
#include "src/devboard/utils/types.h"
#include "../communication/Transmitter.h"
#include "../communication/can/CanReceiver.h"
#include "../communication/can/comm_can.h"
#include "../devboard/safety/safety.h"
#include "../devboard/utils/types.h"
class CanInverterProtocol : public InverterProtocol, Transmitter, CanReceiver {
public:

View file

@ -4,7 +4,7 @@
#include "../devboard/hal/hal.h"
#include "../devboard/utils/events.h"
void KostalInverterProtocol::float2frame(byte* arr, float value, byte framepointer) {
void KostalInverterProtocol::float2frame(uint8_t* arr, float value, uint8_t framepointer) {
f32b g;
g.f = value;
arr[framepointer] = g.b[0];
@ -21,7 +21,7 @@ static void dbg_timestamp(void) {
#endif
}
static void dbg_frame(byte* frame, int len, const char* prefix) {
static void dbg_frame(uint8_t* frame, int len, const char* prefix) {
dbg_timestamp();
#ifdef DEBUG_KOSTAL_RS485_DATA
logging.print(prefix);
@ -58,22 +58,22 @@ static void dbg_message(const char* msg) {
/* https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing#Encoding_examples */
static void null_stuffer(byte* lfc, int len) {
static void null_stuffer(uint8_t* lfc, int len) {
int last_null_byte = 0;
for (int i = 0; i < len; i++) {
if (lfc[i] == '\0') {
lfc[last_null_byte] = (byte)(i - last_null_byte);
lfc[last_null_byte] = (uint8_t)(i - last_null_byte);
last_null_byte = i;
}
}
}
static void send_kostal(byte* frame, int len) {
static void send_kostal(uint8_t* frame, int len) {
dbg_frame(frame, len, "TX");
Serial2.write(frame, len);
}
static byte calculate_kostal_crc(byte* lfc, int len) {
static uint8_t calculate_kostal_crc(byte* lfc, int len) {
unsigned int sum = 0;
if (lfc[0] != 0) {
logging.printf("WARNING: first byte should be 0, but is 0x%02x\n", lfc[0]);
@ -81,7 +81,7 @@ static byte calculate_kostal_crc(byte* lfc, int len) {
for (int i = 1; i < len; i++) {
sum += lfc[i];
}
return (byte)(-sum & 0xff);
return (uint8_t)(-sum & 0xff);
}
bool KostalInverterProtocol::check_kostal_frame_crc(int len) {
@ -189,7 +189,7 @@ void KostalInverterProtocol::update_values() {
float2frame(CYCLIC_DATA, (float)datalayer.battery.status.cell_max_voltage_mV / 1000, 46);
float2frame(CYCLIC_DATA, (float)datalayer.battery.status.cell_min_voltage_mV / 1000, 50);
CYCLIC_DATA[58] = (byte)(datalayer.battery.status.reported_soc / 100);
CYCLIC_DATA[58] = (uint8_t)(datalayer.battery.status.reported_soc / 100);
register_content_ok = true;
@ -260,7 +260,7 @@ void KostalInverterProtocol::receive() // Runs as fast as possible to handle th
if (f2_startup_count < 15) {
f2_startup_count++;
}
byte tmpframe[64]; //copy values to prevent data manipulation during rewrite/crc calculation
uint8_t tmpframe[64]; //copy values to prevent data manipulation during rewrite/crc calculation
memcpy(tmpframe, CYCLIC_DATA, 64);
tmpframe[62] = calculate_kostal_crc(tmpframe, 62);
null_stuffer(tmpframe, 64);
@ -269,7 +269,7 @@ void KostalInverterProtocol::receive() // Runs as fast as possible to handle th
}
if (code == 0x84a) {
//Send battery info
byte tmpframe[40]; //copy values to prevent data manipulation during rewrite/crc calculation
uint8_t tmpframe[40]; //copy values to prevent data manipulation during rewrite/crc calculation
memcpy(tmpframe, BATTERY_INFO, 40);
tmpframe[38] = calculate_kostal_crc(tmpframe, 38);
null_stuffer(tmpframe, 40);
@ -282,7 +282,7 @@ void KostalInverterProtocol::receive() // Runs as fast as possible to handle th
}
if (code == 0x353) {
//Send battery error/status
byte tmpframe[9]; //copy values to prevent data manipulation during rewrite/crc calculation
uint8_t tmpframe[9]; //copy values to prevent data manipulation during rewrite/crc calculation
memcpy(tmpframe, STATUS_FRAME, 9);
tmpframe[7] = calculate_kostal_crc(tmpframe, 7);
null_stuffer(tmpframe, 9);

View file

@ -1,7 +1,6 @@
#ifndef BYD_KOSTAL_RS485_H
#define BYD_KOSTAL_RS485_H
#include <Arduino.h>
#include <stdint.h>
#include "Rs485InverterProtocol.h"
#ifdef BYD_KOSTAL_RS485
@ -26,7 +25,7 @@ class KostalInverterProtocol : public Rs485InverterProtocol {
private:
int baud_rate() { return 57600; }
void float2frame(byte* arr, float value, byte framepointer);
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 \
@ -40,18 +39,18 @@ class KostalInverterProtocol : public Rs485InverterProtocol {
uint8_t incoming_message_counter = RS485_HEALTHY;
int8_t f2_startup_count = 0;
boolean B1_delay = false;
bool B1_delay = false;
unsigned long B1_last_millis = 0;
unsigned long currentMillis;
unsigned long startupMillis = 0;
unsigned long contactorMillis = 0;
uint16_t rx_index = 0;
boolean RX_allow = false;
bool RX_allow = false;
union f32b {
float f;
byte b[4];
uint8_t b[4];
};
// clang-format off

View file

@ -1,4 +1,10 @@
#include "ModbusInverterProtocol.h"
#include "../lib/eModbus-eModbus/ModbusServerRTU.h"
static const int MB_RTU_NUM_VALUES = 13100;
uint16_t mbPV[MB_RTU_NUM_VALUES]; // Process variable memory
ModbusInverterProtocol::ModbusInverterProtocol() {
mbPV = ::mbPV;
MBserver = new ModbusServerRTU(2000);
}

View file

@ -1,9 +1,9 @@
#ifndef MODBUS_INVERTER_PROTOCOL_H
#define MODBUS_INVERTER_PROTOCOL_H
#include <HardwareSerial.h>
#include <stdint.h>
#include "../lib/eModbus-eModbus/ModbusServerRTU.h"
#include "HardwareSerial.h"
#include "../lib/eModbus-eModbus/ModbusServer.h"
#include "InverterProtocol.h"
extern uint16_t mbPV[];
@ -15,14 +15,14 @@ class ModbusInverterProtocol : public InverterProtocol {
protected:
// Create a ModbusRTU server instance with 2000ms timeout
ModbusInverterProtocol() : MBserver(2000) { mbPV = ::mbPV; }
ModbusInverterProtocol();
static const int MB_RTU_NUM_VALUES = 13100;
// Modbus register file
uint16_t* mbPV;
ModbusServerRTU MBserver;
ModbusServer* MBserver;
};
#endif

View file

@ -3,7 +3,7 @@
#include "InverterProtocol.h"
#include "src/communication/rs485/comm_rs485.h"
#include "../communication/rs485/comm_rs485.h"
class Rs485InverterProtocol : public InverterProtocol, Rs485Receiver {
public:

View file

@ -1,8 +1,8 @@
#ifndef SMA_BYD_H_CAN_H
#define SMA_BYD_H_CAN_H
#include "../devboard/hal/hal.h"
#include "SmaInverterBase.h"
#include "src/devboard/hal/hal.h"
#ifdef SMA_BYD_H_CAN
#define SELECTED_INVERTER_CLASS SmaBydHInverter

View file

@ -1,8 +1,8 @@
#ifndef SMA_BYD_HVS_CAN_H
#define SMA_BYD_HVS_CAN_H
#include "../devboard/hal/hal.h"
#include "SmaInverterBase.h"
#include "src/devboard/hal/hal.h"
#ifdef SMA_BYD_HVS_CAN
#define SELECTED_INVERTER_CLASS SmaBydHvsInverter

View file

@ -1,8 +1,8 @@
#ifndef SMA_CAN_TRIPOWER_H
#define SMA_CAN_TRIPOWER_H
#include "../devboard/hal/hal.h"
#include "SmaInverterBase.h"
#include "src/devboard/hal/hal.h"
#ifdef SMA_TRIPOWER_CAN
#define SELECTED_INVERTER_CLASS SmaTripowerInverter

View file

@ -1,4 +1,6 @@
#include "SOFAR-CAN.h"
#include <Arduino.h>
#include <string.h>
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"

View file

@ -1,4 +1,5 @@
#include "SOLAX-CAN.h"
#include <Arduino.h>
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"

View file

@ -1,9 +1,10 @@
#ifndef _SMA_INVERTER_BASE_H
#define _SMA_INVERTER_BASE_H
#include <Arduino.h>
#include "../datalayer/datalayer.h"
#include "../devboard/hal/hal.h"
#include "CanInverterProtocol.h"
#include "src/devboard/hal/hal.h"
class SmaInverterBase : public CanInverterProtocol {
public:

1
test/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
build/

View file

@ -1,18 +1,99 @@
# Include the directory with your source files
include_directories(${CMAKE_SOURCE_DIR}/Software/src/devboard ${CMAKE_SOURCE_DIR}/Software/src/devboard/utils . )
cmake_minimum_required(VERSION 4.0)
enable_testing()
# Create a variable to store the list of test files
file(GLOB TEST_SOURCES utils/*.cpp)
# set the project name
project(UnitTests)
# Loop through each test source file and create an executable
foreach(TEST_SOURCE ${TEST_SOURCES})
# Extract the test name without extension
get_filename_component(TEST_NAME ${TEST_SOURCE} NAME_WE)
# Enable ExternalProject CMake module
include(ExternalProject)
include(GoogleTest)
# Create an executable for the test
add_executable(${TEST_NAME} ${TEST_SOURCE} test_lib.cpp)
# specify the C++ standard
# At least 17 is required by Gtest
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED True)
# Apply the target_compile_definitions for the test
target_compile_definitions(${TEST_NAME} PRIVATE UNIT_TEST)
# Download and install GoogleTest
ExternalProject_Add(
gtest
URL https://github.com/google/googletest/archive/refs/tags/v1.17.0.zip
PREFIX ${CMAKE_CURRENT_BINARY_DIR}/gtest
# Disable install step
INSTALL_COMMAND ""
)
endforeach()
# Get GTest source and binary directories from CMake project
ExternalProject_Get_Property(gtest source_dir binary_dir)
# Create a libgtest target to be used as a dependency by test programs
add_library(libgtest IMPORTED STATIC GLOBAL)
add_dependencies(libgtest gtest)
# Set libgtest properties
if(WIN32)
set_target_properties(libgtest PROPERTIES
"IMPORTED_LOCATION" "${binary_dir}/lib/Debug/gtest.lib"
"IMPORTED_LINK_INTERFACE_LIBRARIES" "${CMAKE_THREAD_LIBS_INIT}")
else()
set_target_properties(libgtest PROPERTIES
"IMPORTED_LOCATION" "${binary_dir}/lib/libgtest.a"
"IMPORTED_LINK_INTERFACE_LIBRARIES" "${CMAKE_THREAD_LIBS_INIT}")
endif(WIN32)
# Create a libgmock target to be used as a dependency by test programs
add_library(libgmock IMPORTED STATIC GLOBAL)
add_dependencies(libgmock gtest)
# Set libgmock properties
if(WIN32)
set_target_properties(libgmock PROPERTIES
"IMPORTED_LOCATION" "${binary_dir}/lib/Debug/gmock.lib"
"IMPORTED_LINK_INTERFACE_LIBRARIES" "${CMAKE_THREAD_LIBS_INIT}")
else()
set_target_properties(libgmock PROPERTIES
"IMPORTED_LOCATION" "${binary_dir}/lib/libgmock.a"
"IMPORTED_LINK_INTERFACE_LIBRARIES" "${CMAKE_THREAD_LIBS_INIT}")
endif(WIN32)
# I couldn't make it work with INTERFACE_INCLUDE_DIRECTORIES
include_directories("${source_dir}/googletest/include"
"${source_dir}/googlemock/include")
include_directories(emul)
# For eModBus
add_compile_definitions(__linux__)
# add the executable
add_executable(tests
tests.cpp
safety_tests.cpp
battery/NissanLeafTest.cpp
../Software/src/devboard/safety/safety.cpp
../Software/src/devboard/hal/hal.cpp
../Software/src/devboard/utils/events.cpp
../Software/src/datalayer/datalayer.cpp
../Software/src/datalayer/datalayer_extended.cpp
../Software/src/lib/eModbus-eModbus/ModbusMessage.cpp
../Software/src/lib/eModbus-eModbus/ModbusTypeDefs.cpp
../Software/src/lib/eModbus-eModbus/Logging.cpp
# ../Software/src/lib/eModbus-eModbus/scripts/mbServerFCs.cpp
../Software/USER_SETTINGS.cpp
../Software/src/battery/BATTERIES.cpp
../Software/src/battery/Battery.cpp
../Software/src/battery/CanBattery.cpp
../Software/src/battery/NISSAN-LEAF-BATTERY.cpp
../Software/src/inverter/INVERTERS.cpp
../Software/src/inverter/BYD-MODBUS.cpp
../Software/src/charger/CHARGERS.cpp
emul/can.cpp
emul/time.cpp
emul/serial.cpp
)
target_link_libraries(tests
libgtest
libgmock
)
gtest_discover_tests(tests)

View file

@ -0,0 +1,21 @@
#include <gtest/gtest.h>
#include "../../Software/src/battery/NISSAN-LEAF-BATTERY.h"
#include "../../Software/src/datalayer/datalayer.h"
TEST(NissanLeafTests, ShouldReportVoltage) {
auto battery = new NissanLeafBattery();
battery->setup();
int expected_dV = 440;
int divided = expected_dV / 5;
CAN_frame frame = {.ID = 0x1DB, .data = {.u8 = {0, 0, (uint8_t)(divided >> 2), (uint8_t)((divided & 0xC0) << 6)}}};
frame.data.u8[7] = battery->calculate_crc(frame);
battery->handle_incoming_can_frame(frame);
battery->update_values();
EXPECT_EQ(datalayer.battery.status.voltage_dV, expected_dV);
}

18
test/emul/Arduino.h Normal file
View file

@ -0,0 +1,18 @@
#ifndef ARDUINO_H
#define ARDUINO_H
#include <stdint.h>
#include "esp-hal-gpio.h"
void pinMode(uint8_t pin, uint8_t mode);
void digitalWrite(uint8_t pin, uint8_t val);
int digitalRead(uint8_t pin);
// Can be previously declared as a macro in stupid eModbus
#undef millis
unsigned long millis();
#define max(a, b) std::max(a, b)
#endif

View file

@ -0,0 +1,44 @@
#ifndef HARDWARESERIAL_H
#define HARDWARESERIAL_H
#include <stdint.h>
#include "Stream.h"
enum SerialConfig {
SERIAL_5N1 = 0x8000010,
SERIAL_6N1 = 0x8000014,
SERIAL_7N1 = 0x8000018,
SERIAL_8N1 = 0x800001c,
SERIAL_5N2 = 0x8000030,
SERIAL_6N2 = 0x8000034,
SERIAL_7N2 = 0x8000038,
SERIAL_8N2 = 0x800003c,
SERIAL_5E1 = 0x8000012,
SERIAL_6E1 = 0x8000016,
SERIAL_7E1 = 0x800001a,
SERIAL_8E1 = 0x800001e,
SERIAL_5E2 = 0x8000032,
SERIAL_6E2 = 0x8000036,
SERIAL_7E2 = 0x800003a,
SERIAL_8E2 = 0x800003e,
SERIAL_5O1 = 0x8000013,
SERIAL_6O1 = 0x8000017,
SERIAL_7O1 = 0x800001b,
SERIAL_8O1 = 0x800001f,
SERIAL_5O2 = 0x8000033,
SERIAL_6O2 = 0x8000037,
SERIAL_7O2 = 0x800003b,
SERIAL_8O2 = 0x800003f
};
class HardwareSerial : public Stream {
public:
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) {}
};
extern HardwareSerial Serial0;
extern HardwareSerial Serial1;
extern HardwareSerial Serial2;
#endif

6
test/emul/Print.h Normal file
View file

@ -0,0 +1,6 @@
#ifndef PRINT_H
#define PRINT_H
class Print {};
#endif

6
test/emul/Stream.h Normal file
View file

@ -0,0 +1,6 @@
#ifndef STREAM_H
#define STREAM_H
class Stream {};
#endif

91
test/emul/WString.h Normal file
View file

@ -0,0 +1,91 @@
#ifndef WSTRING_H
#define WSTRING_H
/*#include <string>
class String : public std::string {
public:
String() : std::string() {}
String(uint32_t val) : std::string(std::to_string(val)) {}
String(double value, unsigned int decimalPlaces) : std::string(std::to_string(value)) {}
String(const char* str) : std::string(str) {}
};
*/
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
class String {
private:
std::string data;
public:
// Constructors
String() = default;
String(const char* s) : data(s) {}
String(const std::string& s) : data(s) {}
String(const String& other) = default;
String(String&& other) = default;
// Numeric constructors (Arduino-style)
String(uint64_t value) { data = std::to_string(value); }
String(int value) { data = std::to_string(value); }
String(unsigned int value) { data = std::to_string(value); }
String(long value) { data = std::to_string(value); }
String(unsigned long value) { data = std::to_string(value); }
String(float value) { data = std::to_string(value); }
String(double value) { data = std::to_string(value); }
String(float value, unsigned int decimalPlaces) {
std::ostringstream oss;
oss << std::fixed << std::setprecision(decimalPlaces) << value;
data = oss.str();
}
// Assignment operators
String& operator=(const String& other) = default;
String& operator=(String&& other) = default;
// Conversion operator to std::string
operator std::string() const { return data; }
// Accessor
const std::string& str() const { return data; }
// Concatenation
String operator+(const String& rhs) const { return String(data + rhs.data); }
String operator+(const std::string& rhs) const { return String(data + rhs); }
String operator+(const char* rhs) const { return String(data + std::string(rhs)); }
// Append
String& operator+=(const String& rhs) {
data += rhs.data;
return *this;
}
String& operator+=(const std::string& rhs) {
data += rhs;
return *this;
}
String& operator+=(const char* rhs) {
data += rhs;
return *this;
}
// Arduino-like methods (example)
int length() const { return static_cast<int>(data.length()); }
const char* c_str() const { return data.c_str(); }
// Friend functions to allow std::string + String
friend String operator+(const std::string& lhs, const String& rhs) { return String(lhs + rhs.data); }
friend String operator+(const char* lhs, const String& rhs) { return String(std::string(lhs) + rhs.data); }
friend std::ostream& operator<<(std::ostream& os, const String& s) { return os << s.data; }
};
#endif

20
test/emul/can.cpp Normal file
View file

@ -0,0 +1,20 @@
#include "../../Software/src/communication/Transmitter.h"
#include "../../Software/src/communication/can/comm_can.h"
void transmit_can_frame_to_interface(CAN_frame* tx_frame, int interface) {}
void register_can_receiver(CanReceiver* receiver, CAN_Interface interface, CAN_Speed speed) {}
CAN_Speed change_can_speed(CAN_Interface interface, CAN_Speed speed) {
return CAN_Speed::CAN_SPEED_500KBPS;
}
void stop_can() {}
void restart_can() {}
char const* getCANInterfaceName(CAN_Interface) {
return "Foobar";
}
void register_transmitter(Transmitter* transmitter) {}

16
test/emul/esp-hal-gpio.h Normal file
View file

@ -0,0 +1,16 @@
#ifndef ESP_HAL_GPIO_H
#define ESP_HAL_GPIO_H
#define LOW 0x0
#define HIGH 0x1
#define INPUT 0x01
// Changed OUTPUT from 0x02 to behave the same as Arduino pinMode(pin,OUTPUT)
// where you can read the state of pin even when it is set as OUTPUT
#define OUTPUT 0x03
void pinMode(uint8_t pin, uint8_t mode);
void digitalWrite(uint8_t pin, uint8_t val);
int digitalRead(uint8_t pin);
#endif

5
test/emul/serial.cpp Normal file
View file

@ -0,0 +1,5 @@
#include "HardwareSerial.h"
HardwareSerial Serial0;
HardwareSerial Serial1;
HardwareSerial Serial2;

45
test/emul/soc/gpio_num.h Normal file
View file

@ -0,0 +1,45 @@
#pragma once
typedef enum {
GPIO_NUM_NC = -1, /*!< Use to signal not connected to S/W */
GPIO_NUM_0 = 0, /*!< GPIO0, input and output */
GPIO_NUM_1 = 1, /*!< GPIO1, input and output */
GPIO_NUM_2 = 2, /*!< GPIO2, input and output */
GPIO_NUM_3 = 3, /*!< GPIO3, input and output */
GPIO_NUM_4 = 4, /*!< GPIO4, input and output */
GPIO_NUM_5 = 5, /*!< GPIO5, input and output */
GPIO_NUM_6 = 6, /*!< GPIO6, input and output */
GPIO_NUM_7 = 7, /*!< GPIO7, input and output */
GPIO_NUM_8 = 8, /*!< GPIO8, input and output */
GPIO_NUM_9 = 9, /*!< GPIO9, input and output */
GPIO_NUM_10 = 10, /*!< GPIO10, input and output */
GPIO_NUM_11 = 11, /*!< GPIO11, input and output */
GPIO_NUM_12 = 12, /*!< GPIO12, input and output */
GPIO_NUM_13 = 13, /*!< GPIO13, input and output */
GPIO_NUM_14 = 14, /*!< GPIO14, input and output */
GPIO_NUM_15 = 15, /*!< GPIO15, input and output */
GPIO_NUM_16 = 16, /*!< GPIO16, input and output */
GPIO_NUM_17 = 17, /*!< GPIO17, input and output */
GPIO_NUM_18 = 18, /*!< GPIO18, input and output */
GPIO_NUM_19 = 19, /*!< GPIO19, input and output */
GPIO_NUM_20 = 20, /*!< GPIO20, input and output */
GPIO_NUM_21 = 21, /*!< GPIO21, input and output */
GPIO_NUM_22 = 22, /*!< GPIO22, input and output */
GPIO_NUM_23 = 23, /*!< GPIO23, input and output */
GPIO_NUM_25 = 25, /*!< GPIO25, input and output */
GPIO_NUM_26 = 26, /*!< GPIO26, input and output */
GPIO_NUM_27 = 27, /*!< GPIO27, input and output */
GPIO_NUM_28 = 28, /*!< GPIO28, input and output */
GPIO_NUM_29 = 29, /*!< GPIO29, input and output */
GPIO_NUM_30 = 30, /*!< GPIO30, input and output */
GPIO_NUM_31 = 31, /*!< GPIO31, input and output */
GPIO_NUM_32 = 32, /*!< GPIO32, input and output */
GPIO_NUM_33 = 33, /*!< GPIO33, input and output */
GPIO_NUM_34 = 34, /*!< GPIO34, input mode only */
GPIO_NUM_35 = 35, /*!< GPIO35, input mode only */
GPIO_NUM_36 = 36, /*!< GPIO36, input mode only */
GPIO_NUM_37 = 37, /*!< GPIO37, input mode only */
GPIO_NUM_38 = 38, /*!< GPIO38, input mode only */
GPIO_NUM_39 = 39, /*!< GPIO39, input mode only */
GPIO_NUM_MAX,
} gpio_num_t;

9
test/emul/time.cpp Normal file
View file

@ -0,0 +1,9 @@
#include <stdint.h>
unsigned long millis() {
return 0;
}
uint64_t get_timestamp(unsigned long millis) {
return 0;
}

Some files were not shown because too many files have changed in this diff Show more