From 8404dd57a76bed05c0d0845b971d00911a6a41c4 Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Mon, 6 Mar 2023 22:16:34 +0100 Subject: [PATCH] Add a HoymilesRadio base class This enables to have multiple radio implementations while the inverter classes just refere to the base class --- lib/Hoymiles/src/Hoymiles.cpp | 24 +-- lib/Hoymiles/src/Hoymiles.h | 6 +- lib/Hoymiles/src/HoymilesRadio.cpp | 260 +----------------------- lib/Hoymiles/src/HoymilesRadio.h | 46 +---- lib/Hoymiles/src/HoymilesRadio_NRF.cpp | 270 +++++++++++++++++++++++++ lib/Hoymiles/src/HoymilesRadio_NRF.h | 54 +++++ src/InverterSettings.cpp | 4 +- src/MqttHandleDtu.cpp | 2 +- src/MqttHandleHass.cpp | 2 +- src/MqttHandleInverter.cpp | 2 +- src/WebApi_dtu.cpp | 4 +- src/WebApi_sysstatus.cpp | 4 +- src/WebApi_ws_live.cpp | 2 +- 13 files changed, 352 insertions(+), 328 deletions(-) create mode 100644 lib/Hoymiles/src/HoymilesRadio_NRF.cpp create mode 100644 lib/Hoymiles/src/HoymilesRadio_NRF.h diff --git a/lib/Hoymiles/src/Hoymiles.cpp b/lib/Hoymiles/src/Hoymiles.cpp index 2392af7..64c1eb1 100644 --- a/lib/Hoymiles/src/Hoymiles.cpp +++ b/lib/Hoymiles/src/Hoymiles.cpp @@ -22,20 +22,20 @@ void HoymilesClass::init(SPIClass* initialisedSpiBus, uint8_t pinCE, uint8_t pin HOY_SEMAPHORE_GIVE(); // release before first use _pollInterval = 0; - _radio.reset(new HoymilesRadio()); - _radio->init(initialisedSpiBus, pinCE, pinIRQ); + _radioNrf.reset(new HoymilesRadio_NRF()); + _radioNrf->init(initialisedSpiBus, pinCE, pinIRQ); } void HoymilesClass::loop() { HOY_SEMAPHORE_TAKE(); - _radio->loop(); + _radioNrf->loop(); if (getNumInverters() > 0) { if (millis() - _lastPoll > (_pollInterval * 1000)) { static uint8_t inverterPos = 0; - if (_radio->isIdle()) { + if (_radioNrf->isIdle()) { std::shared_ptr iv = getInverterByPos(inverterPos); if (iv != nullptr) { _messageOutput->print("Fetch inverter: "); @@ -89,17 +89,17 @@ std::shared_ptr HoymilesClass::addInverter(const char* name, u { std::shared_ptr i = nullptr; if (HMS_4CH::isValidSerial(serial)) { - i = std::make_shared(_radio.get(), serial); + i = std::make_shared(_radioNrf.get(), serial); } else if (HMS_2CH::isValidSerial(serial)) { - i = std::make_shared(_radio.get(), serial); + i = std::make_shared(_radioNrf.get(), serial); } else if (HMS_1CH::isValidSerial(serial)) { - i = std::make_shared(_radio.get(), serial); + i = std::make_shared(_radioNrf.get(), serial); } else if (HM_4CH::isValidSerial(serial)) { - i = std::make_shared(_radio.get(), serial); + i = std::make_shared(_radioNrf.get(), serial); } else if (HM_2CH::isValidSerial(serial)) { - i = std::make_shared(_radio.get(), serial); + i = std::make_shared(_radioNrf.get(), serial); } else if (HM_1CH::isValidSerial(serial)) { - i = std::make_shared(_radio.get(), serial); + i = std::make_shared(_radioNrf.get(), serial); } if (i) { @@ -171,9 +171,9 @@ size_t HoymilesClass::getNumInverters() return _inverters.size(); } -HoymilesRadio* HoymilesClass::getRadio() +HoymilesRadio_NRF* HoymilesClass::getRadioNrf() { - return _radio.get(); + return _radioNrf.get(); } uint32_t HoymilesClass::PollInterval() diff --git a/lib/Hoymiles/src/Hoymiles.h b/lib/Hoymiles/src/Hoymiles.h index 27e3d03..de12ce2 100644 --- a/lib/Hoymiles/src/Hoymiles.h +++ b/lib/Hoymiles/src/Hoymiles.h @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #pragma once -#include "HoymilesRadio.h" +#include "HoymilesRadio_NRF.h" #include "inverters/InverterAbstract.h" #include "types.h" #include @@ -27,14 +27,14 @@ public: void removeInverterBySerial(uint64_t serial); size_t getNumInverters(); - HoymilesRadio* getRadio(); + HoymilesRadio_NRF* getRadioNrf(); uint32_t PollInterval(); void setPollInterval(uint32_t interval); private: std::vector> _inverters; - std::unique_ptr _radio; + std::unique_ptr _radioNrf; SemaphoreHandle_t _xSemaphore; diff --git a/lib/Hoymiles/src/HoymilesRadio.cpp b/lib/Hoymiles/src/HoymilesRadio.cpp index eb25d55..08ef1c3 100644 --- a/lib/Hoymiles/src/HoymilesRadio.cpp +++ b/lib/Hoymiles/src/HoymilesRadio.cpp @@ -1,158 +1,9 @@ // 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 "Hoymiles.h" -#include "commands/RequestFrameCommand.h" -#include "crc.h" -#include -#include - -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 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 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() { @@ -162,62 +13,6 @@ serial_u HoymilesRadio::DtuSerial() void HoymilesRadio::setDtuSerial(uint64_t 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) @@ -232,59 +27,6 @@ serial_u HoymilesRadio::convertSerialToRadioId(serial_u serial) 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) { diff --git a/lib/Hoymiles/src/HoymilesRadio.h b/lib/Hoymiles/src/HoymilesRadio.h index e80975c..557bfaf 100644 --- a/lib/Hoymiles/src/HoymilesRadio.h +++ b/lib/Hoymiles/src/HoymilesRadio.h @@ -1,30 +1,15 @@ // SPDX-License-Identifier: GPL-2.0-or-later #pragma once -#include "TimeoutHelper.h" #include "commands/CommandAbstract.h" #include "types.h" -#include #include -#include #include -#include - -// number of fragments hold in buffer -#define FRAGMENT_BUFFER_SIZE 30 class HoymilesRadio { public: - void init(SPIClass* initialisedSpiBus, uint8_t pinCE, uint8_t pinIRQ); - void loop(); - void setPALevel(rf24_pa_dbm_e paLevel); - serial_u DtuSerial(); - void setDtuSerial(uint64_t serial); - - bool isIdle(); - bool isConnected(); - bool isPVariant(); + virtual void setDtuSerial(uint64_t serial); template T* enqueCommand() @@ -33,37 +18,10 @@ public: return static_cast(_commandQueue.back().get()); } -private: - void ARDUINO_ISR_ATTR handleIntr(); +protected: 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 sendEsbPacket(CommandAbstract* cmd); - void sendRetransmitPacket(uint8_t fragment_id); - void sendLastPacketAgain(); - - std::unique_ptr _spiPtr; - std::unique_ptr _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 _rxBuffer; - TimeoutHelper _rxTimeout; - serial_u _dtuSerial; - - bool _busyFlag = false; - std::queue> _commandQueue; }; \ No newline at end of file diff --git a/lib/Hoymiles/src/HoymilesRadio_NRF.cpp b/lib/Hoymiles/src/HoymilesRadio_NRF.cpp new file mode 100644 index 0000000..120bde5 --- /dev/null +++ b/lib/Hoymiles/src/HoymilesRadio_NRF.cpp @@ -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 +#include + +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 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 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); +} + diff --git a/lib/Hoymiles/src/HoymilesRadio_NRF.h b/lib/Hoymiles/src/HoymilesRadio_NRF.h new file mode 100644 index 0000000..5ed2e36 --- /dev/null +++ b/lib/Hoymiles/src/HoymilesRadio_NRF.h @@ -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 +#include +#include +#include + +// 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 _spiPtr; + std::unique_ptr _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 _rxBuffer; + TimeoutHelper _rxTimeout; + + bool _busyFlag = false; +}; \ No newline at end of file diff --git a/src/InverterSettings.cpp b/src/InverterSettings.cpp index 39719d2..1562b13 100644 --- a/src/InverterSettings.cpp +++ b/src/InverterSettings.cpp @@ -29,10 +29,10 @@ void InverterSettingsClass::init() Hoymiles.init(spiClass, pin.nrf24_en, pin.nrf24_irq); 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... "); - Hoymiles.getRadio()->setDtuSerial(config.Dtu_Serial); + Hoymiles.getRadioNrf()->setDtuSerial(config.Dtu_Serial); MessageOutput.println(" Setting poll interval... "); Hoymiles.setPollInterval(config.Dtu_PollInterval); diff --git a/src/MqttHandleDtu.cpp b/src/MqttHandleDtu.cpp index 818a1b4..f9dfcc5 100644 --- a/src/MqttHandleDtu.cpp +++ b/src/MqttHandleDtu.cpp @@ -16,7 +16,7 @@ void MqttHandleDtuClass::init() void MqttHandleDtuClass::loop() { - if (!MqttSettings.getConnected() || !Hoymiles.getRadio()->isIdle()) { + if (!MqttSettings.getConnected() || !Hoymiles.getRadioNrf()->isIdle()) { return; } diff --git a/src/MqttHandleHass.cpp b/src/MqttHandleHass.cpp index 9aa3e13..3f72feb 100644 --- a/src/MqttHandleHass.cpp +++ b/src/MqttHandleHass.cpp @@ -41,7 +41,7 @@ void MqttHandleHassClass::publishConfig() return; } - if (!MqttSettings.getConnected() && Hoymiles.getRadio()->isIdle()) { + if (!MqttSettings.getConnected() && Hoymiles.getRadioNrf()->isIdle()) { return; } diff --git a/src/MqttHandleInverter.cpp b/src/MqttHandleInverter.cpp index ba73767..af18ac5 100644 --- a/src/MqttHandleInverter.cpp +++ b/src/MqttHandleInverter.cpp @@ -36,7 +36,7 @@ void MqttHandleInverterClass::init() void MqttHandleInverterClass::loop() { - if (!MqttSettings.getConnected() || !Hoymiles.getRadio()->isIdle()) { + if (!MqttSettings.getConnected() || !Hoymiles.getRadioNrf()->isIdle()) { return; } diff --git a/src/WebApi_dtu.cpp b/src/WebApi_dtu.cpp index 93365a4..8131177 100644 --- a/src/WebApi_dtu.cpp +++ b/src/WebApi_dtu.cpp @@ -132,7 +132,7 @@ void WebApiDtuClass::onDtuAdminPost(AsyncWebServerRequest* request) response->setLength(); request->send(response); - Hoymiles.getRadio()->setPALevel((rf24_pa_dbm_e)config.Dtu_PaLevel); - Hoymiles.getRadio()->setDtuSerial(config.Dtu_Serial); + Hoymiles.getRadioNrf()->setPALevel((rf24_pa_dbm_e)config.Dtu_PaLevel); + Hoymiles.getRadioNrf()->setDtuSerial(config.Dtu_Serial); Hoymiles.setPollInterval(config.Dtu_PollInterval); } \ No newline at end of file diff --git a/src/WebApi_sysstatus.cpp b/src/WebApi_sysstatus.cpp index f70cdd8..c392d6c 100644 --- a/src/WebApi_sysstatus.cpp +++ b/src/WebApi_sysstatus.cpp @@ -69,8 +69,8 @@ void WebApiSysstatusClass::onSystemStatus(AsyncWebServerRequest* request) root["uptime"] = esp_timer_get_time() / 1000000; - root["radio_connected"] = Hoymiles.getRadio()->isConnected(); - root["radio_pvariant"] = Hoymiles.getRadio()->isPVariant(); + root["radio_connected"] = Hoymiles.getRadioNrf()->isConnected(); + root["radio_pvariant"] = Hoymiles.getRadioNrf()->isPVariant(); response->setLength(); request->send(response); diff --git a/src/WebApi_ws_live.cpp b/src/WebApi_ws_live.cpp index 3507b50..1b4b4ec 100644 --- a/src/WebApi_ws_live.cpp +++ b/src/WebApi_ws_live.cpp @@ -175,7 +175,7 @@ void WebApiWsLiveClass::generateJsonResponse(JsonVariant& root) JsonObject hintObj = root.createNestedObject("hints"); struct tm timeinfo; 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)) { hintObj["default_password"] = true; } else {