diff --git a/include/BatteryCanReceiver.h b/include/BatteryCanReceiver.h new file mode 100644 index 00000000..cf3864a8 --- /dev/null +++ b/include/BatteryCanReceiver.h @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + +#include "Battery.h" +#include +#include + +class BatteryCanReceiver : public BatteryProvider { +public: + bool init(bool verboseLogging, char const* providerName); + void deinit() final; + void loop() final; + + virtual void onMessage(twai_message_t rx_message) = 0; + +protected: + uint16_t readUnsignedInt16(uint8_t *data); + int16_t readSignedInt16(uint8_t *data); + float scaleValue(int16_t value, float factor); + bool getBit(uint8_t value, uint8_t bit); + + bool _verboseLogging = true; + +private: + char const* _providerName = "Battery CAN"; +}; diff --git a/include/PylontechCanReceiver.h b/include/PylontechCanReceiver.h index 2b2b922d..24bae8c0 100644 --- a/include/PylontechCanReceiver.h +++ b/include/PylontechCanReceiver.h @@ -3,27 +3,20 @@ #include "Configuration.h" #include "Battery.h" -#include +#include "BatteryCanReceiver.h" #include #include -#include -class PylontechCanReceiver : public BatteryProvider { +class PylontechCanReceiver : public BatteryCanReceiver { public: bool init(bool verboseLogging) final; - void deinit() final; - void loop() final; + void onMessage(twai_message_t rx_message) final; + std::shared_ptr getStats() const final { return _stats; } private: - uint16_t readUnsignedInt16(uint8_t *data); - int16_t readSignedInt16(uint8_t *data); - float scaleValue(int16_t value, float factor); - bool getBit(uint8_t value, uint8_t bit); - void dummyData(); - bool _verboseLogging = true; std::shared_ptr _stats = std::make_shared(); }; diff --git a/src/BatteryCanReceiver.cpp b/src/BatteryCanReceiver.cpp new file mode 100644 index 00000000..c780b927 --- /dev/null +++ b/src/BatteryCanReceiver.cpp @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include "BatteryCanReceiver.h" +#include "MessageOutput.h" +#include "PinMapping.h" +#include + +bool BatteryCanReceiver::init(bool verboseLogging, char const* providerName) +{ + _verboseLogging = verboseLogging; + _providerName = providerName; + + MessageOutput.printf("[%s] Initialize interface...\r\n", + _providerName); + + const PinMapping_t& pin = PinMapping.get(); + MessageOutput.printf("[%s] Interface rx = %d, tx = %d\r\n", + _providerName, pin.battery_rx, pin.battery_tx); + + if (pin.battery_rx < 0 || pin.battery_tx < 0) { + MessageOutput.printf("[%s] Invalid pin config\r\n", + _providerName); + return false; + } + + auto tx = static_cast(pin.battery_tx); + auto rx = static_cast(pin.battery_rx); + twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(tx, rx, TWAI_MODE_NORMAL); + + // Initialize configuration structures using macro initializers + twai_timing_config_t t_config = TWAI_TIMING_CONFIG_500KBITS(); + twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL(); + + // Install TWAI driver + esp_err_t twaiLastResult = twai_driver_install(&g_config, &t_config, &f_config); + switch (twaiLastResult) { + case ESP_OK: + MessageOutput.printf("[%s] Twai driver installed\r\n", + _providerName); + break; + case ESP_ERR_INVALID_ARG: + MessageOutput.printf("[%s] Twai driver install - invalid arg\r\n", + _providerName); + return false; + break; + case ESP_ERR_NO_MEM: + MessageOutput.printf("[%s] Twai driver install - no memory\r\n", + _providerName); + return false; + break; + case ESP_ERR_INVALID_STATE: + MessageOutput.printf("[%s] Twai driver install - invalid state\r\n", + _providerName); + return false; + break; + } + + // Start TWAI driver + twaiLastResult = twai_start(); + switch (twaiLastResult) { + case ESP_OK: + MessageOutput.printf("[%s] Twai driver started\r\n", + _providerName); + break; + case ESP_ERR_INVALID_STATE: + MessageOutput.printf("[%s] Twai driver start - invalid state\r\n", + _providerName); + return false; + break; + } + + return true; +} + +void BatteryCanReceiver::deinit() +{ + // Stop TWAI driver + esp_err_t twaiLastResult = twai_stop(); + switch (twaiLastResult) { + case ESP_OK: + MessageOutput.printf("[%s] Twai driver stopped\r\n", + _providerName); + break; + case ESP_ERR_INVALID_STATE: + MessageOutput.printf("[%s] Twai driver stop - invalid state\r\n", + _providerName); + break; + } + + // Uninstall TWAI driver + twaiLastResult = twai_driver_uninstall(); + switch (twaiLastResult) { + case ESP_OK: + MessageOutput.printf("[%s] Twai driver uninstalled\r\n", + _providerName); + break; + case ESP_ERR_INVALID_STATE: + MessageOutput.printf("[%s] Twai driver uninstall - invalid state\r\n", + _providerName); + break; + } +} + +void BatteryCanReceiver::loop() +{ + // Check for messages. twai_receive is blocking when there is no data so we return if there are no frames in the buffer + twai_status_info_t status_info; + esp_err_t twaiLastResult = twai_get_status_info(&status_info); + if (twaiLastResult != ESP_OK) { + switch (twaiLastResult) { + case ESP_ERR_INVALID_ARG: + MessageOutput.printf("[%s] Twai driver get status - invalid arg\r\n", + _providerName); + break; + case ESP_ERR_INVALID_STATE: + MessageOutput.printf("[%s] Twai driver get status - invalid state\r\n", + _providerName); + break; + } + return; + } + if (status_info.msgs_to_rx == 0) { + return; + } + + // Wait for message to be received, function is blocking + twai_message_t rx_message; + if (twai_receive(&rx_message, pdMS_TO_TICKS(100)) != ESP_OK) { + MessageOutput.printf("[%s] Failed to receive message", + _providerName); + return; + } + + onMessage(rx_message); +} + +uint16_t BatteryCanReceiver::readUnsignedInt16(uint8_t *data) +{ + uint8_t bytes[2]; + bytes[0] = *data; + bytes[1] = *(data + 1); + return (bytes[1] << 8) + bytes[0]; +} + +int16_t BatteryCanReceiver::readSignedInt16(uint8_t *data) +{ + return this->readUnsignedInt16(data); +} + +float BatteryCanReceiver::scaleValue(int16_t value, float factor) +{ + return value * factor; +} + +bool BatteryCanReceiver::getBit(uint8_t value, uint8_t bit) +{ + return (value & (1 << bit)) >> bit; +} diff --git a/src/PylontechCanReceiver.cpp b/src/PylontechCanReceiver.cpp index e19cff59..f2ed9a51 100644 --- a/src/PylontechCanReceiver.cpp +++ b/src/PylontechCanReceiver.cpp @@ -1,127 +1,18 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "PylontechCanReceiver.h" -#include "Configuration.h" #include "MessageOutput.h" #include "PinMapping.h" #include #include -//#define PYLONTECH_DUMMY - bool PylontechCanReceiver::init(bool verboseLogging) { - _verboseLogging = verboseLogging; - - MessageOutput.println("[Pylontech] Initialize interface..."); - - const PinMapping_t& pin = PinMapping.get(); - MessageOutput.printf("[Pylontech] Interface rx = %d, tx = %d\r\n", - pin.battery_rx, pin.battery_tx); - - if (pin.battery_rx < 0 || pin.battery_tx < 0) { - MessageOutput.println("[Pylontech] Invalid pin config"); - return false; - } - - auto tx = static_cast(pin.battery_tx); - auto rx = static_cast(pin.battery_rx); - twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(tx, rx, TWAI_MODE_NORMAL); - - // Initialize configuration structures using macro initializers - twai_timing_config_t t_config = TWAI_TIMING_CONFIG_500KBITS(); - twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL(); - - // Install TWAI driver - esp_err_t twaiLastResult = twai_driver_install(&g_config, &t_config, &f_config); - switch (twaiLastResult) { - case ESP_OK: - MessageOutput.println("[Pylontech] Twai driver installed"); - break; - case ESP_ERR_INVALID_ARG: - MessageOutput.println("[Pylontech] Twai driver install - invalid arg"); - return false; - break; - case ESP_ERR_NO_MEM: - MessageOutput.println("[Pylontech] Twai driver install - no memory"); - return false; - break; - case ESP_ERR_INVALID_STATE: - MessageOutput.println("[Pylontech] Twai driver install - invalid state"); - return false; - break; - } - - // Start TWAI driver - twaiLastResult = twai_start(); - switch (twaiLastResult) { - case ESP_OK: - MessageOutput.println("[Pylontech] Twai driver started"); - break; - case ESP_ERR_INVALID_STATE: - MessageOutput.println("[Pylontech] Twai driver start - invalid state"); - return false; - break; - } - - return true; + return BatteryCanReceiver::init(verboseLogging, "Pylontech"); } -void PylontechCanReceiver::deinit() + +void PylontechCanReceiver::onMessage(twai_message_t rx_message) { - // Stop TWAI driver - esp_err_t twaiLastResult = twai_stop(); - switch (twaiLastResult) { - case ESP_OK: - MessageOutput.println("[Pylontech] Twai driver stopped"); - break; - case ESP_ERR_INVALID_STATE: - MessageOutput.println("[Pylontech] Twai driver stop - invalid state"); - break; - } - - // Uninstall TWAI driver - twaiLastResult = twai_driver_uninstall(); - switch (twaiLastResult) { - case ESP_OK: - MessageOutput.println("[Pylontech] Twai driver uninstalled"); - break; - case ESP_ERR_INVALID_STATE: - MessageOutput.println("[Pylontech] Twai driver uninstall - invalid state"); - break; - } -} - -void PylontechCanReceiver::loop() -{ -#ifdef PYLONTECH_DUMMY - return dummyData(); -#endif - - // Check for messages. twai_receive is blocking when there is no data so we return if there are no frames in the buffer - twai_status_info_t status_info; - esp_err_t twaiLastResult = twai_get_status_info(&status_info); - if (twaiLastResult != ESP_OK) { - switch (twaiLastResult) { - case ESP_ERR_INVALID_ARG: - MessageOutput.println("[Pylontech] Twai driver get status - invalid arg"); - break; - case ESP_ERR_INVALID_STATE: - MessageOutput.println("[Pylontech] Twai driver get status - invalid state"); - break; - } - return; - } - if (status_info.msgs_to_rx == 0) { - return; - } - - // Wait for message to be received, function is blocking - twai_message_t rx_message; - if (twai_receive(&rx_message, pdMS_TO_TICKS(100)) != ESP_OK) { - MessageOutput.println("[Pylontech] Failed to receive message"); - return; - } - switch (rx_message.identifier) { case 0x351: { _stats->_chargeVoltage = this->scaleValue(this->readUnsignedInt16(rx_message.data), 0.1); @@ -129,7 +20,7 @@ void PylontechCanReceiver::loop() _stats->_dischargeCurrentLimitation = this->scaleValue(this->readSignedInt16(rx_message.data + 4), 0.1); if (_verboseLogging) { - MessageOutput.printf("[Pylontech] chargeVoltage: %f chargeCurrentLimitation: %f dischargeCurrentLimitation: %f\n", + MessageOutput.printf("[Pylontech] chargeVoltage: %f chargeCurrentLimitation: %f dischargeCurrentLimitation: %f\r\n", _stats->_chargeVoltage, _stats->_chargeCurrentLimitation, _stats->_dischargeCurrentLimitation); } break; @@ -140,7 +31,7 @@ void PylontechCanReceiver::loop() _stats->_stateOfHealth = this->readUnsignedInt16(rx_message.data + 2); if (_verboseLogging) { - MessageOutput.printf("[Pylontech] soc: %d soh: %d\n", + MessageOutput.printf("[Pylontech] soc: %d soh: %d\r\n", _stats->getSoC(), _stats->_stateOfHealth); } break; @@ -152,7 +43,7 @@ void PylontechCanReceiver::loop() _stats->_temperature = this->scaleValue(this->readSignedInt16(rx_message.data + 4), 0.1); if (_verboseLogging) { - MessageOutput.printf("[Pylontech] voltage: %f current: %f temperature: %f\n", + MessageOutput.printf("[Pylontech] voltage: %f current: %f temperature: %f\r\n", _stats->getVoltage(), _stats->_current, _stats->_temperature); } break; @@ -171,7 +62,7 @@ void PylontechCanReceiver::loop() _stats->_alarmOverCurrentCharge = this->getBit(alarmBits, 0); if (_verboseLogging) { - MessageOutput.printf("[Pylontech] Alarms: %d %d %d %d %d %d %d\n", + MessageOutput.printf("[Pylontech] Alarms: %d %d %d %d %d %d %d\r\n", _stats->_alarmOverCurrentDischarge, _stats->_alarmUnderTemperature, _stats->_alarmOverTemperature, @@ -193,7 +84,7 @@ void PylontechCanReceiver::loop() _stats->_warningHighCurrentCharge = this->getBit(warningBits, 0); if (_verboseLogging) { - MessageOutput.printf("[Pylontech] Warnings: %d %d %d %d %d %d %d\n", + MessageOutput.printf("[Pylontech] Warnings: %d %d %d %d %d %d %d\r\n", _stats->_warningHighCurrentDischarge, _stats->_warningLowTemperature, _stats->_warningHighTemperature, @@ -212,7 +103,7 @@ void PylontechCanReceiver::loop() if (manufacturer.isEmpty()) { break; } if (_verboseLogging) { - MessageOutput.printf("[Pylontech] Manufacturer: %s\n", manufacturer.c_str()); + MessageOutput.printf("[Pylontech] Manufacturer: %s\r\n", manufacturer.c_str()); } _stats->setManufacturer(std::move(manufacturer)); @@ -226,7 +117,7 @@ void PylontechCanReceiver::loop() _stats->_chargeImmediately = this->getBit(chargeStatusBits, 5); if (_verboseLogging) { - MessageOutput.printf("[Pylontech] chargeStatusBits: %d %d %d\n", + MessageOutput.printf("[Pylontech] chargeStatusBits: %d %d %d\r\n", _stats->_chargeEnabled, _stats->_dischargeEnabled, _stats->_chargeImmediately); @@ -243,29 +134,7 @@ void PylontechCanReceiver::loop() _stats->setLastUpdate(millis()); } -uint16_t PylontechCanReceiver::readUnsignedInt16(uint8_t *data) -{ - uint8_t bytes[2]; - bytes[0] = *data; - bytes[1] = *(data + 1); - return (bytes[1] << 8) + bytes[0]; -} - -int16_t PylontechCanReceiver::readSignedInt16(uint8_t *data) -{ - return this->readUnsignedInt16(data); -} - -float PylontechCanReceiver::scaleValue(int16_t value, float factor) -{ - return value * factor; -} - -bool PylontechCanReceiver::getBit(uint8_t value, uint8_t bit) -{ - return (value & (1 << bit)) >> bit; -} - +// Currently not called because there is no nice way to integrate it right now #ifdef PYLONTECH_DUMMY void PylontechCanReceiver::dummyData() {