Add a HoymilesRadio base class

This enables to have multiple radio implementations while the inverter classes just refere to the base class
This commit is contained in:
Thomas Basler 2023-03-06 22:16:34 +01:00
parent a7e9aaa862
commit 8404dd57a7
13 changed files with 352 additions and 328 deletions

View File

@ -22,20 +22,20 @@ void HoymilesClass::init(SPIClass* initialisedSpiBus, uint8_t pinCE, uint8_t pin
HOY_SEMAPHORE_GIVE(); // release before first use HOY_SEMAPHORE_GIVE(); // release before first use
_pollInterval = 0; _pollInterval = 0;
_radio.reset(new HoymilesRadio()); _radioNrf.reset(new HoymilesRadio_NRF());
_radio->init(initialisedSpiBus, pinCE, pinIRQ); _radioNrf->init(initialisedSpiBus, pinCE, pinIRQ);
} }
void HoymilesClass::loop() void HoymilesClass::loop()
{ {
HOY_SEMAPHORE_TAKE(); HOY_SEMAPHORE_TAKE();
_radio->loop(); _radioNrf->loop();
if (getNumInverters() > 0) { if (getNumInverters() > 0) {
if (millis() - _lastPoll > (_pollInterval * 1000)) { if (millis() - _lastPoll > (_pollInterval * 1000)) {
static uint8_t inverterPos = 0; static uint8_t inverterPos = 0;
if (_radio->isIdle()) { if (_radioNrf->isIdle()) {
std::shared_ptr<InverterAbstract> iv = getInverterByPos(inverterPos); std::shared_ptr<InverterAbstract> iv = getInverterByPos(inverterPos);
if (iv != nullptr) { if (iv != nullptr) {
_messageOutput->print("Fetch inverter: "); _messageOutput->print("Fetch inverter: ");
@ -89,17 +89,17 @@ std::shared_ptr<InverterAbstract> HoymilesClass::addInverter(const char* name, u
{ {
std::shared_ptr<InverterAbstract> i = nullptr; std::shared_ptr<InverterAbstract> i = nullptr;
if (HMS_4CH::isValidSerial(serial)) { if (HMS_4CH::isValidSerial(serial)) {
i = std::make_shared<HMS_4CH>(_radio.get(), serial); i = std::make_shared<HMS_4CH>(_radioNrf.get(), serial);
} else if (HMS_2CH::isValidSerial(serial)) { } else if (HMS_2CH::isValidSerial(serial)) {
i = std::make_shared<HMS_2CH>(_radio.get(), serial); i = std::make_shared<HMS_2CH>(_radioNrf.get(), serial);
} else if (HMS_1CH::isValidSerial(serial)) { } else if (HMS_1CH::isValidSerial(serial)) {
i = std::make_shared<HMS_1CH>(_radio.get(), serial); i = std::make_shared<HMS_1CH>(_radioNrf.get(), serial);
} else if (HM_4CH::isValidSerial(serial)) { } else if (HM_4CH::isValidSerial(serial)) {
i = std::make_shared<HM_4CH>(_radio.get(), serial); i = std::make_shared<HM_4CH>(_radioNrf.get(), serial);
} else if (HM_2CH::isValidSerial(serial)) { } else if (HM_2CH::isValidSerial(serial)) {
i = std::make_shared<HM_2CH>(_radio.get(), serial); i = std::make_shared<HM_2CH>(_radioNrf.get(), serial);
} else if (HM_1CH::isValidSerial(serial)) { } else if (HM_1CH::isValidSerial(serial)) {
i = std::make_shared<HM_1CH>(_radio.get(), serial); i = std::make_shared<HM_1CH>(_radioNrf.get(), serial);
} }
if (i) { if (i) {
@ -171,9 +171,9 @@ size_t HoymilesClass::getNumInverters()
return _inverters.size(); return _inverters.size();
} }
HoymilesRadio* HoymilesClass::getRadio() HoymilesRadio_NRF* HoymilesClass::getRadioNrf()
{ {
return _radio.get(); return _radioNrf.get();
} }
uint32_t HoymilesClass::PollInterval() uint32_t HoymilesClass::PollInterval()

View File

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
#include "HoymilesRadio.h" #include "HoymilesRadio_NRF.h"
#include "inverters/InverterAbstract.h" #include "inverters/InverterAbstract.h"
#include "types.h" #include "types.h"
#include <Print.h> #include <Print.h>
@ -27,14 +27,14 @@ public:
void removeInverterBySerial(uint64_t serial); void removeInverterBySerial(uint64_t serial);
size_t getNumInverters(); size_t getNumInverters();
HoymilesRadio* getRadio(); HoymilesRadio_NRF* getRadioNrf();
uint32_t PollInterval(); uint32_t PollInterval();
void setPollInterval(uint32_t interval); void setPollInterval(uint32_t interval);
private: private:
std::vector<std::shared_ptr<InverterAbstract>> _inverters; std::vector<std::shared_ptr<InverterAbstract>> _inverters;
std::unique_ptr<HoymilesRadio> _radio; std::unique_ptr<HoymilesRadio_NRF> _radioNrf;
SemaphoreHandle_t _xSemaphore; SemaphoreHandle_t _xSemaphore;

View File

@ -1,158 +1,9 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* Copyright (C) 2022 Thomas Basler and others * Copyright (C) 2023 Thomas Basler and others
*/ */
#include "HoymilesRadio.h" #include "HoymilesRadio.h"
#include "Hoymiles.h" #include "Hoymiles.h"
#include "commands/RequestFrameCommand.h"
#include "crc.h"
#include <Every.h>
#include <FunctionalInterrupt.h>
void HoymilesRadio::init(SPIClass* initialisedSpiBus, uint8_t pinCE, uint8_t pinIRQ)
{
_dtuSerial.u64 = 0;
_spiPtr.reset(initialisedSpiBus);
_radio.reset(new RF24(pinCE, initialisedSpiBus->pinSS()));
_radio->begin(_spiPtr.get());
_radio->setDataRate(RF24_250KBPS);
_radio->enableDynamicPayloads();
_radio->setCRCLength(RF24_CRC_16);
_radio->setAddressWidth(5);
_radio->setRetries(0, 0);
_radio->maskIRQ(true, true, false); // enable only receiving interrupts
if (_radio->isChipConnected()) {
Hoymiles.getMessageOutput()->println("Connection successful");
} else {
Hoymiles.getMessageOutput()->println("Connection error!!");
}
attachInterrupt(digitalPinToInterrupt(pinIRQ), std::bind(&HoymilesRadio::handleIntr, this), FALLING);
openReadingPipe();
_radio->startListening();
}
void HoymilesRadio::loop()
{
EVERY_N_MILLIS(4)
{
switchRxCh();
}
if (_packetReceived) {
Hoymiles.getMessageOutput()->println("Interrupt received");
while (_radio->available()) {
if (!(_rxBuffer.size() > FRAGMENT_BUFFER_SIZE)) {
fragment_t f;
memset(f.fragment, 0xcc, MAX_RF_PAYLOAD_SIZE);
f.len = _radio->getDynamicPayloadSize();
f.channel = _radio->getChannel();
if (f.len > MAX_RF_PAYLOAD_SIZE)
f.len = MAX_RF_PAYLOAD_SIZE;
_radio->read(f.fragment, f.len);
_rxBuffer.push(f);
} else {
Hoymiles.getMessageOutput()->println("Buffer full");
_radio->flush_rx();
}
}
_packetReceived = false;
} else {
// Perform package parsing only if no packages are received
if (!_rxBuffer.empty()) {
fragment_t f = _rxBuffer.back();
if (checkFragmentCrc(&f)) {
std::shared_ptr<InverterAbstract> inv = Hoymiles.getInverterByFragment(&f);
if (nullptr != inv) {
// Save packet in inverter rx buffer
char buf[30];
snprintf(buf, sizeof(buf), "RX Channel: %d --> ", f.channel);
dumpBuf(buf, f.fragment, f.len);
inv->addRxFragment(f.fragment, f.len);
} else {
Hoymiles.getMessageOutput()->println("Inverter Not found!");
}
} else {
Hoymiles.getMessageOutput()->println("Frame kaputt");
}
// Remove paket from buffer even it was corrupted
_rxBuffer.pop();
}
}
if (_busyFlag && _rxTimeout.occured()) {
Hoymiles.getMessageOutput()->println("RX Period End");
std::shared_ptr<InverterAbstract> inv = Hoymiles.getInverterBySerial(_commandQueue.front().get()->getTargetAddress());
if (nullptr != inv) {
CommandAbstract* cmd = _commandQueue.front().get();
uint8_t verifyResult = inv->verifyAllFragments(cmd);
if (verifyResult == FRAGMENT_ALL_MISSING_RESEND) {
Hoymiles.getMessageOutput()->println("Nothing received, resend whole request");
sendLastPacketAgain();
} else if (verifyResult == FRAGMENT_ALL_MISSING_TIMEOUT) {
Hoymiles.getMessageOutput()->println("Nothing received, resend count exeeded");
_commandQueue.pop();
_busyFlag = false;
} else if (verifyResult == FRAGMENT_RETRANSMIT_TIMEOUT) {
Hoymiles.getMessageOutput()->println("Retransmit timeout");
_commandQueue.pop();
_busyFlag = false;
} else if (verifyResult == FRAGMENT_HANDLE_ERROR) {
Hoymiles.getMessageOutput()->println("Packet handling error");
_commandQueue.pop();
_busyFlag = false;
} else if (verifyResult > 0) {
// Perform Retransmit
Hoymiles.getMessageOutput()->print("Request retransmit: ");
Hoymiles.getMessageOutput()->println(verifyResult);
sendRetransmitPacket(verifyResult);
} else {
// Successful received all packages
Hoymiles.getMessageOutput()->println("Success");
_commandQueue.pop();
_busyFlag = false;
}
} else {
// If inverter was not found, assume the command is invalid
Hoymiles.getMessageOutput()->println("RX: Invalid inverter found");
_commandQueue.pop();
_busyFlag = false;
}
} else if (!_busyFlag) {
// Currently in idle mode --> send packet if one is in the queue
if (!_commandQueue.empty()) {
CommandAbstract* cmd = _commandQueue.front().get();
auto inv = Hoymiles.getInverterBySerial(cmd->getTargetAddress());
if (nullptr != inv) {
inv->clearRxFragmentBuffer();
sendEsbPacket(cmd);
} else {
Hoymiles.getMessageOutput()->println("TX: Invalid inverter found");
_commandQueue.pop();
}
}
}
}
void HoymilesRadio::setPALevel(rf24_pa_dbm_e paLevel)
{
_radio->setPALevel(paLevel);
}
serial_u HoymilesRadio::DtuSerial() serial_u HoymilesRadio::DtuSerial()
{ {
@ -162,62 +13,6 @@ serial_u HoymilesRadio::DtuSerial()
void HoymilesRadio::setDtuSerial(uint64_t serial) void HoymilesRadio::setDtuSerial(uint64_t serial)
{ {
_dtuSerial.u64 = serial; _dtuSerial.u64 = serial;
openReadingPipe();
}
bool HoymilesRadio::isIdle()
{
return !_busyFlag;
}
bool HoymilesRadio::isConnected()
{
return _radio->isChipConnected();
}
bool HoymilesRadio::isPVariant()
{
return _radio->isPVariant();
}
void HoymilesRadio::openReadingPipe()
{
serial_u s;
s = convertSerialToRadioId(_dtuSerial);
_radio->openReadingPipe(1, s.u64);
}
void HoymilesRadio::openWritingPipe(serial_u serial)
{
serial_u s;
s = convertSerialToRadioId(serial);
_radio->openWritingPipe(s.u64);
}
void ARDUINO_ISR_ATTR HoymilesRadio::handleIntr()
{
_packetReceived = true;
}
uint8_t HoymilesRadio::getRxNxtChannel()
{
if (++_rxChIdx >= sizeof(_rxChLst))
_rxChIdx = 0;
return _rxChLst[_rxChIdx];
}
uint8_t HoymilesRadio::getTxNxtChannel()
{
if (++_txChIdx >= sizeof(_txChLst))
_txChIdx = 0;
return _txChLst[_txChIdx];
}
void HoymilesRadio::switchRxCh()
{
_radio->stopListening();
_radio->setChannel(getRxNxtChannel());
_radio->startListening();
} }
serial_u HoymilesRadio::convertSerialToRadioId(serial_u serial) serial_u HoymilesRadio::convertSerialToRadioId(serial_u serial)
@ -232,59 +27,6 @@ serial_u HoymilesRadio::convertSerialToRadioId(serial_u serial)
return radioId; return radioId;
} }
bool HoymilesRadio::checkFragmentCrc(fragment_t* fragment)
{
uint8_t crc = crc8(fragment->fragment, fragment->len - 1);
return (crc == fragment->fragment[fragment->len - 1]);
}
void HoymilesRadio::sendEsbPacket(CommandAbstract* cmd)
{
cmd->incrementSendCount();
cmd->setRouterAddress(DtuSerial().u64);
_radio->stopListening();
_radio->setChannel(getTxNxtChannel());
serial_u s;
s.u64 = cmd->getTargetAddress();
openWritingPipe(s);
_radio->setRetries(3, 15);
Hoymiles.getMessageOutput()->print("TX ");
Hoymiles.getMessageOutput()->print(cmd->getCommandName());
Hoymiles.getMessageOutput()->print(" Channel: ");
Hoymiles.getMessageOutput()->print(_radio->getChannel());
Hoymiles.getMessageOutput()->print(" --> ");
cmd->dumpDataPayload(Hoymiles.getMessageOutput());
_radio->write(cmd->getDataPayload(), cmd->getDataSize());
_radio->setRetries(0, 0);
openReadingPipe();
_radio->setChannel(getRxNxtChannel());
_radio->startListening();
_busyFlag = true;
_rxTimeout.set(cmd->getTimeout());
}
void HoymilesRadio::sendRetransmitPacket(uint8_t fragment_id)
{
CommandAbstract* cmd = _commandQueue.front().get();
CommandAbstract* requestCmd = cmd->getRequestFrameCommand(fragment_id);
if (requestCmd != nullptr) {
sendEsbPacket(requestCmd);
}
}
void HoymilesRadio::sendLastPacketAgain()
{
CommandAbstract* cmd = _commandQueue.front().get();
sendEsbPacket(cmd);
}
void HoymilesRadio::dumpBuf(const char* info, uint8_t buf[], uint8_t len) void HoymilesRadio::dumpBuf(const char* info, uint8_t buf[], uint8_t len)
{ {

View File

@ -1,30 +1,15 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
#include "TimeoutHelper.h"
#include "commands/CommandAbstract.h" #include "commands/CommandAbstract.h"
#include "types.h" #include "types.h"
#include <RF24.h>
#include <memory> #include <memory>
#include <nRF24L01.h>
#include <queue> #include <queue>
#include <cmt2300a.h>
// number of fragments hold in buffer
#define FRAGMENT_BUFFER_SIZE 30
class HoymilesRadio { class HoymilesRadio {
public: public:
void init(SPIClass* initialisedSpiBus, uint8_t pinCE, uint8_t pinIRQ);
void loop();
void setPALevel(rf24_pa_dbm_e paLevel);
serial_u DtuSerial(); serial_u DtuSerial();
void setDtuSerial(uint64_t serial); virtual void setDtuSerial(uint64_t serial);
bool isIdle();
bool isConnected();
bool isPVariant();
template <typename T> template <typename T>
T* enqueCommand() T* enqueCommand()
@ -33,37 +18,10 @@ public:
return static_cast<T*>(_commandQueue.back().get()); return static_cast<T*>(_commandQueue.back().get());
} }
private: protected:
void ARDUINO_ISR_ATTR handleIntr();
static serial_u convertSerialToRadioId(serial_u serial); static serial_u convertSerialToRadioId(serial_u serial);
uint8_t getRxNxtChannel();
uint8_t getTxNxtChannel();
void switchRxCh();
void openReadingPipe();
void openWritingPipe(serial_u serial);
bool checkFragmentCrc(fragment_t* fragment);
void dumpBuf(const char* info, uint8_t buf[], uint8_t len); void dumpBuf(const char* info, uint8_t buf[], uint8_t len);
void sendEsbPacket(CommandAbstract* cmd);
void sendRetransmitPacket(uint8_t fragment_id);
void sendLastPacketAgain();
std::unique_ptr<SPIClass> _spiPtr;
std::unique_ptr<RF24> _radio;
uint8_t _rxChLst[5] = { 3, 23, 40, 61, 75 };
uint8_t _rxChIdx = 0;
uint8_t _txChLst[5] = { 3, 23, 40, 61, 75 };
uint8_t _txChIdx = 0;
volatile bool _packetReceived = false;
std::queue<fragment_t> _rxBuffer;
TimeoutHelper _rxTimeout;
serial_u _dtuSerial; serial_u _dtuSerial;
bool _busyFlag = false;
std::queue<std::shared_ptr<CommandAbstract>> _commandQueue; std::queue<std::shared_ptr<CommandAbstract>> _commandQueue;
}; };

View File

@ -0,0 +1,270 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2022 Thomas Basler and others
*/
#include "HoymilesRadio_NRF.h"
#include "Hoymiles.h"
#include "commands/RequestFrameCommand.h"
#include "crc.h"
#include <Every.h>
#include <FunctionalInterrupt.h>
void HoymilesRadio_NRF::init(SPIClass* initialisedSpiBus, uint8_t pinCE, uint8_t pinIRQ)
{
_dtuSerial.u64 = 0;
_spiPtr.reset(initialisedSpiBus);
_radio.reset(new RF24(pinCE, initialisedSpiBus->pinSS()));
_radio->begin(_spiPtr.get());
_radio->setDataRate(RF24_250KBPS);
_radio->enableDynamicPayloads();
_radio->setCRCLength(RF24_CRC_16);
_radio->setAddressWidth(5);
_radio->setRetries(0, 0);
_radio->maskIRQ(true, true, false); // enable only receiving interrupts
if (_radio->isChipConnected()) {
Hoymiles.getMessageOutput()->println("Connection successful");
} else {
Hoymiles.getMessageOutput()->println("Connection error!!");
}
attachInterrupt(digitalPinToInterrupt(pinIRQ), std::bind(&HoymilesRadio_NRF::handleIntr, this), FALLING);
openReadingPipe();
_radio->startListening();
}
void HoymilesRadio_NRF::loop()
{
EVERY_N_MILLIS(4)
{
switchRxCh();
}
if (_packetReceived) {
Hoymiles.getMessageOutput()->println("Interrupt received");
while (_radio->available()) {
if (!(_rxBuffer.size() > FRAGMENT_BUFFER_SIZE)) {
fragment_t f;
memset(f.fragment, 0xcc, MAX_RF_PAYLOAD_SIZE);
f.len = _radio->getDynamicPayloadSize();
f.channel = _radio->getChannel();
if (f.len > MAX_RF_PAYLOAD_SIZE)
f.len = MAX_RF_PAYLOAD_SIZE;
_radio->read(f.fragment, f.len);
_rxBuffer.push(f);
} else {
Hoymiles.getMessageOutput()->println("Buffer full");
_radio->flush_rx();
}
}
_packetReceived = false;
} else {
// Perform package parsing only if no packages are received
if (!_rxBuffer.empty()) {
fragment_t f = _rxBuffer.back();
if (checkFragmentCrc(&f)) {
std::shared_ptr<InverterAbstract> inv = Hoymiles.getInverterByFragment(&f);
if (nullptr != inv) {
// Save packet in inverter rx buffer
char buf[30];
snprintf(buf, sizeof(buf), "RX Channel: %d --> ", f.channel);
dumpBuf(buf, f.fragment, f.len);
inv->addRxFragment(f.fragment, f.len);
} else {
Hoymiles.getMessageOutput()->println("Inverter Not found!");
}
} else {
Hoymiles.getMessageOutput()->println("Frame kaputt");
}
// Remove paket from buffer even it was corrupted
_rxBuffer.pop();
}
}
if (_busyFlag && _rxTimeout.occured()) {
Hoymiles.getMessageOutput()->println("RX Period End");
std::shared_ptr<InverterAbstract> inv = Hoymiles.getInverterBySerial(_commandQueue.front().get()->getTargetAddress());
if (nullptr != inv) {
CommandAbstract* cmd = _commandQueue.front().get();
uint8_t verifyResult = inv->verifyAllFragments(cmd);
if (verifyResult == FRAGMENT_ALL_MISSING_RESEND) {
Hoymiles.getMessageOutput()->println("Nothing received, resend whole request");
sendLastPacketAgain();
} else if (verifyResult == FRAGMENT_ALL_MISSING_TIMEOUT) {
Hoymiles.getMessageOutput()->println("Nothing received, resend count exeeded");
_commandQueue.pop();
_busyFlag = false;
} else if (verifyResult == FRAGMENT_RETRANSMIT_TIMEOUT) {
Hoymiles.getMessageOutput()->println("Retransmit timeout");
_commandQueue.pop();
_busyFlag = false;
} else if (verifyResult == FRAGMENT_HANDLE_ERROR) {
Hoymiles.getMessageOutput()->println("Packet handling error");
_commandQueue.pop();
_busyFlag = false;
} else if (verifyResult > 0) {
// Perform Retransmit
Hoymiles.getMessageOutput()->print("Request retransmit: ");
Hoymiles.getMessageOutput()->println(verifyResult);
sendRetransmitPacket(verifyResult);
} else {
// Successful received all packages
Hoymiles.getMessageOutput()->println("Success");
_commandQueue.pop();
_busyFlag = false;
}
} else {
// If inverter was not found, assume the command is invalid
Hoymiles.getMessageOutput()->println("RX: Invalid inverter found");
_commandQueue.pop();
_busyFlag = false;
}
} else if (!_busyFlag) {
// Currently in idle mode --> send packet if one is in the queue
if (!_commandQueue.empty()) {
CommandAbstract* cmd = _commandQueue.front().get();
auto inv = Hoymiles.getInverterBySerial(cmd->getTargetAddress());
if (nullptr != inv) {
inv->clearRxFragmentBuffer();
sendEsbPacket(cmd);
} else {
Hoymiles.getMessageOutput()->println("TX: Invalid inverter found");
_commandQueue.pop();
}
}
}
}
void HoymilesRadio_NRF::setPALevel(rf24_pa_dbm_e paLevel)
{
_radio->setPALevel(paLevel);
}
void HoymilesRadio_NRF::setDtuSerial(uint64_t serial)
{
HoymilesRadio::setDtuSerial(serial);
openReadingPipe();
}
bool HoymilesRadio_NRF::isIdle()
{
return !_busyFlag;
}
bool HoymilesRadio_NRF::isConnected()
{
return _radio->isChipConnected();
}
bool HoymilesRadio_NRF::isPVariant()
{
return _radio->isPVariant();
}
void HoymilesRadio_NRF::openReadingPipe()
{
serial_u s;
s = convertSerialToRadioId(_dtuSerial);
_radio->openReadingPipe(1, s.u64);
}
void HoymilesRadio_NRF::openWritingPipe(serial_u serial)
{
serial_u s;
s = convertSerialToRadioId(serial);
_radio->openWritingPipe(s.u64);
}
void ARDUINO_ISR_ATTR HoymilesRadio_NRF::handleIntr()
{
_packetReceived = true;
}
uint8_t HoymilesRadio_NRF::getRxNxtChannel()
{
if (++_rxChIdx >= sizeof(_rxChLst))
_rxChIdx = 0;
return _rxChLst[_rxChIdx];
}
uint8_t HoymilesRadio_NRF::getTxNxtChannel()
{
if (++_txChIdx >= sizeof(_txChLst))
_txChIdx = 0;
return _txChLst[_txChIdx];
}
void HoymilesRadio_NRF::switchRxCh()
{
_radio->stopListening();
_radio->setChannel(getRxNxtChannel());
_radio->startListening();
}
bool HoymilesRadio_NRF::checkFragmentCrc(fragment_t* fragment)
{
uint8_t crc = crc8(fragment->fragment, fragment->len - 1);
return (crc == fragment->fragment[fragment->len - 1]);
}
void HoymilesRadio_NRF::sendEsbPacket(CommandAbstract* cmd)
{
cmd->incrementSendCount();
cmd->setRouterAddress(DtuSerial().u64);
_radio->stopListening();
_radio->setChannel(getTxNxtChannel());
serial_u s;
s.u64 = cmd->getTargetAddress();
openWritingPipe(s);
_radio->setRetries(3, 15);
Hoymiles.getMessageOutput()->print("TX ");
Hoymiles.getMessageOutput()->print(cmd->getCommandName());
Hoymiles.getMessageOutput()->print(" Channel: ");
Hoymiles.getMessageOutput()->print(_radio->getChannel());
Hoymiles.getMessageOutput()->print(" --> ");
cmd->dumpDataPayload(Hoymiles.getMessageOutput());
_radio->write(cmd->getDataPayload(), cmd->getDataSize());
_radio->setRetries(0, 0);
openReadingPipe();
_radio->setChannel(getRxNxtChannel());
_radio->startListening();
_busyFlag = true;
_rxTimeout.set(cmd->getTimeout());
}
void HoymilesRadio_NRF::sendRetransmitPacket(uint8_t fragment_id)
{
CommandAbstract* cmd = _commandQueue.front().get();
CommandAbstract* requestCmd = cmd->getRequestFrameCommand(fragment_id);
if (requestCmd != nullptr) {
sendEsbPacket(requestCmd);
}
}
void HoymilesRadio_NRF::sendLastPacketAgain()
{
CommandAbstract* cmd = _commandQueue.front().get();
sendEsbPacket(cmd);
}

View File

@ -0,0 +1,54 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "HoymilesRadio.h"
#include "TimeoutHelper.h"
#include "commands/CommandAbstract.h"
#include <RF24.h>
#include <memory>
#include <nRF24L01.h>
#include <queue>
// number of fragments hold in buffer
#define FRAGMENT_BUFFER_SIZE 30
class HoymilesRadio_NRF : public HoymilesRadio {
public:
void init(SPIClass* initialisedSpiBus, uint8_t pinCE, uint8_t pinIRQ);
void loop();
void setPALevel(rf24_pa_dbm_e paLevel);
virtual void setDtuSerial(uint64_t serial);
bool isIdle();
bool isConnected();
bool isPVariant();
private:
void ARDUINO_ISR_ATTR handleIntr();
uint8_t getRxNxtChannel();
uint8_t getTxNxtChannel();
void switchRxCh();
void openReadingPipe();
void openWritingPipe(serial_u serial);
bool checkFragmentCrc(fragment_t* fragment);
void sendEsbPacket(CommandAbstract* cmd);
void sendRetransmitPacket(uint8_t fragment_id);
void sendLastPacketAgain();
std::unique_ptr<SPIClass> _spiPtr;
std::unique_ptr<RF24> _radio;
uint8_t _rxChLst[5] = { 3, 23, 40, 61, 75 };
uint8_t _rxChIdx = 0;
uint8_t _txChLst[5] = { 3, 23, 40, 61, 75 };
uint8_t _txChIdx = 0;
volatile bool _packetReceived = false;
std::queue<fragment_t> _rxBuffer;
TimeoutHelper _rxTimeout;
bool _busyFlag = false;
};

View File

@ -29,10 +29,10 @@ void InverterSettingsClass::init()
Hoymiles.init(spiClass, pin.nrf24_en, pin.nrf24_irq); Hoymiles.init(spiClass, pin.nrf24_en, pin.nrf24_irq);
MessageOutput.println(" Setting radio PA level... "); MessageOutput.println(" Setting radio PA level... ");
Hoymiles.getRadio()->setPALevel((rf24_pa_dbm_e)config.Dtu_PaLevel); Hoymiles.getRadioNrf()->setPALevel((rf24_pa_dbm_e)config.Dtu_PaLevel);
MessageOutput.println(" Setting DTU serial... "); MessageOutput.println(" Setting DTU serial... ");
Hoymiles.getRadio()->setDtuSerial(config.Dtu_Serial); Hoymiles.getRadioNrf()->setDtuSerial(config.Dtu_Serial);
MessageOutput.println(" Setting poll interval... "); MessageOutput.println(" Setting poll interval... ");
Hoymiles.setPollInterval(config.Dtu_PollInterval); Hoymiles.setPollInterval(config.Dtu_PollInterval);

View File

@ -16,7 +16,7 @@ void MqttHandleDtuClass::init()
void MqttHandleDtuClass::loop() void MqttHandleDtuClass::loop()
{ {
if (!MqttSettings.getConnected() || !Hoymiles.getRadio()->isIdle()) { if (!MqttSettings.getConnected() || !Hoymiles.getRadioNrf()->isIdle()) {
return; return;
} }

View File

@ -41,7 +41,7 @@ void MqttHandleHassClass::publishConfig()
return; return;
} }
if (!MqttSettings.getConnected() && Hoymiles.getRadio()->isIdle()) { if (!MqttSettings.getConnected() && Hoymiles.getRadioNrf()->isIdle()) {
return; return;
} }

View File

@ -36,7 +36,7 @@ void MqttHandleInverterClass::init()
void MqttHandleInverterClass::loop() void MqttHandleInverterClass::loop()
{ {
if (!MqttSettings.getConnected() || !Hoymiles.getRadio()->isIdle()) { if (!MqttSettings.getConnected() || !Hoymiles.getRadioNrf()->isIdle()) {
return; return;
} }

View File

@ -132,7 +132,7 @@ void WebApiDtuClass::onDtuAdminPost(AsyncWebServerRequest* request)
response->setLength(); response->setLength();
request->send(response); request->send(response);
Hoymiles.getRadio()->setPALevel((rf24_pa_dbm_e)config.Dtu_PaLevel); Hoymiles.getRadioNrf()->setPALevel((rf24_pa_dbm_e)config.Dtu_PaLevel);
Hoymiles.getRadio()->setDtuSerial(config.Dtu_Serial); Hoymiles.getRadioNrf()->setDtuSerial(config.Dtu_Serial);
Hoymiles.setPollInterval(config.Dtu_PollInterval); Hoymiles.setPollInterval(config.Dtu_PollInterval);
} }

View File

@ -69,8 +69,8 @@ void WebApiSysstatusClass::onSystemStatus(AsyncWebServerRequest* request)
root["uptime"] = esp_timer_get_time() / 1000000; root["uptime"] = esp_timer_get_time() / 1000000;
root["radio_connected"] = Hoymiles.getRadio()->isConnected(); root["radio_connected"] = Hoymiles.getRadioNrf()->isConnected();
root["radio_pvariant"] = Hoymiles.getRadio()->isPVariant(); root["radio_pvariant"] = Hoymiles.getRadioNrf()->isPVariant();
response->setLength(); response->setLength();
request->send(response); request->send(response);

View File

@ -175,7 +175,7 @@ void WebApiWsLiveClass::generateJsonResponse(JsonVariant& root)
JsonObject hintObj = root.createNestedObject("hints"); JsonObject hintObj = root.createNestedObject("hints");
struct tm timeinfo; struct tm timeinfo;
hintObj["time_sync"] = !getLocalTime(&timeinfo, 5); hintObj["time_sync"] = !getLocalTime(&timeinfo, 5);
hintObj["radio_problem"] = (!Hoymiles.getRadio()->isConnected() || !Hoymiles.getRadio()->isPVariant()); hintObj["radio_problem"] = (!Hoymiles.getRadioNrf()->isConnected() || !Hoymiles.getRadioNrf()->isPVariant());
if (!strcmp(Configuration.get().Security_Password, ACCESS_POINT_PASSWORD)) { if (!strcmp(Configuration.get().Security_Password, ACCESS_POINT_PASSWORD)) {
hintObj["default_password"] = true; hintObj["default_password"] = true;
} else { } else {