diff --git a/documentation/CCGX-Modbus-TCP-register-list-3.73.xlsx b/documentation/CCGX-Modbus-TCP-register-list-3.73.xlsx new file mode 100644 index 0000000..d46825d Binary files /dev/null and b/documentation/CCGX-Modbus-TCP-register-list-3.73.xlsx differ diff --git a/manifest.xml b/manifest.xml index c598a5a..72b58a3 100644 --- a/manifest.xml +++ b/manifest.xml @@ -7,11 +7,6 @@ http:// http:// - + + diff --git a/src/BatteryData.hpp b/src/BatteryData.hpp new file mode 100644 index 0000000..ab17cb6 --- /dev/null +++ b/src/BatteryData.hpp @@ -0,0 +1,385 @@ +#ifndef POWER_VICTRON_BATTERY_DATA_HPP +#define POWER_VICTRON_BATTERY_DATA_HPP + +#include "BatteryRegisters.hpp" +#include +#include +#include +#include +#include + +namespace power_victron { + + /** + * Struct representing com.victronenergy.battery data + */ + struct BatteryData { + enum Alarm { + NONE = 0, + LOW_VOLTAGE_ALARM = 1 << 0, + HIGH_VOLTAGE_ALARM = 1 << 1, + LOW_STARTER_VOLTAGE_ALARM = 1 << 2, + HIGH_STARTER_VOLTAGE_ALARM = 1 << 3, + LOW_STATE_OF_CHARGE_ALARM = 1 << 4, + LOW_TEMPERATURE_ALARM = 1 << 5, + HIGH_TEMPERATURE_ALARM = 1 << 6, + MID_VOLTAGE_ALARM = 1 << 7, + LOW_FUSED_VOLTAGE_ALARM = 1 << 8, + HIGH_FUSED_VOLTAGE_ALARM = 1 << 9, + FUSE_BLOWN_ALARM = 1 << 10, + HIGH_INTERNAL_TEMPERATURE_ALARM = 1 << 11, + HIGH_CHARGE_CURRENT_ALARM = 1 << 12, + HIGH_DISCHARGE_CURRENT_ALARM = 1 << 13, + CELL_IMBALANCE_ALARM = 1 << 14, + INTERNAL_FAILURE_ALARM = 1 << 15, + HIGH_CHARGE_TEMPERATURE_ALARM = 1 << 16, + LOW_CHARGE_TEMPERATURE_ALARM = 1 << 17, + LOW_CELL_VOLTAGE_ALARM = 1 << 18, + BMS_CABLE_ALARM = 1 << 19, + BMS_CONTACTOR_RESISTANCE_ALARM = 1 << 20, + BMS_HIGH_CURRENT_ALARM = 1 << 21, + }; + + enum DiagnosticError { + NO_ERROR = 0, + BATTERY_INITIALIZATION_ERROR = 1, + NO_BATTERIES_CONNECTED = 2, + UNKNOWN_BATTERY_CONNECTED = 3, + DIFFERENT_BATTERY_TYPE = 4, + NUMBER_OF_BATTERIES_INCORRECT = 5, + LYNX_SHUNT_NOT_FOUND = 6, + BATTERY_MEASURE_ERROR = 7, + INTERNAL_CALCULATION_ERROR = 8, + BATTERIES_IN_SERIES_NOT_OK = 9, + NUMBER_OF_BATTERIES_INCORRECT_10 = 10, + HARDWARE_ERROR = 11, + WATCHDOG_ERROR = 12, + OVER_VOLTAGE = 13, + UNDER_VOLTAGE = 14, + OVER_TEMPERATURE = 15, + UNDER_TEMPERATURE = 16, + HARDWARE_FAULT = 17, + STANDBY_SHUTDOWN = 18, + PRE_CHARGE_CHARGE_ERROR = 19, + SAFETY_CONTACTOR_CHECK_ERROR = 20, + PRE_CHARGE_DISCHARGE_ERROR = 21, + ADC_ERROR = 22, + SLAVE_ERROR = 23, + SLAVE_WARNING = 24, + PRE_CHARGE_ERROR = 25, + SAFETY_CONTACTOR_ERROR = 26, + OVER_CURRENT = 27, + SLAVE_UPDATE_FAILED = 28, + SLAVE_UPDATE_UNAVAILABLE = 29, + CALIBRATION_DATA_LOST = 30, + SETTINGS_INVALID = 31, + BMS_CABLE = 32, + REFERENCE_FAILURE = 33, + WRONG_SYSTEM_VOLTAGE = 34, + PRE_CHARGE_TIMEOUT = 35 + }; + + struct DiagnosticEvent { + base::Time time; + DiagnosticError error; + }; + + enum Mode { + ON = 3, + STANDBY = 252 + }; + + enum BalancerStatus { + UNKNOWN = 0, + BALANCED = 1, + BALANCING = 2, + CELL_IMBALANCE = 3 + }; + + enum State { + INITIALIZING_WAIT_START = 0, + INITIALIZING_BEFORE_BOOT = 1, + INITIALIZING_BEFORE_BOOT_DELAY = 2, + INITIALIZING_WAIT_BOOT = 3, + INITIALIZING = 4, + INITIALIZING_MEASURE_BATTERY_VOLTAGE = 5, + INITIALIZING_CALCULATE_BATTERY_VOLTAGE = 6, + INITIALIZING_WAIT_BUS_VOLTAGE = 7, + INITIALIZING_WAIT_FOR_LYNX_SHUNT = 8, + RUNNING = 9, + ERROR_10 = 10, + UNUSED_11 = 11, + SHUTDOWN = 12, + SLAVE_UPDATING = 13, + STATE_STANDBY = 14, + GOING_TO_RUN = 15, + PRE_CHARGING = 16, + CONTACTOR_CHECK = 17 + }; + + struct AlarmRegister { + Alarm alarm; + BatteryRegisters register_id; + }; + + static constexpr AlarmRegister alarm_register[] = { + { LOW_VOLTAGE_ALARM,BatteryRegisters::LOW_VOLTAGE_ALARM }, + { HIGH_VOLTAGE_ALARM, BatteryRegisters::HIGH_VOLTAGE_ALARM}, + { LOW_STARTER_VOLTAGE_ALARM, BatteryRegisters::LOW_STARTER_VOLTAGE_ALARM}, + { HIGH_STARTER_VOLTAGE_ALARM, BatteryRegisters::HIGH_STARTER_VOLTAGE_ALARM}, + { LOW_STATE_OF_CHARGE_ALARM, BatteryRegisters::LOW_STATE_OF_CHARGE_ALARM}, + { LOW_TEMPERATURE_ALARM, BatteryRegisters::LOW_TEMPERATURE_ALARM}, + { HIGH_TEMPERATURE_ALARM, BatteryRegisters::HIGH_TEMPERATURE_ALARM}, + { MID_VOLTAGE_ALARM, BatteryRegisters::MID_VOLTAGE_ALARM}, + { LOW_FUSED_VOLTAGE_ALARM, BatteryRegisters::LOW_FUSED_VOLTAGE_ALARM}, + { HIGH_FUSED_VOLTAGE_ALARM, BatteryRegisters::HIGH_FUSED_VOLTAGE_ALARM}, + { FUSE_BLOWN_ALARM, BatteryRegisters::FUSE_BLOWN_ALARM}, + {HIGH_INTERNAL_TEMPERATURE_ALARM, + BatteryRegisters::HIGH_INTERNAL_TEMPERATURE_ALARM }, + { HIGH_CHARGE_CURRENT_ALARM, BatteryRegisters::HIGH_CHARGE_CURRENT_ALARM}, + { HIGH_DISCHARGE_CURRENT_ALARM, + BatteryRegisters::HIGH_DISCHARGE_CURRENT_ALARM }, + { CELL_IMBALANCE_ALARM, BatteryRegisters::CELL_IMBALANCE_ALARM}, + { INTERNAL_FAILURE_ALARM, BatteryRegisters::INTERNAL_FAILURE_ALARM}, + { HIGH_CHARGE_TEMPERATURE_ALARM, + BatteryRegisters::HIGH_CHARGE_TEMPERATURE_ALARM }, + { LOW_CHARGE_TEMPERATURE_ALARM, + BatteryRegisters::LOW_CHARGE_TEMPERATURE_ALARM }, + { LOW_CELL_VOLTAGE_ALARM, BatteryRegisters::LOW_CELL_VOLTAGE_ALARM}, + { BMS_CABLE_ALARM, BatteryRegisters::BMS_CABLE_ALARM}, + { BMS_CONTACTOR_RESISTANCE_ALARM, + BatteryRegisters::BMS_CONTACTOR_RESISTANCE_ALARM }, + { BMS_HIGH_CURRENT_ALARM, BatteryRegisters::BMS_HIGH_CURRENT_ALARM} + }; + + /** @meta bitfield /power_victron/BatteryData/Alarm */ + uint32_t alarms = 0; + + /** + * @brief Battery power (W) + */ + int32_t battery_power; + /** + * @brief Battery voltage (V) + */ + double battery_voltage; + /** + * @brief Starter battery voltage (V) + */ + double starter_battery_voltage; + /** + * @brief Current (A) + */ + double current; + /** + * @brief Battery temperature + */ + base::Temperature battery_temperature; + /** + * @brief Mid-point voltage of the battery bank(V) + */ + double mid_point_voltage; + /** + * @brief Mid-point deviation of the battery bank (%) + */ + double mid_point_deviation; + /** + * @brief Consumed Amphours (Ah) + */ + double consumed_amphours; + /** + * @brief State of charge (%) + */ + double state_of_charge; + /** + * @brief Relay status + */ + uint32_t relay_status; + /** + * @brief Deepest discharge (Ah) + */ + double deepest_discharge; + /** + * @brief Last discharge (Ah) + */ + double last_discharge; + /** + * @brief Average discharge (Ah) + */ + double average_discharge; + /** + * @brief Charge cycles count + */ + uint32_t charge_cycles; + /** + * @brief Full discharges count + */ + uint32_t full_discharges; + /** + * @brief Total Ah drawn (Ah) + */ + double total_ah_drawn; + /** + * @brief Minimum voltage (V) + */ + double minimum_voltage; + /** + * @brief Maximum voltage (V) + */ + double maximum_voltage; + /** + * @brief Time since last full charge + */ + base::Time time_since_last_full_charge; + /** + * @brief Automatic syncs count + */ + uint32_t automatic_syncs; + /** + * @brief Low voltage alarms count + */ + uint32_t low_voltage_alarms; + /** + * @brief High voltage alarms count + */ + uint32_t high_voltage_alarms; + /** + * @brief Low starter voltage alarms count + */ + uint32_t low_starter_voltage_alarms; + /** + * @brief High starter voltage alarms count + */ + uint32_t high_starter_voltage_alarms; + /** + * @brief Minimum starter voltage (V) + */ + double minimum_starter_voltage; + /** + * @brief Maximum starter voltage (V) + */ + double maximum_starter_voltage; + /** + * @brief Low fused-voltage alarms count + */ + uint32_t low_fused_voltage_alarms; + /** + * @brief High fused-voltage alarms count + */ + uint32_t high_fused_voltage_alarms; + /** + * @brief Minimum fused voltage (V) + */ + double minimum_fused_voltage; + /** + * @brief Maximum fused voltage (V) + */ + double maximum_fused_voltage; + /** + * @brief Discharged Energy (kWh) + */ + double discharged_energy; + /** + * @brief Charged Energy (kWh) + */ + double charged_energy; + /** + * @brief Time to go + */ + base::Time time_to_go; + /** + * @brief State of health (%) + */ + double state_of_health; + /** + * @brief Max charge voltage (V) + */ + double max_charge_voltage; + /** + * @brief Min discharge voltage (V) + */ + double min_discharge_voltage; + /** + * @brief Max charge current (A) + */ + double max_charge_current; + /** + * @brief Max discharge current (A) + */ + double max_discharge_current; + /** + * @brief Capacity (Ah) + */ + double capacity; + /** + * @brief History of last diagnostic errors + * + * index 0 is the most recent (1st last error), while index 3 is the oldest (4th + * last error). + */ + std::vector diagnostics_errors; + /** + * @brief Minimum cell temperature + */ + base::Temperature minimum_cell_temperature; + /** + * @brief Maximum cell temperature + */ + base::Temperature maximum_cell_temperature; + /** + * @brief Consumed Amphours (Ah) + */ + double consumed_amphours_1; + /** + * @brief State + */ + State state; + /** + * @brief Error + */ + DiagnosticError error_code; + /** + * @brief System-switch + */ + bool system_switch; + /** + * @brief Balancing + */ + bool balancing; + /** + * @brief System minimum cell voltage (V) + */ + double system_minimum_cell_voltage; + /** + * @brief System maximum cell voltage (V) + */ + double system_maximum_cell_voltage; + /** + * @brief Diagnostics shutdowns due to error count + * + */ + uint32_t diagnostics_shutdowns_due_to_error; + /** + * @brief Historical min cell-voltage (V) + */ + double historical_min_cell_voltage; + /** + * @brief Historical max cell-voltage (V) + */ + double historical_max_cell_voltage; + /** + * @brief Mode + */ + Mode mode; + /** + * @brief Balancer status + */ + BalancerStatus balancer_status; + /** + * @brief Connection information + */ + std::string connection_information; + }; +} + +#endif diff --git a/src/BatteryRegisters.hpp b/src/BatteryRegisters.hpp new file mode 100644 index 0000000..4f01525 --- /dev/null +++ b/src/BatteryRegisters.hpp @@ -0,0 +1,116 @@ +#ifndef POWER_VICTRON_BATTERY_REGISTERS_HPP +#define POWER_VICTRON_BATTERY_REGISTERS_HPP + +namespace power_victron { + + enum BatteryRegisters { + BATTERY_POWER = 256, + BATTERY_POWER_2 = 258, + BATTERY_VOLTAGE = 259, + STARTER_BATTERY_VOLTAGE = 260, + CURRENT = 261, + BATTERY_TEMPERATURE = 262, + MID_POINT_VOLTAGE_OF_THE_BATTERY_BANK = 263, + MID_POINT_DEVIATION_OF_THE_BATTERY_BANK = 264, + CONSUMED_AMPHOURS = 265, + STATE_OF_CHARGE = 266, + ALARM = 267, + LOW_VOLTAGE_ALARM = 268, + HIGH_VOLTAGE_ALARM = 269, + LOW_STARTER_VOLTAGE_ALARM = 270, + HIGH_STARTER_VOLTAGE_ALARM = 271, + LOW_STATE_OF_CHARGE_ALARM = 272, + LOW_TEMPERATURE_ALARM = 273, + HIGH_TEMPERATURE_ALARM = 274, + MID_VOLTAGE_ALARM = 275, + LOW_FUSED_VOLTAGE_ALARM = 276, + HIGH_FUSED_VOLTAGE_ALARM = 277, + FUSE_BLOWN_ALARM = 278, + HIGH_INTERNAL_TEMPERATURE_ALARM = 279, + RELAY_STATUS = 280, + DEEPEST_DISCHARGE = 281, + LAST_DISCHARGE = 282, + AVERAGE_DISCHARGE = 283, + CHARGE_CYCLES = 284, + FULL_DISCHARGES = 285, + TOTAL_AH_DRAWN = 286, + MINIMUM_VOLTAGE = 287, + MAXIMUM_VOLTAGE = 288, + TIME_SINCE_LAST_FULL_CHARGE = 289, + AUTOMATIC_SYNCS = 290, + LOW_VOLTAGE_ALARMS = 291, + HIGH_VOLTAGE_ALARMS = 292, + LOW_STARTER_VOLTAGE_ALARMS = 293, + HIGH_STARTER_VOLTAGE_ALARMS = 294, + MINIMUM_STARTER_VOLTAGE = 295, + MAXIMUM_STARTER_VOLTAGE = 296, + LOW_FUSED_VOLTAGE_ALARMS = 297, + HIGH_FUSED_VOLTAGE_ALARMS = 298, + MINIMUM_FUSED_VOLTAGE = 299, + MAXIMUM_FUSED_VOLTAGE = 300, + DISCHARGED_ENERGY = 301, + CHARGED_ENERGY = 302, + TIME_TO_GO = 303, + STATE_OF_HEALTH = 304, + MAX_CHARGE_VOLTAGE = 305, + MIN_DISCHARGE_VOLTAGE = 306, + MAX_CHARGE_CURRENT = 307, + MAX_DISCHARGE_CURRENT = 308, + CAPACITY = 309, + DIAGNOSTICS_1ST_LAST_ERROR_TIMESTAMP = 310, + DIAGNOSTICS_2ND_LAST_ERROR_TIMESTAMP = 312, + DIAGNOSTICS_3RD_LAST_ERROR_TIMESTAMP = 314, + DIAGNOSTICS_4TH_LAST_ERROR_TIMESTAMP = 316, + MINIMUM_CELL_TEMPERATURE = 318, + MAXIMUM_CELL_TEMPERATURE = 319, + HIGH_CHARGE_CURRENT_ALARM = 320, + HIGH_DISCHARGE_CURRENT_ALARM = 321, + CELL_IMBALANCE_ALARM = 322, + INTERNAL_FAILURE_ALARM = 323, + HIGH_CHARGE_TEMPERATURE_ALARM = 324, + LOW_CHARGE_TEMPERATURE_ALARM = 325, + LOW_CELL_VOLTAGE_ALARM = 326, + BMS_CABLE_ALARM = 327, + BMS_CONTACTOR_RESISTANCE_ALARM = 328, + BMS_HIGH_CURRENT_ALARM = 329, + CONSUMED_AMPHOURS_2 = 330, + STATE = 1282, + ERROR = 1283, + SYSTEM_SWITCH = 1284, + BALANCING = 1285, + SYSTEM_NUMBER_OF_BATTERIES = 1286, + SYSTEM_BATTERIES_PARALLEL = 1287, + SYSTEM_BATTERIES_SERIES = 1288, + SYSTEM_NUMBER_OF_CELLS_PER_BATTERY = 1289, + SYSTEM_MINIMUM_CELL_VOLTAGE = 1290, + SYSTEM_MAXIMUM_CELL_VOLTAGE = 1291, + DIAGNOSTICS_SHUTDOWNS_DUE_TO_ERROR = 1292, + DIAGNOSTICS_1ST_LAST_ERROR = 1293, + DIAGNOSTICS_2ND_LAST_ERROR = 1294, + DIAGNOSTICS_3RD_LAST_ERROR = 1295, + DIAGNOSTICS_4TH_LAST_ERROR = 1296, + IO_ALLOW_TO_CHARGE = 1297, + IO_ALLOW_TO_DISCHARGE = 1298, + IO_EXTERNAL_RELAY = 1299, + HISTORY_MIN_CELL_VOLTAGE = 1300, + HISTORY_MAX_CELL_VOLTAGE = 1301, + SYSTEM_NUMBER_OF_MODULES_OFFLINE = 1302, + SYSTEM_NUMBER_OF_MODULES_ONLINE = 1303, + SYSTEM_NUMBER_OF_MODULES_BLOCKING_CHARGE = 1304, + SYSTEM_NUMBER_OF_MODULES_BLOCKING_DISCHARGE = 1305, + SYSTEM_ID_OF_MODULE_WITH_LOWEST_CELL_VOLTAGE = 1306, + SYSTEM_ID_OF_MODULE_WITH_HIGHEST_CELL_VOLTAGE = 1310, + SYSTEM_ID_OF_MODULE_WITH_LOWEST_CELL_TEMPERATURE = 1314, + SYSTEM_ID_OF_MODULE_WITH_HIGHEST_CELL_TEMPERATURE = 1318, + MODE = 1322, + BALANCER_STATUS = 1323, + SMARTLITHIUM_ERROR_FLAG_COMMUNICATION = 1324, + SMARTLITHIUM_ERROR_FLAG_VOLTAGE = 1325, + SMARTLITHIUM_ERROR_FLAG_BATTERY_NUMBER = 1326, + SMARTLITHIUM_ERROR_FLAG_INVALID_CONFIGURATION = 1327, + CONNECTION_INFORMATION = 1328, + }; + +} + +#endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 577e4a3..ef751fa 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,7 +1,7 @@ rock_library(power_victron - SOURCES Dummy.cpp - HEADERS Dummy.hpp) + SOURCES Driver.cpp + HEADERS Driver.hpp BatteryRegisters.hpp BatteryData.hpp + DEPS_PKGCONFIG modbus) -rock_executable(power_victron_bin Main.cpp +rock_executable(power_victron_read_battery ReadBatteryData.cpp DEPS power_victron) - diff --git a/src/Driver.cpp b/src/Driver.cpp new file mode 100644 index 0000000..8dbc1a6 --- /dev/null +++ b/src/Driver.cpp @@ -0,0 +1,187 @@ +#include + +using namespace power_victron; + +constexpr BatteryData::AlarmRegister BatteryData::alarm_register[]; + +Driver::Driver() + : modbus::TCPMaster(252) +{ +} + +void Driver::setAddress(int address) +{ + m_address = address; +} + +BatteryData Driver::readBatteryData() +{ + BatteryData data; + uint32_t alarms_val = 0; + for (const auto& ar : BatteryData::alarm_register) { + if (readSingleRegister(m_address, true, ar.register_id) == 2) { + alarms_val |= ar.alarm; + } + } + data.alarms = alarms_val; + + { + auto regs = readRegisters(m_address, true, BatteryRegisters::BATTERY_POWER, 2); + data.battery_power = (static_cast(regs[0]) << 16) | regs[1]; + } + data.battery_voltage = + readSingleRegister(m_address, true, BatteryRegisters::BATTERY_VOLTAGE) / 100.0; + data.starter_battery_voltage = + readSingleRegister(m_address, true, BatteryRegisters::STARTER_BATTERY_VOLTAGE) / + 100.0; + data.current = readSingleRegister(m_address, true, BatteryRegisters::CURRENT) / 10.0; + data.battery_temperature = base::Temperature::fromCelsius( + readSingleRegister(m_address, true, BatteryRegisters::BATTERY_TEMPERATURE) / + 10.0); + data.mid_point_voltage = + readSingleRegister(m_address, + true, + BatteryRegisters::MID_POINT_VOLTAGE_OF_THE_BATTERY_BANK) / + 100.0; + data.mid_point_deviation = + readSingleRegister(m_address, + true, + BatteryRegisters::MID_POINT_DEVIATION_OF_THE_BATTERY_BANK) / + 100.0; + data.consumed_amphours = + readSingleRegister(m_address, true, BatteryRegisters::CONSUMED_AMPHOURS) * 10.0; + data.state_of_charge = + readSingleRegister(m_address, true, BatteryRegisters::STATE_OF_CHARGE) / 10.0; + data.relay_status = + readSingleRegister(m_address, false, BatteryRegisters::RELAY_STATUS); + data.deepest_discharge = + readSingleRegister(m_address, true, BatteryRegisters::DEEPEST_DISCHARGE) * 10.0; + data.last_discharge = + readSingleRegister(m_address, true, BatteryRegisters::LAST_DISCHARGE) * 10.0; + data.average_discharge = + readSingleRegister(m_address, true, BatteryRegisters::AVERAGE_DISCHARGE) * 10.0; + data.charge_cycles = + readSingleRegister(m_address, true, BatteryRegisters::CHARGE_CYCLES); + data.full_discharges = + readSingleRegister(m_address, true, BatteryRegisters::FULL_DISCHARGES); + data.total_ah_drawn = + readSingleRegister(m_address, true, BatteryRegisters::TOTAL_AH_DRAWN) * 10.0; + data.minimum_voltage = + readSingleRegister(m_address, true, BatteryRegisters::MINIMUM_VOLTAGE) / 100.0; + data.maximum_voltage = + readSingleRegister(m_address, true, BatteryRegisters::MAXIMUM_VOLTAGE) / 100.0; + data.time_since_last_full_charge = + base::Time::fromSeconds(readSingleRegister(m_address, + true, + BatteryRegisters::TIME_SINCE_LAST_FULL_CHARGE) / + 0.01); + data.automatic_syncs = + readSingleRegister(m_address, true, BatteryRegisters::AUTOMATIC_SYNCS); + data.low_voltage_alarms = + readSingleRegister(m_address, true, BatteryRegisters::LOW_VOLTAGE_ALARMS); + data.high_voltage_alarms = + readSingleRegister(m_address, true, BatteryRegisters::HIGH_VOLTAGE_ALARMS); + data.low_starter_voltage_alarms = + readSingleRegister(m_address, true, BatteryRegisters::LOW_STARTER_VOLTAGE_ALARMS); + data.high_starter_voltage_alarms = readSingleRegister(m_address, + true, + BatteryRegisters::HIGH_STARTER_VOLTAGE_ALARMS); + data.minimum_starter_voltage = + readSingleRegister(m_address, true, BatteryRegisters::MINIMUM_STARTER_VOLTAGE) / + 100.0; + data.maximum_starter_voltage = + readSingleRegister(m_address, true, BatteryRegisters::MAXIMUM_STARTER_VOLTAGE) / + 100.0; + data.low_fused_voltage_alarms = + readSingleRegister(m_address, true, BatteryRegisters::LOW_FUSED_VOLTAGE_ALARMS); + data.high_fused_voltage_alarms = + readSingleRegister(m_address, true, BatteryRegisters::HIGH_FUSED_VOLTAGE_ALARMS); + data.minimum_fused_voltage = + readSingleRegister(m_address, true, BatteryRegisters::MINIMUM_FUSED_VOLTAGE) / + 100.0; + data.maximum_fused_voltage = + readSingleRegister(m_address, true, BatteryRegisters::MAXIMUM_FUSED_VOLTAGE) / + 100.0; + data.discharged_energy = + readSingleRegister(m_address, true, BatteryRegisters::DISCHARGED_ENERGY) / 10.0; + data.charged_energy = + readSingleRegister(m_address, true, BatteryRegisters::CHARGED_ENERGY) / 10.0; + data.time_to_go = base::Time::fromSeconds( + readSingleRegister(m_address, true, BatteryRegisters::TIME_TO_GO) / 0.01); + data.state_of_health = + readSingleRegister(m_address, true, BatteryRegisters::STATE_OF_HEALTH) / 10.0; + data.max_charge_voltage = + readSingleRegister(m_address, true, BatteryRegisters::MAX_CHARGE_VOLTAGE) / 10.0; + data.min_discharge_voltage = + readSingleRegister(m_address, true, BatteryRegisters::MIN_DISCHARGE_VOLTAGE) / + 10.0; + data.max_charge_current = + readSingleRegister(m_address, true, BatteryRegisters::MAX_CHARGE_CURRENT) / 10.0; + data.max_discharge_current = + readSingleRegister(m_address, true, BatteryRegisters::MAX_DISCHARGE_CURRENT) / + 10.0; + data.capacity = + readSingleRegister(m_address, true, BatteryRegisters::CAPACITY) / 10.0; + data.diagnostics_errors.resize(4); + uint16_t time_regs[] = {BatteryRegisters::DIAGNOSTICS_1ST_LAST_ERROR_TIMESTAMP, + BatteryRegisters::DIAGNOSTICS_2ND_LAST_ERROR_TIMESTAMP, + BatteryRegisters::DIAGNOSTICS_3RD_LAST_ERROR_TIMESTAMP, + BatteryRegisters::DIAGNOSTICS_4TH_LAST_ERROR_TIMESTAMP}; + uint16_t error_regs[] = {BatteryRegisters::DIAGNOSTICS_1ST_LAST_ERROR, + BatteryRegisters::DIAGNOSTICS_2ND_LAST_ERROR, + BatteryRegisters::DIAGNOSTICS_3RD_LAST_ERROR, + BatteryRegisters::DIAGNOSTICS_4TH_LAST_ERROR}; + for (int i = 0; i < 4; ++i) { + auto regs = readRegisters(m_address, true, time_regs[i], 2); + uint32_t val = (static_cast(regs[0]) << 16) | regs[1]; + data.diagnostics_errors[i].time = + base::Time::fromSeconds(static_cast(val)); + data.diagnostics_errors[i].error = static_cast( + readSingleRegister(m_address, true, error_regs[i])); + } + data.minimum_cell_temperature = base::Temperature::fromCelsius( + readSingleRegister(m_address, true, BatteryRegisters::MINIMUM_CELL_TEMPERATURE) / + 10.0); + data.maximum_cell_temperature = base::Temperature::fromCelsius( + readSingleRegister(m_address, true, BatteryRegisters::MAXIMUM_CELL_TEMPERATURE) / + 10.0); + { + auto regs = + readRegisters(m_address, true, BatteryRegisters::CONSUMED_AMPHOURS_2, 2); + data.consumed_amphours_1 = (static_cast(regs[0]) << 16) | regs[1]; + } + data.state = static_cast( + readSingleRegister(m_address, true, BatteryRegisters::STATE)); + data.error_code = static_cast( + readSingleRegister(m_address, true, BatteryRegisters::ERROR)); + data.system_switch = + (readSingleRegister(m_address, true, BatteryRegisters::SYSTEM_SWITCH) == 1); + data.balancing = + (readSingleRegister(m_address, true, BatteryRegisters::BALANCING) == 1); + data.system_minimum_cell_voltage = + readSingleRegister(m_address, + true, + BatteryRegisters::SYSTEM_MINIMUM_CELL_VOLTAGE) / + 100.0; + data.system_maximum_cell_voltage = + readSingleRegister(m_address, + true, + BatteryRegisters::SYSTEM_MAXIMUM_CELL_VOLTAGE) / + 100.0; + data.diagnostics_shutdowns_due_to_error = readSingleRegister(m_address, + true, + BatteryRegisters::DIAGNOSTICS_SHUTDOWNS_DUE_TO_ERROR); + data.historical_min_cell_voltage = + readSingleRegister(m_address, true, BatteryRegisters::HISTORY_MIN_CELL_VOLTAGE) / + 100.0; + data.historical_max_cell_voltage = + readSingleRegister(m_address, true, BatteryRegisters::HISTORY_MAX_CELL_VOLTAGE) / + 100.0; + data.mode = static_cast( + readSingleRegister(m_address, false, BatteryRegisters::MODE)); + data.balancer_status = static_cast( + readSingleRegister(m_address, true, BatteryRegisters::BALANCER_STATUS)); + // TODO: + // data.connection_information = ""; + return data; +} diff --git a/src/Driver.hpp b/src/Driver.hpp new file mode 100644 index 0000000..c36e2f7 --- /dev/null +++ b/src/Driver.hpp @@ -0,0 +1,30 @@ +#ifndef POWER_VICTRON_DRIVER_HPP +#define POWER_VICTRON_DRIVER_HPP + +#include +#include +#include +#include "BatteryData.hpp" +#include "BatteryRegisters.hpp" + +namespace power_victron { + + /** + * Driver for Victron devices using Modbus TCP + */ + class Driver : public modbus::TCPMaster { + int m_address; + + public: + Driver(); + + /** + * Set the Modbus address (Unit ID) for the device + */ + void setAddress(int address); + + BatteryData readBatteryData(); + }; +} + +#endif diff --git a/src/Dummy.cpp b/src/Dummy.cpp deleted file mode 100644 index 46b7c17..0000000 --- a/src/Dummy.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "Dummy.hpp" -#include - -using namespace std; -using namespace power_victron; - -void DummyClass::welcome() -{ - cout << "You successfully compiled and executed DummyProject. Welcome!" << endl; -} diff --git a/src/Dummy.hpp b/src/Dummy.hpp deleted file mode 100644 index 82542ba..0000000 --- a/src/Dummy.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef _DUMMYPROJECT_DUMMY_HPP_ -#define _DUMMYPROJECT_DUMMY_HPP_ - -namespace power_victron { - class DummyClass { - public: - /** - * Print a welcome to stdout - * \return nothing - */ - void welcome(); - }; - -} // end namespace power_victron - -#endif // _DUMMYPROJECT_DUMMY_HPP_ diff --git a/src/Main.cpp b/src/Main.cpp deleted file mode 100644 index 05b78f7..0000000 --- a/src/Main.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include -#include - -int main(int argc, char** argv) -{ - power_victron::DummyClass dummyClass; - dummyClass.welcome(); - - return 0; -} diff --git a/src/ReadBatteryData.cpp b/src/ReadBatteryData.cpp new file mode 100644 index 0000000..b6aa67a --- /dev/null +++ b/src/ReadBatteryData.cpp @@ -0,0 +1,123 @@ +#include +#include +#include +#include +#include + +using namespace std; +using namespace power_victron; + +void usage() +{ + cout << "Usage: power_victron_read_battery
" << endl; +} + +int main(int argc, char** argv) +{ + if (argc < 4) { + usage(); + return 1; + } + + string ip = argv[1]; + string port = argv[2]; + int address = stoi(argv[3]); + + Driver driver; + driver.setAddress(address); + try { + driver.openURI("tcp://" + ip + ":" + port); + } + catch (const std::exception& e) { + cerr << "Error opening connection: " << e.what() << endl; + return 1; + } + + while (true) { + try { + BatteryData data = driver.readBatteryData(); + cout << "\033[2J\033[H"; + cout << "--- Battery Data (Address 226) ---" << endl; + cout << "alarms: " << data.alarms << endl; + cout << "battery_power: " << data.battery_power << endl; + cout << "battery_voltage: " << data.battery_voltage << endl; + cout << "starter_battery_voltage: " << data.starter_battery_voltage << endl; + cout << "current: " << data.current << endl; + cout << "battery_temperature: " << data.battery_temperature.getCelsius() + << endl; + cout << "mid_point_voltage: " << data.mid_point_voltage << endl; + cout << "mid_point_deviation: " << data.mid_point_deviation << endl; + cout << "consumed_amphours: " << data.consumed_amphours << endl; + cout << "state_of_charge: " << data.state_of_charge << endl; + cout << "relay_status: " << data.relay_status << endl; + cout << "deepest_discharge: " << data.deepest_discharge << endl; + cout << "last_discharge: " << data.last_discharge << endl; + cout << "average_discharge: " << data.average_discharge << endl; + cout << "charge_cycles: " << data.charge_cycles << endl; + cout << "full_discharges: " << data.full_discharges << endl; + cout << "total_ah_drawn: " << data.total_ah_drawn << endl; + cout << "minimum_voltage: " << data.minimum_voltage << endl; + cout << "maximum_voltage: " << data.maximum_voltage << endl; + cout << "time_since_last_full_charge: " + << data.time_since_last_full_charge.toSeconds() << endl; + cout << "automatic_syncs: " << data.automatic_syncs << endl; + cout << "low_voltage_alarms: " << data.low_voltage_alarms << endl; + cout << "high_voltage_alarms: " << data.high_voltage_alarms << endl; + cout << "low_starter_voltage_alarms: " << data.low_starter_voltage_alarms + << endl; + cout << "high_starter_voltage_alarms: " << data.high_starter_voltage_alarms + << endl; + cout << "minimum_starter_voltage: " << data.minimum_starter_voltage << endl; + cout << "maximum_starter_voltage: " << data.maximum_starter_voltage << endl; + cout << "low_fused_voltage_alarms: " << data.low_fused_voltage_alarms << endl; + cout << "high_fused_voltage_alarms: " << data.high_fused_voltage_alarms + << endl; + cout << "minimum_fused_voltage: " << data.minimum_fused_voltage << endl; + cout << "maximum_fused_voltage: " << data.maximum_fused_voltage << endl; + cout << "discharged_energy: " << data.discharged_energy << endl; + cout << "charged_energy: " << data.charged_energy << endl; + cout << "time_to_go: " << data.time_to_go.toSeconds() << endl; + cout << "state_of_health: " << data.state_of_health << endl; + cout << "max_charge_voltage: " << data.max_charge_voltage << endl; + cout << "min_discharge_voltage: " << data.min_discharge_voltage << endl; + cout << "max_charge_current: " << data.max_charge_current << endl; + cout << "max_discharge_current: " << data.max_discharge_current << endl; + cout << "capacity: " << data.capacity << endl; + cout << "diagnostics_errors: " << endl; + for (size_t i = 0; i < data.diagnostics_errors.size(); ++i) { + cout << " [" << i + << "] Time: " << data.diagnostics_errors[i].time.toSeconds() + << " Error: " << static_cast(data.diagnostics_errors[i].error) + << endl; + } + cout << "minimum_cell_temperature: " + << data.minimum_cell_temperature.getCelsius() << endl; + cout << "maximum_cell_temperature: " + << data.maximum_cell_temperature.getCelsius() << endl; + cout << "consumed_amphours_1: " << data.consumed_amphours_1 << endl; + cout << "state: " << data.state << endl; + cout << "error_code: " << static_cast(data.error_code) << endl; + cout << std::boolalpha; + cout << "system_switch: " << data.system_switch << endl; + cout << "balancing: " << data.balancing << endl; + cout << std::noboolalpha; + cout << "system_minimum_cell_voltage: " << data.system_minimum_cell_voltage + << endl; + cout << "system_maximum_cell_voltage: " << data.system_maximum_cell_voltage + << endl; + cout << "diagnostics_shutdowns_due_to_error: " + << data.diagnostics_shutdowns_due_to_error << endl; + cout << "historical_min_cell_voltage: " << data.historical_min_cell_voltage << endl; + cout << "historical_max_cell_voltage: " << data.historical_max_cell_voltage << endl; + cout << "mode: " << static_cast(data.mode) << endl; + cout << "balancer_status: " << static_cast(data.balancer_status) << endl; + cout << "connection_information: " << data.connection_information << endl; + } + catch (const std::exception& e) { + cerr << "Error during Modbus operation: " << e.what() << endl; + } + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + } + + return 0; +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ffc9fa5..b42d3df 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,3 +1,3 @@ -rock_testsuite(test_suite suite.cpp - test_Dummy.cpp +rock_gtest(test_suite suite.cpp + test_Driver.cpp DEPS power_victron) diff --git a/test/Helpers.hpp b/test/Helpers.hpp new file mode 100644 index 0000000..d9d261e --- /dev/null +++ b/test/Helpers.hpp @@ -0,0 +1,103 @@ +#ifndef POWER_VICTRON_HELPERS_HPP +#define POWER_VICTRON_HELPERS_HPP + +#include +#include + +namespace power_victron { + +template +struct Helpers { + Test& test; + int transactionID = 0; + + Helpers(Test& test); + + void EXPECT_MODBUS_WRITE(int address, int registerID, uint16_t value); + void EXPECT_MODBUS_WRITE_COIL(int address, int registerID, bool value); + void EXPECT_MODBUS_READ( + int address, bool input, + int register_id, std::vector expected_values + ); + + int allocateTransactionID(); +}; + +template +Helpers::Helpers(Test& test) + : test(test) { +} + +template +int Helpers::allocateTransactionID() { + return 0xAA00 | ++transactionID; +} + +template +void Helpers::EXPECT_MODBUS_WRITE( + int address, int registerID, uint16_t value +) { + int transactionID = allocateTransactionID(); + + uint8_t requestFrame[256]; + uint8_t* requestEnd = modbus::TCP::formatWriteRegister( + requestFrame, transactionID, address, registerID, value + ); + + uint8_t responseFrame[256]; + uint8_t* responseEnd = modbus::TCP::formatFrame( + responseFrame, transactionID, address, modbus::FUNCTION_WRITE_SINGLE_REGISTER, + nullptr, nullptr + ); + + test.EXPECT_REPLY(std::vector(requestFrame, requestEnd), + std::vector(responseFrame, responseEnd)); +} + +template +void Helpers::EXPECT_MODBUS_WRITE_COIL( + int address, int registerID, bool value +) { + int transactionID = allocateTransactionID(); + + uint8_t requestFrame[256]; + uint8_t* requestEnd = modbus::TCP::formatWriteSingleCoil( + requestFrame, transactionID, address, registerID, value + ); + + test.EXPECT_REPLY(std::vector(requestFrame, requestEnd), + std::vector(requestFrame, requestEnd)); +} + +template +void Helpers::EXPECT_MODBUS_READ( + int address, bool input, int start, std::vector expected_values +) { + int transactionID = allocateTransactionID(); + + uint8_t requestFrame[256]; + uint8_t* requestEnd = modbus::TCP::formatReadRegisters( + requestFrame, transactionID, address, input, start, expected_values.size() + ); + + uint8_t responseFrame[256]; + uint8_t size_in_bytes = expected_values.size() * 2; + std::vector responsePayload{ size_in_bytes }; + for (auto v : expected_values) { + responsePayload.push_back((v >> 8) & 0xFF); + responsePayload.push_back(v & 0xFF); + } + int function = input ? modbus::FUNCTION_READ_INPUT_REGISTERS : + modbus::FUNCTION_READ_HOLDING_REGISTERS; + uint8_t* responseEnd = modbus::TCP::formatFrame( + responseFrame, transactionID, address, function, + &responsePayload[0], &responsePayload[0] + responsePayload.size() + ); + + test.EXPECT_REPLY(std::vector(requestFrame, requestEnd), + std::vector(responseFrame, responseEnd)); +} + +} + +#endif diff --git a/test/suite.cpp b/test/suite.cpp index af38cdc..4d820af 100644 --- a/test/suite.cpp +++ b/test/suite.cpp @@ -1,5 +1,6 @@ -// Do NOT add anything to this file -// This header from boost takes ages to compile, so we make sure it is compiled -// only once (here) -#define BOOST_TEST_MAIN -#include +#include + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/test_Driver.cpp b/test/test_Driver.cpp new file mode 100644 index 0000000..a8901ca --- /dev/null +++ b/test/test_Driver.cpp @@ -0,0 +1,197 @@ +#include "Helpers.hpp" +#include +#include +#include + +using namespace power_victron; + +struct DriverTest : public testing::Test, + public iodrivers_base::Fixture, + public Helpers { + DriverTest() + : Helpers(*this) + { + } +}; + +TEST_F(DriverTest, it_reads_battery_data) +{ + IODRIVERS_BASE_MOCK(); + driver.setAddress(12); + + EXPECT_MODBUS_READ(12, true, BatteryRegisters::LOW_VOLTAGE_ALARM, {0}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::HIGH_VOLTAGE_ALARM, {2}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::LOW_STARTER_VOLTAGE_ALARM, {0}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::HIGH_STARTER_VOLTAGE_ALARM, {2}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::LOW_STATE_OF_CHARGE_ALARM, {0}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::LOW_TEMPERATURE_ALARM, {2}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::HIGH_TEMPERATURE_ALARM, {0}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::MID_VOLTAGE_ALARM, {2}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::LOW_FUSED_VOLTAGE_ALARM, {0}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::HIGH_FUSED_VOLTAGE_ALARM, {2}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::FUSE_BLOWN_ALARM, {0}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::HIGH_INTERNAL_TEMPERATURE_ALARM, {2}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::HIGH_CHARGE_CURRENT_ALARM, {0}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::HIGH_DISCHARGE_CURRENT_ALARM, {2}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::CELL_IMBALANCE_ALARM, {0}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::INTERNAL_FAILURE_ALARM, {2}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::HIGH_CHARGE_TEMPERATURE_ALARM, {0}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::LOW_CHARGE_TEMPERATURE_ALARM, {2}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::LOW_CELL_VOLTAGE_ALARM, {0}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::BMS_CABLE_ALARM, {2}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::BMS_CONTACTOR_RESISTANCE_ALARM, {0}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::BMS_HIGH_CURRENT_ALARM, {2}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::BATTERY_POWER, {0, 23}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::BATTERY_VOLTAGE, {24}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::STARTER_BATTERY_VOLTAGE, {25}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::CURRENT, {26}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::BATTERY_TEMPERATURE, {27}); + EXPECT_MODBUS_READ(12, + true, + BatteryRegisters::MID_POINT_VOLTAGE_OF_THE_BATTERY_BANK, + {28}); + EXPECT_MODBUS_READ(12, + true, + BatteryRegisters::MID_POINT_DEVIATION_OF_THE_BATTERY_BANK, + {29}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::CONSUMED_AMPHOURS, {30}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::STATE_OF_CHARGE, {31}); + EXPECT_MODBUS_READ(12, false, BatteryRegisters::RELAY_STATUS, {32}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::DEEPEST_DISCHARGE, {33}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::LAST_DISCHARGE, {34}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::AVERAGE_DISCHARGE, {35}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::CHARGE_CYCLES, {36}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::FULL_DISCHARGES, {37}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::TOTAL_AH_DRAWN, {38}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::MINIMUM_VOLTAGE, {39}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::MAXIMUM_VOLTAGE, {40}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::TIME_SINCE_LAST_FULL_CHARGE, {41}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::AUTOMATIC_SYNCS, {42}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::LOW_VOLTAGE_ALARMS, {43}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::HIGH_VOLTAGE_ALARMS, {44}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::LOW_STARTER_VOLTAGE_ALARMS, {45}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::HIGH_STARTER_VOLTAGE_ALARMS, {46}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::MINIMUM_STARTER_VOLTAGE, {47}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::MAXIMUM_STARTER_VOLTAGE, {48}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::LOW_FUSED_VOLTAGE_ALARMS, {49}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::HIGH_FUSED_VOLTAGE_ALARMS, {50}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::MINIMUM_FUSED_VOLTAGE, {51}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::MAXIMUM_FUSED_VOLTAGE, {52}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::DISCHARGED_ENERGY, {53}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::CHARGED_ENERGY, {54}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::TIME_TO_GO, {55}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::STATE_OF_HEALTH, {56}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::MAX_CHARGE_VOLTAGE, {57}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::MIN_DISCHARGE_VOLTAGE, {58}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::MAX_CHARGE_CURRENT, {59}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::MAX_DISCHARGE_CURRENT, {60}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::CAPACITY, {61}); + EXPECT_MODBUS_READ(12, + true, + BatteryRegisters::DIAGNOSTICS_1ST_LAST_ERROR_TIMESTAMP, + {0, 62}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::DIAGNOSTICS_1ST_LAST_ERROR, {63}); + EXPECT_MODBUS_READ(12, + true, + BatteryRegisters::DIAGNOSTICS_2ND_LAST_ERROR_TIMESTAMP, + {0, 64}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::DIAGNOSTICS_2ND_LAST_ERROR, {65}); + EXPECT_MODBUS_READ(12, + true, + BatteryRegisters::DIAGNOSTICS_3RD_LAST_ERROR_TIMESTAMP, + {0, 66}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::DIAGNOSTICS_3RD_LAST_ERROR, {67}); + EXPECT_MODBUS_READ(12, + true, + BatteryRegisters::DIAGNOSTICS_4TH_LAST_ERROR_TIMESTAMP, + {0, 68}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::DIAGNOSTICS_4TH_LAST_ERROR, {69}); + + EXPECT_MODBUS_READ(12, true, BatteryRegisters::MINIMUM_CELL_TEMPERATURE, {70}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::MAXIMUM_CELL_TEMPERATURE, {71}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::CONSUMED_AMPHOURS_2, {0, 72}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::STATE, {73}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::ERROR, {74}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::SYSTEM_SWITCH, {75}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::BALANCING, {76}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::SYSTEM_MINIMUM_CELL_VOLTAGE, {77}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::SYSTEM_MAXIMUM_CELL_VOLTAGE, {78}); + EXPECT_MODBUS_READ(12, + true, + BatteryRegisters::DIAGNOSTICS_SHUTDOWNS_DUE_TO_ERROR, + {79}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::HISTORY_MIN_CELL_VOLTAGE, {80}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::HISTORY_MAX_CELL_VOLTAGE, {81}); + EXPECT_MODBUS_READ(12, false, BatteryRegisters::MODE, {82}); + EXPECT_MODBUS_READ(12, true, BatteryRegisters::BALANCER_STATUS, {83}); + + BatteryData data = driver.readBatteryData(); + + ASSERT_EQ(23, data.battery_power); + ASSERT_NEAR(0.24, data.battery_voltage, 1e-3); + ASSERT_NEAR(0.25, data.starter_battery_voltage, 1e-3); + ASSERT_NEAR(2.6, data.current, 1e-3); + ASSERT_NEAR(2.7, data.battery_temperature.getCelsius(), 1e-3); + ASSERT_NEAR(0.28, data.mid_point_voltage, 1e-3); + ASSERT_NEAR(0.29, data.mid_point_deviation, 1e-3); + ASSERT_NEAR(300.0, data.consumed_amphours, 1e-3); + ASSERT_NEAR(3.1, data.state_of_charge, 1e-3); + ASSERT_EQ(32, data.relay_status); + ASSERT_NEAR(330.0, data.deepest_discharge, 1e-3); + ASSERT_NEAR(340.0, data.last_discharge, 1e-3); + ASSERT_NEAR(350.0, data.average_discharge, 1e-3); + ASSERT_EQ(36, data.charge_cycles); + ASSERT_EQ(37, data.full_discharges); + ASSERT_NEAR(380.0, data.total_ah_drawn, 1e-3); + ASSERT_NEAR(0.39, data.minimum_voltage, 1e-3); + ASSERT_NEAR(0.40, data.maximum_voltage, 1e-3); + ASSERT_NEAR(4100.0, data.time_since_last_full_charge.toSeconds(), 1e-3); + ASSERT_EQ(42, data.automatic_syncs); + ASSERT_EQ(43, data.low_voltage_alarms); + ASSERT_EQ(44, data.high_voltage_alarms); + ASSERT_EQ(45, data.low_starter_voltage_alarms); + ASSERT_EQ(46, data.high_starter_voltage_alarms); + ASSERT_NEAR(0.47, data.minimum_starter_voltage, 1e-3); + ASSERT_NEAR(0.48, data.maximum_starter_voltage, 1e-3); + ASSERT_EQ(49, data.low_fused_voltage_alarms); + ASSERT_EQ(50, data.high_fused_voltage_alarms); + ASSERT_NEAR(0.51, data.minimum_fused_voltage, 1e-3); + ASSERT_NEAR(0.52, data.maximum_fused_voltage, 1e-3); + ASSERT_NEAR(5.3, data.discharged_energy, 1e-3); + ASSERT_NEAR(5.4, data.charged_energy, 1e-3); + ASSERT_NEAR(5500.0, data.time_to_go.toSeconds(), 1e-3); + ASSERT_NEAR(5.6, data.state_of_health, 1e-3); + ASSERT_NEAR(5.7, data.max_charge_voltage, 1e-3); + ASSERT_NEAR(5.8, data.min_discharge_voltage, 1e-3); + ASSERT_NEAR(5.9, data.max_charge_current, 1e-3); + ASSERT_NEAR(6.0, data.max_discharge_current, 1e-3); + ASSERT_NEAR(6.1, data.capacity, 1e-3); + + ASSERT_EQ(base::Time::fromSeconds(62), data.diagnostics_errors[0].time); + ASSERT_EQ(static_cast(63), + data.diagnostics_errors[0].error); + ASSERT_EQ(base::Time::fromSeconds(64), data.diagnostics_errors[1].time); + ASSERT_EQ(static_cast(65), + data.diagnostics_errors[1].error); + ASSERT_EQ(base::Time::fromSeconds(66), data.diagnostics_errors[2].time); + ASSERT_EQ(static_cast(67), + data.diagnostics_errors[2].error); + ASSERT_EQ(base::Time::fromSeconds(68), data.diagnostics_errors[3].time); + ASSERT_EQ(static_cast(69), + data.diagnostics_errors[3].error); + + ASSERT_NEAR(7.0, data.minimum_cell_temperature.getCelsius(), 1e-3); + ASSERT_NEAR(7.1, data.maximum_cell_temperature.getCelsius(), 1e-3); + ASSERT_NEAR(72.0, data.consumed_amphours_1, 1e-3); + ASSERT_EQ(static_cast(73), data.state); + ASSERT_EQ(static_cast(74), data.error_code); + ASSERT_EQ(75 == 1, data.system_switch); + ASSERT_EQ(76 == 1, data.balancing); + ASSERT_NEAR(0.77, data.system_minimum_cell_voltage, 1e-3); + ASSERT_NEAR(0.78, data.system_maximum_cell_voltage, 1e-3); + ASSERT_EQ(79, data.diagnostics_shutdowns_due_to_error); + ASSERT_NEAR(0.80, data.historical_min_cell_voltage, 1e-3); + ASSERT_NEAR(0.81, data.historical_max_cell_voltage, 1e-3); + ASSERT_EQ(static_cast(82), data.mode); + ASSERT_EQ(static_cast(83), data.balancer_status); +} diff --git a/test/test_Dummy.cpp b/test/test_Dummy.cpp deleted file mode 100644 index 11e4fa6..0000000 --- a/test/test_Dummy.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include -#include - -using namespace power_victron; - -BOOST_AUTO_TEST_CASE(it_should_not_crash_when_welcome_is_called) -{ - power_victron::DummyClass dummy; - dummy.welcome(); -}