First simple implementation of polling algorithm
This commit is contained in:
parent
37dbb343a9
commit
9bfe6a9e63
@ -1,4 +1,6 @@
|
||||
#include "Hoymiles.h"
|
||||
#include "inverters/HM_1CH.h"
|
||||
#include "inverters/HM_2CH.h"
|
||||
#include "inverters/HM_4CH.h"
|
||||
#include <Arduino.h>
|
||||
#include <Every.h>
|
||||
@ -15,6 +17,33 @@ void HoymilesClass::init()
|
||||
void HoymilesClass::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)
|
||||
@ -23,6 +52,12 @@ std::shared_ptr<InverterAbstract> HoymilesClass::addInverter(const char* name, u
|
||||
if (HM_4CH::isValidSerial(serial)) {
|
||||
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) {
|
||||
i->setSerial(serial);
|
||||
@ -78,7 +113,9 @@ std::shared_ptr<InverterAbstract> HoymilesClass::getInverterByFragment(fragment_
|
||||
|
||||
void HoymilesClass::removeInverterByPos(uint8_t pos)
|
||||
{
|
||||
_inverters.erase(_inverters.begin() + pos);
|
||||
if (pos < _inverters.size()) {
|
||||
_inverters.erase(_inverters.begin() + pos);
|
||||
}
|
||||
}
|
||||
|
||||
size_t HoymilesClass::getNumInverters()
|
||||
|
||||
@ -35,9 +35,6 @@ void HoymilesRadio::loop()
|
||||
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) {
|
||||
Serial.println(F("Interrupt received"));
|
||||
while (_radio->available()) {
|
||||
@ -45,7 +42,6 @@ void HoymilesRadio::loop()
|
||||
fragment_t* f;
|
||||
f = _rxBuffer.getFront();
|
||||
memset(f->fragment, 0xcc, MAX_RF_PAYLOAD_SIZE);
|
||||
f->rxCh = _rxChLst[_rxChIdx];
|
||||
f->len = _radio->getDynamicPayloadSize();
|
||||
if (f->len > MAX_RF_PAYLOAD_SIZE)
|
||||
f->len = MAX_RF_PAYLOAD_SIZE;
|
||||
@ -64,23 +60,56 @@ void HoymilesRadio::loop()
|
||||
if (!_rxBuffer.empty()) {
|
||||
fragment_t* f = _rxBuffer.getBack();
|
||||
if (checkFragmentCrc(f)) {
|
||||
Serial.println("Frame Ok");
|
||||
std::shared_ptr<InverterAbstract> inv = Hoymiles.getInverterByFragment(f);
|
||||
|
||||
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 {
|
||||
Serial.println("Inverter Not found!");
|
||||
Serial.println(F("Inverter Not found!"));
|
||||
}
|
||||
|
||||
} else {
|
||||
Serial.println("Frame kaputt");
|
||||
Serial.println(F("Frame kaputt"));
|
||||
}
|
||||
|
||||
// Remove paket from buffer even it was corrupted
|
||||
_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)
|
||||
@ -99,6 +128,11 @@ void HoymilesRadio::setDtuSerial(uint64_t serial)
|
||||
openReadingPipe();
|
||||
}
|
||||
|
||||
bool HoymilesRadio::isIdle()
|
||||
{
|
||||
return !_busyFlag;
|
||||
}
|
||||
|
||||
void HoymilesRadio::openReadingPipe()
|
||||
{
|
||||
serial_u s;
|
||||
@ -106,6 +140,13 @@ void HoymilesRadio::openReadingPipe()
|
||||
_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;
|
||||
@ -118,6 +159,13 @@ uint8_t HoymilesRadio::getRxNxtChannel()
|
||||
return _rxChLst[_rxChIdx];
|
||||
}
|
||||
|
||||
uint8_t HoymilesRadio::getTxNxtChannel()
|
||||
{
|
||||
if (++_txChIdx >= 1)
|
||||
_txChIdx = 0;
|
||||
return _txChLst[_txChIdx];
|
||||
}
|
||||
|
||||
bool HoymilesRadio::switchRxCh(uint8_t addLoop)
|
||||
{
|
||||
_rxLoopCnt += addLoop;
|
||||
@ -144,8 +192,99 @@ serial_u HoymilesRadio::convertSerialToRadioId(serial_u serial)
|
||||
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)
|
||||
{
|
||||
uint8_t crc = crc8(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
|
||||
|
||||
#include "CircularBuffer.h"
|
||||
#include "TimeoutHelper.h"
|
||||
#include "inverters/InverterAbstract.h"
|
||||
#include "types.h"
|
||||
#include <RF24.h>
|
||||
#include <memory>
|
||||
#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
|
||||
#define FRAGMENT_BUFFER_SIZE 30
|
||||
|
||||
typedef struct {
|
||||
uint8_t rxCh;
|
||||
uint8_t fragment[MAX_RF_PAYLOAD_SIZE];
|
||||
uint8_t len;
|
||||
} fragment_t;
|
||||
|
||||
|
||||
class HoymilesRadio {
|
||||
public:
|
||||
@ -27,22 +22,40 @@ public:
|
||||
serial_u DtuSerial();
|
||||
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:
|
||||
void ARDUINO_ISR_ATTR handleIntr();
|
||||
static serial_u convertSerialToRadioId(serial_u serial);
|
||||
static void convertSerialToPacketId(uint8_t buffer[], serial_u serial);
|
||||
uint8_t getRxNxtChannel();
|
||||
uint8_t getTxNxtChannel();
|
||||
bool switchRxCh(uint8_t addLoop = 0);
|
||||
void openReadingPipe();
|
||||
void openWritingPipe(serial_u serial);
|
||||
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;
|
||||
uint8_t _rxChLst[4] = { 3, 23, 61, 75 };
|
||||
uint8_t _rxChIdx;
|
||||
uint16_t _rxLoopCnt;
|
||||
|
||||
uint8_t _txChLst[1] = { 40 };
|
||||
uint8_t _txChIdx;
|
||||
|
||||
volatile bool _packetReceived;
|
||||
|
||||
CircularBuffer<fragment_t, FRAGMENT_BUFFER_SIZE> _rxBuffer;
|
||||
TimeoutHelper _rxTimeout;
|
||||
|
||||
serial_u _dtuSerial;
|
||||
serial_u _activeSerial;
|
||||
|
||||
bool _busyFlag = false;
|
||||
};
|
||||
@ -1,4 +1,5 @@
|
||||
#include "InverterAbstract.h"
|
||||
#include "crc.h"
|
||||
#include <cstring>
|
||||
|
||||
void InverterAbstract::setSerial(uint64_t serial)
|
||||
@ -14,7 +15,11 @@ uint64_t InverterAbstract::serial()
|
||||
void InverterAbstract::setName(const char* 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()
|
||||
@ -24,5 +29,230 @@ const char* InverterAbstract::name()
|
||||
|
||||
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
|
||||
} 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 {
|
||||
public:
|
||||
void setSerial(uint64_t serial);
|
||||
@ -68,9 +97,26 @@ public:
|
||||
const char* name();
|
||||
virtual String typeName() = 0;
|
||||
virtual const byteAssign_t* getByteAssignment() = 0;
|
||||
uint8_t getChannelCount();
|
||||
uint16_t getChannelMaxPower(uint8_t channel);
|
||||
|
||||
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:
|
||||
serial_u _serial;
|
||||
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;
|
||||
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