refactor PylontechCanBattery to be more generic (#1064)
This commit is contained in:
parent
cb0f8f20a8
commit
79214d750f
26
include/BatteryCanReceiver.h
Normal file
26
include/BatteryCanReceiver.h
Normal file
@ -0,0 +1,26 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#pragma once
|
||||
|
||||
#include "Battery.h"
|
||||
#include <driver/twai.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
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";
|
||||
};
|
||||
@ -3,27 +3,20 @@
|
||||
|
||||
#include "Configuration.h"
|
||||
#include "Battery.h"
|
||||
#include <espMqttClient.h>
|
||||
#include "BatteryCanReceiver.h"
|
||||
#include <driver/twai.h>
|
||||
#include <Arduino.h>
|
||||
#include <memory>
|
||||
|
||||
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<BatteryStats> 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<PylontechBatteryStats> _stats =
|
||||
std::make_shared<PylontechBatteryStats>();
|
||||
};
|
||||
|
||||
157
src/BatteryCanReceiver.cpp
Normal file
157
src/BatteryCanReceiver.cpp
Normal file
@ -0,0 +1,157 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#include "BatteryCanReceiver.h"
|
||||
#include "MessageOutput.h"
|
||||
#include "PinMapping.h"
|
||||
#include <driver/twai.h>
|
||||
|
||||
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<gpio_num_t>(pin.battery_tx);
|
||||
auto rx = static_cast<gpio_num_t>(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;
|
||||
}
|
||||
@ -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 <driver/twai.h>
|
||||
#include <ctime>
|
||||
|
||||
//#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<gpio_num_t>(pin.battery_tx);
|
||||
auto rx = static_cast<gpio_num_t>(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()
|
||||
{
|
||||
|
||||
Loading…
Reference in New Issue
Block a user