Skip to content

Commit

Permalink
Add a HoymilesRadio base class
Browse files Browse the repository at this point in the history
This enables to have multiple radio implementations while the inverter classes just refere to the base class
  • Loading branch information
tbnobody committed Apr 15, 2023
1 parent a7e9aaa commit 8404dd5
Show file tree
Hide file tree
Showing 13 changed files with 352 additions and 328 deletions.
24 changes: 12 additions & 12 deletions lib/Hoymiles/src/Hoymiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<InverterAbstract> iv = getInverterByPos(inverterPos);
if (iv != nullptr) {
_messageOutput->print("Fetch inverter: ");
Expand Down Expand Up @@ -89,17 +89,17 @@ std::shared_ptr<InverterAbstract> HoymilesClass::addInverter(const char* name, u
{
std::shared_ptr<InverterAbstract> i = nullptr;
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)) {
i = std::make_shared<HMS_2CH>(_radio.get(), serial);
i = std::make_shared<HMS_2CH>(_radioNrf.get(), 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)) {
i = std::make_shared<HM_4CH>(_radio.get(), serial);
i = std::make_shared<HM_4CH>(_radioNrf.get(), 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)) {
i = std::make_shared<HM_1CH>(_radio.get(), serial);
i = std::make_shared<HM_1CH>(_radioNrf.get(), serial);
}

if (i) {
Expand Down Expand Up @@ -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()
Expand Down
6 changes: 3 additions & 3 deletions lib/Hoymiles/src/Hoymiles.h
Original file line number Diff line number Diff line change
@@ -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 <Print.h>
Expand All @@ -27,14 +27,14 @@ class HoymilesClass {
void removeInverterBySerial(uint64_t serial);
size_t getNumInverters();

HoymilesRadio* getRadio();
HoymilesRadio_NRF* getRadioNrf();

uint32_t PollInterval();
void setPollInterval(uint32_t interval);

private:
std::vector<std::shared_ptr<InverterAbstract>> _inverters;
std::unique_ptr<HoymilesRadio> _radio;
std::unique_ptr<HoymilesRadio_NRF> _radioNrf;

SemaphoreHandle_t _xSemaphore;

Expand Down
260 changes: 1 addition & 259 deletions lib/Hoymiles/src/HoymilesRadio.cpp
Original file line number Diff line number Diff line change
@@ -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 <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()
{
Expand All @@ -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)
Expand All @@ -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)
{

Expand Down
Loading

0 comments on commit 8404dd5

Please sign in to comment.