First simple implementation of polling algorithm
This commit is contained in:
parent
37dbb343a9
commit
9bfe6a9e63
@ -1,4 +1,6 @@
|
|||||||
#include "Hoymiles.h"
|
#include "Hoymiles.h"
|
||||||
|
#include "inverters/HM_1CH.h"
|
||||||
|
#include "inverters/HM_2CH.h"
|
||||||
#include "inverters/HM_4CH.h"
|
#include "inverters/HM_4CH.h"
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <Every.h>
|
#include <Every.h>
|
||||||
@ -15,6 +17,33 @@ void HoymilesClass::init()
|
|||||||
void HoymilesClass::loop()
|
void HoymilesClass::loop()
|
||||||
{
|
{
|
||||||
_radio->loop();
|
_radio->loop();
|
||||||
|
|
||||||
|
if (getNumInverters() > 0) {
|
||||||
|
EVERY_N_SECONDS(_pollInterval)
|
||||||
|
{
|
||||||
|
static uint8_t inverterPos = 0;
|
||||||
|
|
||||||
|
std::shared_ptr<InverterAbstract> iv = getInverterByPos(inverterPos);
|
||||||
|
if (iv != nullptr && _radio->isIdle()) {
|
||||||
|
Serial.print("Fetch inverter: ");
|
||||||
|
Serial.println(iv->serial());
|
||||||
|
|
||||||
|
iv->clearRxFragmentBuffer();
|
||||||
|
|
||||||
|
time_t now;
|
||||||
|
time(&now);
|
||||||
|
if (now > 0) {
|
||||||
|
_radio->sendTimePacket(iv, now);
|
||||||
|
} else {
|
||||||
|
Serial.println("Cancled. Time not yet synced.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (++inverterPos >= getNumInverters()) {
|
||||||
|
inverterPos = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<InverterAbstract> HoymilesClass::addInverter(const char* name, uint64_t serial)
|
std::shared_ptr<InverterAbstract> HoymilesClass::addInverter(const char* name, uint64_t serial)
|
||||||
@ -23,6 +52,12 @@ std::shared_ptr<InverterAbstract> HoymilesClass::addInverter(const char* name, u
|
|||||||
if (HM_4CH::isValidSerial(serial)) {
|
if (HM_4CH::isValidSerial(serial)) {
|
||||||
i = std::make_shared<HM_4CH>();
|
i = std::make_shared<HM_4CH>();
|
||||||
}
|
}
|
||||||
|
else if (HM_2CH::isValidSerial(serial)) {
|
||||||
|
i = std::make_shared<HM_2CH>();
|
||||||
|
}
|
||||||
|
else if (HM_1CH::isValidSerial(serial)) {
|
||||||
|
i = std::make_shared<HM_1CH>();
|
||||||
|
}
|
||||||
|
|
||||||
if (i) {
|
if (i) {
|
||||||
i->setSerial(serial);
|
i->setSerial(serial);
|
||||||
@ -78,7 +113,9 @@ std::shared_ptr<InverterAbstract> HoymilesClass::getInverterByFragment(fragment_
|
|||||||
|
|
||||||
void HoymilesClass::removeInverterByPos(uint8_t pos)
|
void HoymilesClass::removeInverterByPos(uint8_t pos)
|
||||||
{
|
{
|
||||||
_inverters.erase(_inverters.begin() + pos);
|
if (pos < _inverters.size()) {
|
||||||
|
_inverters.erase(_inverters.begin() + pos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t HoymilesClass::getNumInverters()
|
size_t HoymilesClass::getNumInverters()
|
||||||
|
|||||||
@ -35,9 +35,6 @@ void HoymilesRadio::loop()
|
|||||||
switchRxCh(1);
|
switchRxCh(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Irgendwie muss man hier die paket crc prüfen und ggf. einen retransmit anfordern
|
|
||||||
// ggf aber immer nur ein paket analysieren damit die loop schnell bleibt
|
|
||||||
|
|
||||||
if (_packetReceived) {
|
if (_packetReceived) {
|
||||||
Serial.println(F("Interrupt received"));
|
Serial.println(F("Interrupt received"));
|
||||||
while (_radio->available()) {
|
while (_radio->available()) {
|
||||||
@ -45,7 +42,6 @@ void HoymilesRadio::loop()
|
|||||||
fragment_t* f;
|
fragment_t* f;
|
||||||
f = _rxBuffer.getFront();
|
f = _rxBuffer.getFront();
|
||||||
memset(f->fragment, 0xcc, MAX_RF_PAYLOAD_SIZE);
|
memset(f->fragment, 0xcc, MAX_RF_PAYLOAD_SIZE);
|
||||||
f->rxCh = _rxChLst[_rxChIdx];
|
|
||||||
f->len = _radio->getDynamicPayloadSize();
|
f->len = _radio->getDynamicPayloadSize();
|
||||||
if (f->len > MAX_RF_PAYLOAD_SIZE)
|
if (f->len > MAX_RF_PAYLOAD_SIZE)
|
||||||
f->len = MAX_RF_PAYLOAD_SIZE;
|
f->len = MAX_RF_PAYLOAD_SIZE;
|
||||||
@ -64,23 +60,56 @@ void HoymilesRadio::loop()
|
|||||||
if (!_rxBuffer.empty()) {
|
if (!_rxBuffer.empty()) {
|
||||||
fragment_t* f = _rxBuffer.getBack();
|
fragment_t* f = _rxBuffer.getBack();
|
||||||
if (checkFragmentCrc(f)) {
|
if (checkFragmentCrc(f)) {
|
||||||
Serial.println("Frame Ok");
|
|
||||||
std::shared_ptr<InverterAbstract> inv = Hoymiles.getInverterByFragment(f);
|
std::shared_ptr<InverterAbstract> inv = Hoymiles.getInverterByFragment(f);
|
||||||
|
|
||||||
if (nullptr != inv) {
|
if (nullptr != inv) {
|
||||||
Serial.println("Found Inverter");
|
// Save packet in inverter rx buffer
|
||||||
|
dumpBuf("RX ", f->fragment, f->len);
|
||||||
|
inv->addRxFragment(f->fragment, f->len);
|
||||||
} else {
|
} else {
|
||||||
Serial.println("Inverter Not found!");
|
Serial.println(F("Inverter Not found!"));
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Serial.println("Frame kaputt");
|
Serial.println(F("Frame kaputt"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove paket from buffer even it was corrupted
|
// Remove paket from buffer even it was corrupted
|
||||||
_rxBuffer.popBack();
|
_rxBuffer.popBack();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_busyFlag && _rxTimeout.occured()) {
|
||||||
|
Serial.println("Timeout");
|
||||||
|
std::shared_ptr<InverterAbstract> inv = Hoymiles.getInverterBySerial(_activeSerial.u64);
|
||||||
|
|
||||||
|
if (nullptr != inv) {
|
||||||
|
uint8_t verifyResult = inv->verifyAllFragments();
|
||||||
|
if (verifyResult == 255) {
|
||||||
|
Serial.println("Should Retransmit whole thing");
|
||||||
|
// todo: irgendwas tun wenn garnichts ankam....
|
||||||
|
_busyFlag = false;
|
||||||
|
|
||||||
|
} else if (verifyResult == 254) {
|
||||||
|
Serial.println("Retransmit timeout");
|
||||||
|
_busyFlag = false;
|
||||||
|
|
||||||
|
} else if (verifyResult == 253) {
|
||||||
|
Serial.println("Packet CRC error");
|
||||||
|
_busyFlag = false;
|
||||||
|
|
||||||
|
} else if (verifyResult > 0) {
|
||||||
|
// Perform Retransmit
|
||||||
|
Serial.print(F("Request retransmit: "));
|
||||||
|
Serial.println(verifyResult);
|
||||||
|
sendRetransmitPacket(verifyResult);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Successfull received all packages
|
||||||
|
_busyFlag = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HoymilesRadio::setPALevel(rf24_pa_dbm_e paLevel)
|
void HoymilesRadio::setPALevel(rf24_pa_dbm_e paLevel)
|
||||||
@ -99,6 +128,11 @@ void HoymilesRadio::setDtuSerial(uint64_t serial)
|
|||||||
openReadingPipe();
|
openReadingPipe();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool HoymilesRadio::isIdle()
|
||||||
|
{
|
||||||
|
return !_busyFlag;
|
||||||
|
}
|
||||||
|
|
||||||
void HoymilesRadio::openReadingPipe()
|
void HoymilesRadio::openReadingPipe()
|
||||||
{
|
{
|
||||||
serial_u s;
|
serial_u s;
|
||||||
@ -106,6 +140,13 @@ void HoymilesRadio::openReadingPipe()
|
|||||||
_radio->openReadingPipe(1, s.u64);
|
_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()
|
void ARDUINO_ISR_ATTR HoymilesRadio::handleIntr()
|
||||||
{
|
{
|
||||||
_packetReceived = true;
|
_packetReceived = true;
|
||||||
@ -118,6 +159,13 @@ uint8_t HoymilesRadio::getRxNxtChannel()
|
|||||||
return _rxChLst[_rxChIdx];
|
return _rxChLst[_rxChIdx];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t HoymilesRadio::getTxNxtChannel()
|
||||||
|
{
|
||||||
|
if (++_txChIdx >= 1)
|
||||||
|
_txChIdx = 0;
|
||||||
|
return _txChLst[_txChIdx];
|
||||||
|
}
|
||||||
|
|
||||||
bool HoymilesRadio::switchRxCh(uint8_t addLoop)
|
bool HoymilesRadio::switchRxCh(uint8_t addLoop)
|
||||||
{
|
{
|
||||||
_rxLoopCnt += addLoop;
|
_rxLoopCnt += addLoop;
|
||||||
@ -144,8 +192,99 @@ serial_u HoymilesRadio::convertSerialToRadioId(serial_u serial)
|
|||||||
return radioId;
|
return radioId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HoymilesRadio::convertSerialToPacketId(uint8_t buffer[], serial_u serial)
|
||||||
|
{
|
||||||
|
buffer[3] = serial.b[0];
|
||||||
|
buffer[2] = serial.b[1];
|
||||||
|
buffer[1] = serial.b[2];
|
||||||
|
buffer[0] = serial.b[3];
|
||||||
|
}
|
||||||
|
|
||||||
bool HoymilesRadio::checkFragmentCrc(fragment_t* fragment)
|
bool HoymilesRadio::checkFragmentCrc(fragment_t* fragment)
|
||||||
{
|
{
|
||||||
uint8_t crc = crc8(fragment->fragment, fragment->len - 1);
|
uint8_t crc = crc8(fragment->fragment, fragment->len - 1);
|
||||||
return (crc == fragment->fragment[fragment->len - 1]);
|
return (crc == fragment->fragment[fragment->len - 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HoymilesRadio::sendEsbPacket(serial_u target, uint8_t mainCmd, uint8_t subCmd, uint8_t payload[], uint8_t len, uint32_t timeout, bool resend)
|
||||||
|
{
|
||||||
|
static uint8_t txBuffer[MAX_RF_PAYLOAD_SIZE];
|
||||||
|
|
||||||
|
if (!resend) {
|
||||||
|
memset(txBuffer, 0, MAX_RF_PAYLOAD_SIZE);
|
||||||
|
|
||||||
|
txBuffer[0] = mainCmd;
|
||||||
|
convertSerialToPacketId(&txBuffer[1], target); // 4 byte long
|
||||||
|
convertSerialToPacketId(&txBuffer[5], DtuSerial()); // 4 byte long
|
||||||
|
txBuffer[9] = subCmd;
|
||||||
|
|
||||||
|
memcpy(&txBuffer[10], payload, len);
|
||||||
|
txBuffer[10 + len] = crc8(txBuffer, 10 + len);
|
||||||
|
}
|
||||||
|
|
||||||
|
_radio->stopListening();
|
||||||
|
_radio->setChannel(getTxNxtChannel());
|
||||||
|
openWritingPipe(target);
|
||||||
|
_radio->setRetries(3, 15);
|
||||||
|
|
||||||
|
dumpBuf(NULL, txBuffer, 10 + len + 1);
|
||||||
|
_radio->write(txBuffer, 10 + len + 1);
|
||||||
|
|
||||||
|
_radio->setRetries(0, 0);
|
||||||
|
openReadingPipe();
|
||||||
|
_radio->setChannel(getRxNxtChannel());
|
||||||
|
_radio->startListening();
|
||||||
|
_busyFlag = true;
|
||||||
|
_rxTimeout.set(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HoymilesRadio::sendTimePacket(std::shared_ptr<InverterAbstract> iv, time_t ts)
|
||||||
|
{
|
||||||
|
uint8_t payload[16] = { 0 };
|
||||||
|
|
||||||
|
payload[0] = 0x0b;
|
||||||
|
payload[1] = 0x00;
|
||||||
|
u32CpyLittleEndian(&payload[2], ts); // sets the 4 following elements {2, 3, 4, 5}
|
||||||
|
payload[9] = 0x05;
|
||||||
|
|
||||||
|
uint16_t crc = crc16(&payload[0], 14);
|
||||||
|
payload[14] = (crc >> 8) & 0xff;
|
||||||
|
payload[15] = (crc)&0xff;
|
||||||
|
|
||||||
|
serial_u s;
|
||||||
|
s.u64 = iv->serial();
|
||||||
|
_activeSerial.u64 = iv->serial();
|
||||||
|
|
||||||
|
sendEsbPacket(s, 0x15, 0x80, payload, 16, 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HoymilesRadio::sendRetransmitPacket(uint8_t fragment_id)
|
||||||
|
{
|
||||||
|
sendEsbPacket(_activeSerial, 0x15, (uint8_t)(0x80 + fragment_id), 0, 0, 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HoymilesRadio::sendLastPacketAgain()
|
||||||
|
{
|
||||||
|
sendEsbPacket(_activeSerial, 0, 0, 0, 0, 60, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HoymilesRadio::u32CpyLittleEndian(uint8_t dest[], uint32_t src)
|
||||||
|
{
|
||||||
|
dest[0] = ((src >> 24) & 0xff);
|
||||||
|
dest[1] = ((src >> 16) & 0xff);
|
||||||
|
dest[2] = ((src >> 8) & 0xff);
|
||||||
|
dest[3] = ((src)&0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HoymilesRadio::dumpBuf(const char* info, uint8_t buf[], uint8_t len)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (NULL != info)
|
||||||
|
Serial.print(String(info));
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < len; i++) {
|
||||||
|
Serial.print(buf[i], 16);
|
||||||
|
Serial.print(" ");
|
||||||
|
}
|
||||||
|
Serial.println("");
|
||||||
|
}
|
||||||
@ -1,22 +1,17 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "CircularBuffer.h"
|
#include "CircularBuffer.h"
|
||||||
|
#include "TimeoutHelper.h"
|
||||||
|
#include "inverters/InverterAbstract.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include <RF24.h>
|
#include <RF24.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <nRF24L01.h>
|
#include <nRF24L01.h>
|
||||||
|
|
||||||
// maximum buffer length of packet received / sent to RF24 module
|
|
||||||
#define MAX_RF_PAYLOAD_SIZE 32
|
|
||||||
|
|
||||||
// number of fragments hold in buffer
|
// number of fragments hold in buffer
|
||||||
#define FRAGMENT_BUFFER_SIZE 30
|
#define FRAGMENT_BUFFER_SIZE 30
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint8_t rxCh;
|
|
||||||
uint8_t fragment[MAX_RF_PAYLOAD_SIZE];
|
|
||||||
uint8_t len;
|
|
||||||
} fragment_t;
|
|
||||||
|
|
||||||
class HoymilesRadio {
|
class HoymilesRadio {
|
||||||
public:
|
public:
|
||||||
@ -27,22 +22,40 @@ public:
|
|||||||
serial_u DtuSerial();
|
serial_u DtuSerial();
|
||||||
void setDtuSerial(uint64_t serial);
|
void setDtuSerial(uint64_t serial);
|
||||||
|
|
||||||
|
bool isIdle();
|
||||||
|
void sendEsbPacket(serial_u target, uint8_t mainCmd, uint8_t subCmd, uint8_t payload[], uint8_t len, uint32_t timeout, bool resend = false);
|
||||||
|
void sendTimePacket(std::shared_ptr<InverterAbstract> iv, time_t ts);
|
||||||
|
void sendRetransmitPacket(uint8_t fragment_id);
|
||||||
|
void sendLastPacketAgain();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void ARDUINO_ISR_ATTR handleIntr();
|
void ARDUINO_ISR_ATTR handleIntr();
|
||||||
static serial_u convertSerialToRadioId(serial_u serial);
|
static serial_u convertSerialToRadioId(serial_u serial);
|
||||||
|
static void convertSerialToPacketId(uint8_t buffer[], serial_u serial);
|
||||||
uint8_t getRxNxtChannel();
|
uint8_t getRxNxtChannel();
|
||||||
|
uint8_t getTxNxtChannel();
|
||||||
bool switchRxCh(uint8_t addLoop = 0);
|
bool switchRxCh(uint8_t addLoop = 0);
|
||||||
void openReadingPipe();
|
void openReadingPipe();
|
||||||
|
void openWritingPipe(serial_u serial);
|
||||||
bool checkFragmentCrc(fragment_t* fragment);
|
bool checkFragmentCrc(fragment_t* fragment);
|
||||||
|
void dumpBuf(const char* info, uint8_t buf[], uint8_t len);
|
||||||
|
void u32CpyLittleEndian(uint8_t dest[], uint32_t src);
|
||||||
|
|
||||||
std::unique_ptr<RF24> _radio;
|
std::unique_ptr<RF24> _radio;
|
||||||
uint8_t _rxChLst[4] = { 3, 23, 61, 75 };
|
uint8_t _rxChLst[4] = { 3, 23, 61, 75 };
|
||||||
uint8_t _rxChIdx;
|
uint8_t _rxChIdx;
|
||||||
uint16_t _rxLoopCnt;
|
uint16_t _rxLoopCnt;
|
||||||
|
|
||||||
|
uint8_t _txChLst[1] = { 40 };
|
||||||
|
uint8_t _txChIdx;
|
||||||
|
|
||||||
volatile bool _packetReceived;
|
volatile bool _packetReceived;
|
||||||
|
|
||||||
CircularBuffer<fragment_t, FRAGMENT_BUFFER_SIZE> _rxBuffer;
|
CircularBuffer<fragment_t, FRAGMENT_BUFFER_SIZE> _rxBuffer;
|
||||||
|
TimeoutHelper _rxTimeout;
|
||||||
|
|
||||||
serial_u _dtuSerial;
|
serial_u _dtuSerial;
|
||||||
|
serial_u _activeSerial;
|
||||||
|
|
||||||
|
bool _busyFlag = false;
|
||||||
};
|
};
|
||||||
@ -1,4 +1,5 @@
|
|||||||
#include "InverterAbstract.h"
|
#include "InverterAbstract.h"
|
||||||
|
#include "crc.h"
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
void InverterAbstract::setSerial(uint64_t serial)
|
void InverterAbstract::setSerial(uint64_t serial)
|
||||||
@ -14,7 +15,11 @@ uint64_t InverterAbstract::serial()
|
|||||||
void InverterAbstract::setName(const char* name)
|
void InverterAbstract::setName(const char* name)
|
||||||
{
|
{
|
||||||
uint8_t len = strlen(name);
|
uint8_t len = strlen(name);
|
||||||
strncpy(_name, name, (len > MAX_NAME_LENGTH) ? MAX_NAME_LENGTH : len);
|
if (len + 1 > MAX_NAME_LENGTH) {
|
||||||
|
len = MAX_NAME_LENGTH - 1;
|
||||||
|
}
|
||||||
|
strncpy(_name, name, len);
|
||||||
|
_name[len] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* InverterAbstract::name()
|
const char* InverterAbstract::name()
|
||||||
@ -24,5 +29,230 @@ const char* InverterAbstract::name()
|
|||||||
|
|
||||||
void InverterAbstract::clearRxFragmentBuffer()
|
void InverterAbstract::clearRxFragmentBuffer()
|
||||||
{
|
{
|
||||||
|
memset(_rxFragmentBuffer, 0, MAX_RF_FRAGMENT_COUNT * MAX_RF_PAYLOAD_SIZE);
|
||||||
|
_rxFragmentMaxPacketId = 0;
|
||||||
|
_rxFragmentLastPacketId = 0;
|
||||||
|
_rxFragmentRetransmitCnt = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InverterAbstract::addRxFragment(uint8_t fragment[], uint8_t len)
|
||||||
|
{
|
||||||
|
uint8_t fragmentCount = fragment[9];
|
||||||
|
if ((fragmentCount & 0b01111111) < MAX_RF_FRAGMENT_COUNT) {
|
||||||
|
// Packets with 0x81 will be seen as 1
|
||||||
|
memcpy(_rxFragmentBuffer[(fragmentCount & 0b01111111) - 1].fragment, &fragment[10], len - 11);
|
||||||
|
_rxFragmentBuffer[(fragmentCount & 0b01111111) - 1].len = len - 11;
|
||||||
|
|
||||||
|
if ((fragmentCount & 0b01111111) > _rxFragmentLastPacketId) {
|
||||||
|
_rxFragmentLastPacketId = fragmentCount & 0b01111111;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 0b10000000 == 0x80
|
||||||
|
if ((fragmentCount & 0b10000000) == 0b10000000) {
|
||||||
|
_rxFragmentMaxPacketId = fragmentCount & 0b01111111;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns Zero on Success or the Fragment ID for retransmit or error code
|
||||||
|
uint8_t InverterAbstract::verifyAllFragments()
|
||||||
|
{
|
||||||
|
// All missing
|
||||||
|
if (_rxFragmentLastPacketId == 0) {
|
||||||
|
Serial.println(F("All missing"));
|
||||||
|
return 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Last fragment is missing (thte one with 0x80)
|
||||||
|
if (_rxFragmentMaxPacketId == 0) {
|
||||||
|
Serial.println(F("Last missing"));
|
||||||
|
if (_rxFragmentRetransmitCnt++ < MAX_RETRANSMIT_COUNT) {
|
||||||
|
return _rxFragmentLastPacketId + 1;
|
||||||
|
} else {
|
||||||
|
return 254;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Middle fragment is missing
|
||||||
|
for (uint8_t i = 0; i < _rxFragmentMaxPacketId - 1; i++) {
|
||||||
|
if (_rxFragmentBuffer[i].len == 0) {
|
||||||
|
Serial.println(F("Middle missing"));
|
||||||
|
if (_rxFragmentRetransmitCnt++ < MAX_RETRANSMIT_COUNT) {
|
||||||
|
return i + 1;
|
||||||
|
} else {
|
||||||
|
return 254;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// All fragments are available --> Check CRC
|
||||||
|
uint16_t crc = 0xffff, crcRcv;
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < _rxFragmentMaxPacketId; i++) {
|
||||||
|
if (i == _rxFragmentMaxPacketId - 1) {
|
||||||
|
// Last packet
|
||||||
|
crc = crc16(_rxFragmentBuffer[i].fragment, _rxFragmentBuffer[i].len - 2, crc);
|
||||||
|
crcRcv = (_rxFragmentBuffer[i].fragment[_rxFragmentBuffer[i].len - 2] << 8)
|
||||||
|
| (_rxFragmentBuffer[i].fragment[_rxFragmentBuffer[i].len - 1]);
|
||||||
|
} else {
|
||||||
|
crc = crc16(_rxFragmentBuffer[i].fragment, _rxFragmentBuffer[i].len, crc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (crc != crcRcv) {
|
||||||
|
return 253;
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: hier muss noch ein check bzgl. packet type usw rein (ist ja nicht alles statistik)
|
||||||
|
// Move all fragments into target buffer
|
||||||
|
memset(_payloadStats, 0, MAX_RF_FRAGMENT_COUNT * MAX_RF_PAYLOAD_SIZE);
|
||||||
|
uint8_t offs = 0;
|
||||||
|
for (uint8_t i = 0; i < _rxFragmentMaxPacketId; i++) {
|
||||||
|
memcpy(&_payloadStats[offs], _rxFragmentBuffer[i].fragment, _rxFragmentBuffer[i].len);
|
||||||
|
offs += (_rxFragmentBuffer[i].len);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t InverterAbstract::getChannelCount()
|
||||||
|
{
|
||||||
|
const byteAssign_t* b = getByteAssignment();
|
||||||
|
uint8_t cnt = 0;
|
||||||
|
for (uint8_t pos = 0; pos < sizeof(b) / sizeof(byteAssign_t); pos++) {
|
||||||
|
if (b[pos].ch > cnt) {
|
||||||
|
cnt = b[pos].ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t InverterAbstract::getChannelMaxPower(uint8_t channel)
|
||||||
|
{
|
||||||
|
// todo;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t InverterAbstract::getAssignIdxByChannelField(uint8_t channel, uint8_t fieldId)
|
||||||
|
{
|
||||||
|
const byteAssign_t* b = getByteAssignment();
|
||||||
|
|
||||||
|
uint8_t pos;
|
||||||
|
for (pos = 0; pos < sizeof(b) / sizeof(byteAssign_t); pos++) {
|
||||||
|
if (b[pos].ch == channel && b[pos].fieldId == fieldId) {
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
float InverterAbstract::getValue(uint8_t channel, uint8_t fieldId)
|
||||||
|
{
|
||||||
|
uint8_t pos = getAssignIdxByChannelField(channel, fieldId);
|
||||||
|
if (pos = 0xff) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const byteAssign_t* b = getByteAssignment();
|
||||||
|
|
||||||
|
uint8_t ptr = b[pos].start;
|
||||||
|
uint8_t end = ptr + b[pos].num;
|
||||||
|
uint16_t div = b[pos].div;
|
||||||
|
|
||||||
|
if (CMD_CALC != div) {
|
||||||
|
// Value is a static value
|
||||||
|
uint32_t val = 0;
|
||||||
|
do {
|
||||||
|
val <<= 8;
|
||||||
|
val |= _payloadStats[ptr];
|
||||||
|
} while (++ptr != end);
|
||||||
|
|
||||||
|
return (float)(val) / (float)(div);
|
||||||
|
} else {
|
||||||
|
// Value has to be calculated
|
||||||
|
return calcFunctions[b[pos].start].func(this, b[pos].num);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InverterAbstract::hasValue(uint8_t channel, uint8_t fieldId)
|
||||||
|
{
|
||||||
|
uint8_t pos = getAssignIdxByChannelField(channel, fieldId);
|
||||||
|
return pos != 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* InverterAbstract::getUnit(uint8_t channel, uint8_t fieldId)
|
||||||
|
{
|
||||||
|
uint8_t pos = getAssignIdxByChannelField(channel, fieldId);
|
||||||
|
const byteAssign_t* b = getByteAssignment();
|
||||||
|
|
||||||
|
return units[b[pos].unitId];
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* InverterAbstract::getName(uint8_t channel, uint8_t fieldId)
|
||||||
|
{
|
||||||
|
uint8_t pos = getAssignIdxByChannelField(channel, fieldId);
|
||||||
|
const byteAssign_t* b = getByteAssignment();
|
||||||
|
|
||||||
|
return fields[b[pos].fieldId];
|
||||||
|
}
|
||||||
|
|
||||||
|
static float calcYieldTotalCh0(InverterAbstract* iv, uint8_t arg0)
|
||||||
|
{
|
||||||
|
float yield = 0;
|
||||||
|
for (uint8_t i = 1; i <= iv->getChannelCount(); i++) {
|
||||||
|
yield += iv->getValue(i, FLD_YT);
|
||||||
|
}
|
||||||
|
return yield;
|
||||||
|
}
|
||||||
|
|
||||||
|
static float calcYieldDayCh0(InverterAbstract* iv, uint8_t arg0)
|
||||||
|
{
|
||||||
|
float yield = 0;
|
||||||
|
for (uint8_t i = 1; i <= iv->getChannelCount(); i++) {
|
||||||
|
yield += iv->getValue(i, FLD_YD);
|
||||||
|
}
|
||||||
|
return yield;
|
||||||
|
}
|
||||||
|
|
||||||
|
// arg0 = channel of source
|
||||||
|
static float calcUdcCh(InverterAbstract* iv, uint8_t arg0)
|
||||||
|
{
|
||||||
|
return iv->getValue(arg0, FLD_UDC);
|
||||||
|
}
|
||||||
|
|
||||||
|
static float calcPowerDcCh0(InverterAbstract* iv, uint8_t arg0)
|
||||||
|
{
|
||||||
|
float dcPower = 0;
|
||||||
|
for (uint8_t i = 1; i <= iv->getChannelCount(); i++) {
|
||||||
|
dcPower += iv->getValue(i, FLD_PDC);
|
||||||
|
}
|
||||||
|
return dcPower;
|
||||||
|
}
|
||||||
|
|
||||||
|
// arg0 = channel
|
||||||
|
static float calcEffiencyCh0(InverterAbstract* iv, uint8_t arg0)
|
||||||
|
{
|
||||||
|
float acPower = iv->getValue(CH0, FLD_PAC);
|
||||||
|
float dcPower = 0;
|
||||||
|
for (uint8_t i = 1; i <= iv->getChannelCount(); i++) {
|
||||||
|
dcPower += iv->getValue(i, FLD_PDC);
|
||||||
|
}
|
||||||
|
if (dcPower > 0) {
|
||||||
|
return acPower / dcPower * 100.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// arg0 = channel
|
||||||
|
static float calcIrradiation(InverterAbstract* iv, uint8_t arg0)
|
||||||
|
{
|
||||||
|
if (NULL != iv) {
|
||||||
|
if (iv->getChannelMaxPower(arg0) > 0)
|
||||||
|
return iv->getValue(arg0, FLD_PDC) / iv->getChannelMaxPower(arg0) * 100.0f;
|
||||||
|
}
|
||||||
|
return 0.0;
|
||||||
}
|
}
|
||||||
@ -60,6 +60,35 @@ typedef struct {
|
|||||||
uint16_t div; // divisor / calc command
|
uint16_t div; // divisor / calc command
|
||||||
} byteAssign_t;
|
} byteAssign_t;
|
||||||
|
|
||||||
|
#define MAX_RF_FRAGMENT_COUNT 5
|
||||||
|
#define MAX_RETRANSMIT_COUNT 5
|
||||||
|
|
||||||
|
class InverterAbstract;
|
||||||
|
|
||||||
|
// prototypes
|
||||||
|
static float calcYieldTotalCh0(InverterAbstract* iv, uint8_t arg0);
|
||||||
|
static float calcYieldDayCh0(InverterAbstract* iv, uint8_t arg0);
|
||||||
|
static float calcUdcCh(InverterAbstract* iv, uint8_t arg0);
|
||||||
|
static float calcPowerDcCh0(InverterAbstract* iv, uint8_t arg0);
|
||||||
|
static float calcEffiencyCh0(InverterAbstract* iv, uint8_t arg0);
|
||||||
|
static float calcIrradiation(InverterAbstract* iv, uint8_t arg0);
|
||||||
|
|
||||||
|
using func_t = float(InverterAbstract*, uint8_t);
|
||||||
|
|
||||||
|
struct calcFunc_t {
|
||||||
|
uint8_t funcId; // unique id
|
||||||
|
func_t* func; // function pointer
|
||||||
|
};
|
||||||
|
|
||||||
|
const calcFunc_t calcFunctions[] = {
|
||||||
|
{ CALC_YT_CH0, &calcYieldTotalCh0 },
|
||||||
|
{ CALC_YD_CH0, &calcYieldDayCh0 },
|
||||||
|
{ CALC_UDC_CH, &calcUdcCh },
|
||||||
|
{ CALC_PDC_CH0, &calcPowerDcCh0 },
|
||||||
|
{ CALC_EFF_CH0, &calcEffiencyCh0 },
|
||||||
|
{ CALC_IRR_CH, &calcIrradiation }
|
||||||
|
};
|
||||||
|
|
||||||
class InverterAbstract {
|
class InverterAbstract {
|
||||||
public:
|
public:
|
||||||
void setSerial(uint64_t serial);
|
void setSerial(uint64_t serial);
|
||||||
@ -68,9 +97,26 @@ public:
|
|||||||
const char* name();
|
const char* name();
|
||||||
virtual String typeName() = 0;
|
virtual String typeName() = 0;
|
||||||
virtual const byteAssign_t* getByteAssignment() = 0;
|
virtual const byteAssign_t* getByteAssignment() = 0;
|
||||||
|
uint8_t getChannelCount();
|
||||||
|
uint16_t getChannelMaxPower(uint8_t channel);
|
||||||
|
|
||||||
void clearRxFragmentBuffer();
|
void clearRxFragmentBuffer();
|
||||||
|
void addRxFragment(uint8_t fragment[], uint8_t len);
|
||||||
|
uint8_t verifyAllFragments();
|
||||||
|
|
||||||
|
uint8_t getAssignIdxByChannelField(uint8_t channel, uint8_t fieldId);
|
||||||
|
float getValue(uint8_t channel, uint8_t fieldId);
|
||||||
|
bool hasValue(uint8_t channel, uint8_t fieldId);
|
||||||
|
const char* getUnit(uint8_t channel, uint8_t fieldId);
|
||||||
|
const char* getName(uint8_t channel, uint8_t fieldId);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
serial_u _serial;
|
serial_u _serial;
|
||||||
char _name[MAX_NAME_LENGTH];
|
char _name[MAX_NAME_LENGTH];
|
||||||
|
fragment_t _rxFragmentBuffer[MAX_RF_FRAGMENT_COUNT];
|
||||||
|
uint8_t _rxFragmentMaxPacketId = 0;
|
||||||
|
uint8_t _rxFragmentLastPacketId = 0;
|
||||||
|
uint8_t _rxFragmentRetransmitCnt = 0;
|
||||||
|
|
||||||
|
uint8_t _payloadStats[MAX_RF_FRAGMENT_COUNT * MAX_RF_PAYLOAD_SIZE];
|
||||||
};
|
};
|
||||||
@ -6,3 +6,11 @@ union serial_u {
|
|||||||
uint64_t u64;
|
uint64_t u64;
|
||||||
uint8_t b[8];
|
uint8_t b[8];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// maximum buffer length of packet received / sent to RF24 module
|
||||||
|
#define MAX_RF_PAYLOAD_SIZE 32
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t fragment[MAX_RF_PAYLOAD_SIZE];
|
||||||
|
uint8_t len;
|
||||||
|
} fragment_t;
|
||||||
Loading…
Reference in New Issue
Block a user