mirror of https://github.com/lumapu/ahoy.git
committed by
GitHub
101 changed files with 10691 additions and 4199 deletions
@ -0,0 +1,360 @@ |
|||
diff --git a/src/GxEPD2_EPD.cpp b/src/GxEPD2_EPD.cpp
|
|||
index 1588444..592869b 100644
|
|||
--- a/src/GxEPD2_EPD.cpp
|
|||
+++ b/src/GxEPD2_EPD.cpp
|
|||
@@ -19,9 +19,9 @@
|
|||
|
|||
GxEPD2_EPD::GxEPD2_EPD(int16_t cs, int16_t dc, int16_t rst, int16_t busy, int16_t busy_level, uint32_t busy_timeout, |
|||
uint16_t w, uint16_t h, GxEPD2::Panel p, bool c, bool pu, bool fpu) : |
|||
- WIDTH(w), HEIGHT(h), panel(p), hasColor(c), hasPartialUpdate(pu), hasFastPartialUpdate(fpu),
|
|||
+ WIDTH(w), HEIGHT(h), panel(p), hasColor(c), hasPartialUpdate(pu), hasFastPartialUpdate(fpu), _sck(-1), _mosi(-1),
|
|||
_cs(cs), _dc(dc), _rst(rst), _busy(busy), _busy_level(busy_level), _busy_timeout(busy_timeout), _diag_enabled(false), |
|||
- _pSPIx(&SPI), _spi_settings(4000000, MSBFIRST, SPI_MODE0)
|
|||
+ _spi_settings(4000000, MSBFIRST, SPI_MODE0)
|
|||
{ |
|||
_initial_write = true; |
|||
_initial_refresh = true; |
|||
@@ -71,27 +71,30 @@ void GxEPD2_EPD::init(uint32_t serial_diag_bitrate, bool initial, uint16_t reset
|
|||
{ |
|||
pinMode(_busy, INPUT); |
|||
} |
|||
- _pSPIx->begin();
|
|||
- if (_busy == MISO) // may be overridden
|
|||
- {
|
|||
- pinMode(_busy, INPUT);
|
|||
- }
|
|||
- if (_dc == MISO) // may be overridden, TTGO T5 V2.66
|
|||
- {
|
|||
- pinMode(_dc, OUTPUT);
|
|||
- }
|
|||
- if (_cs == MISO) // may be overridden
|
|||
+ if (_sck < 0) SPI.begin();
|
|||
+}
|
|||
+
|
|||
+void GxEPD2_EPD::init(int16_t sck, int16_t mosi, uint32_t serial_diag_bitrate, bool initial, uint16_t reset_duration, bool pulldown_rst_mode)
|
|||
+{
|
|||
+ if ((sck >= 0) && (mosi >= 0))
|
|||
{ |
|||
- pinMode(_cs, INPUT);
|
|||
- }
|
|||
+ _sck = sck;
|
|||
+ _mosi = mosi;
|
|||
+ digitalWrite(_sck, LOW);
|
|||
+ digitalWrite(_mosi, LOW);
|
|||
+ pinMode(_sck, OUTPUT);
|
|||
+ pinMode(_mosi, OUTPUT);
|
|||
+ } else _sck = -1;
|
|||
+ init(serial_diag_bitrate, initial, reset_duration, pulldown_rst_mode);
|
|||
} |
|||
|
|||
void GxEPD2_EPD::end() |
|||
{ |
|||
- _pSPIx->end();
|
|||
if (_cs >= 0) pinMode(_cs, INPUT); |
|||
if (_dc >= 0) pinMode(_dc, INPUT); |
|||
if (_rst >= 0) pinMode(_rst, INPUT); |
|||
+ if (_sck >= 0) pinMode(_sck, INPUT);
|
|||
+ if (_mosi >= 0) pinMode(_mosi, INPUT);
|
|||
} |
|||
|
|||
void GxEPD2_EPD::setBusyCallback(void (*busyCallback)(const void*), const void* busy_callback_parameter) |
|||
@@ -100,12 +103,6 @@ void GxEPD2_EPD::setBusyCallback(void (*busyCallback)(const void*), const void*
|
|||
_busy_callback_parameter = busy_callback_parameter; |
|||
} |
|||
|
|||
-void GxEPD2_EPD::selectSPI(SPIClass& spi, SPISettings spi_settings)
|
|||
-{
|
|||
- _pSPIx = &spi;
|
|||
- _spi_settings = spi_settings;
|
|||
-}
|
|||
-
|
|||
void GxEPD2_EPD::_reset() |
|||
{ |
|||
if (_rst >= 0) |
|||
@@ -174,115 +169,201 @@ void GxEPD2_EPD::_waitWhileBusy(const char* comment, uint16_t busy_time)
|
|||
|
|||
void GxEPD2_EPD::_writeCommand(uint8_t c) |
|||
{ |
|||
- _pSPIx->beginTransaction(_spi_settings);
|
|||
+ _beginTransaction(_spi_settings);
|
|||
if (_dc >= 0) digitalWrite(_dc, LOW); |
|||
if (_cs >= 0) digitalWrite(_cs, LOW); |
|||
- _pSPIx->transfer(c);
|
|||
+ _spi_write(c);
|
|||
if (_cs >= 0) digitalWrite(_cs, HIGH); |
|||
if (_dc >= 0) digitalWrite(_dc, HIGH); |
|||
- _pSPIx->endTransaction();
|
|||
+ _endTransaction();
|
|||
} |
|||
|
|||
void GxEPD2_EPD::_writeData(uint8_t d) |
|||
{ |
|||
- _pSPIx->beginTransaction(_spi_settings);
|
|||
+ _beginTransaction(_spi_settings);
|
|||
if (_cs >= 0) digitalWrite(_cs, LOW); |
|||
- _pSPIx->transfer(d);
|
|||
+ _spi_write(d);
|
|||
if (_cs >= 0) digitalWrite(_cs, HIGH); |
|||
- _pSPIx->endTransaction();
|
|||
+ _endTransaction();
|
|||
} |
|||
|
|||
void GxEPD2_EPD::_writeData(const uint8_t* data, uint16_t n) |
|||
{ |
|||
- _pSPIx->beginTransaction(_spi_settings);
|
|||
+ _beginTransaction(_spi_settings);
|
|||
if (_cs >= 0) digitalWrite(_cs, LOW); |
|||
- for (uint16_t i = 0; i < n; i++)
|
|||
+ for (uint8_t i = 0; i < n; i++)
|
|||
{ |
|||
- _pSPIx->transfer(*data++);
|
|||
+ _spi_write(*data++);
|
|||
} |
|||
if (_cs >= 0) digitalWrite(_cs, HIGH); |
|||
- _pSPIx->endTransaction();
|
|||
+ _endTransaction();
|
|||
} |
|||
|
|||
void GxEPD2_EPD::_writeDataPGM(const uint8_t* data, uint16_t n, int16_t fill_with_zeroes) |
|||
{ |
|||
- _pSPIx->beginTransaction(_spi_settings);
|
|||
+ _beginTransaction(_spi_settings);
|
|||
if (_cs >= 0) digitalWrite(_cs, LOW); |
|||
- for (uint16_t i = 0; i < n; i++)
|
|||
+ for (uint8_t i = 0; i < n; i++)
|
|||
{ |
|||
- _pSPIx->transfer(pgm_read_byte(&*data++));
|
|||
+ _spi_write(pgm_read_byte(&*data++));
|
|||
} |
|||
while (fill_with_zeroes > 0) |
|||
{ |
|||
- _pSPIx->transfer(0x00);
|
|||
+ _spi_write(0x00);
|
|||
fill_with_zeroes--; |
|||
} |
|||
if (_cs >= 0) digitalWrite(_cs, HIGH); |
|||
- _pSPIx->endTransaction();
|
|||
+ _endTransaction();
|
|||
} |
|||
|
|||
void GxEPD2_EPD::_writeDataPGM_sCS(const uint8_t* data, uint16_t n, int16_t fill_with_zeroes) |
|||
{ |
|||
- _pSPIx->beginTransaction(_spi_settings);
|
|||
+ _beginTransaction(_spi_settings);
|
|||
for (uint8_t i = 0; i < n; i++) |
|||
{ |
|||
if (_cs >= 0) digitalWrite(_cs, LOW); |
|||
- _pSPIx->transfer(pgm_read_byte(&*data++));
|
|||
+ _spi_write(pgm_read_byte(&*data++));
|
|||
if (_cs >= 0) digitalWrite(_cs, HIGH); |
|||
} |
|||
while (fill_with_zeroes > 0) |
|||
{ |
|||
if (_cs >= 0) digitalWrite(_cs, LOW); |
|||
- _pSPIx->transfer(0x00);
|
|||
+ _spi_write(0x00);
|
|||
fill_with_zeroes--; |
|||
if (_cs >= 0) digitalWrite(_cs, HIGH); |
|||
} |
|||
- _pSPIx->endTransaction();
|
|||
+ _endTransaction();
|
|||
} |
|||
|
|||
void GxEPD2_EPD::_writeCommandData(const uint8_t* pCommandData, uint8_t datalen) |
|||
{ |
|||
- _pSPIx->beginTransaction(_spi_settings);
|
|||
+ _beginTransaction(_spi_settings);
|
|||
if (_dc >= 0) digitalWrite(_dc, LOW); |
|||
if (_cs >= 0) digitalWrite(_cs, LOW); |
|||
- _pSPIx->transfer(*pCommandData++);
|
|||
+ _spi_write(*pCommandData++);
|
|||
if (_dc >= 0) digitalWrite(_dc, HIGH); |
|||
for (uint8_t i = 0; i < datalen - 1; i++) // sub the command |
|||
{ |
|||
- _pSPIx->transfer(*pCommandData++);
|
|||
+ _spi_write(*pCommandData++);
|
|||
} |
|||
if (_cs >= 0) digitalWrite(_cs, HIGH); |
|||
- _pSPIx->endTransaction();
|
|||
+ _endTransaction();
|
|||
} |
|||
|
|||
void GxEPD2_EPD::_writeCommandDataPGM(const uint8_t* pCommandData, uint8_t datalen) |
|||
{ |
|||
- _pSPIx->beginTransaction(_spi_settings);
|
|||
+ _beginTransaction(_spi_settings);
|
|||
if (_dc >= 0) digitalWrite(_dc, LOW); |
|||
if (_cs >= 0) digitalWrite(_cs, LOW); |
|||
- _pSPIx->transfer(pgm_read_byte(&*pCommandData++));
|
|||
+ _spi_write(pgm_read_byte(&*pCommandData++));
|
|||
if (_dc >= 0) digitalWrite(_dc, HIGH); |
|||
for (uint8_t i = 0; i < datalen - 1; i++) // sub the command |
|||
{ |
|||
- _pSPIx->transfer(pgm_read_byte(&*pCommandData++));
|
|||
+ _spi_write(pgm_read_byte(&*pCommandData++));
|
|||
} |
|||
if (_cs >= 0) digitalWrite(_cs, HIGH); |
|||
- _pSPIx->endTransaction();
|
|||
+ _endTransaction();
|
|||
} |
|||
|
|||
void GxEPD2_EPD::_startTransfer() |
|||
{ |
|||
- _pSPIx->beginTransaction(_spi_settings);
|
|||
+ _beginTransaction(_spi_settings);
|
|||
if (_cs >= 0) digitalWrite(_cs, LOW); |
|||
} |
|||
|
|||
void GxEPD2_EPD::_transfer(uint8_t value) |
|||
{ |
|||
- _pSPIx->transfer(value);
|
|||
+ _spi_write(value);
|
|||
} |
|||
|
|||
void GxEPD2_EPD::_endTransfer() |
|||
{ |
|||
if (_cs >= 0) digitalWrite(_cs, HIGH); |
|||
- _pSPIx->endTransaction();
|
|||
+ _endTransaction();
|
|||
+}
|
|||
+
|
|||
+void GxEPD2_EPD::_beginTransaction(const SPISettings& settings)
|
|||
+{
|
|||
+ if (_sck < 0) SPI.beginTransaction(settings);
|
|||
+}
|
|||
+
|
|||
+void GxEPD2_EPD::_spi_write(uint8_t data)
|
|||
+{
|
|||
+ if (_sck < 0) SPI.transfer(data);
|
|||
+ else
|
|||
+ {
|
|||
+#if defined (ESP8266)
|
|||
+ yield();
|
|||
+#endif
|
|||
+ for (int i = 0; i < 8; i++)
|
|||
+ {
|
|||
+ digitalWrite(_mosi, (data & 0x80) ? HIGH : LOW);
|
|||
+ data <<= 1;
|
|||
+ digitalWrite(_sck, HIGH);
|
|||
+ digitalWrite(_sck, LOW);
|
|||
+ }
|
|||
+ }
|
|||
+}
|
|||
+
|
|||
+void GxEPD2_EPD::_endTransaction()
|
|||
+{
|
|||
+ if (_sck < 0) SPI.endTransaction();
|
|||
+}
|
|||
+
|
|||
+uint8_t GxEPD2_EPD::_readData()
|
|||
+{
|
|||
+ uint8_t data = 0;
|
|||
+ _beginTransaction(_spi_settings);
|
|||
+ if (_cs >= 0) digitalWrite(_cs, LOW);
|
|||
+ if (_sck < 0)
|
|||
+ {
|
|||
+ data = SPI.transfer(0);
|
|||
+ }
|
|||
+ else
|
|||
+ {
|
|||
+ pinMode(_mosi, INPUT);
|
|||
+ for (int i = 0; i < 8; i++)
|
|||
+ {
|
|||
+ data <<= 1;
|
|||
+ digitalWrite(_sck, HIGH);
|
|||
+ data |= digitalRead(_mosi);
|
|||
+ digitalWrite(_sck, LOW);
|
|||
+ }
|
|||
+ pinMode(_mosi, OUTPUT);
|
|||
+ }
|
|||
+ if (_cs >= 0) digitalWrite(_cs, HIGH);
|
|||
+ _endTransaction();
|
|||
+ return data;
|
|||
+}
|
|||
+
|
|||
+void GxEPD2_EPD::_readData(uint8_t* data, uint16_t n)
|
|||
+{
|
|||
+ _beginTransaction(_spi_settings);
|
|||
+ if (_cs >= 0) digitalWrite(_cs, LOW);
|
|||
+ if (_sck < 0)
|
|||
+ {
|
|||
+ for (uint8_t i = 0; i < n; i++)
|
|||
+ {
|
|||
+ *data++ = SPI.transfer(0);
|
|||
+ }
|
|||
+ }
|
|||
+ else
|
|||
+ {
|
|||
+ pinMode(_mosi, INPUT);
|
|||
+ for (uint8_t i = 0; i < n; i++)
|
|||
+ {
|
|||
+ *data = 0;
|
|||
+ for (int i = 0; i < 8; i++)
|
|||
+ {
|
|||
+ *data <<= 1;
|
|||
+ digitalWrite(_sck, HIGH);
|
|||
+ *data |= digitalRead(_mosi);
|
|||
+ digitalWrite(_sck, LOW);
|
|||
+ }
|
|||
+ data++;
|
|||
+ }
|
|||
+ pinMode(_mosi, OUTPUT);
|
|||
+ }
|
|||
+ if (_cs >= 0) digitalWrite(_cs, HIGH);
|
|||
+ _endTransaction();
|
|||
} |
|||
diff --git a/src/GxEPD2_EPD.h b/src/GxEPD2_EPD.h
|
|||
index ef2318f..50aa961 100644
|
|||
--- a/src/GxEPD2_EPD.h
|
|||
+++ b/src/GxEPD2_EPD.h
|
|||
@@ -8,6 +8,10 @@
|
|||
// Version: see library.properties |
|||
// |
|||
// Library: https://github.com/ZinggJM/GxEPD2 |
|||
+// To use SW SPI with GxEPD2:
|
|||
+// add the special call to the added init method BEFORE the normal init method:
|
|||
+// display.epd2.init(SW_SCK, SW_MOSI, 115200, true, 20, false); // define or replace SW_SCK, SW_MOSI
|
|||
+// display.init(115200); // needed to init upper level
|
|||
|
|||
#ifndef _GxEPD2_EPD_H_ |
|||
#define _GxEPD2_EPD_H_ |
|||
@@ -35,6 +39,7 @@ class GxEPD2_EPD
|
|||
uint16_t w, uint16_t h, GxEPD2::Panel p, bool c, bool pu, bool fpu); |
|||
virtual void init(uint32_t serial_diag_bitrate = 0); // serial_diag_bitrate = 0 : disabled |
|||
virtual void init(uint32_t serial_diag_bitrate, bool initial, uint16_t reset_duration = 10, bool pulldown_rst_mode = false); |
|||
+ virtual void init(int16_t sck, int16_t mosi, uint32_t serial_diag_bitrate, bool initial, uint16_t reset_duration = 20, bool pulldown_rst_mode = false);
|
|||
virtual void end(); // release SPI and control pins |
|||
// Support for Bitmaps (Sprites) to Controller Buffer and to Screen |
|||
virtual void clearScreen(uint8_t value) = 0; // init controller memory and screen (default white) |
|||
@@ -97,7 +102,6 @@ class GxEPD2_EPD
|
|||
{ |
|||
return (a > b ? a : b); |
|||
}; |
|||
- void selectSPI(SPIClass& spi, SPISettings spi_settings);
|
|||
protected: |
|||
void _reset(); |
|||
void _waitWhileBusy(const char* comment = 0, uint16_t busy_time = 5000); |
|||
@@ -111,9 +115,14 @@ class GxEPD2_EPD
|
|||
void _startTransfer(); |
|||
void _transfer(uint8_t value); |
|||
void _endTransfer(); |
|||
+ void _beginTransaction(const SPISettings& settings);
|
|||
+ void _spi_write(uint8_t data);
|
|||
+ void _endTransaction();
|
|||
+ public:
|
|||
+ uint8_t _readData();
|
|||
+ void _readData(uint8_t* data, uint16_t n);
|
|||
protected: |
|||
- int16_t _cs, _dc, _rst, _busy, _busy_level;
|
|||
+ int16_t _cs, _dc, _rst, _busy, _busy_level, _sck, _mosi;;
|
|||
uint32_t _busy_timeout; |
|||
bool _diag_enabled, _pulldown_rst_mode; |
|||
- SPIClass* _pSPIx;
|
|||
SPISettings _spi_settings; |
|||
@@ -123,5 +124,5 @@ class GxEPD2_EPD
|
|||
uint16_t _reset_duration; |
|||
- void (*_busy_callback)(const void*);
|
|||
+ void (*_busy_callback)(const void*);
|
|||
const void* _busy_callback_parameter; |
|||
}; |
|||
|
|||
@ -0,0 +1,981 @@ |
|||
diff --git a/RF24.cpp b/RF24.cpp
|
|||
index c0cc732..b6708d9 100644
|
|||
--- a/RF24.cpp
|
|||
+++ b/RF24.cpp
|
|||
@@ -12,228 +12,24 @@
|
|||
|
|||
/****************************************************************************/ |
|||
|
|||
-void RF24::csn(bool mode)
|
|||
-{
|
|||
-#if defined(RF24_TINY)
|
|||
- if (ce_pin != csn_pin) {
|
|||
- digitalWrite(csn_pin, mode);
|
|||
- }
|
|||
- else {
|
|||
- if (mode == HIGH) {
|
|||
- PORTB |= (1 << PINB2); // SCK->CSN HIGH
|
|||
- delayMicroseconds(RF24_CSN_SETTLE_HIGH_DELAY); // allow csn to settle.
|
|||
- }
|
|||
- else {
|
|||
- PORTB &= ~(1 << PINB2); // SCK->CSN LOW
|
|||
- delayMicroseconds(RF24_CSN_SETTLE_LOW_DELAY); // allow csn to settle
|
|||
- }
|
|||
- }
|
|||
- // Return, CSN toggle complete
|
|||
- return;
|
|||
-
|
|||
-#elif defined(ARDUINO) && !defined(RF24_SPI_TRANSACTIONS)
|
|||
- // Minimum ideal SPI bus speed is 2x data rate
|
|||
- // If we assume 2Mbs data rate and 16Mhz clock, a
|
|||
- // divider of 4 is the minimum we want.
|
|||
- // CLK:BUS 8Mhz:2Mhz, 16Mhz:4Mhz, or 20Mhz:5Mhz
|
|||
-
|
|||
- #if !defined(SOFTSPI)
|
|||
- // applies to SPI_UART and inherent hardware SPI
|
|||
- #if defined(RF24_SPI_PTR)
|
|||
- _spi->setBitOrder(MSBFIRST);
|
|||
- _spi->setDataMode(SPI_MODE0);
|
|||
-
|
|||
- #if !defined(F_CPU) || F_CPU < 20000000
|
|||
- _spi->setClockDivider(SPI_CLOCK_DIV2);
|
|||
- #elif F_CPU < 40000000
|
|||
- _spi->setClockDivider(SPI_CLOCK_DIV4);
|
|||
- #elif F_CPU < 80000000
|
|||
- _spi->setClockDivider(SPI_CLOCK_DIV8);
|
|||
- #elif F_CPU < 160000000
|
|||
- _spi->setClockDivider(SPI_CLOCK_DIV16);
|
|||
- #elif F_CPU < 320000000
|
|||
- _spi->setClockDivider(SPI_CLOCK_DIV32);
|
|||
- #elif F_CPU < 640000000
|
|||
- _spi->setClockDivider(SPI_CLOCK_DIV64);
|
|||
- #elif F_CPU < 1280000000
|
|||
- _spi->setClockDivider(SPI_CLOCK_DIV128);
|
|||
- #else // F_CPU >= 1280000000
|
|||
- #error "Unsupported CPU frequency. Please set correct SPI divider."
|
|||
- #endif // F_CPU to SPI_CLOCK_DIV translation
|
|||
-
|
|||
- #else // !defined(RF24_SPI_PTR)
|
|||
- _SPI.setBitOrder(MSBFIRST);
|
|||
- _SPI.setDataMode(SPI_MODE0);
|
|||
-
|
|||
- #if !defined(F_CPU) || F_CPU < 20000000
|
|||
- _SPI.setClockDivider(SPI_CLOCK_DIV2);
|
|||
- #elif F_CPU < 40000000
|
|||
- _SPI.setClockDivider(SPI_CLOCK_DIV4);
|
|||
- #elif F_CPU < 80000000
|
|||
- _SPI.setClockDivider(SPI_CLOCK_DIV8);
|
|||
- #elif F_CPU < 160000000
|
|||
- _SPI.setClockDivider(SPI_CLOCK_DIV16);
|
|||
- #elif F_CPU < 320000000
|
|||
- _SPI.setClockDivider(SPI_CLOCK_DIV32);
|
|||
- #elif F_CPU < 640000000
|
|||
- _SPI.setClockDivider(SPI_CLOCK_DIV64);
|
|||
- #elif F_CPU < 1280000000
|
|||
- _SPI.setClockDivider(SPI_CLOCK_DIV128);
|
|||
- #else // F_CPU >= 1280000000
|
|||
- #error "Unsupported CPU frequency. Please set correct SPI divider."
|
|||
- #endif // F_CPU to SPI_CLOCK_DIV translation
|
|||
- #endif // !defined(RF24_SPI_PTR)
|
|||
- #endif // !defined(SOFTSPI)
|
|||
-
|
|||
-#elif defined(RF24_RPi)
|
|||
- if (!mode)
|
|||
- _SPI.chipSelect(csn_pin);
|
|||
-#endif // defined(RF24_RPi)
|
|||
-
|
|||
-#if !defined(RF24_LINUX)
|
|||
- digitalWrite(csn_pin, mode);
|
|||
- delayMicroseconds(csDelay);
|
|||
-#else
|
|||
- static_cast<void>(mode); // ignore -Wunused-parameter
|
|||
-#endif // !defined(RF24_LINUX)
|
|||
-}
|
|||
-
|
|||
-/****************************************************************************/
|
|||
-
|
|||
void RF24::ce(bool level) |
|||
{ |
|||
-#ifndef RF24_LINUX
|
|||
- //Allow for 3-pin use on ATTiny
|
|||
- if (ce_pin != csn_pin) {
|
|||
-#endif
|
|||
- digitalWrite(ce_pin, level);
|
|||
-#ifndef RF24_LINUX
|
|||
- }
|
|||
-#endif
|
|||
-}
|
|||
-
|
|||
-/****************************************************************************/
|
|||
-
|
|||
-inline void RF24::beginTransaction()
|
|||
-{
|
|||
-#if defined(RF24_SPI_TRANSACTIONS)
|
|||
- #if defined(RF24_SPI_PTR)
|
|||
- #if defined(RF24_RP2)
|
|||
- _spi->beginTransaction(spi_speed);
|
|||
- #else // ! defined (RF24_RP2)
|
|||
- _spi->beginTransaction(SPISettings(spi_speed, MSBFIRST, SPI_MODE0));
|
|||
- #endif // ! defined (RF24_RP2)
|
|||
- #else // !defined(RF24_SPI_PTR)
|
|||
- _SPI.beginTransaction(SPISettings(spi_speed, MSBFIRST, SPI_MODE0));
|
|||
- #endif // !defined(RF24_SPI_PTR)
|
|||
-#endif // defined (RF24_SPI_TRANSACTIONS)
|
|||
- csn(LOW);
|
|||
-}
|
|||
-
|
|||
-/****************************************************************************/
|
|||
-
|
|||
-inline void RF24::endTransaction()
|
|||
-{
|
|||
- csn(HIGH);
|
|||
-#if defined(RF24_SPI_TRANSACTIONS)
|
|||
- #if defined(RF24_SPI_PTR)
|
|||
- _spi->endTransaction();
|
|||
- #else // !defined(RF24_SPI_PTR)
|
|||
- _SPI.endTransaction();
|
|||
- #endif // !defined(RF24_SPI_PTR)
|
|||
-#endif // defined (RF24_SPI_TRANSACTIONS)
|
|||
+ hal->ce(level);
|
|||
} |
|||
|
|||
/****************************************************************************/ |
|||
|
|||
void RF24::read_register(uint8_t reg, uint8_t* buf, uint8_t len) |
|||
{ |
|||
-#if defined(RF24_LINUX) || defined(RF24_RP2)
|
|||
- beginTransaction(); //configures the spi settings for RPi, locks mutex and setting csn low
|
|||
- uint8_t* prx = spi_rxbuff;
|
|||
- uint8_t* ptx = spi_txbuff;
|
|||
- uint8_t size = static_cast<uint8_t>(len + 1); // Add register value to transmit buffer
|
|||
-
|
|||
- *ptx++ = (R_REGISTER | reg);
|
|||
-
|
|||
- while (len--) {
|
|||
- *ptx++ = RF24_NOP; // Dummy operation, just for reading
|
|||
- }
|
|||
-
|
|||
- #if defined(RF24_RP2)
|
|||
- _spi->transfernb((const uint8_t*)spi_txbuff, spi_rxbuff, size);
|
|||
- #else // !defined (RF24_RP2)
|
|||
- _SPI.transfernb(reinterpret_cast<char*>(spi_txbuff), reinterpret_cast<char*>(spi_rxbuff), size);
|
|||
- #endif // !defined (RF24_RP2)
|
|||
-
|
|||
- status = *prx++; // status is 1st byte of receive buffer
|
|||
-
|
|||
- // decrement before to skip status byte
|
|||
- while (--size) {
|
|||
- *buf++ = *prx++;
|
|||
- }
|
|||
-
|
|||
- endTransaction(); // unlocks mutex and setting csn high
|
|||
-
|
|||
-#else // !defined(RF24_LINUX) && !defined(RF24_RP2)
|
|||
-
|
|||
- beginTransaction();
|
|||
- #if defined(RF24_SPI_PTR)
|
|||
- status = _spi->transfer(R_REGISTER | reg);
|
|||
- while (len--) {
|
|||
- *buf++ = _spi->transfer(0xFF);
|
|||
- }
|
|||
-
|
|||
- #else // !defined(RF24_SPI_PTR)
|
|||
- status = _SPI.transfer(R_REGISTER | reg);
|
|||
- while (len--) {
|
|||
- *buf++ = _SPI.transfer(0xFF);
|
|||
- }
|
|||
-
|
|||
- #endif // !defined(RF24_SPI_PTR)
|
|||
- endTransaction();
|
|||
-#endif // !defined(RF24_LINUX) && !defined(RF24_RP2)
|
|||
+ status = hal->read(R_REGISTER | reg, buf, len);
|
|||
} |
|||
|
|||
/****************************************************************************/ |
|||
|
|||
uint8_t RF24::read_register(uint8_t reg) |
|||
{ |
|||
- uint8_t result;
|
|||
-
|
|||
-#if defined(RF24_LINUX) || defined(RF24_RP2)
|
|||
- beginTransaction();
|
|||
-
|
|||
- uint8_t* prx = spi_rxbuff;
|
|||
- uint8_t* ptx = spi_txbuff;
|
|||
- *ptx++ = (R_REGISTER | reg);
|
|||
- *ptx++ = RF24_NOP; // Dummy operation, just for reading
|
|||
-
|
|||
- #if defined(RF24_RP2)
|
|||
- _spi->transfernb((const uint8_t*)spi_txbuff, spi_rxbuff, 2);
|
|||
- #else // !defined(RF24_RP2)
|
|||
- _SPI.transfernb(reinterpret_cast<char*>(spi_txbuff), reinterpret_cast<char*>(spi_rxbuff), 2);
|
|||
- #endif // !defined(RF24_RP2)
|
|||
-
|
|||
- status = *prx; // status is 1st byte of receive buffer
|
|||
- result = *++prx; // result is 2nd byte of receive buffer
|
|||
-
|
|||
- endTransaction();
|
|||
-#else // !defined(RF24_LINUX) && !defined(RF24_RP2)
|
|||
-
|
|||
- beginTransaction();
|
|||
- #if defined(RF24_SPI_PTR)
|
|||
- status = _spi->transfer(R_REGISTER | reg);
|
|||
- result = _spi->transfer(0xff);
|
|||
-
|
|||
- #else // !defined(RF24_SPI_PTR)
|
|||
- status = _SPI.transfer(R_REGISTER | reg);
|
|||
- result = _SPI.transfer(0xff);
|
|||
-
|
|||
- #endif // !defined(RF24_SPI_PTR)
|
|||
- endTransaction();
|
|||
-#endif // !defined(RF24_LINUX) && !defined(RF24_RP2)
|
|||
-
|
|||
+ uint8_t result = 0xff;
|
|||
+ status = hal->read(R_REGISTER | reg, &result, sizeof(result));
|
|||
return result; |
|||
} |
|||
|
|||
@@ -241,43 +37,7 @@ uint8_t RF24::read_register(uint8_t reg)
|
|||
|
|||
void RF24::write_register(uint8_t reg, const uint8_t* buf, uint8_t len) |
|||
{ |
|||
-#if defined(RF24_LINUX) || defined(RF24_RP2)
|
|||
- beginTransaction();
|
|||
- uint8_t* prx = spi_rxbuff;
|
|||
- uint8_t* ptx = spi_txbuff;
|
|||
- uint8_t size = static_cast<uint8_t>(len + 1); // Add register value to transmit buffer
|
|||
-
|
|||
- *ptx++ = (W_REGISTER | (REGISTER_MASK & reg));
|
|||
- while (len--) {
|
|||
- *ptx++ = *buf++;
|
|||
- }
|
|||
-
|
|||
- #if defined(RF24_RP2)
|
|||
- _spi->transfernb((const uint8_t*)spi_txbuff, spi_rxbuff, size);
|
|||
- #else // !defined(RF24_RP2)
|
|||
- _SPI.transfernb(reinterpret_cast<char*>(spi_txbuff), reinterpret_cast<char*>(spi_rxbuff), size);
|
|||
- #endif // !defined(RF24_RP2)
|
|||
-
|
|||
- status = *prx; // status is 1st byte of receive buffer
|
|||
- endTransaction();
|
|||
-#else // !defined(RF24_LINUX) && !defined(RF24_RP2)
|
|||
-
|
|||
- beginTransaction();
|
|||
- #if defined(RF24_SPI_PTR)
|
|||
- status = _spi->transfer(W_REGISTER | reg);
|
|||
- while (len--) {
|
|||
- _spi->transfer(*buf++);
|
|||
- }
|
|||
-
|
|||
- #else // !defined(RF24_SPI_PTR)
|
|||
- status = _SPI.transfer(W_REGISTER | reg);
|
|||
- while (len--) {
|
|||
- _SPI.transfer(*buf++);
|
|||
- }
|
|||
-
|
|||
- #endif // !defined(RF24_SPI_PTR)
|
|||
- endTransaction();
|
|||
-#endif // !defined(RF24_LINUX) && !defined(RF24_RP2)
|
|||
+ status = hal->write(W_REGISTER | reg, buf, len);
|
|||
} |
|||
|
|||
/****************************************************************************/ |
|||
@@ -288,47 +48,11 @@ void RF24::write_register(uint8_t reg, uint8_t value, bool is_cmd_only)
|
|||
if (reg != RF24_NOP) { // don't print the get_status() operation |
|||
IF_SERIAL_DEBUG(printf_P(PSTR("write_register(%02x)\r\n"), reg)); |
|||
} |
|||
- beginTransaction();
|
|||
-#if defined(RF24_LINUX)
|
|||
- status = _SPI.transfer(W_REGISTER | reg);
|
|||
-#else // !defined(RF24_LINUX) || defined (RF24_RP2)
|
|||
- #if defined(RF24_SPI_PTR)
|
|||
- status = _spi->transfer(W_REGISTER | reg);
|
|||
- #else // !defined (RF24_SPI_PTR)
|
|||
- status = _SPI.transfer(W_REGISTER | reg);
|
|||
- #endif // !defined (RF24_SPI_PTR)
|
|||
-#endif // !defined(RF24_LINUX) || defined(RF24_RP2)
|
|||
- endTransaction();
|
|||
+ status = hal->write(W_REGISTER | reg, nullptr, 0);
|
|||
} |
|||
else { |
|||
IF_SERIAL_DEBUG(printf_P(PSTR("write_register(%02x,%02x)\r\n"), reg, value)); |
|||
-#if defined(RF24_LINUX) || defined(RF24_RP2)
|
|||
- beginTransaction();
|
|||
- uint8_t* prx = spi_rxbuff;
|
|||
- uint8_t* ptx = spi_txbuff;
|
|||
- *ptx++ = (W_REGISTER | reg);
|
|||
- *ptx = value;
|
|||
-
|
|||
- #if defined(RF24_RP2)
|
|||
- _spi->transfernb((const uint8_t*)spi_txbuff, spi_rxbuff, 2);
|
|||
- #else // !defined(RF24_RP2)
|
|||
- _SPI.transfernb(reinterpret_cast<char*>(spi_txbuff), reinterpret_cast<char*>(spi_rxbuff), 2);
|
|||
- #endif // !defined(RF24_RP2)
|
|||
-
|
|||
- status = *prx++; // status is 1st byte of receive buffer
|
|||
- endTransaction();
|
|||
-#else // !defined(RF24_LINUX) && !defined(RF24_RP2)
|
|||
-
|
|||
- beginTransaction();
|
|||
- #if defined(RF24_SPI_PTR)
|
|||
- status = _spi->transfer(W_REGISTER | reg);
|
|||
- _spi->transfer(value);
|
|||
- #else // !defined(RF24_SPI_PTR)
|
|||
- status = _SPI.transfer(W_REGISTER | reg);
|
|||
- _SPI.transfer(value);
|
|||
- #endif // !defined(RF24_SPI_PTR)
|
|||
- endTransaction();
|
|||
-#endif // !defined(RF24_LINUX) && !defined(RF24_RP2)
|
|||
+ status = hal->write(W_REGISTER | reg, &value, sizeof(value));
|
|||
} |
|||
} |
|||
|
|||
@@ -347,60 +71,8 @@ void RF24::write_payload(const void* buf, uint8_t data_len, const uint8_t writeT
|
|||
data_len = rf24_min(data_len, static_cast<uint8_t>(32)); |
|||
} |
|||
|
|||
- //printf("[Writing %u bytes %u blanks]",data_len,blank_len);
|
|||
IF_SERIAL_DEBUG(printf("[Writing %u bytes %u blanks]\n", data_len, blank_len);); |
|||
-
|
|||
-#if defined(RF24_LINUX) || defined(RF24_RP2)
|
|||
- beginTransaction();
|
|||
- uint8_t* prx = spi_rxbuff;
|
|||
- uint8_t* ptx = spi_txbuff;
|
|||
- uint8_t size;
|
|||
- size = static_cast<uint8_t>(data_len + blank_len + 1); // Add register value to transmit buffer
|
|||
-
|
|||
- *ptx++ = writeType;
|
|||
- while (data_len--) {
|
|||
- *ptx++ = *current++;
|
|||
- }
|
|||
-
|
|||
- while (blank_len--) {
|
|||
- *ptx++ = 0;
|
|||
- }
|
|||
-
|
|||
- #if defined(RF24_RP2)
|
|||
- _spi->transfernb((const uint8_t*)spi_txbuff, spi_rxbuff, size);
|
|||
- #else // !defined(RF24_RP2)
|
|||
- _SPI.transfernb(reinterpret_cast<char*>(spi_txbuff), reinterpret_cast<char*>(spi_rxbuff), size);
|
|||
- #endif // !defined(RF24_RP2)
|
|||
-
|
|||
- status = *prx; // status is 1st byte of receive buffer
|
|||
- endTransaction();
|
|||
-
|
|||
-#else // !defined(RF24_LINUX) && !defined(RF24_RP2)
|
|||
-
|
|||
- beginTransaction();
|
|||
- #if defined(RF24_SPI_PTR)
|
|||
- status = _spi->transfer(writeType);
|
|||
- while (data_len--) {
|
|||
- _spi->transfer(*current++);
|
|||
- }
|
|||
-
|
|||
- while (blank_len--) {
|
|||
- _spi->transfer(0);
|
|||
- }
|
|||
-
|
|||
- #else // !defined(RF24_SPI_PTR)
|
|||
- status = _SPI.transfer(writeType);
|
|||
- while (data_len--) {
|
|||
- _SPI.transfer(*current++);
|
|||
- }
|
|||
-
|
|||
- while (blank_len--) {
|
|||
- _SPI.transfer(0);
|
|||
- }
|
|||
-
|
|||
- #endif // !defined(RF24_SPI_PTR)
|
|||
- endTransaction();
|
|||
-#endif // !defined(RF24_LINUX) && !defined(RF24_RP2)
|
|||
+ status = hal->write(writeType, current, data_len, blank_len);
|
|||
} |
|||
|
|||
/****************************************************************************/ |
|||
@@ -421,65 +93,7 @@ void RF24::read_payload(void* buf, uint8_t data_len)
|
|||
//printf("[Reading %u bytes %u blanks]",data_len,blank_len); |
|||
|
|||
IF_SERIAL_DEBUG(printf("[Reading %u bytes %u blanks]\n", data_len, blank_len);); |
|||
-
|
|||
-#if defined(RF24_LINUX) || defined(RF24_RP2)
|
|||
- beginTransaction();
|
|||
- uint8_t* prx = spi_rxbuff;
|
|||
- uint8_t* ptx = spi_txbuff;
|
|||
- uint8_t size;
|
|||
- size = static_cast<uint8_t>(data_len + blank_len + 1); // Add register value to transmit buffer
|
|||
-
|
|||
- *ptx++ = R_RX_PAYLOAD;
|
|||
- while (--size) {
|
|||
- *ptx++ = RF24_NOP;
|
|||
- }
|
|||
-
|
|||
- size = static_cast<uint8_t>(data_len + blank_len + 1); // Size has been lost during while, re affect
|
|||
-
|
|||
- #if defined(RF24_RP2)
|
|||
- _spi->transfernb((const uint8_t*)spi_txbuff, spi_rxbuff, size);
|
|||
- #else // !defined(RF24_RP2)
|
|||
- _SPI.transfernb(reinterpret_cast<char*>(spi_txbuff), reinterpret_cast<char*>(spi_rxbuff), size);
|
|||
- #endif // !defined(RF24_RP2)
|
|||
-
|
|||
- status = *prx++; // 1st byte is status
|
|||
-
|
|||
- if (data_len > 0) {
|
|||
- // Decrement before to skip 1st status byte
|
|||
- while (--data_len) {
|
|||
- *current++ = *prx++;
|
|||
- }
|
|||
-
|
|||
- *current = *prx;
|
|||
- }
|
|||
- endTransaction();
|
|||
-#else // !defined(RF24_LINUX) && !defined(RF24_RP2)
|
|||
-
|
|||
- beginTransaction();
|
|||
- #if defined(RF24_SPI_PTR)
|
|||
- status = _spi->transfer(R_RX_PAYLOAD);
|
|||
- while (data_len--) {
|
|||
- *current++ = _spi->transfer(0xFF);
|
|||
- }
|
|||
-
|
|||
- while (blank_len--) {
|
|||
- _spi->transfer(0xFF);
|
|||
- }
|
|||
-
|
|||
- #else // !defined(RF24_SPI_PTR)
|
|||
- status = _SPI.transfer(R_RX_PAYLOAD);
|
|||
- while (data_len--) {
|
|||
- *current++ = _SPI.transfer(0xFF);
|
|||
- }
|
|||
-
|
|||
- while (blank_len--) {
|
|||
- _SPI.transfer(0xff);
|
|||
- }
|
|||
-
|
|||
- #endif // !defined(RF24_SPI_PTR)
|
|||
- endTransaction();
|
|||
-
|
|||
-#endif // !defined(RF24_LINUX) && !defined(RF24_RP2)
|
|||
+ status = hal->read(R_RX_PAYLOAD, current, data_len, blank_len);
|
|||
} |
|||
|
|||
/****************************************************************************/ |
|||
@@ -577,16 +191,16 @@ uint8_t RF24::sprintf_address_register(char* out_buffer, uint8_t reg, uint8_t qt
|
|||
|
|||
/****************************************************************************/ |
|||
|
|||
-RF24::RF24(rf24_gpio_pin_t _cepin, rf24_gpio_pin_t _cspin, uint32_t _spi_speed)
|
|||
- : ce_pin(_cepin), csn_pin(_cspin), spi_speed(_spi_speed), payload_size(32), _is_p_variant(false), _is_p0_rx(false), addr_width(5), dynamic_payloads_enabled(true), csDelay(5)
|
|||
+RF24::RF24(RF24_hal* _hal)
|
|||
+ : hal(_hal), payload_size(32), _is_p_variant(false), _is_p0_rx(false), addr_width(5), dynamic_payloads_enabled(true), csDelay(5)
|
|||
{ |
|||
_init_obj(); |
|||
} |
|||
|
|||
/****************************************************************************/ |
|||
|
|||
-RF24::RF24(uint32_t _spi_speed)
|
|||
- : ce_pin(RF24_PIN_INVALID), csn_pin(RF24_PIN_INVALID), spi_speed(_spi_speed), payload_size(32), _is_p_variant(false), _is_p0_rx(false), addr_width(5), dynamic_payloads_enabled(true), csDelay(5)
|
|||
+RF24::RF24()
|
|||
+ : hal(nullptr), payload_size(32), _is_p_variant(false), _is_p0_rx(false), addr_width(5), dynamic_payloads_enabled(true), csDelay(5)
|
|||
{ |
|||
_init_obj(); |
|||
} |
|||
@@ -595,16 +209,7 @@ RF24::RF24(uint32_t _spi_speed)
|
|||
|
|||
void RF24::_init_obj() |
|||
{ |
|||
- // Use a pointer on the Arduino platform
|
|||
-
|
|||
-#if defined(RF24_SPI_PTR) && !defined(RF24_RP2)
|
|||
- _spi = &SPI;
|
|||
-#endif // defined (RF24_SPI_PTR)
|
|||
-
|
|||
pipe0_reading_address[0] = 0; |
|||
- if (spi_speed <= 35000) { //Handle old BCM2835 speed constants, default to RF24_SPI_SPEED
|
|||
- spi_speed = RF24_SPI_SPEED;
|
|||
- }
|
|||
} |
|||
|
|||
/****************************************************************************/ |
|||
@@ -677,19 +282,6 @@ static const PROGMEM char* const rf24_pa_dbm_e_str_P[] = {
|
|||
rf24_pa_dbm_e_str_3, |
|||
}; |
|||
|
|||
- #if defined(RF24_LINUX)
|
|||
-static const char rf24_csn_e_str_0[] = "CE0 (PI Hardware Driven)";
|
|||
-static const char rf24_csn_e_str_1[] = "CE1 (PI Hardware Driven)";
|
|||
-static const char rf24_csn_e_str_2[] = "CE2 (PI Hardware Driven)";
|
|||
-static const char rf24_csn_e_str_3[] = "Custom GPIO Software Driven";
|
|||
-static const char* const rf24_csn_e_str_P[] = {
|
|||
- rf24_csn_e_str_0,
|
|||
- rf24_csn_e_str_1,
|
|||
- rf24_csn_e_str_2,
|
|||
- rf24_csn_e_str_3,
|
|||
-};
|
|||
- #endif // defined(RF24_LINUX)
|
|||
-
|
|||
static const PROGMEM char rf24_feature_e_str_on[] = "= Enabled"; |
|||
static const PROGMEM char rf24_feature_e_str_allowed[] = "= Allowed"; |
|||
static const PROGMEM char rf24_feature_e_str_open[] = " open "; |
|||
@@ -704,19 +296,6 @@ static const PROGMEM char* const rf24_feature_e_str_P[] = {
|
|||
|
|||
void RF24::printDetails(void) |
|||
{ |
|||
-
|
|||
- #if defined(RF24_LINUX)
|
|||
- printf("================ SPI Configuration ================\n");
|
|||
- uint8_t bus_ce = static_cast<uint8_t>(csn_pin % 10);
|
|||
- uint8_t bus_numb = static_cast<uint8_t>((csn_pin - bus_ce) / 10);
|
|||
- printf("CSN Pin\t\t= /dev/spidev%d.%d\n", bus_numb, bus_ce);
|
|||
- printf("CE Pin\t\t= Custom GPIO%d\n", ce_pin);
|
|||
- #endif
|
|||
- printf_P(PSTR("SPI Speedz\t= %d Mhz\n"), static_cast<uint8_t>(spi_speed / 1000000)); //Print the SPI speed on non-Linux devices
|
|||
- #if defined(RF24_LINUX)
|
|||
- printf("================ NRF Configuration ================\n");
|
|||
- #endif // defined(RF24_LINUX)
|
|||
-
|
|||
print_status(get_status()); |
|||
|
|||
print_address_register(PSTR("RX_ADDR_P0-1"), RX_ADDR_P0, 2); |
|||
@@ -748,19 +327,6 @@ void RF24::printDetails(void)
|
|||
|
|||
void RF24::printPrettyDetails(void) |
|||
{ |
|||
-
|
|||
- #if defined(RF24_LINUX)
|
|||
- printf("================ SPI Configuration ================\n");
|
|||
- uint8_t bus_ce = static_cast<uint8_t>(csn_pin % 10);
|
|||
- uint8_t bus_numb = static_cast<uint8_t>((csn_pin - bus_ce) / 10);
|
|||
- printf("CSN Pin\t\t\t= /dev/spidev%d.%d\n", bus_numb, bus_ce);
|
|||
- printf("CE Pin\t\t\t= Custom GPIO%d\n", ce_pin);
|
|||
- #endif
|
|||
- printf_P(PSTR("SPI Frequency\t\t= %d Mhz\n"), static_cast<uint8_t>(spi_speed / 1000000)); //Print the SPI speed on non-Linux devices
|
|||
- #if defined(RF24_LINUX)
|
|||
- printf("================ NRF Configuration ================\n");
|
|||
- #endif // defined(RF24_LINUX)
|
|||
-
|
|||
uint8_t channel = getChannel(); |
|||
uint16_t frequency = static_cast<uint16_t>(channel + 2400); |
|||
printf_P(PSTR("Channel\t\t\t= %u (~ %u MHz)\r\n"), channel, frequency); |
|||
@@ -846,11 +412,6 @@ void RF24::printPrettyDetails(void)
|
|||
uint16_t RF24::sprintfPrettyDetails(char* debugging_information) |
|||
{ |
|||
const char* format_string = PSTR( |
|||
- "================ SPI Configuration ================\n"
|
|||
- "CSN Pin\t\t\t= %d\n"
|
|||
- "CE Pin\t\t\t= %d\n"
|
|||
- "SPI Frequency\t\t= %d Mhz\n"
|
|||
- "================ NRF Configuration ================\n"
|
|||
"Channel\t\t\t= %u (~ %u MHz)\n" |
|||
"RF Data Rate\t\t" PRIPSTR "\n" |
|||
"RF Power Amplifier\t" PRIPSTR "\n" |
|||
@@ -870,8 +431,7 @@ uint16_t RF24::sprintfPrettyDetails(char* debugging_information)
|
|||
const char* format_str3 = PSTR("\nPipe %d (" PRIPSTR ") bound\t= 0x"); |
|||
|
|||
uint16_t offset = sprintf_P( |
|||
- debugging_information, format_string, csn_pin, ce_pin,
|
|||
- static_cast<uint8_t>(spi_speed / 1000000), getChannel(),
|
|||
+ debugging_information, format_string, getChannel(),
|
|||
static_cast<uint16_t>(getChannel() + 2400), |
|||
(char*)(pgm_read_ptr(&rf24_datarate_e_str_P[getDataRate()])), |
|||
(char*)(pgm_read_ptr(&rf24_pa_dbm_e_str_P[getPALevel()])), |
|||
@@ -940,87 +500,26 @@ void RF24::encodeRadioDetails(uint8_t* encoded_details)
|
|||
*encoded_details++ = read_register(i); |
|||
} |
|||
} |
|||
- *encoded_details++ = ce_pin >> 4;
|
|||
- *encoded_details++ = ce_pin & 0xFF;
|
|||
- *encoded_details++ = csn_pin >> 4;
|
|||
- *encoded_details++ = csn_pin & 0xFF;
|
|||
- *encoded_details = static_cast<uint8_t>((spi_speed / 1000000) | _BV(_is_p_variant * 4));
|
|||
} |
|||
#endif // !defined(MINIMAL) |
|||
|
|||
/****************************************************************************/ |
|||
-#if defined(RF24_SPI_PTR) || defined(DOXYGEN_FORCED)
|
|||
-// does not apply to RF24_LINUX
|
|||
|
|||
-bool RF24::begin(_SPI* spiBus)
|
|||
+bool RF24::begin(void)
|
|||
{ |
|||
- _spi = spiBus;
|
|||
return _init_pins() && _init_radio(); |
|||
} |
|||
|
|||
/****************************************************************************/ |
|||
|
|||
-bool RF24::begin(_SPI* spiBus, rf24_gpio_pin_t _cepin, rf24_gpio_pin_t _cspin)
|
|||
+bool RF24::begin(RF24_hal* _hal)
|
|||
{ |
|||
- ce_pin = _cepin;
|
|||
- csn_pin = _cspin;
|
|||
- return begin(spiBus);
|
|||
-}
|
|||
-
|
|||
-#endif // defined (RF24_SPI_PTR) || defined (DOXYGEN_FORCED)
|
|||
-
|
|||
-/****************************************************************************/
|
|||
-
|
|||
-bool RF24::begin(rf24_gpio_pin_t _cepin, rf24_gpio_pin_t _cspin)
|
|||
-{
|
|||
- ce_pin = _cepin;
|
|||
- csn_pin = _cspin;
|
|||
+ hal = _hal;
|
|||
return begin(); |
|||
} |
|||
|
|||
/****************************************************************************/ |
|||
|
|||
-bool RF24::begin(void)
|
|||
-{
|
|||
-#if defined(RF24_LINUX)
|
|||
- #if defined(RF24_RPi)
|
|||
- switch (csn_pin) { // Ensure valid hardware CS pin
|
|||
- case 0: break;
|
|||
- case 1: break;
|
|||
- // Allow BCM2835 enums for RPi
|
|||
- case 8: csn_pin = 0; break;
|
|||
- case 7: csn_pin = 1; break;
|
|||
- case 18: csn_pin = 10; break; // to make it work on SPI1
|
|||
- case 17: csn_pin = 11; break;
|
|||
- case 16: csn_pin = 12; break;
|
|||
- default: csn_pin = 0; break;
|
|||
- }
|
|||
- #endif // RF24_RPi
|
|||
-
|
|||
- _SPI.begin(csn_pin, spi_speed);
|
|||
-
|
|||
-#elif defined(XMEGA_D3)
|
|||
- _spi->begin(csn_pin);
|
|||
-
|
|||
-#elif defined(RF24_RP2)
|
|||
- _spi = new SPI();
|
|||
- _spi->begin(PICO_DEFAULT_SPI ? spi1 : spi0);
|
|||
-
|
|||
-#else // using an Arduino platform || defined (LITTLEWIRE)
|
|||
-
|
|||
- #if defined(RF24_SPI_PTR)
|
|||
- _spi->begin();
|
|||
- #else // !defined(RF24_SPI_PTR)
|
|||
- _SPI.begin();
|
|||
- #endif // !defined(RF24_SPI_PTR)
|
|||
-
|
|||
-#endif // !defined(XMEGA_D3) && !defined(RF24_LINUX)
|
|||
-
|
|||
- return _init_pins() && _init_radio();
|
|||
-}
|
|||
-
|
|||
-/****************************************************************************/
|
|||
-
|
|||
bool RF24::_init_pins() |
|||
{ |
|||
if (!isValid()) { |
|||
@@ -1028,46 +527,7 @@ bool RF24::_init_pins()
|
|||
return false; |
|||
} |
|||
|
|||
-#if defined(RF24_LINUX)
|
|||
-
|
|||
- #if defined(MRAA)
|
|||
- GPIO();
|
|||
- gpio.begin(ce_pin, csn_pin);
|
|||
- #endif
|
|||
-
|
|||
- pinMode(ce_pin, OUTPUT);
|
|||
- ce(LOW);
|
|||
- delay(100);
|
|||
-
|
|||
-#elif defined(LITTLEWIRE)
|
|||
- pinMode(csn_pin, OUTPUT);
|
|||
- csn(HIGH);
|
|||
-
|
|||
-#elif defined(XMEGA_D3)
|
|||
- if (ce_pin != csn_pin) {
|
|||
- pinMode(ce_pin, OUTPUT);
|
|||
- };
|
|||
- ce(LOW);
|
|||
- csn(HIGH);
|
|||
- delay(200);
|
|||
-
|
|||
-#else // using an Arduino platform
|
|||
-
|
|||
- // Initialize pins
|
|||
- if (ce_pin != csn_pin) {
|
|||
- pinMode(ce_pin, OUTPUT);
|
|||
- pinMode(csn_pin, OUTPUT);
|
|||
- }
|
|||
-
|
|||
- ce(LOW);
|
|||
- csn(HIGH);
|
|||
-
|
|||
- #if defined(__ARDUINO_X86__)
|
|||
- delay(100);
|
|||
- #endif
|
|||
-#endif // !defined(XMEGA_D3) && !defined(LITTLEWIRE) && !defined(RF24_LINUX)
|
|||
-
|
|||
- return true; // assuming pins are connected properly
|
|||
+ return hal->begin();
|
|||
} |
|||
|
|||
/****************************************************************************/ |
|||
@@ -1151,7 +611,7 @@ bool RF24::isChipConnected()
|
|||
|
|||
bool RF24::isValid() |
|||
{ |
|||
- return ce_pin != RF24_PIN_INVALID && csn_pin != RF24_PIN_INVALID;
|
|||
+ return hal != nullptr;
|
|||
} |
|||
|
|||
/****************************************************************************/ |
|||
@@ -1676,15 +1136,8 @@ void RF24::closeReadingPipe(uint8_t pipe)
|
|||
|
|||
void RF24::toggle_features(void) |
|||
{ |
|||
- beginTransaction();
|
|||
-#if defined(RF24_SPI_PTR)
|
|||
- status = _spi->transfer(ACTIVATE);
|
|||
- _spi->transfer(0x73);
|
|||
-#else
|
|||
- status = _SPI.transfer(ACTIVATE);
|
|||
- _SPI.transfer(0x73);
|
|||
-#endif
|
|||
- endTransaction();
|
|||
+ uint8_t value = 0x73;
|
|||
+ status = hal->write(ACTIVATE, &value, sizeof(value));
|
|||
} |
|||
|
|||
/****************************************************************************/ |
|||
diff --git a/RF24.h b/RF24.h
|
|||
index dbd32ae..f774bba 100644
|
|||
--- a/RF24.h
|
|||
+++ b/RF24.h
|
|||
@@ -16,12 +16,7 @@
|
|||
#define __RF24_H__ |
|||
|
|||
#include "RF24_config.h" |
|||
-
|
|||
-#if defined(RF24_LINUX) || defined(LITTLEWIRE)
|
|||
- #include "utility/includes.h"
|
|||
-#elif defined SOFTSPI
|
|||
- #include <DigitalIO.h>
|
|||
-#endif
|
|||
+#include "RF24_hal.h"
|
|||
|
|||
/** |
|||
* @defgroup PALevel Power Amplifier level |
|||
@@ -115,29 +110,8 @@ typedef enum
|
|||
class RF24 |
|||
{ |
|||
private: |
|||
-#ifdef SOFTSPI
|
|||
- SoftSPI<SOFT_SPI_MISO_PIN, SOFT_SPI_MOSI_PIN, SOFT_SPI_SCK_PIN, SPI_MODE> spi;
|
|||
-#elif defined(SPI_UART)
|
|||
- SPIUARTClass uspi;
|
|||
-#endif
|
|||
-
|
|||
-#if defined(RF24_LINUX) || defined(XMEGA_D3) /* XMEGA can use SPI class */
|
|||
- SPI spi;
|
|||
-#endif // defined (RF24_LINUX) || defined (XMEGA_D3)
|
|||
-#if defined(RF24_SPI_PTR)
|
|||
- _SPI* _spi;
|
|||
-#endif // defined (RF24_SPI_PTR)
|
|||
-#if defined(MRAA)
|
|||
- GPIO gpio;
|
|||
-#endif
|
|||
+ RF24_hal *hal;
|
|||
|
|||
- rf24_gpio_pin_t ce_pin; /* "Chip Enable" pin, activates the RX or TX role */
|
|||
- rf24_gpio_pin_t csn_pin; /* SPI Chip select */
|
|||
- uint32_t spi_speed; /* SPI Bus Speed */
|
|||
-#if defined(RF24_LINUX) || defined(XMEGA_D3) || defined(RF24_RP2)
|
|||
- uint8_t spi_rxbuff[32 + 1]; //SPI receive buffer (payload max 32 bytes)
|
|||
- uint8_t spi_txbuff[32 + 1]; //SPI transmit buffer (payload max 32 bytes + 1 byte for the command)
|
|||
-#endif
|
|||
uint8_t status; /* The status byte returned from every SPI transaction */ |
|||
uint8_t payload_size; /* Fixed size of payloads */ |
|||
uint8_t pipe0_reading_address[5]; /* Last address set on pipe 0 for reading. */ |
|||
@@ -146,16 +120,6 @@ private:
|
|||
bool _is_p0_rx; /* For keeping track of pipe 0's usage in user-triggered RX mode. */ |
|||
|
|||
protected: |
|||
- /**
|
|||
- * SPI transactions
|
|||
- *
|
|||
- * Common code for SPI transactions including CSN toggle
|
|||
- *
|
|||
- */
|
|||
- inline void beginTransaction();
|
|||
-
|
|||
- inline void endTransaction();
|
|||
-
|
|||
/** Whether ack payloads are enabled. */ |
|||
bool ack_payloads_enabled; |
|||
/** The address width to use (3, 4 or 5 bytes). */ |
|||
@@ -198,30 +162,15 @@ public:
|
|||
* |
|||
* See [Related Pages](pages.html) for device specific information |
|||
* |
|||
- * @param _cepin The pin attached to Chip Enable on the RF module
|
|||
- * @param _cspin The pin attached to Chip Select (often labeled CSN) on the radio module.
|
|||
- * - For the Arduino Due board, the [Arduino Due extended SPI feature](https://www.arduino.cc/en/Reference/DueExtendedSPI)
|
|||
- * is not supported. This means that the Due's pins 4, 10, or 52 are not mandated options (can use any digital output pin) for
|
|||
- * the radio's CSN pin.
|
|||
- * @param _spi_speed The SPI speed in Hz ie: 1000000 == 1Mhz
|
|||
- * - Users can specify default SPI speed by modifying @ref RF24_SPI_SPEED in @ref RF24_config.h
|
|||
- * - For Arduino, the default SPI speed will only be properly configured this way on devices supporting SPI TRANSACTIONS
|
|||
- * - Older/Unsupported Arduino devices will use a default clock divider & settings configuration
|
|||
- * - For Linux: The old way of setting SPI speeds using BCM2835 driver enums has been removed as of v1.3.7
|
|||
+ * @param _hal A pointer to the device specific hardware abstraction layer
|
|||
*/ |
|||
- RF24(rf24_gpio_pin_t _cepin, rf24_gpio_pin_t _cspin, uint32_t _spi_speed = RF24_SPI_SPEED);
|
|||
+ RF24(RF24_hal *_hal);
|
|||
|
|||
/** |
|||
* A constructor for initializing the radio's hardware dynamically |
|||
- * @warning You MUST use begin(rf24_gpio_pin_t, rf24_gpio_pin_t) or begin(_SPI*, rf24_gpio_pin_t, rf24_gpio_pin_t) to pass both the
|
|||
- * digital output pin numbers connected to the radio's CE and CSN pins.
|
|||
- * @param _spi_speed The SPI speed in Hz ie: 1000000 == 1Mhz
|
|||
- * - Users can specify default SPI speed by modifying @ref RF24_SPI_SPEED in @ref RF24_config.h
|
|||
- * - For Arduino, the default SPI speed will only be properly configured this way on devices supporting SPI TRANSACTIONS
|
|||
- * - Older/Unsupported Arduino devices will use a default clock divider & settings configuration
|
|||
- * - For Linux: The old way of setting SPI speeds using BCM2835 driver enums has been removed as of v1.3.7
|
|||
+ * @warning You MUST use begin(RF24_hal*)
|
|||
*/ |
|||
- RF24(uint32_t _spi_speed = RF24_SPI_SPEED);
|
|||
+ RF24(void);
|
|||
|
|||
#if defined(RF24_LINUX) |
|||
virtual ~RF24() {}; |
|||
@@ -243,58 +192,16 @@ public:
|
|||
*/ |
|||
bool begin(void); |
|||
|
|||
-#if defined(RF24_SPI_PTR) || defined(DOXYGEN_FORCED)
|
|||
/** |
|||
* Same as begin(), but allows specifying a non-default SPI bus to use. |
|||
* |
|||
- * @note This function assumes the `SPI::begin()` method was called before to
|
|||
- * calling this function.
|
|||
- *
|
|||
- * @warning This function is for the Arduino platforms only
|
|||
- *
|
|||
- * @param spiBus A pointer or reference to an instantiated SPI bus object.
|
|||
- * The `_SPI` datatype is a "wrapped" definition that will represent
|
|||
- * various SPI implementations based on the specified platform.
|
|||
- * @see Review the [Arduino support page](md_docs_arduino.html).
|
|||
- *
|
|||
- * @return same result as begin()
|
|||
- */
|
|||
- bool begin(_SPI* spiBus);
|
|||
-
|
|||
- /**
|
|||
- * Same as begin(), but allows dynamically specifying a SPI bus, CE pin,
|
|||
- * and CSN pin to use.
|
|||
- *
|
|||
- * @note This function assumes the `SPI::begin()` method was called before to
|
|||
- * calling this function.
|
|||
- *
|
|||
* @warning This function is for the Arduino platforms only |
|||
* |
|||
- * @param spiBus A pointer or reference to an instantiated SPI bus object.
|
|||
- * The `_SPI` datatype is a "wrapped" definition that will represent
|
|||
- * various SPI implementations based on the specified platform.
|
|||
- * @param _cepin The pin attached to Chip Enable on the RF module
|
|||
- * @param _cspin The pin attached to Chip Select (often labeled CSN) on the radio module.
|
|||
- * - For the Arduino Due board, the [Arduino Due extended SPI feature](https://www.arduino.cc/en/Reference/DueExtendedSPI)
|
|||
- * is not supported. This means that the Due's pins 4, 10, or 52 are not mandated options (can use any digital output pin) for the radio's CSN pin.
|
|||
+ * @param _hal A pointer to the device specific hardware abstraction layer
|
|||
* |
|||
- * @see Review the [Arduino support page](md_docs_arduino.html).
|
|||
- *
|
|||
- * @return same result as begin()
|
|||
- */
|
|||
- bool begin(_SPI* spiBus, rf24_gpio_pin_t _cepin, rf24_gpio_pin_t _cspin);
|
|||
-#endif // defined (RF24_SPI_PTR) || defined (DOXYGEN_FORCED)
|
|||
-
|
|||
- /**
|
|||
- * Same as begin(), but allows dynamically specifying a CE pin
|
|||
- * and CSN pin to use.
|
|||
- * @param _cepin The pin attached to Chip Enable on the RF module
|
|||
- * @param _cspin The pin attached to Chip Select (often labeled CSN) on the radio module.
|
|||
- * - For the Arduino Due board, the [Arduino Due extended SPI feature](https://www.arduino.cc/en/Reference/DueExtendedSPI)
|
|||
- * is not supported. This means that the Due's pins 4, 10, or 52 are not mandated options (can use any digital output pin) for the radio's CSN pin.
|
|||
* @return same result as begin() |
|||
*/ |
|||
- bool begin(rf24_gpio_pin_t _cepin, rf24_gpio_pin_t _cspin);
|
|||
+ bool begin(RF24_hal* _hal);
|
|||
|
|||
/** |
|||
* Checks if the chip is connected to the SPI bus |
|||
@@ -667,12 +574,12 @@ public:
|
|||
* This function uses much less ram than other `*print*Details()` methods. |
|||
* |
|||
* @code |
|||
- * uint8_t encoded_details[43] = {0};
|
|||
+ * uint8_t encoded_details[38] = {0};
|
|||
* radio.encodeRadioDetails(encoded_details); |
|||
* @endcode |
|||
* |
|||
* @param encoded_status The uint8_t array that RF24 radio details are |
|||
- * encoded into. This array must be at least 43 bytes in length; any less would surely
|
|||
+ * encoded into. This array must be at least 38 bytes in length; any less would surely
|
|||
* cause undefined behavior. |
|||
* |
|||
* Registers names and/or data corresponding to the index of the `encoded_details` array: |
|||
@@ -704,9 +611,6 @@ public:
|
|||
* | 35 | FIFO_STATUS | |
|||
* | 36 | DYNPD | |
|||
* | 37 | FEATURE | |
|||
- * | 38-39 | ce_pin |
|
|||
- * | 40-41 | csn_pin |
|
|||
- * | 42 | SPI speed (in MHz) or'd with (isPlusVariant << 4) |
|
|||
*/ |
|||
void encodeRadioDetails(uint8_t* encoded_status); |
|||
|
|||
@@ -1896,18 +1800,6 @@ private:
|
|||
*/ |
|||
bool _init_pins(); |
|||
|
|||
- /**
|
|||
- * Set chip select pin
|
|||
- *
|
|||
- * Running SPI bus at PI_CLOCK_DIV2 so we don't waste time transferring data
|
|||
- * and best of all, we make use of the radio's FIFO buffers. A lower speed
|
|||
- * means we're less likely to effectively leverage our FIFOs and pay a higher
|
|||
- * AVR runtime cost as toll.
|
|||
- *
|
|||
- * @param mode HIGH to take this unit off the SPI bus, LOW to put it on
|
|||
- */
|
|||
- void csn(bool mode);
|
|||
-
|
|||
/** |
|||
* Set chip enable |
|||
* |
|||
diff --git a/RF24_hal.cpp b/RF24_hal.cpp
|
|||
new file mode 100644 |
|||
index 0000000..3cc78e4
|
|||
--- /dev/null
|
|||
+++ b/RF24_hal.cpp
|
|||
@@ -0,0 +1 @@
|
|||
+#include "RF24_hal.h"
|
|||
diff --git a/RF24_hal.h b/RF24_hal.h
|
|||
new file mode 100644 |
|||
index 0000000..baceab3
|
|||
--- /dev/null
|
|||
+++ b/RF24_hal.h
|
|||
@@ -0,0 +1,15 @@
|
|||
+#pragma once
|
|||
+
|
|||
+#include "RF24_config.h"
|
|||
+
|
|||
+class RF24_hal
|
|||
+{
|
|||
+public:
|
|||
+ virtual void ce(bool level) = 0;
|
|||
+ virtual uint8_t read(uint8_t cmd, uint8_t* buf, uint8_t len) = 0;
|
|||
+ virtual uint8_t read(uint8_t cmd, uint8_t* buf, uint8_t data_len, uint8_t blank_len) = 0;
|
|||
+ virtual uint8_t write(uint8_t cmd, const uint8_t* buf, uint8_t len) = 0;
|
|||
+ virtual uint8_t write(uint8_t cmd, const uint8_t* buf, uint8_t len, uint8_t blank_len) = 0;
|
|||
+ virtual bool begin() = 0;
|
|||
+ virtual void end() = 0;
|
|||
+};
|
|||
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 43 KiB |
@ -0,0 +1,141 @@ |
|||
//-----------------------------------------------------------------------------
|
|||
// 2023 Ahoy, https://www.mikrocontroller.net/topic/525778
|
|||
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
|
|||
//-----------------------------------------------------------------------------
|
|||
|
|||
|
|||
#if defined(CONFIG_IDF_TARGET_ESP32S3) |
|||
#if defined(ETHERNET) |
|||
#ifndef __ETH_SPI_H__ |
|||
#define __ETH_SPI_H__ |
|||
|
|||
#pragma once |
|||
|
|||
#include <Arduino.h> |
|||
#include <esp_netif.h> |
|||
#include <WiFiGeneric.h> |
|||
#include <driver/spi_master.h> |
|||
|
|||
// Functions from WiFiGeneric
|
|||
void tcpipInit(); |
|||
void add_esp_interface_netif(esp_interface_t interface, esp_netif_t* esp_netif); |
|||
|
|||
class EthSpi { |
|||
public: |
|||
|
|||
EthSpi() : |
|||
eth_handle(nullptr), |
|||
eth_netif(nullptr) {} |
|||
|
|||
void begin(int8_t pin_miso, int8_t pin_mosi, int8_t pin_sclk, int8_t pin_cs, int8_t pin_int, int8_t pin_rst) { |
|||
gpio_reset_pin(static_cast<gpio_num_t>(pin_rst)); |
|||
gpio_set_direction(static_cast<gpio_num_t>(pin_rst), GPIO_MODE_OUTPUT); |
|||
gpio_set_level(static_cast<gpio_num_t>(pin_rst), 0); |
|||
|
|||
gpio_reset_pin(static_cast<gpio_num_t>(pin_sclk)); |
|||
gpio_reset_pin(static_cast<gpio_num_t>(pin_mosi)); |
|||
gpio_reset_pin(static_cast<gpio_num_t>(pin_miso)); |
|||
gpio_reset_pin(static_cast<gpio_num_t>(pin_cs)); |
|||
gpio_set_pull_mode(static_cast<gpio_num_t>(pin_miso), GPIO_PULLUP_ONLY); |
|||
|
|||
// Workaround, because calling gpio_install_isr_service directly causes issues with attachInterrupt later
|
|||
attachInterrupt(digitalPinToInterrupt(pin_int), nullptr, CHANGE); |
|||
detachInterrupt(digitalPinToInterrupt(pin_int)); |
|||
gpio_reset_pin(static_cast<gpio_num_t>(pin_int)); |
|||
gpio_set_pull_mode(static_cast<gpio_num_t>(pin_int), GPIO_PULLUP_ONLY); |
|||
|
|||
spi_bus_config_t buscfg = { |
|||
.mosi_io_num = pin_mosi, |
|||
.miso_io_num = pin_miso, |
|||
.sclk_io_num = pin_sclk, |
|||
.quadwp_io_num = -1, |
|||
.quadhd_io_num = -1, |
|||
.data4_io_num = -1, |
|||
.data5_io_num = -1, |
|||
.data6_io_num = -1, |
|||
.data7_io_num = -1, |
|||
.max_transfer_sz = 0, // uses default value internally
|
|||
.flags = 0, |
|||
.intr_flags = 0 |
|||
}; |
|||
|
|||
ESP_ERROR_CHECK(spi_bus_initialize(SPI3_HOST, &buscfg, SPI_DMA_CH_AUTO)); |
|||
|
|||
spi_device_interface_config_t devcfg = { |
|||
.command_bits = 16, // actually address phase
|
|||
.address_bits = 8, // actually command phase
|
|||
.dummy_bits = 0, |
|||
.mode = 0, |
|||
.duty_cycle_pos = 0, |
|||
.cs_ena_pretrans = 0, // only 0 supported
|
|||
.cs_ena_posttrans = 0, // only 0 supported
|
|||
.clock_speed_hz = 20000000, // stable with on OpenDTU Fusion Shield
|
|||
.input_delay_ns = 0, |
|||
.spics_io_num = pin_cs, |
|||
.flags = 0, |
|||
.queue_size = 20, |
|||
.pre_cb = nullptr, |
|||
.post_cb = nullptr |
|||
}; |
|||
|
|||
spi_device_handle_t spi; |
|||
ESP_ERROR_CHECK(spi_bus_add_device(SPI3_HOST, &devcfg, &spi)); |
|||
|
|||
// Reset sequence
|
|||
delayMicroseconds(500); |
|||
gpio_set_level(static_cast<gpio_num_t>(pin_rst), 1); |
|||
delayMicroseconds(1000); |
|||
|
|||
// Arduino function to start networking stack if not already started
|
|||
tcpipInit(); |
|||
|
|||
ESP_ERROR_CHECK(tcpip_adapter_set_default_eth_handlers()); // ?
|
|||
|
|||
eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(spi); |
|||
w5500_config.int_gpio_num = pin_int; |
|||
|
|||
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); |
|||
mac_config.rx_task_stack_size = 4096; |
|||
esp_eth_mac_t *mac = esp_eth_mac_new_w5500(&w5500_config, &mac_config); |
|||
|
|||
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); |
|||
phy_config.reset_gpio_num = -1; |
|||
esp_eth_phy_t *phy = esp_eth_phy_new_w5500(&phy_config); |
|||
|
|||
esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy); |
|||
ESP_ERROR_CHECK(esp_eth_driver_install(ð_config, ð_handle)); |
|||
|
|||
// Configure MAC address
|
|||
uint8_t mac_addr[6]; |
|||
ESP_ERROR_CHECK(esp_efuse_mac_get_default(mac_addr)); |
|||
mac_addr[5] |= 0x03; // derive ethernet MAC address from base MAC address
|
|||
ESP_ERROR_CHECK(esp_eth_ioctl(eth_handle, ETH_CMD_S_MAC_ADDR, mac_addr)); |
|||
|
|||
esp_netif_config_t netif_config = ESP_NETIF_DEFAULT_ETH(); |
|||
eth_netif = esp_netif_new(&netif_config); |
|||
|
|||
ESP_ERROR_CHECK(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handle))); |
|||
|
|||
// Add to Arduino
|
|||
add_esp_interface_netif(ESP_IF_ETH, eth_netif); |
|||
|
|||
ESP_ERROR_CHECK(esp_eth_start(eth_handle)); |
|||
} |
|||
|
|||
String macAddress() { |
|||
uint8_t mac_addr[6] = {0, 0, 0, 0, 0, 0}; |
|||
esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr); |
|||
char mac_addr_str[24]; |
|||
snprintf(mac_addr_str, sizeof(mac_addr_str), "%02X:%02X:%02X:%02X:%02X:%02X", mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]); |
|||
return String(mac_addr_str); |
|||
} |
|||
|
|||
|
|||
private: |
|||
esp_eth_handle_t eth_handle; |
|||
esp_netif_t *eth_netif; |
|||
}; |
|||
|
|||
#endif /*__ETH_SPI_H__*/ |
|||
#endif /*ETHERNET*/ |
|||
#endif /*CONFIG_IDF_TARGET_ESP32S3*/ |
|||
@ -0,0 +1,119 @@ |
|||
//-----------------------------------------------------------------------------
|
|||
// 2023 Ahoy, https://github.com/lumpapu/ahoy
|
|||
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/4.0/deed
|
|||
//-----------------------------------------------------------------------------
|
|||
|
|||
#ifndef __COMM_QUEUE_H__ |
|||
#define __COMM_QUEUE_H__ |
|||
|
|||
#include <array> |
|||
#include <functional> |
|||
#include "hmInverter.h" |
|||
#include "../utils/dbg.h" |
|||
|
|||
template <uint8_t N=100> |
|||
class CommQueue { |
|||
public: |
|||
CommQueue() {} |
|||
|
|||
void addImportant(Inverter<> *iv, uint8_t cmd) { |
|||
dec(&mRdPtr); |
|||
mQueue[mRdPtr] = queue_s(iv, cmd, true); |
|||
} |
|||
|
|||
void add(Inverter<> *iv, uint8_t cmd) { |
|||
mQueue[mWrPtr] = queue_s(iv, cmd, false); |
|||
inc(&mWrPtr); |
|||
} |
|||
|
|||
void chgCmd(Inverter<> *iv, uint8_t cmd) { |
|||
mQueue[mWrPtr] = queue_s(iv, cmd, false); |
|||
} |
|||
|
|||
uint8_t getFillState(void) { |
|||
//DPRINTLN(DBG_INFO, "wr: " + String(mWrPtr) + ", rd: " + String(mRdPtr));
|
|||
return abs(mRdPtr - mWrPtr); |
|||
} |
|||
|
|||
uint8_t getMaxFill(void) { |
|||
return N; |
|||
} |
|||
|
|||
protected: |
|||
struct queue_s { |
|||
Inverter<> *iv; |
|||
uint8_t cmd; |
|||
uint8_t attempts; |
|||
uint32_t ts; |
|||
bool isDevControl; |
|||
queue_s() {} |
|||
queue_s(Inverter<> *i, uint8_t c, bool dev) : |
|||
iv(i), cmd(c), attempts(5), ts(0), isDevControl(dev) {} |
|||
}; |
|||
|
|||
protected: |
|||
void add(queue_s q) { |
|||
mQueue[mWrPtr] = q; |
|||
inc(&mWrPtr); |
|||
} |
|||
|
|||
void add(const queue_s *q, bool rstAttempts = false) { |
|||
mQueue[mWrPtr] = *q; |
|||
if(rstAttempts) |
|||
mQueue[mWrPtr].attempts = 5; |
|||
inc(&mWrPtr); |
|||
} |
|||
|
|||
void chgCmd(uint8_t cmd) { |
|||
mQueue[mRdPtr].cmd = cmd; |
|||
mQueue[mRdPtr].isDevControl = false; |
|||
} |
|||
|
|||
void get(std::function<void(bool valid, const queue_s *q)> cb) { |
|||
if(mRdPtr == mWrPtr) { |
|||
cb(false, &mQueue[mRdPtr]); // empty
|
|||
return; |
|||
} |
|||
cb(true, &mQueue[mRdPtr]); |
|||
} |
|||
|
|||
void cmdDone(bool keep = false) { |
|||
if(keep) { |
|||
mQueue[mRdPtr].attempts = 5; |
|||
add(mQueue[mRdPtr]); // add to the end again
|
|||
} |
|||
inc(&mRdPtr); |
|||
} |
|||
|
|||
void setTs(uint32_t *ts) { |
|||
mQueue[mRdPtr].ts = *ts; |
|||
} |
|||
|
|||
void setAttempt(void) { |
|||
if(mQueue[mRdPtr].attempts) |
|||
mQueue[mRdPtr].attempts--; |
|||
} |
|||
|
|||
void incrAttempt(uint8_t attempts = 1) { |
|||
mQueue[mRdPtr].attempts += attempts; |
|||
} |
|||
|
|||
void inc(uint8_t *ptr) { |
|||
if(++(*ptr) >= N) |
|||
*ptr = 0; |
|||
} |
|||
void dec(uint8_t *ptr) { |
|||
if((*ptr) == 0) |
|||
*ptr = N-1; |
|||
else |
|||
--(*ptr); |
|||
} |
|||
|
|||
protected: |
|||
std::array<queue_s, N> mQueue; |
|||
uint8_t mWrPtr = 0; |
|||
uint8_t mRdPtr = 0; |
|||
}; |
|||
|
|||
|
|||
#endif /*__COMM_QUEUE_H__*/ |
|||
@ -0,0 +1,918 @@ |
|||
//-----------------------------------------------------------------------------
|
|||
// 2023 Ahoy, https://github.com/lumpapu/ahoy
|
|||
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/4.0/deed
|
|||
//-----------------------------------------------------------------------------
|
|||
|
|||
#ifndef __COMMUNICATION_H__ |
|||
#define __COMMUNICATION_H__ |
|||
|
|||
#include "CommQueue.h" |
|||
#include <Arduino.h> |
|||
#include "../utils/crc.h" |
|||
#include "../utils/timemonitor.h" |
|||
#include "Heuristic.h" |
|||
|
|||
#define MI_TIMEOUT 250 // timeout for MI type requests
|
|||
#define FRSTMSG_TIMEOUT 150 // how long to wait for first msg to be received
|
|||
#define DEFAULT_TIMEOUT 500 // timeout for regular requests
|
|||
#define SINGLEFR_TIMEOUT 100 // timeout for single frame requests
|
|||
#define MAX_BUFFER 250 |
|||
|
|||
typedef std::function<void(uint8_t, Inverter<> *)> payloadListenerType; |
|||
typedef std::function<void(Inverter<> *)> alarmListenerType; |
|||
|
|||
class Communication : public CommQueue<> { |
|||
public: |
|||
void setup(uint32_t *timestamp, bool *serialDebug, bool *privacyMode, bool *printWholeTrace, uint16_t *inverterGap) { |
|||
mTimestamp = timestamp; |
|||
mPrivacyMode = privacyMode; |
|||
mSerialDebug = serialDebug; |
|||
mPrintWholeTrace = printWholeTrace; |
|||
mInverterGap = inverterGap; |
|||
} |
|||
|
|||
void addImportant(Inverter<> *iv, uint8_t cmd) { |
|||
mState = States::RESET; // cancel current operation
|
|||
CommQueue::addImportant(iv, cmd); |
|||
} |
|||
|
|||
void addPayloadListener(payloadListenerType cb) { |
|||
mCbPayload = cb; |
|||
} |
|||
|
|||
void addAlarmListener(alarmListenerType cb) { |
|||
mCbAlarm = cb; |
|||
} |
|||
|
|||
void loop() { |
|||
get([this](bool valid, const queue_s *q) { |
|||
if(!valid) { |
|||
if(mPrintSequenceDuration) { |
|||
mPrintSequenceDuration = false; |
|||
DPRINT(DBG_INFO, F("com loop duration: ")); |
|||
DBGPRINT(String(millis() - mLastEmptyQueueMillis)); |
|||
DBGPRINTLN(F("ms")); |
|||
DBGPRINTLN(F("-----")); |
|||
} |
|||
return; // empty
|
|||
} |
|||
if(!mPrintSequenceDuration) // entry was added to the queue
|
|||
mLastEmptyQueueMillis = millis(); |
|||
mPrintSequenceDuration = true; |
|||
|
|||
uint16_t timeout = (q->iv->ivGen == IV_MI) ? MI_TIMEOUT : (((q->iv->mGotFragment && q->iv->mGotLastMsg) || mIsRetransmit) ? SINGLEFR_TIMEOUT : ((q->cmd != AlarmData) && (q->cmd != GridOnProFilePara) ? DEFAULT_TIMEOUT : (1.5 * DEFAULT_TIMEOUT))); |
|||
|
|||
/*if(mDebugState != mState) {
|
|||
DPRINT(DBG_INFO, F("State: ")); |
|||
DBGHEXLN((uint8_t)(mState)); |
|||
mDebugState = mState; |
|||
}*/ |
|||
switch(mState) { |
|||
case States::RESET: |
|||
if (!mWaitTime.isTimeout()) |
|||
return; |
|||
|
|||
mMaxFrameId = 0; |
|||
for(uint8_t i = 0; i < MAX_PAYLOAD_ENTRIES; i++) { |
|||
mLocalBuf[i].len = 0; |
|||
} |
|||
|
|||
if(*mSerialDebug) |
|||
mHeu.printStatus(q->iv); |
|||
mHeu.getTxCh(q->iv); |
|||
q->iv->mGotFragment = false; |
|||
q->iv->mGotLastMsg = false; |
|||
q->iv->curFrmCnt = 0; |
|||
mIsRetransmit = false; |
|||
if(NULL == q->iv->radio) |
|||
cmdDone(false); // can't communicate while radio is not defined!
|
|||
q->iv->mCmd = q->cmd; |
|||
q->iv->mIsSingleframeReq = false; |
|||
mState = States::START; |
|||
break; |
|||
|
|||
case States::START: |
|||
setTs(mTimestamp); |
|||
if((IV_HMS == q->iv->ivGen) || (IV_HMT == q->iv->ivGen)) { |
|||
// frequency was changed during runtime
|
|||
if(q->iv->curCmtFreq != q->iv->config->frequency) { |
|||
if(q->iv->radio->switchFrequencyCh(q->iv, q->iv->curCmtFreq, q->iv->config->frequency)) |
|||
q->iv->curCmtFreq = q->iv->config->frequency; |
|||
} |
|||
} |
|||
|
|||
if(q->isDevControl) { |
|||
if(ActivePowerContr == q->cmd) |
|||
q->iv->powerLimitAck = false; |
|||
q->iv->radio->sendControlPacket(q->iv, q->cmd, q->iv->powerLimit, false); |
|||
} else |
|||
q->iv->radio->prepareDevInformCmd(q->iv, q->cmd, q->ts, q->iv->alarmLastId, false); |
|||
|
|||
q->iv->radioStatistics.txCnt++; |
|||
mWaitTime.startTimeMonitor(timeout); |
|||
mIsRetransmit = false; |
|||
setAttempt(); |
|||
if((q->cmd == AlarmData) || (q->cmd == GridOnProFilePara)) |
|||
incrAttempt(q->cmd == AlarmData? 5 : 3); |
|||
|
|||
mState = States::WAIT; |
|||
break; |
|||
|
|||
case States::WAIT: |
|||
if (!mWaitTime.isTimeout()) |
|||
return; |
|||
mState = States::CHECK_FRAMES; |
|||
break; |
|||
|
|||
case States::CHECK_FRAMES: { |
|||
if((q->iv->radio->mBufCtrl.empty() && !mIsRetransmit) || (0 == q->attempts)) { // radio buffer empty or no more answers
|
|||
if(*mSerialDebug) { |
|||
DPRINT_IVID(DBG_INFO, q->iv->id); |
|||
DBGPRINT(F("request timeout: ")); |
|||
DBGPRINT(String(mWaitTime.getRunTime())); |
|||
DBGPRINTLN(F("ms")); |
|||
} |
|||
if(!q->iv->mGotFragment) { |
|||
if((IV_HMS == q->iv->ivGen) || (IV_HMT == q->iv->ivGen)) { |
|||
q->iv->radio->switchFrequency(q->iv, HOY_BOOT_FREQ_KHZ, (q->iv->config->frequency*FREQ_STEP_KHZ + HOY_BASE_FREQ_KHZ)); |
|||
mWaitTime.startTimeMonitor(1000); |
|||
} |
|||
} |
|||
closeRequest(q, false); |
|||
break; |
|||
} |
|||
mFirstTry = false; // for correct reset
|
|||
if((IV_MI != q->iv->ivGen) || (0 == q->attempts)) |
|||
mIsRetransmit = false; |
|||
|
|||
while(!q->iv->radio->mBufCtrl.empty()) { |
|||
packet_t *p = &q->iv->radio->mBufCtrl.front(); |
|||
printRxInfo(q, p); |
|||
|
|||
if(validateIvSerial(&p->packet[1], q->iv)) { |
|||
q->iv->radioStatistics.frmCnt++; |
|||
q->iv->mDtuRxCnt++; |
|||
|
|||
if (p->packet[0] == (TX_REQ_INFO + ALL_FRAMES)) { // response from get information command
|
|||
if(parseFrame(p)) |
|||
q->iv->curFrmCnt++; |
|||
} else if (p->packet[0] == (TX_REQ_DEVCONTROL + ALL_FRAMES)) { // response from dev control command
|
|||
if(parseDevCtrl(p, q)) |
|||
closeRequest(q, true); |
|||
else |
|||
closeRequest(q, false); |
|||
q->iv->radio->mBufCtrl.pop(); |
|||
return; // don't wait for empty buffer
|
|||
} else if(IV_MI == q->iv->ivGen) { |
|||
if(parseMiFrame(p, q)) |
|||
q->iv->curFrmCnt++; |
|||
} |
|||
} //else -> serial does not match
|
|||
|
|||
q->iv->radio->mBufCtrl.pop(); |
|||
yield(); |
|||
} |
|||
|
|||
if(0 == q->attempts) { |
|||
DPRINT_IVID(DBG_INFO, q->iv->id); |
|||
DBGPRINT(F("no attempts left")); |
|||
closeRequest(q, false); |
|||
} else { |
|||
if(q->iv->ivGen != IV_MI) { |
|||
mState = States::CHECK_PACKAGE; |
|||
} else { |
|||
bool fastNext = true; |
|||
if(q->iv->miMultiParts < 6) { |
|||
mState = States::WAIT; |
|||
if((mWaitTime.isTimeout() && mIsRetransmit) || !mIsRetransmit) { |
|||
miRepeatRequest(q); |
|||
return; |
|||
} |
|||
} else { |
|||
mHeu.evalTxChQuality(q->iv, true, (4 - q->attempts), q->iv->curFrmCnt); |
|||
if(((q->cmd == 0x39) && (q->iv->type == INV_TYPE_4CH)) |
|||
|| ((q->cmd == MI_REQ_CH2) && (q->iv->type == INV_TYPE_2CH)) |
|||
|| ((q->cmd == MI_REQ_CH1) && (q->iv->type == INV_TYPE_1CH))) { |
|||
miComplete(q->iv); |
|||
fastNext = false; |
|||
} |
|||
if(fastNext) |
|||
miNextRequest(q->iv->type == INV_TYPE_4CH ? MI_REQ_4CH : MI_REQ_CH1, q); |
|||
else |
|||
closeRequest(q, true); |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
break; |
|||
|
|||
case States::CHECK_PACKAGE: |
|||
uint8_t framnr = 0; |
|||
if(0 == mMaxFrameId) { |
|||
uint8_t i = 0; |
|||
while(i < MAX_PAYLOAD_ENTRIES) { |
|||
if(mLocalBuf[i].len == 0) { |
|||
framnr = i+1; |
|||
break; |
|||
} |
|||
i++; |
|||
} |
|||
} |
|||
|
|||
if(!framnr) { |
|||
for(uint8_t i = 0; i < mMaxFrameId; i++) { |
|||
if(mLocalBuf[i].len == 0) { |
|||
framnr = i+1; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
if(framnr) { |
|||
setAttempt(); |
|||
|
|||
if(*mSerialDebug) { |
|||
DPRINT_IVID(DBG_WARN, q->iv->id); |
|||
DBGPRINT(F("frame ")); |
|||
DBGPRINT(String(framnr)); |
|||
DBGPRINT(F(" missing: request retransmit (")); |
|||
DBGPRINT(String(q->attempts)); |
|||
DBGPRINTLN(F(" attempts left)")); |
|||
} |
|||
if (!mIsRetransmit) |
|||
q->iv->mIsSingleframeReq = true; |
|||
sendRetransmit(q, (framnr-1)); |
|||
mIsRetransmit = true; |
|||
return; |
|||
} |
|||
|
|||
compilePayload(q); |
|||
|
|||
if((NULL != mCbPayload) && (GridOnProFilePara != q->cmd) && (GetLossRate != q->cmd)) |
|||
(mCbPayload)(q->cmd, q->iv); |
|||
|
|||
closeRequest(q, true); |
|||
break; |
|||
} |
|||
}); |
|||
} |
|||
|
|||
private: |
|||
inline void printRxInfo(const queue_s *q, packet_t *p) { |
|||
DPRINT_IVID(DBG_INFO, q->iv->id); |
|||
DBGPRINT(F("RX ")); |
|||
if(p->millis < 100) |
|||
DBGPRINT(F(" ")); |
|||
DBGPRINT(String(p->millis)); |
|||
DBGPRINT(F("ms | ")); |
|||
DBGPRINT(String(p->len)); |
|||
if((IV_HM == q->iv->ivGen) || (IV_MI == q->iv->ivGen)) { |
|||
DBGPRINT(F(" CH")); |
|||
if(3 == p->ch) |
|||
DBGPRINT(F("0")); |
|||
DBGPRINT(String(p->ch)); |
|||
DBGPRINT(F(" ")); |
|||
} else { |
|||
DBGPRINT(F(" ")); |
|||
DBGPRINT(String(p->rssi)); |
|||
DBGPRINT(F("dBm ")); |
|||
} |
|||
if(*mPrintWholeTrace) { |
|||
DBGPRINT(F("| ")); |
|||
if(*mPrivacyMode) |
|||
ah::dumpBuf(p->packet, p->len, 1, 8); |
|||
else |
|||
ah::dumpBuf(p->packet, p->len); |
|||
} else { |
|||
DBGPRINT(F("| ")); |
|||
DHEX(p->packet[0]); |
|||
DBGPRINT(F(" ")); |
|||
DBGHEXLN(p->packet[9]); |
|||
} |
|||
} |
|||
|
|||
inline bool validateIvSerial(uint8_t buf[], Inverter<> *iv) { |
|||
uint8_t tmp[4]; |
|||
CP_U32_BigEndian(tmp, iv->radioId.u64 >> 8); |
|||
for(uint8_t i = 0; i < 4; i++) { |
|||
if(tmp[i] != buf[i]) { |
|||
DPRINT(DBG_WARN, F("Inverter serial does not match, got: 0x")); |
|||
DHEX(buf[0]);DHEX(buf[1]);DHEX(buf[2]);DHEX(buf[3]); |
|||
DBGPRINT(F(", expected: 0x")); |
|||
DHEX(tmp[0]);DHEX(tmp[1]);DHEX(tmp[2]);DHEX(tmp[3]); |
|||
DBGPRINTLN(""); |
|||
return false; |
|||
} |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
inline bool checkFrameCrc(uint8_t buf[], uint8_t len) { |
|||
return (ah::crc8(buf, len - 1) == buf[len-1]); |
|||
} |
|||
|
|||
inline bool parseFrame(packet_t *p) { |
|||
uint8_t *frameId = &p->packet[9]; |
|||
if(0x00 == *frameId) { |
|||
DPRINTLN(DBG_WARN, F("invalid frameId 0x00")); |
|||
return false; // skip current packet
|
|||
} |
|||
if((*frameId & 0x7f) > MAX_PAYLOAD_ENTRIES) { |
|||
DPRINTLN(DBG_WARN, F("local buffer to small for payload fragments")); |
|||
return false; // local storage is to small for id
|
|||
} |
|||
|
|||
if(!checkFrameCrc(p->packet, p->len)) { |
|||
DPRINTLN(DBG_WARN, F("frame CRC is wrong")); |
|||
return false; // CRC8 is wrong, frame invalid
|
|||
} |
|||
|
|||
if((*frameId & ALL_FRAMES) == ALL_FRAMES) { |
|||
mMaxFrameId = (*frameId & 0x7f); |
|||
if(mMaxFrameId > 8) // large payloads, e.g. AlarmData
|
|||
incrAttempt(mMaxFrameId - 6); |
|||
} |
|||
|
|||
frame_t *f = &mLocalBuf[(*frameId & 0x7f) - 1]; |
|||
memcpy(f->buf, &p->packet[10], p->len-11); |
|||
f->len = p->len - 11; |
|||
f->rssi = p->rssi; |
|||
|
|||
return true; |
|||
} |
|||
|
|||
inline bool parseMiFrame(packet_t *p, const queue_s *q) { |
|||
if ((p->packet[0] == MI_REQ_CH1 + ALL_FRAMES) |
|||
|| (p->packet[0] == MI_REQ_CH2 + ALL_FRAMES) |
|||
|| ((p->packet[0] >= (MI_REQ_4CH + ALL_FRAMES)) |
|||
&& (p->packet[0] < (0x39 + SINGLE_FRAME)) |
|||
)) { //&& (p->packet[0] != (0x0f + ALL_FRAMES)))) {
|
|||
// small MI or MI 1500 data responses to 0x09, 0x11, 0x36, 0x37, 0x38 and 0x39
|
|||
//mPayload[iv->id].txId = p->packet[0];
|
|||
miDataDecode(p, q); |
|||
} else if (p->packet[0] == (0x0f + ALL_FRAMES)) |
|||
miHwDecode(p, q); |
|||
else if ((p->packet[0] == 0x88) || (p->packet[0] == 0x92)) { |
|||
record_t<> *rec = q->iv->getRecordStruct(RealTimeRunData_Debug); // choose the record structure
|
|||
rec->ts = q->ts; |
|||
miStsConsolidate(q, ((p->packet[0] == 0x88) ? 1 : 2), rec, p->packet[10], p->packet[12], p->packet[9], p->packet[11]); |
|||
//mHeu.setGotFragment(q->iv); only do this when we are through the cycle?
|
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
inline bool parseDevCtrl(packet_t *p, const queue_s *q) { |
|||
switch(p->packet[12]) { |
|||
case ActivePowerContr: |
|||
if(p->packet[13] != 0x00) |
|||
return false; |
|||
break; |
|||
|
|||
case TurnOn: [[fallthrough]]; |
|||
case TurnOff: [[fallthrough]]; |
|||
case Restart: |
|||
return true; |
|||
break; |
|||
|
|||
default: |
|||
DPRINT(DBG_WARN, F("unknown dev ctrl: ")); |
|||
DBGHEXLN(p->packet[12]); |
|||
break; |
|||
} |
|||
|
|||
bool accepted = true; |
|||
if((p->packet[10] == 0x00) && (p->packet[11] == 0x00)) |
|||
q->iv->powerLimitAck = true; |
|||
else |
|||
accepted = false; |
|||
|
|||
DPRINT_IVID(DBG_INFO, q->iv->id); |
|||
DBGPRINT(F("has ")); |
|||
if(!accepted) DBGPRINT(F("not ")); |
|||
DBGPRINT(F("accepted power limit set point ")); |
|||
DBGPRINT(String(q->iv->powerLimit[0])); |
|||
DBGPRINT(F(" with PowerLimitControl ")); |
|||
DBGPRINTLN(String(q->iv->powerLimit[1])); |
|||
q->iv->actPowerLimit = 0xffff; // unknown, readback current value
|
|||
|
|||
return accepted; |
|||
} |
|||
|
|||
inline void compilePayload(const queue_s *q) { |
|||
uint16_t crc = 0xffff, crcRcv = 0x0000; |
|||
for(uint8_t i = 0; i < mMaxFrameId; i++) { |
|||
if(i == (mMaxFrameId - 1)) { |
|||
crc = ah::crc16(mLocalBuf[i].buf, mLocalBuf[i].len - 2, crc); |
|||
crcRcv = (mLocalBuf[i].buf[mLocalBuf[i].len-2] << 8); |
|||
crcRcv |= mLocalBuf[i].buf[mLocalBuf[i].len-1]; |
|||
} else |
|||
crc = ah::crc16(mLocalBuf[i].buf, mLocalBuf[i].len, crc); |
|||
} |
|||
|
|||
if(crc != crcRcv) { |
|||
DPRINT_IVID(DBG_WARN, q->iv->id); |
|||
DBGPRINT(F("CRC Error ")); |
|||
if(q->attempts == 0) { |
|||
DBGPRINTLN(F("-> Fail")); |
|||
closeRequest(q, false); |
|||
|
|||
} else |
|||
DBGPRINTLN(F("-> complete retransmit")); |
|||
mState = States::RESET; |
|||
return; |
|||
} |
|||
|
|||
/*DPRINT_IVID(DBG_INFO, q->iv->id);
|
|||
DBGPRINT(F("procPyld: cmd: 0x")); |
|||
DBGHEXLN(q->cmd);*/ |
|||
|
|||
memset(mPayload, 0, MAX_BUFFER); |
|||
int8_t rssi = -127; |
|||
uint8_t len = 0; |
|||
|
|||
for(uint8_t i = 0; i < mMaxFrameId; i++) { |
|||
if(mLocalBuf[i].len + len > MAX_BUFFER) { |
|||
DPRINTLN(DBG_ERROR, F("payload buffer to small!")); |
|||
return; |
|||
} |
|||
memcpy(&mPayload[len], mLocalBuf[i].buf, mLocalBuf[i].len); |
|||
len += mLocalBuf[i].len; |
|||
// get worst RSSI (high value is better)
|
|||
if(mLocalBuf[i].rssi > rssi) |
|||
rssi = mLocalBuf[i].rssi; |
|||
} |
|||
|
|||
len -= 2; |
|||
|
|||
DPRINT_IVID(DBG_INFO, q->iv->id); |
|||
DBGPRINT(F("Payload (")); |
|||
DBGPRINT(String(len)); |
|||
if(*mPrintWholeTrace) { |
|||
DBGPRINT(F("): ")); |
|||
ah::dumpBuf(mPayload, len); |
|||
} else |
|||
DBGPRINTLN(F(")")); |
|||
|
|||
if(GridOnProFilePara == q->cmd) { |
|||
q->iv->addGridProfile(mPayload, len); |
|||
return; |
|||
} |
|||
|
|||
record_t<> *rec = q->iv->getRecordStruct(q->cmd); |
|||
if(NULL == rec) { |
|||
if(GetLossRate == q->cmd) { |
|||
q->iv->parseGetLossRate(mPayload, len); |
|||
return; |
|||
} else { |
|||
DPRINTLN(DBG_ERROR, F("record is NULL!")); |
|||
closeRequest(q, false); |
|||
} |
|||
return; |
|||
} |
|||
if((rec->pyldLen != len) && (0 != rec->pyldLen)) { |
|||
if(*mSerialDebug) { |
|||
DPRINT(DBG_ERROR, F("plausibility check failed, expected ")); |
|||
DBGPRINT(String(rec->pyldLen)); |
|||
DBGPRINTLN(F(" bytes")); |
|||
} |
|||
/*q->iv->radioStatistics.rxFail++;*/ |
|||
closeRequest(q, false); |
|||
|
|||
return; |
|||
} |
|||
|
|||
rec->ts = q->ts; |
|||
for (uint8_t i = 0; i < rec->length; i++) { |
|||
q->iv->addValue(i, mPayload, rec); |
|||
} |
|||
|
|||
q->iv->rssi = rssi; |
|||
q->iv->doCalculations(); |
|||
|
|||
if(AlarmData == q->cmd) { |
|||
uint8_t i = 0; |
|||
while(1) { |
|||
if(0 == q->iv->parseAlarmLog(i++, mPayload, len)) |
|||
break; |
|||
if (NULL != mCbAlarm) |
|||
(mCbAlarm)(q->iv); |
|||
yield(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
void sendRetransmit(const queue_s *q, uint8_t i) { |
|||
if(q->attempts) { |
|||
q->iv->radio->sendCmdPacket(q->iv, TX_REQ_INFO, (SINGLE_FRAME + i), true); |
|||
q->iv->radioStatistics.retransmits++; |
|||
mWaitTime.startTimeMonitor(SINGLEFR_TIMEOUT); // timeout
|
|||
mState = States::WAIT; |
|||
} else { |
|||
//add(q, true);
|
|||
closeRequest(q, false); |
|||
} |
|||
} |
|||
|
|||
private: |
|||
void closeRequest(const queue_s *q, bool crcPass) { |
|||
mHeu.evalTxChQuality(q->iv, crcPass, (4 - q->attempts), q->iv->curFrmCnt); |
|||
if(crcPass) |
|||
q->iv->radioStatistics.rxSuccess++; |
|||
else if(q->iv->mGotFragment) |
|||
q->iv->radioStatistics.rxFail++; // got no complete payload
|
|||
else |
|||
q->iv->radioStatistics.rxFailNoAnser++; // got nothing
|
|||
mWaitTime.startTimeMonitor(*mInverterGap); |
|||
|
|||
bool keep = false; |
|||
if(q->isDevControl) |
|||
keep = !crcPass; |
|||
|
|||
cmdDone(keep); |
|||
q->iv->mGotFragment = false; |
|||
q->iv->mGotLastMsg = false; |
|||
q->iv->miMultiParts = 0; |
|||
mIsRetransmit = false; |
|||
mFirstTry = false; // for correct reset
|
|||
mState = States::RESET; |
|||
DBGPRINTLN(F("-----")); |
|||
} |
|||
|
|||
inline void miHwDecode(packet_t *p, const queue_s *q) { |
|||
record_t<> *rec = q->iv->getRecordStruct(InverterDevInform_All); // choose the record structure
|
|||
rec->ts = q->ts; |
|||
/*
|
|||
Polling the device software and hardware version number command |
|||
start byte Command word routing address target address User data check end byte |
|||
byte[0] byte[1] byte[2] byte[3] byte[4] byte[5] byte[6] byte[7] byte[8] byte[9] byte[10] byte[11] byte[12] |
|||
0x7e 0x0f xx xx xx xx YY YY YY YY 0x00 CRC 0x7f |
|||
Command Receipt - First Frame |
|||
start byte Command word target address routing address Multi-frame marking User data User data User data User data User data User data User data User data User data User data User data User data User data User data User data User data check end byte |
|||
byte[0] byte[1] byte[2] byte[3] byte[4] byte[5] byte[6] byte[7] byte[8] byte[9] byte[10] byte[11] byte[12] byte[13] byte[14] byte[15] byte[16] byte[17] byte[18] byte[19] byte[20] byte[21] byte[22] byte[23] byte[24] byte[25] byte[26] byte[27] byte[28] |
|||
0x7e 0x8f YY YY YY YY xx xx xx xx 0x00 USFWBuild_VER APPFWBuild_VER APPFWBuild_YYYY APPFWBuild_MMDD APPFWBuild_HHMM APPFW_PN HW_VER CRC 0x7f |
|||
Command Receipt - Second Frame |
|||
start byte Command word target address routing address Multi-frame marking User data User data User data User data User data User data User data User data User data User data User data User data User data User data User data User data check end byte |
|||
byte[0] byte[1] byte[2] byte[3] byte[4] byte[5] byte[6] byte[7] byte[8] byte[9] byte[10] byte[11] byte[12] byte[13] byte[14] byte[15] byte[16] byte[17] byte[18] byte[19] byte[20] byte[21] byte[22] byte[23] byte[24] byte[25] byte[26] byte[27] byte[28] |
|||
0x7e 0x8f YY YY YY YY xx xx xx xx 0x01 HW_PN HW_FB_TLmValue HW_FB_ReSPRT HW_GridSamp_ResValule HW_ECapValue Matching_APPFW_PN CRC 0x7f |
|||
Command receipt - third frame |
|||
start byte Command word target address routing address Multi-frame marking User data User data User data User data User data User data User data User data check end byte |
|||
byte[0] byte[1] byte[2] byte[3] byte[4] byte[5] byte[6] byte[7] byte[8] byte[9] byte[10] byte[11] byte[12] byte[13] byte[14] byte[15] byte[16] byte[15] byte[16] byte[17] byte[18] |
|||
0x7e 0x8f YY YY YY YY xx xx xx xx 0x12 APPFW_MINVER HWInfoAddr PNInfoCRC_gusv PNInfoCRC_gusv CRC 0x7f |
|||
*/ |
|||
|
|||
/*
|
|||
case InverterDevInform_All: |
|||
rec->length = (uint8_t)(HMINFO_LIST_LEN); |
|||
rec->assign = (byteAssign_t *)InfoAssignment; |
|||
rec->pyldLen = HMINFO_PAYLOAD_LEN; |
|||
break; |
|||
const byteAssign_t InfoAssignment[] = { |
|||
{ FLD_FW_VERSION, UNIT_NONE, CH0, 0, 2, 1 }, |
|||
{ FLD_FW_BUILD_YEAR, UNIT_NONE, CH0, 2, 2, 1 }, |
|||
{ FLD_FW_BUILD_MONTH_DAY, UNIT_NONE, CH0, 4, 2, 1 }, |
|||
{ FLD_FW_BUILD_HOUR_MINUTE, UNIT_NONE, CH0, 6, 2, 1 }, |
|||
{ FLD_BOOTLOADER_VER, UNIT_NONE, CH0, 8, 2, 1 } |
|||
}; |
|||
*/ |
|||
|
|||
if ( p->packet[9] == 0x00 ) {//first frame
|
|||
//FLD_FW_VERSION
|
|||
for (uint8_t i = 0; i < 5; i++) { |
|||
q->iv->setValue(i, rec, (float) ((p->packet[(12+2*i)] << 8) + p->packet[(13+2*i)])/1); |
|||
} |
|||
q->iv->isConnected = true; |
|||
if(*mSerialDebug) { |
|||
DPRINT_IVID(DBG_INFO, q->iv->id); |
|||
DBGPRINT(F("HW_VER is ")); |
|||
DBGPRINTLN(String((p->packet[24] << 8) + p->packet[25])); |
|||
} |
|||
record_t<> *rec = q->iv->getRecordStruct(InverterDevInform_Simple); // choose the record structure
|
|||
rec->ts = q->ts; |
|||
q->iv->setValue(1, rec, (uint32_t) ((p->packet[24] << 8) + p->packet[25])/1); |
|||
q->iv->miMultiParts +=4; |
|||
} else if ( p->packet[9] == 0x01 || p->packet[9] == 0x10 ) {//second frame for MI, 3rd gen. answers in 0x10
|
|||
DPRINT_IVID(DBG_INFO, q->iv->id); |
|||
if ( p->packet[9] == 0x01 ) { |
|||
DBGPRINTLN(F("got 2nd frame (hw info)")); |
|||
/* according to xlsx (different start byte -1!)
|
|||
byte[11] to byte[14] HW_PN |
|||
byte[15] byte[16] HW_FB_TLmValue |
|||
byte[17] byte[18] HW_FB_ReSPRT |
|||
byte[19] byte[20] HW_GridSamp_ResValule |
|||
byte[21] byte[22] HW_ECapValue |
|||
byte[23] to byte[26] Matching_APPFW_PN*/ |
|||
DPRINT(DBG_INFO,F("HW_PartNo ")); |
|||
DBGPRINTLN(String((uint32_t) (((p->packet[10] << 8) | p->packet[11]) << 8 | p->packet[12]) << 8 | p->packet[13])); |
|||
record_t<> *rec = q->iv->getRecordStruct(InverterDevInform_Simple); // choose the record structure
|
|||
rec->ts = q->ts; |
|||
q->iv->setValue(0, rec, (uint32_t) ((((p->packet[10] << 8) | p->packet[11]) << 8 | p->packet[12]) << 8 | p->packet[13])/1); |
|||
|
|||
if(*mSerialDebug) { |
|||
DPRINT(DBG_INFO,F("HW_FB_TLmValue ")); |
|||
DBGPRINTLN(String((p->packet[14] << 8) + p->packet[15])); |
|||
DBGPRINT(F("HW_FB_ReSPRT ")); |
|||
DBGPRINTLN(String((p->packet[16] << 8) + p->packet[17])); |
|||
DBGPRINT(F("HW_GridSamp_ResValule ")); |
|||
DBGPRINTLN(String((p->packet[18] << 8) + p->packet[19])); |
|||
DBGPRINT(F("HW_ECapValue ")); |
|||
DBGPRINTLN(String((p->packet[20] << 8) + p->packet[21])); |
|||
DBGPRINT(F("Matching_APPFW_PN ")); |
|||
DBGPRINTLN(String((uint32_t) (((p->packet[22] << 8) | p->packet[23]) << 8 | p->packet[24]) << 8 | p->packet[25])); |
|||
} |
|||
if(NULL != mCbPayload) |
|||
(mCbPayload)(InverterDevInform_All, q->iv); |
|||
q->iv->miMultiParts +=2; |
|||
|
|||
} else { |
|||
DBGPRINTLN(F("3rd gen. inverter!")); |
|||
} |
|||
|
|||
} else if ( p->packet[9] == 0x12 ) {//3rd frame
|
|||
DPRINT_IVID(DBG_INFO, q->iv->id); |
|||
DBGPRINTLN(F("got 3rd frame (hw info)")); |
|||
/* according to xlsx (different start byte -1!)
|
|||
byte[11] byte[12] APPFW_MINVER |
|||
byte[13] byte[14] HWInfoAddr |
|||
byte[15] byte[16] PNInfoCRC_gusv |
|||
byte[15] byte[16] PNInfoCRC_gusv (this really is double mentionned in xlsx...) |
|||
*/ |
|||
if(*mSerialDebug) { |
|||
DPRINT(DBG_INFO,F("APPFW_MINVER ")); |
|||
DBGPRINTLN(String((p->packet[10] << 8) + p->packet[11])); |
|||
DBGPRINT(F("HWInfoAddr ")); |
|||
DBGPRINTLN(String((p->packet[12] << 8) + p->packet[13])); |
|||
DBGPRINT(F("PNInfoCRC_gusv ")); |
|||
DBGPRINTLN(String((p->packet[14] << 8) + p->packet[15])); |
|||
} |
|||
if(NULL != mCbPayload) |
|||
(mCbPayload)(InverterDevInform_Simple, q->iv); |
|||
q->iv->miMultiParts++; |
|||
} |
|||
//if(q->iv->miMultiParts > 5)
|
|||
//closeRequest(q->iv, true);
|
|||
//else
|
|||
//if(q->iv->miMultiParts < 6)
|
|||
// mState = States::WAIT;
|
|||
|
|||
/*if (mPayload[iv->id].multi_parts > 5) {
|
|||
iv->setQueuedCmdFinished(); |
|||
mPayload[iv->id].complete = true; |
|||
mPayload[iv->id].rxTmo = true; |
|||
mPayload[iv->id].requested= false; |
|||
iv->radioStatistics.rxSuccess++; |
|||
} |
|||
if (mHighPrioIv == NULL) |
|||
mHighPrioIv = iv; |
|||
*/ |
|||
} |
|||
|
|||
inline void miDataDecode(packet_t *p, const queue_s *q) { |
|||
record_t<> *rec = q->iv->getRecordStruct(RealTimeRunData_Debug); // choose the parser
|
|||
rec->ts = q->ts; |
|||
//mState = States::RESET;
|
|||
if(q->iv->miMultiParts < 6) |
|||
q->iv->miMultiParts += 6; |
|||
|
|||
uint8_t datachan = ( p->packet[0] == (MI_REQ_CH1 + ALL_FRAMES) || p->packet[0] == (MI_REQ_4CH + ALL_FRAMES) ) ? CH1 : |
|||
( p->packet[0] == (MI_REQ_CH2 + ALL_FRAMES) || p->packet[0] == (0x37 + ALL_FRAMES) ) ? CH2 : |
|||
p->packet[0] == (0x38 + ALL_FRAMES) ? CH3 : |
|||
CH4; |
|||
// count in RF_communication_protocol.xlsx is with offset = -1
|
|||
q->iv->setValue(q->iv->getPosByChFld(datachan, FLD_UDC, rec), rec, (float)((p->packet[9] << 8) + p->packet[10])/10); |
|||
|
|||
q->iv->setValue(q->iv->getPosByChFld(datachan, FLD_IDC, rec), rec, (float)((p->packet[11] << 8) + p->packet[12])/10); |
|||
|
|||
q->iv->setValue(q->iv->getPosByChFld(0, FLD_UAC, rec), rec, (float)((p->packet[13] << 8) + p->packet[14])/10); |
|||
|
|||
q->iv->setValue(q->iv->getPosByChFld(0, FLD_F, rec), rec, (float) ((p->packet[15] << 8) + p->packet[16])/100); |
|||
q->iv->setValue(q->iv->getPosByChFld(datachan, FLD_PDC, rec), rec, (float)((p->packet[17] << 8) + p->packet[18])/10); |
|||
|
|||
q->iv->setValue(q->iv->getPosByChFld(datachan, FLD_YD, rec), rec, (float)((p->packet[19] << 8) + p->packet[20])/1); |
|||
|
|||
q->iv->setValue(q->iv->getPosByChFld(0, FLD_T, rec), rec, (float) ((int16_t)(p->packet[21] << 8) + p->packet[22])/10); |
|||
q->iv->setValue(q->iv->getPosByChFld(0, FLD_IRR, rec), rec, (float) (calcIrradiation(q->iv, datachan))); |
|||
|
|||
if (datachan == 1) |
|||
q->iv->rssi = p->rssi; |
|||
else if(q->iv->rssi > p->rssi) |
|||
q->iv->rssi = p->rssi; |
|||
|
|||
if (p->packet[0] >= (MI_REQ_4CH + ALL_FRAMES) ) { |
|||
/*For MI1500:
|
|||
if (MI1500) { |
|||
STAT = (uint8_t)(p->packet[25] ); |
|||
FCNT = (uint8_t)(p->packet[26]); |
|||
FCODE = (uint8_t)(p->packet[27]); |
|||
}*/ |
|||
miStsConsolidate(q, datachan, rec, p->packet[23], p->packet[24]); |
|||
|
|||
if (p->packet[0] < (0x39 + ALL_FRAMES) ) { |
|||
mHeu.evalTxChQuality(q->iv, true, (4 - q->attempts), 1); |
|||
miNextRequest((p->packet[0] - ALL_FRAMES + 1), q); |
|||
} else { |
|||
q->iv->miMultiParts = 7; // indicate we are ready
|
|||
//miComplete(q->iv);
|
|||
} |
|||
} else if((p->packet[0] == (MI_REQ_CH1 + ALL_FRAMES)) && (q->iv->type == INV_TYPE_2CH)) { |
|||
//addImportant(q->iv, MI_REQ_CH2);
|
|||
miNextRequest(MI_REQ_CH2, q); |
|||
mHeu.evalTxChQuality(q->iv, true, (4 - q->attempts), q->iv->curFrmCnt); |
|||
//use also miMultiParts here for better statistics?
|
|||
//mHeu.setGotFragment(q->iv);
|
|||
} else { // first data msg for 1ch, 2nd for 2ch
|
|||
q->iv->miMultiParts += 6; // indicate we are ready
|
|||
//miComplete(q->iv);
|
|||
} |
|||
} |
|||
|
|||
void miNextRequest(uint8_t cmd, const queue_s *q) { |
|||
incrAttempt(); // if function is called, we got something, and we necessarily need more transmissions for MI types...
|
|||
if(*mSerialDebug) { |
|||
DPRINT_IVID(DBG_WARN, q->iv->id); |
|||
DBGPRINT(F("next request (")); |
|||
DBGPRINT(String(q->attempts)); |
|||
DBGPRINT(F(" attempts left): 0x")); |
|||
DBGHEXLN(cmd); |
|||
} |
|||
|
|||
if(q->iv->miMultiParts == 7) { |
|||
//mHeu.setGotAll(q->iv);
|
|||
q->iv->radioStatistics.rxSuccess++; |
|||
} else |
|||
//mHeu.setGotFragment(q->iv);
|
|||
/*iv->radioStatistics.rxFail++; // got no complete payload*/ |
|||
//q->iv->radioStatistics.retransmits++;
|
|||
q->iv->radio->sendCmdPacket(q->iv, cmd, 0x00, true); |
|||
|
|||
mWaitTime.startTimeMonitor(MI_TIMEOUT); |
|||
q->iv->miMultiParts = 0; |
|||
q->iv->mGotFragment = 0; |
|||
mIsRetransmit = true; |
|||
chgCmd(cmd); |
|||
//mState = States::WAIT;
|
|||
} |
|||
|
|||
void miRepeatRequest(const queue_s *q) { |
|||
setAttempt(); // if function is called, we got something, and we necessarily need more transmissions for MI types...
|
|||
if(*mSerialDebug) { |
|||
DPRINT_IVID(DBG_WARN, q->iv->id); |
|||
DBGPRINT(F("resend request (")); |
|||
DBGPRINT(String(q->attempts)); |
|||
DBGPRINT(F(" attempts left): 0x")); |
|||
DBGHEXLN(q->cmd); |
|||
} |
|||
|
|||
q->iv->radio->sendCmdPacket(q->iv, q->cmd, 0x00, true); |
|||
|
|||
mWaitTime.startTimeMonitor(MI_TIMEOUT); |
|||
//mState = States::WAIT;
|
|||
mIsRetransmit = false; |
|||
} |
|||
|
|||
void miStsConsolidate(const queue_s *q, uint8_t stschan, record_t<> *rec, uint8_t uState, uint8_t uEnum, uint8_t lState = 0, uint8_t lEnum = 0) { |
|||
//uint8_t status = (p->packet[11] << 8) + p->packet[12];
|
|||
uint16_t statusMi = 3; // regular status for MI, change to 1 later?
|
|||
if ( uState == 2 ) { |
|||
statusMi = 5050 + stschan; //first approach, needs review!
|
|||
if (lState) |
|||
statusMi += lState*10; |
|||
} else if ( uState > 3 ) { |
|||
statusMi = uState*1000 + uEnum*10; |
|||
if (lState) |
|||
statusMi += lState*100; //needs review, esp. for 4ch-8310 state!
|
|||
//if (lEnum)
|
|||
statusMi += lEnum; |
|||
if (uEnum < 6) { |
|||
statusMi += stschan; |
|||
} |
|||
if (statusMi == 8000) |
|||
statusMi = 8310; //trick?
|
|||
} |
|||
|
|||
uint16_t prntsts = statusMi == 3 ? 1 : statusMi; |
|||
bool stsok = true; |
|||
if ( prntsts != rec->record[q->iv->getPosByChFld(0, FLD_EVT, rec)] ) { //sth.'s changed?
|
|||
q->iv->alarmCnt = 1; // minimum...
|
|||
stsok = false; |
|||
//sth is or was wrong?
|
|||
if ( (q->iv->type != INV_TYPE_1CH) && ( (statusMi != 3) |
|||
|| ((q->iv->lastAlarm[stschan].code) && (statusMi == 3) && (q->iv->lastAlarm[stschan].code != 1))) |
|||
) { |
|||
q->iv->lastAlarm[stschan+q->iv->type==INV_TYPE_2CH ? 2: 4] = alarm_t(q->iv->lastAlarm[stschan].code, q->iv->lastAlarm[stschan].start,q->ts); |
|||
q->iv->lastAlarm[stschan] = alarm_t(prntsts, q->ts,0); |
|||
q->iv->alarmCnt = q->iv->type == INV_TYPE_2CH ? 3 : 5; |
|||
} else if ( (q->iv->type == INV_TYPE_1CH) && ( (statusMi != 3) |
|||
|| ((q->iv->lastAlarm[stschan].code) && (statusMi == 3) && (q->iv->lastAlarm[stschan].code != 1))) |
|||
) { |
|||
q->iv->lastAlarm[stschan] = alarm_t(q->iv->lastAlarm[0].code, q->iv->lastAlarm[0].start,q->ts); |
|||
} else if (q->iv->type == INV_TYPE_1CH) |
|||
stsok = true; |
|||
|
|||
q->iv->alarmLastId = prntsts; //iv->alarmMesIndex;
|
|||
|
|||
if (q->iv->alarmCnt > 1) { //more than one channel
|
|||
for (uint8_t ch = 0; ch < (q->iv->alarmCnt); ++ch) { //start with 1
|
|||
if (q->iv->lastAlarm[ch].code == 1) { |
|||
stsok = true; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
if(*mSerialDebug) { |
|||
DPRINT(DBG_WARN, F("New state on CH")); |
|||
DBGPRINT(String(stschan)); DBGPRINT(F(" (")); |
|||
DBGPRINT(String(prntsts)); DBGPRINT(F("): ")); |
|||
DBGPRINTLN(q->iv->getAlarmStr(prntsts)); |
|||
} |
|||
if(!q->iv->miMultiParts) |
|||
q->iv->miMultiParts = 1; // indicate we got status info (1+2 ch types)
|
|||
} |
|||
|
|||
if (!stsok) { |
|||
q->iv->setValue(q->iv->getPosByChFld(0, FLD_EVT, rec), rec, prntsts); |
|||
q->iv->lastAlarm[0] = alarm_t(prntsts, q->ts, 0); |
|||
} |
|||
|
|||
if (q->iv->alarmMesIndex < rec->record[q->iv->getPosByChFld(0, FLD_EVT, rec)]) { |
|||
q->iv->alarmMesIndex = rec->record[q->iv->getPosByChFld(0, FLD_EVT, rec)]; // seems there's no status per channel in 3rd gen. models?!?
|
|||
if (*mSerialDebug) { |
|||
DPRINT_IVID(DBG_INFO, q->iv->id); |
|||
DBGPRINT(F("alarm ID incremented to ")); |
|||
DBGPRINTLN(String(q->iv->alarmMesIndex)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
void miComplete(Inverter<> *iv) { |
|||
if (*mSerialDebug) { |
|||
DPRINT_IVID(DBG_INFO, iv->id); |
|||
DBGPRINTLN(F("got all data msgs")); |
|||
} |
|||
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); |
|||
iv->setValue(iv->getPosByChFld(0, FLD_YD, rec), rec, calcYieldDayCh0(iv,0)); |
|||
|
|||
//preliminary AC calculation...
|
|||
float ac_pow = 0; |
|||
if (iv->type == INV_TYPE_1CH) { |
|||
if ((!iv->lastAlarm[0].code) || (iv->lastAlarm[0].code == 1)) |
|||
ac_pow += iv->getValue(iv->getPosByChFld(1, FLD_PDC, rec), rec); |
|||
} else { |
|||
for(uint8_t i = 1; i <= iv->channels; i++) { |
|||
if ((!iv->lastAlarm[i].code) || (iv->lastAlarm[i].code == 1)) { |
|||
uint8_t pos = iv->getPosByChFld(i, FLD_PDC, rec); |
|||
ac_pow += iv->getValue(pos, rec); |
|||
} |
|||
} |
|||
} |
|||
ac_pow = (int) (ac_pow*9.5); |
|||
iv->setValue(iv->getPosByChFld(0, FLD_PAC, rec), rec, (float) ac_pow/10); |
|||
|
|||
iv->doCalculations(); |
|||
// update status state-machine,
|
|||
if (ac_pow) |
|||
iv->isProducing(); |
|||
//closeRequest(iv, iv->miMultiParts > 5);
|
|||
|
|||
//mHeu.setGotAll(iv);
|
|||
//cmdDone(false);
|
|||
if(NULL != mCbPayload) |
|||
(mCbPayload)(RealTimeRunData_Debug, iv); |
|||
|
|||
//mState = States::RESET; // everything ok, next request
|
|||
} |
|||
|
|||
private: |
|||
enum class States : uint8_t { |
|||
RESET, START, WAIT, CHECK_FRAMES, CHECK_PACKAGE |
|||
}; |
|||
|
|||
typedef struct { |
|||
uint8_t buf[MAX_RF_PAYLOAD_SIZE]; |
|||
uint8_t len; |
|||
int8_t rssi; |
|||
} frame_t; |
|||
|
|||
private: |
|||
States mState = States::RESET; |
|||
uint32_t *mTimestamp; |
|||
bool *mPrivacyMode, *mSerialDebug, *mPrintWholeTrace; |
|||
uint16_t *mInverterGap; |
|||
TimeMonitor mWaitTime = TimeMonitor(0, true); // start as expired (due to code in RESET state)
|
|||
std::array<frame_t, MAX_PAYLOAD_ENTRIES> mLocalBuf; |
|||
bool mFirstTry = false; // see, if we should do a second try
|
|||
bool mIsRetransmit = false; // we already had waited one complete cycle
|
|||
uint8_t mMaxFrameId; |
|||
uint8_t mPayload[MAX_BUFFER]; |
|||
payloadListenerType mCbPayload = NULL; |
|||
alarmListenerType mCbAlarm = NULL; |
|||
Heuristic mHeu; |
|||
uint32_t mLastEmptyQueueMillis = 0; |
|||
bool mPrintSequenceDuration = false; |
|||
|
|||
//States mDebugState = States::START;
|
|||
}; |
|||
|
|||
#endif /*__COMMUNICATION_H__*/ |
|||
@ -0,0 +1,187 @@ |
|||
//-----------------------------------------------------------------------------
|
|||
// 2023 Ahoy, https://github.com/lumpapu/ahoy
|
|||
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/4.0/deed
|
|||
//-----------------------------------------------------------------------------
|
|||
|
|||
#ifndef __HEURISTIC_H__ |
|||
#define __HEURISTIC_H__ |
|||
|
|||
#include "../utils/dbg.h" |
|||
#include "hmInverter.h" |
|||
#include "HeuristicInv.h" |
|||
|
|||
#define RF_TEST_PERIOD_MAX_SEND_CNT 50 |
|||
#define RF_TEST_PERIOD_MAX_FAIL_CNT 5 |
|||
|
|||
#define RF_TX_TEST_CHAN_1ST_USE 0xff |
|||
|
|||
#define RF_TX_CHAN_QUALITY_GOOD 2 |
|||
#define RF_TX_CHAN_QUALITY_OK 1 |
|||
#define RF_TX_CHAN_QUALITY_LOW -1 |
|||
#define RF_TX_CHAN_QUALITY_BAD -2 |
|||
|
|||
class Heuristic { |
|||
public: |
|||
uint8_t getTxCh(Inverter<> *iv) { |
|||
if((IV_HMS == iv->ivGen) || (IV_HMT == iv->ivGen)) |
|||
return 0; // not used for these inverter types
|
|||
|
|||
HeuristicInv *ih = &iv->heuristics; |
|||
|
|||
// start with the next index: round robbin in case of same 'best' quality
|
|||
uint8_t curId = (ih->txRfChId + 1) % RF_MAX_CHANNEL_ID; |
|||
ih->lastBestTxChId = ih->txRfChId; |
|||
ih->txRfChId = curId; |
|||
curId = (curId + 1) % RF_MAX_CHANNEL_ID; |
|||
for(uint8_t i = 1; i < RF_MAX_CHANNEL_ID; i++) { |
|||
if(ih->txRfQuality[curId] > ih->txRfQuality[ih->txRfChId]) |
|||
ih->txRfChId = curId; |
|||
curId = (curId + 1) % RF_MAX_CHANNEL_ID; |
|||
} |
|||
|
|||
if(ih->testPeriodSendCnt < 0xff) |
|||
ih->testPeriodSendCnt++; |
|||
|
|||
if((ih->txRfChId == ih->lastBestTxChId) && (ih->testPeriodSendCnt >= RF_TEST_PERIOD_MAX_SEND_CNT)) { |
|||
if(ih->testPeriodFailCnt > RF_TEST_PERIOD_MAX_FAIL_CNT) { |
|||
// try round robbin another chan and see if it works even better
|
|||
ih->testChId = (ih->testChId + 1) % RF_MAX_CHANNEL_ID; |
|||
if(ih->testChId == ih->txRfChId) |
|||
ih->testChId = (ih->testChId + 1) % RF_MAX_CHANNEL_ID; |
|||
|
|||
// give it a fair chance but remember old status in case of immediate fail
|
|||
ih->saveOldTestQuality = ih->txRfQuality[ih->testChId]; |
|||
ih->txRfQuality[ih->testChId] = ih->txRfQuality[ih->txRfChId]; |
|||
ih->txRfChId = ih->testChId; |
|||
ih->testChId = RF_TX_TEST_CHAN_1ST_USE; // mark the chan as a test and as 1st use during new test period
|
|||
DPRINTLN(DBG_INFO, F("Test CH ") + String(id2Ch(ih->txRfChId))); |
|||
} |
|||
|
|||
// start new test period
|
|||
ih->testPeriodSendCnt = 0; |
|||
ih->testPeriodFailCnt = 0; |
|||
} else if(ih->txRfChId != ih->lastBestTxChId) { |
|||
// start new test period
|
|||
ih->testPeriodSendCnt = 0; |
|||
ih->testPeriodFailCnt = 0; |
|||
} |
|||
|
|||
return id2Ch(ih->txRfChId); |
|||
} |
|||
|
|||
void evalTxChQuality(Inverter<> *iv, bool crcPass, uint8_t retransmits, uint8_t rxFragments) { |
|||
HeuristicInv *ih = &iv->heuristics; |
|||
|
|||
#if (DBG_DEBUG == DEBUG_LEVEL) |
|||
DPRINT(DBG_DEBUG, "eval "); |
|||
DBGPRINT(String(crcPass)); |
|||
DBGPRINT(", "); |
|||
DBGPRINT(String(retransmits)); |
|||
DBGPRINT(", "); |
|||
DBGPRINT(String(rxFragments)); |
|||
DBGPRINT(", "); |
|||
DBGPRINTLN(String(ih->lastRxFragments)); |
|||
#endif |
|||
|
|||
if(ih->lastRxFragments == rxFragments) { |
|||
if(crcPass) |
|||
updateQuality(ih, RF_TX_CHAN_QUALITY_GOOD); |
|||
else if(!retransmits || isNewTxCh(ih)) { // nothing received: send probably lost
|
|||
if(RF_TX_TEST_CHAN_1ST_USE == ih->testChId) { |
|||
// switch back to original quality
|
|||
DPRINTLN(DBG_INFO, F("Test failed (-2)")); |
|||
ih->txRfQuality[ih->txRfChId] = ih->saveOldTestQuality; |
|||
} |
|||
updateQuality(ih, RF_TX_CHAN_QUALITY_BAD); |
|||
if(ih->testPeriodFailCnt < 0xff) |
|||
ih->testPeriodFailCnt++; |
|||
} |
|||
} else if(!ih->lastRxFragments && crcPass) { |
|||
if(!retransmits || isNewTxCh(ih)) { |
|||
// every fragment received successfull immediately
|
|||
updateQuality(ih, RF_TX_CHAN_QUALITY_GOOD); |
|||
} else { |
|||
// every fragment received successfully
|
|||
updateQuality(ih, RF_TX_CHAN_QUALITY_OK); |
|||
} |
|||
} else if(crcPass) { |
|||
if(isNewTxCh(ih)) { |
|||
// last Fragment successfully received on new send channel
|
|||
updateQuality(ih, RF_TX_CHAN_QUALITY_OK); |
|||
} |
|||
} else if(!retransmits || isNewTxCh(ih)) { |
|||
// no complete receive for this send channel
|
|||
if((rxFragments - ih->lastRxFragments) > 2) { |
|||
// graceful evaluation for big inverters that have to send 4 answer packets
|
|||
updateQuality(ih, RF_TX_CHAN_QUALITY_OK); |
|||
} else if((rxFragments - ih->lastRxFragments) < 2) { |
|||
if(RF_TX_TEST_CHAN_1ST_USE == ih->testChId) { |
|||
// switch back to original quality
|
|||
DPRINTLN(DBG_INFO, F("Test failed (-1)")); |
|||
ih->txRfQuality[ih->txRfChId] = ih->saveOldTestQuality; |
|||
} |
|||
updateQuality(ih, RF_TX_CHAN_QUALITY_LOW); |
|||
if(ih->testPeriodFailCnt < 0xff) |
|||
ih->testPeriodFailCnt++; |
|||
} // else: _QUALITY_NEUTRAL, keep any test channel
|
|||
} // else: dont overestimate burst distortion
|
|||
|
|||
ih->testChId = ih->txRfChId; // reset to best
|
|||
ih->lastRxFragments = rxFragments; |
|||
} |
|||
|
|||
void printStatus(Inverter<> *iv) { |
|||
DPRINT_IVID(DBG_INFO, iv->id); |
|||
DBGPRINT(F("Radio infos:")); |
|||
if((IV_HMS != iv->ivGen) && (IV_HMT != iv->ivGen)) { |
|||
for(uint8_t i = 0; i < RF_MAX_CHANNEL_ID; i++) { |
|||
DBGPRINT(F(" ")); |
|||
DBGPRINT(String(iv->heuristics.txRfQuality[i])); |
|||
} |
|||
DBGPRINT(F(" |")); |
|||
} |
|||
DBGPRINT(F(" t: ")); |
|||
DBGPRINT(String(iv->radioStatistics.txCnt)); |
|||
DBGPRINT(F(", s: ")); |
|||
DBGPRINT(String(iv->radioStatistics.rxSuccess)); |
|||
DBGPRINT(F(", f: ")); |
|||
DBGPRINT(String(iv->radioStatistics.rxFail)); |
|||
DBGPRINT(F(", n: ")); |
|||
DBGPRINT(String(iv->radioStatistics.rxFailNoAnser)); |
|||
DBGPRINT(F(" | p: ")); // better debugging for helpers...
|
|||
if((IV_HMS == iv->ivGen) || (IV_HMT == iv->ivGen)) |
|||
DBGPRINTLN(String(iv->config->powerLevel-10)); |
|||
else |
|||
DBGPRINTLN(String(iv->config->powerLevel)); |
|||
} |
|||
|
|||
private: |
|||
bool isNewTxCh(HeuristicInv *ih) { |
|||
return ih->txRfChId != ih->lastBestTxChId; |
|||
} |
|||
|
|||
void updateQuality(HeuristicInv *ih, uint8_t quality) { |
|||
ih->txRfQuality[ih->txRfChId] += quality; |
|||
if(ih->txRfQuality[ih->txRfChId] > RF_MAX_QUALITY) |
|||
ih->txRfQuality[ih->txRfChId] = RF_MAX_QUALITY; |
|||
else if(ih->txRfQuality[ih->txRfChId] < RF_MIN_QUALTIY) |
|||
ih->txRfQuality[ih->txRfChId] = RF_MIN_QUALTIY; |
|||
} |
|||
|
|||
inline uint8_t id2Ch(uint8_t id) { |
|||
switch(id) { |
|||
case 0: return 3; |
|||
case 1: return 23; |
|||
case 2: return 40; |
|||
case 3: return 61; |
|||
case 4: return 75; |
|||
} |
|||
return 3; // standard
|
|||
} |
|||
|
|||
private: |
|||
uint8_t mChList[5] = {03, 23, 40, 61, 75}; |
|||
}; |
|||
|
|||
|
|||
#endif /*__HEURISTIC_H__*/ |
|||
@ -0,0 +1,32 @@ |
|||
//-----------------------------------------------------------------------------
|
|||
// 2023 Ahoy, https://github.com/lumpapu/ahoy
|
|||
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/4.0/deed
|
|||
//-----------------------------------------------------------------------------
|
|||
|
|||
#ifndef __HEURISTIC_INV_H__ |
|||
#define __HEURISTIC_INV_H__ |
|||
|
|||
#define RF_MAX_CHANNEL_ID 5 |
|||
#define RF_MAX_QUALITY 4 |
|||
#define RF_MIN_QUALTIY -6 |
|||
#define RF_NA -99 |
|||
|
|||
class HeuristicInv { |
|||
public: |
|||
HeuristicInv() { |
|||
memset(txRfQuality, -6, RF_MAX_CHANNEL_ID); |
|||
} |
|||
|
|||
public: |
|||
int8_t txRfQuality[RF_MAX_CHANNEL_ID]; // heuristics tx quality (check 'Heuristics.h')
|
|||
uint8_t txRfChId = 0; // RF TX channel id
|
|||
uint8_t lastBestTxChId = 0; |
|||
|
|||
uint8_t testPeriodSendCnt = 0; |
|||
uint8_t testPeriodFailCnt = 0; |
|||
uint8_t testChId = 0; |
|||
int8_t saveOldTestQuality = -6; |
|||
uint8_t lastRxFragments = 0; |
|||
}; |
|||
|
|||
#endif /*__HEURISTIC_INV_H__*/ |
|||
@ -1,418 +0,0 @@ |
|||
//-----------------------------------------------------------------------------
|
|||
// 2023 Ahoy, https://ahoydtu.de
|
|||
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
|
|||
//-----------------------------------------------------------------------------
|
|||
|
|||
#ifndef __HM_PAYLOAD_H__ |
|||
#define __HM_PAYLOAD_H__ |
|||
|
|||
#include "../utils/dbg.h" |
|||
#include "../utils/crc.h" |
|||
#include "../config/config.h" |
|||
#include "hmRadio.h" |
|||
#include <Arduino.h> |
|||
|
|||
typedef struct { |
|||
uint8_t txCmd; |
|||
uint8_t txId; |
|||
uint8_t invId; |
|||
uint32_t ts; |
|||
uint8_t data[MAX_PAYLOAD_ENTRIES][MAX_RF_PAYLOAD_SIZE]; |
|||
uint8_t len[MAX_PAYLOAD_ENTRIES]; |
|||
bool complete; |
|||
uint8_t maxPackId; |
|||
bool lastFound; |
|||
uint8_t retransmits; |
|||
bool requested; |
|||
bool gotFragment; |
|||
} invPayload_t; |
|||
|
|||
|
|||
typedef std::function<void(uint8_t, Inverter<> *)> payloadListenerType; |
|||
typedef std::function<void(Inverter<> *)> alarmListenerType; |
|||
|
|||
|
|||
template<class HMSYSTEM, class HMRADIO> |
|||
class HmPayload { |
|||
public: |
|||
HmPayload() {} |
|||
|
|||
void setup(IApp *app, HMSYSTEM *sys, HMRADIO *radio, statistics_t *stat, uint8_t maxRetransmits, uint32_t *timestamp) { |
|||
mApp = app; |
|||
mSys = sys; |
|||
mRadio = radio; |
|||
mStat = stat; |
|||
mMaxRetrans = maxRetransmits; |
|||
mTimestamp = timestamp; |
|||
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) { |
|||
reset(i); |
|||
} |
|||
mSerialDebug = false; |
|||
mHighPrioIv = NULL; |
|||
mCbAlarm = NULL; |
|||
mCbPayload = NULL; |
|||
} |
|||
|
|||
void enableSerialDebug(bool enable) { |
|||
mSerialDebug = enable; |
|||
} |
|||
|
|||
void addPayloadListener(payloadListenerType cb) { |
|||
mCbPayload = cb; |
|||
} |
|||
|
|||
void addAlarmListener(alarmListenerType cb) { |
|||
mCbAlarm = cb; |
|||
} |
|||
|
|||
void loop() { |
|||
if (NULL != mHighPrioIv) { |
|||
ivSend(mHighPrioIv, true); |
|||
mHighPrioIv = NULL; |
|||
} |
|||
} |
|||
|
|||
/*void simulation() {
|
|||
uint8_t pay[] = { |
|||
0x00, 0x01, 0x01, 0x24, 0x02, 0x28, 0x02, 0x33, |
|||
0x06, 0x49, 0x06, 0x6a, 0x00, 0x05, 0x5f, 0x1b, |
|||
0x00, 0x06, 0x66, 0x9a, 0x03, 0xfd, 0x04, 0x0b, |
|||
0x01, 0x23, 0x02, 0x28, 0x02, 0x28, 0x06, 0x41, |
|||
0x06, 0x43, 0x00, 0x05, 0xdc, 0x2c, 0x00, 0x06, |
|||
0x2e, 0x3f, 0x04, 0x01, 0x03, 0xfb, 0x09, 0x78, |
|||
0x13, 0x86, 0x18, 0x15, 0x00, 0xcf, 0x00, 0xfe, |
|||
0x03, 0xe7, 0x01, 0x42, 0x00, 0x03 |
|||
}; |
|||
|
|||
Inverter<> *iv = mSys->getInverterByPos(0); |
|||
record_t<> *rec = iv->getRecordStruct(0x0b); |
|||
rec->ts = *mTimestamp; |
|||
for (uint8_t i = 0; i < rec->length; i++) { |
|||
iv->addValue(i, pay, rec); |
|||
yield(); |
|||
} |
|||
iv->doCalculations(); |
|||
notify(0x0b, iv); |
|||
}*/ |
|||
|
|||
void ivSendHighPrio(Inverter<> *iv) { |
|||
mHighPrioIv = iv; |
|||
} |
|||
|
|||
void ivSend(Inverter<> *iv, bool highPrio = false) { |
|||
if(!highPrio) { |
|||
if (mPayload[iv->id].requested) { |
|||
if (!mPayload[iv->id].complete) |
|||
process(false); // no retransmit
|
|||
|
|||
if (!mPayload[iv->id].complete) { |
|||
if (mSerialDebug) |
|||
DPRINT_IVID(DBG_INFO, iv->id); |
|||
if (MAX_PAYLOAD_ENTRIES == mPayload[iv->id].maxPackId) { |
|||
mStat->rxFailNoAnser++; // got nothing
|
|||
if (mSerialDebug) |
|||
DBGPRINTLN(F("enqueued cmd failed/timeout")); |
|||
} else { |
|||
mStat->rxFail++; // got fragments but not complete response
|
|||
if (mSerialDebug) { |
|||
DBGPRINT(F("no complete Payload received! (retransmits: ")); |
|||
DBGPRINT(String(mPayload[iv->id].retransmits)); |
|||
DBGPRINTLN(F(")")); |
|||
} |
|||
} |
|||
iv->setQueuedCmdFinished(); // command failed
|
|||
} |
|||
} |
|||
} |
|||
|
|||
reset(iv->id); |
|||
mPayload[iv->id].requested = true; |
|||
|
|||
yield(); |
|||
if (mSerialDebug) { |
|||
DPRINT_IVID(DBG_INFO, iv->id); |
|||
DBGPRINT(F("Requesting Inv SN ")); |
|||
DBGPRINTLN(String(iv->config->serial.u64, HEX)); |
|||
} |
|||
|
|||
if (iv->getDevControlRequest()) { |
|||
if (mSerialDebug) { |
|||
DPRINT_IVID(DBG_INFO, iv->id); |
|||
DBGPRINT(F("Devcontrol request 0x")); |
|||
DBGPRINT(String(iv->devControlCmd, HEX)); |
|||
DBGPRINT(F(" power limit ")); |
|||
DBGPRINTLN(String(iv->powerLimit[0])); |
|||
} |
|||
iv->powerLimitAck = false; |
|||
mRadio->sendControlPacket(iv->radioId.u64, iv->devControlCmd, iv->powerLimit, false); |
|||
mPayload[iv->id].txCmd = iv->devControlCmd; |
|||
//iv->clearCmdQueue();
|
|||
//iv->enqueCommand<InfoCommand>(SystemConfigPara); // read back power limit
|
|||
} else { |
|||
uint8_t cmd = iv->getQueuedCmd(); |
|||
DPRINT_IVID(DBG_INFO, iv->id); |
|||
DBGPRINT(F("prepareDevInformCmd 0x")); |
|||
DBGHEXLN(cmd); |
|||
mRadio->prepareDevInformCmd(iv->radioId.u64, cmd, mPayload[iv->id].ts, iv->alarmMesIndex, false); |
|||
mPayload[iv->id].txCmd = cmd; |
|||
} |
|||
} |
|||
|
|||
void add(Inverter<> *iv, packet_t *p) { |
|||
if (p->packet[0] == (TX_REQ_INFO + ALL_FRAMES)) { // response from get information command
|
|||
mPayload[iv->id].txId = p->packet[0]; |
|||
DPRINTLN(DBG_DEBUG, F("Response from info request received")); |
|||
uint8_t *pid = &p->packet[9]; |
|||
if (*pid == 0x00) { |
|||
DPRINTLN(DBG_DEBUG, F("fragment number zero received and ignored")); |
|||
} else { |
|||
DPRINT(DBG_DEBUG, F("PID: 0x")); |
|||
DPRINTLN(DBG_DEBUG, String(*pid, HEX)); |
|||
if ((*pid & 0x7F) < MAX_PAYLOAD_ENTRIES) { |
|||
memcpy(mPayload[iv->id].data[(*pid & 0x7F) - 1], &p->packet[10], p->len - 11); |
|||
mPayload[iv->id].len[(*pid & 0x7F) - 1] = p->len - 11; |
|||
mPayload[iv->id].gotFragment = true; |
|||
} |
|||
|
|||
if ((*pid & ALL_FRAMES) == ALL_FRAMES) { |
|||
// Last packet
|
|||
if (((*pid & 0x7f) > mPayload[iv->id].maxPackId) || (MAX_PAYLOAD_ENTRIES == mPayload[iv->id].maxPackId)) { |
|||
mPayload[iv->id].maxPackId = (*pid & 0x7f); |
|||
if (*pid > 0x81) |
|||
mPayload[iv->id].lastFound = true; |
|||
} |
|||
} |
|||
} |
|||
} else if (p->packet[0] == (TX_REQ_DEVCONTROL + ALL_FRAMES)) { // response from dev control command
|
|||
DPRINTLN(DBG_DEBUG, F("Response from devcontrol request received")); |
|||
|
|||
mPayload[iv->id].txId = p->packet[0]; |
|||
iv->clearDevControlRequest(); |
|||
|
|||
if ((p->packet[12] == ActivePowerContr) && (p->packet[13] == 0x00)) { |
|||
bool ok = true; |
|||
if((p->packet[10] == 0x00) && (p->packet[11] == 0x00)) { |
|||
mApp->setMqttPowerLimitAck(iv); |
|||
iv->powerLimitAck = true; |
|||
} else |
|||
ok = false; |
|||
|
|||
DPRINT_IVID(DBG_INFO, iv->id); |
|||
DBGPRINT(F(" has ")); |
|||
if(!ok) DBGPRINT(F("not ")); |
|||
DBGPRINT(F("accepted power limit set point ")); |
|||
DBGPRINT(String(iv->powerLimit[0])); |
|||
DBGPRINT(F(" with PowerLimitControl ")); |
|||
DBGPRINTLN(String(iv->powerLimit[1])); |
|||
|
|||
iv->clearCmdQueue(); |
|||
iv->enqueCommand<InfoCommand>(SystemConfigPara); // read back power limit
|
|||
if(mHighPrioIv == NULL) // do it immediately if possible
|
|||
mHighPrioIv = iv; |
|||
} |
|||
iv->devControlCmd = Init; |
|||
} |
|||
} |
|||
|
|||
void process(bool retransmit) { |
|||
for (uint8_t id = 0; id < mSys->getNumInverters(); id++) { |
|||
Inverter<> *iv = mSys->getInverterByPos(id); |
|||
if (NULL == iv) |
|||
continue; // skip to next inverter
|
|||
|
|||
if (IV_HM != iv->ivGen) // only process HM inverters
|
|||
continue; // skip to next inverter
|
|||
|
|||
if ((mPayload[iv->id].txId != (TX_REQ_INFO + ALL_FRAMES)) && (0 != mPayload[iv->id].txId)) { |
|||
// no processing needed if txId is not 0x95
|
|||
mPayload[iv->id].complete = true; |
|||
continue; // skip to next inverter
|
|||
} |
|||
|
|||
if (!mPayload[iv->id].complete) { |
|||
bool crcPass, pyldComplete; |
|||
crcPass = build(iv->id, &pyldComplete); |
|||
if (!crcPass && !pyldComplete) { // payload not complete
|
|||
if ((mPayload[iv->id].requested) && (retransmit)) { |
|||
if (mPayload[iv->id].retransmits < mMaxRetrans) { |
|||
mPayload[iv->id].retransmits++; |
|||
if (iv->devControlCmd == Restart || iv->devControlCmd == CleanState_LockAndAlarm) { |
|||
// This is required to prevent retransmissions without answer.
|
|||
DPRINTLN(DBG_INFO, F("Prevent retransmit on Restart / CleanState_LockAndAlarm...")); |
|||
mPayload[iv->id].retransmits = mMaxRetrans; |
|||
} else if(iv->devControlCmd == ActivePowerContr) { |
|||
DPRINT_IVID(DBG_INFO, iv->id); |
|||
DPRINTLN(DBG_INFO, F("retransmit power limit")); |
|||
mRadio->sendControlPacket(iv->radioId.u64, iv->devControlCmd, iv->powerLimit, true); |
|||
} else { |
|||
if(false == mPayload[iv->id].gotFragment) { |
|||
/*
|
|||
DPRINTLN(DBG_WARN, F("nothing received: Request Complete Retransmit")); |
|||
mPayload[iv->id].txCmd = iv->getQueuedCmd(); |
|||
DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") prepareDevInformCmd 0x") + String(mPayload[iv->id].txCmd, HEX)); |
|||
mRadio->prepareDevInformCmd(iv->radioId.u64, mPayload[iv->id].txCmd, mPayload[iv->id].ts, iv->alarmMesIndex, true); |
|||
*/ |
|||
DPRINT_IVID(DBG_INFO, iv->id); |
|||
DBGPRINTLN(F("nothing received")); |
|||
mPayload[iv->id].retransmits = mMaxRetrans; |
|||
} else { |
|||
for (uint8_t i = 0; i < (mPayload[iv->id].maxPackId - 1); i++) { |
|||
if (mPayload[iv->id].len[i] == 0) { |
|||
DPRINT_IVID(DBG_WARN, iv->id); |
|||
DBGPRINT(F("Frame ")); |
|||
DBGPRINT(String(i + 1)); |
|||
DBGPRINTLN(F(" missing: Request Retransmit")); |
|||
mRadio->sendCmdPacket(iv->radioId.u64, TX_REQ_INFO, (SINGLE_FRAME + i), true); |
|||
break; // only request retransmit one frame per loop
|
|||
} |
|||
yield(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} else if(!crcPass && pyldComplete) { // crc error on complete Payload
|
|||
if (mPayload[iv->id].retransmits < mMaxRetrans) { |
|||
mPayload[iv->id].retransmits++; |
|||
DPRINTLN(DBG_WARN, F("CRC Error: Request Complete Retransmit")); |
|||
mPayload[iv->id].txCmd = iv->getQueuedCmd(); |
|||
DPRINT_IVID(DBG_INFO, iv->id); |
|||
DBGPRINT(F("prepareDevInformCmd 0x")); |
|||
DBGHEXLN(mPayload[iv->id].txCmd); |
|||
mRadio->prepareDevInformCmd(iv->radioId.u64, mPayload[iv->id].txCmd, mPayload[iv->id].ts, iv->alarmMesIndex, true); |
|||
} |
|||
} else { // payload complete
|
|||
DPRINT(DBG_INFO, F("procPyld: cmd: 0x")); |
|||
DBGHEXLN(mPayload[iv->id].txCmd); |
|||
DPRINT(DBG_INFO, F("procPyld: txid: 0x")); |
|||
DBGHEXLN(mPayload[iv->id].txId); |
|||
DPRINT(DBG_DEBUG, F("procPyld: max: ")); |
|||
DPRINTLN(DBG_DEBUG, String(mPayload[iv->id].maxPackId)); |
|||
record_t<> *rec = iv->getRecordStruct(mPayload[iv->id].txCmd); // choose the parser
|
|||
mPayload[iv->id].complete = true; |
|||
|
|||
uint8_t payload[150]; |
|||
uint8_t payloadLen = 0; |
|||
|
|||
memset(payload, 0, 150); |
|||
|
|||
for (uint8_t i = 0; i < (mPayload[iv->id].maxPackId); i++) { |
|||
if((mPayload[iv->id].len[i] + payloadLen) > 150) { |
|||
DPRINTLN(DBG_ERROR, F("payload buffer to small!")); |
|||
break; |
|||
} |
|||
memcpy(&payload[payloadLen], mPayload[iv->id].data[i], (mPayload[iv->id].len[i])); |
|||
payloadLen += (mPayload[iv->id].len[i]); |
|||
yield(); |
|||
} |
|||
payloadLen -= 2; |
|||
|
|||
if (mSerialDebug) { |
|||
DPRINT(DBG_INFO, F("Payload (")); |
|||
DBGPRINT(String(payloadLen)); |
|||
DBGPRINT(F("): ")); |
|||
ah::dumpBuf(payload, payloadLen); |
|||
} |
|||
|
|||
if (NULL == rec) { |
|||
DPRINTLN(DBG_ERROR, F("record is NULL!")); |
|||
} else if ((rec->pyldLen == payloadLen) || (0 == rec->pyldLen)) { |
|||
if (mPayload[iv->id].txId == (TX_REQ_INFO + ALL_FRAMES)) |
|||
mStat->rxSuccess++; |
|||
|
|||
rec->ts = mPayload[iv->id].ts; |
|||
for (uint8_t i = 0; i < rec->length; i++) { |
|||
iv->addValue(i, payload, rec); |
|||
yield(); |
|||
} |
|||
iv->doCalculations(); |
|||
notify(mPayload[iv->id].txCmd, iv); |
|||
|
|||
if(AlarmData == mPayload[iv->id].txCmd) { |
|||
uint8_t i = 0; |
|||
while(1) { |
|||
if(0 == iv->parseAlarmLog(i++, payload, payloadLen)) |
|||
break; |
|||
if (NULL != mCbAlarm) |
|||
(mCbAlarm)(iv); |
|||
yield(); |
|||
} |
|||
} |
|||
} else { |
|||
DPRINT(DBG_ERROR, F("plausibility check failed, expected ")); |
|||
DBGPRINT(String(rec->pyldLen)); |
|||
DBGPRINTLN(F(" bytes")); |
|||
mStat->rxFail++; |
|||
} |
|||
|
|||
iv->setQueuedCmdFinished(); |
|||
} |
|||
} |
|||
yield(); |
|||
} |
|||
} |
|||
|
|||
private: |
|||
void notify(uint8_t val, Inverter<> *iv) { |
|||
if(NULL != mCbPayload) |
|||
(mCbPayload)(val, iv); |
|||
} |
|||
|
|||
bool build(uint8_t id, bool *complete) { |
|||
DPRINTLN(DBG_VERBOSE, F("build")); |
|||
uint16_t crc = 0xffff, crcRcv = 0x0000; |
|||
if (mPayload[id].maxPackId > MAX_PAYLOAD_ENTRIES) |
|||
mPayload[id].maxPackId = MAX_PAYLOAD_ENTRIES; |
|||
|
|||
// check if all fragments are there
|
|||
*complete = true; |
|||
for (uint8_t i = 0; i < mPayload[id].maxPackId; i++) { |
|||
if(mPayload[id].len[i] == 0) |
|||
*complete = false; |
|||
} |
|||
if(!*complete) |
|||
return false; |
|||
|
|||
for (uint8_t i = 0; i < mPayload[id].maxPackId; i++) { |
|||
if (mPayload[id].len[i] > 0) { |
|||
if (i == (mPayload[id].maxPackId - 1)) { |
|||
crc = ah::crc16(mPayload[id].data[i], mPayload[id].len[i] - 2, crc); |
|||
crcRcv = (mPayload[id].data[i][mPayload[id].len[i] - 2] << 8) | (mPayload[id].data[i][mPayload[id].len[i] - 1]); |
|||
} else |
|||
crc = ah::crc16(mPayload[id].data[i], mPayload[id].len[i], crc); |
|||
} |
|||
yield(); |
|||
} |
|||
|
|||
return (crc == crcRcv) ? true : false; |
|||
} |
|||
|
|||
void reset(uint8_t id) { |
|||
DPRINT_IVID(DBG_INFO, id); |
|||
DBGPRINTLN(F("resetPayload")); |
|||
memset(mPayload[id].len, 0, MAX_PAYLOAD_ENTRIES); |
|||
mPayload[id].txCmd = 0; |
|||
mPayload[id].gotFragment = false; |
|||
mPayload[id].retransmits = 0; |
|||
mPayload[id].maxPackId = MAX_PAYLOAD_ENTRIES; |
|||
mPayload[id].lastFound = false; |
|||
mPayload[id].complete = false; |
|||
mPayload[id].requested = false; |
|||
mPayload[id].ts = *mTimestamp; |
|||
} |
|||
|
|||
IApp *mApp; |
|||
HMSYSTEM *mSys; |
|||
HMRADIO *mRadio; |
|||
statistics_t *mStat; |
|||
uint8_t mMaxRetrans; |
|||
uint32_t *mTimestamp; |
|||
invPayload_t mPayload[MAX_NUM_INVERTERS]; |
|||
bool mSerialDebug; |
|||
Inverter<> *mHighPrioIv; |
|||
|
|||
alarmListenerType mCbAlarm; |
|||
payloadListenerType mCbPayload; |
|||
}; |
|||
|
|||
#endif /*__HM_PAYLOAD_H__*/ |
|||
@ -1,775 +0,0 @@ |
|||
//-----------------------------------------------------------------------------
|
|||
// 2023 Ahoy, https://ahoydtu.de
|
|||
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
|
|||
//-----------------------------------------------------------------------------
|
|||
|
|||
#ifndef __MI_PAYLOAD_H__ |
|||
#define __MI_PAYLOAD_H__ |
|||
|
|||
//#include "hmInverter.h"
|
|||
#include "../utils/dbg.h" |
|||
#include "../utils/crc.h" |
|||
#include "../config/config.h" |
|||
#include <Arduino.h> |
|||
|
|||
typedef struct { |
|||
uint32_t ts; |
|||
bool requested; |
|||
bool limitrequested; |
|||
uint8_t txCmd; |
|||
uint8_t len[MAX_PAYLOAD_ENTRIES]; |
|||
bool complete; |
|||
bool dataAB[3]; |
|||
bool stsAB[3]; |
|||
uint16_t sts[6]; |
|||
uint8_t txId; |
|||
uint8_t invId; |
|||
uint8_t retransmits; |
|||
bool gotFragment; |
|||
/*
|
|||
uint8_t data[MAX_PAYLOAD_ENTRIES][MAX_RF_PAYLOAD_SIZE]; |
|||
uint8_t maxPackId; |
|||
bool lastFound;*/ |
|||
} miPayload_t; |
|||
|
|||
|
|||
typedef std::function<void(uint8_t, Inverter<> *)> miPayloadListenerType; |
|||
|
|||
|
|||
template<class HMSYSTEM, class HMRADIO> |
|||
class MiPayload { |
|||
public: |
|||
MiPayload() {} |
|||
|
|||
void setup(IApp *app, HMSYSTEM *sys, HMRADIO *radio, statistics_t *stat, uint8_t maxRetransmits, uint32_t *timestamp) { |
|||
mApp = app; |
|||
mSys = sys; |
|||
mRadio = radio; |
|||
mStat = stat; |
|||
mMaxRetrans = maxRetransmits; |
|||
mTimestamp = timestamp; |
|||
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) { |
|||
reset(i, true); |
|||
mPayload[i].limitrequested = true; |
|||
} |
|||
mSerialDebug = false; |
|||
mHighPrioIv = NULL; |
|||
mCbMiPayload = NULL; |
|||
} |
|||
|
|||
void enableSerialDebug(bool enable) { |
|||
mSerialDebug = enable; |
|||
} |
|||
|
|||
void addPayloadListener(miPayloadListenerType cb) { |
|||
mCbMiPayload = cb; |
|||
} |
|||
|
|||
void addAlarmListener(alarmListenerType cb) { |
|||
mCbAlarm = cb; |
|||
} |
|||
|
|||
void loop() { |
|||
if (NULL != mHighPrioIv) { |
|||
ivSend(mHighPrioIv, true); // for e.g. devcontrol commands
|
|||
mHighPrioIv = NULL; |
|||
} |
|||
} |
|||
|
|||
void ivSendHighPrio(Inverter<> *iv) { |
|||
mHighPrioIv = iv; |
|||
} |
|||
|
|||
void ivSend(Inverter<> *iv, bool highPrio = false) { |
|||
if(!highPrio) { |
|||
if (mPayload[iv->id].requested) { |
|||
if (!mPayload[iv->id].complete) |
|||
process(false); // no retransmit
|
|||
|
|||
if (!mPayload[iv->id].complete) { |
|||
if (mSerialDebug) |
|||
DPRINT_IVID(DBG_INFO, iv->id); |
|||
if (!mPayload[iv->id].gotFragment) { |
|||
mStat->rxFailNoAnser++; // got nothing
|
|||
if (mSerialDebug) |
|||
DBGPRINTLN(F("enqueued cmd failed/timeout")); |
|||
} else { |
|||
mStat->rxFail++; // got "fragments" (part of the required messages)
|
|||
// but no complete set of responses
|
|||
if (mSerialDebug) { |
|||
DBGPRINT(F("no complete Payload received! (retransmits: ")); |
|||
DBGPRINT(String(mPayload[iv->id].retransmits)); |
|||
DBGPRINTLN(F(")")); |
|||
} |
|||
} |
|||
iv->setQueuedCmdFinished(); // command failed
|
|||
} |
|||
} |
|||
} |
|||
|
|||
reset(iv->id); |
|||
mPayload[iv->id].requested = true; |
|||
|
|||
yield(); |
|||
if (mSerialDebug){ |
|||
DPRINT_IVID(DBG_INFO, iv->id); |
|||
DBGPRINT(F("Requesting Inv SN ")); |
|||
DBGPRINTLN(String(iv->config->serial.u64, HEX)); |
|||
} |
|||
|
|||
if (iv->getDevControlRequest()) { |
|||
if (mSerialDebug) { |
|||
DPRINT_IVID(DBG_INFO, iv->id); |
|||
DBGPRINT(F("Devcontrol request 0x")); |
|||
DHEX(iv->devControlCmd); |
|||
DBGPRINT(F(" power limit ")); |
|||
DBGPRINTLN(String(iv->powerLimit[0])); |
|||
} |
|||
iv->powerLimitAck = false; |
|||
mRadio->sendControlPacket(iv->radioId.u64, iv->devControlCmd, iv->powerLimit, false, false); |
|||
mPayload[iv->id].txCmd = iv->devControlCmd; |
|||
mPayload[iv->id].limitrequested = true; |
|||
|
|||
iv->clearCmdQueue(); |
|||
iv->enqueCommand<InfoCommand>(SystemConfigPara); // try to read back power limit
|
|||
} else { |
|||
uint8_t cmd = iv->getQueuedCmd(); |
|||
DPRINT_IVID(DBG_INFO, iv->id); |
|||
DBGPRINT(F("prepareDevInformCmd 0x")); |
|||
DBGHEXLN(cmd); |
|||
uint8_t cmd2 = cmd; |
|||
if ( cmd == SystemConfigPara ) { //0x05 for HM-types
|
|||
if (!mPayload[iv->id].limitrequested) { // only do once at startup
|
|||
iv->setQueuedCmdFinished(); |
|||
cmd = iv->getQueuedCmd(); |
|||
} else { |
|||
mPayload[iv->id].limitrequested = false; |
|||
} |
|||
} |
|||
|
|||
if (cmd == 0x01 || cmd == SystemConfigPara ) { //0x1 and 0x05 for HM-types
|
|||
cmd = 0x0f; // for MI, these seem to make part of the Polling the device software and hardware version number command
|
|||
cmd2 = cmd == SystemConfigPara ? 0x01 : 0x00; //perhaps we can only try to get second frame?
|
|||
mRadio->sendCmdPacket(iv->radioId.u64, cmd, cmd2, false, false); |
|||
} else { |
|||
mRadio->sendCmdPacket(iv->radioId.u64, cmd, cmd2, false, false); |
|||
}; |
|||
|
|||
mPayload[iv->id].txCmd = cmd; |
|||
if (iv->type == INV_TYPE_1CH || iv->type == INV_TYPE_2CH) { |
|||
mPayload[iv->id].dataAB[CH1] = false; |
|||
mPayload[iv->id].stsAB[CH1] = false; |
|||
mPayload[iv->id].dataAB[CH0] = false; |
|||
mPayload[iv->id].stsAB[CH0] = false; |
|||
} |
|||
|
|||
if (iv->type == INV_TYPE_2CH) { |
|||
mPayload[iv->id].dataAB[CH2] = false; |
|||
mPayload[iv->id].stsAB[CH2] = false; |
|||
} |
|||
} |
|||
} |
|||
|
|||
void add(Inverter<> *iv, packet_t *p) { |
|||
//DPRINTLN(DBG_INFO, F("MI got data [0]=") + String(p->packet[0], HEX));
|
|||
if (p->packet[0] == (0x08 + ALL_FRAMES)) { // 0x88; MI status response to 0x09
|
|||
miStsDecode(iv, p); |
|||
} |
|||
|
|||
else if (p->packet[0] == (0x11 + SINGLE_FRAME)) { // 0x92; MI status response to 0x11
|
|||
miStsDecode(iv, p, CH2); |
|||
} |
|||
|
|||
else if ( p->packet[0] == 0x09 + ALL_FRAMES || |
|||
p->packet[0] == 0x11 + ALL_FRAMES || |
|||
( p->packet[0] >= (0x36 + ALL_FRAMES) && p->packet[0] < (0x39 + SINGLE_FRAME) |
|||
&& mPayload[iv->id].txCmd != 0x0f) ) { // small MI or MI 1500 data responses to 0x09, 0x11, 0x36, 0x37, 0x38 and 0x39
|
|||
mPayload[iv->id].txId = p->packet[0]; |
|||
miDataDecode(iv,p); |
|||
} |
|||
|
|||
else if (p->packet[0] == ( 0x0f + ALL_FRAMES)) { |
|||
// MI response from get hardware information request
|
|||
record_t<> *rec = iv->getRecordStruct(InverterDevInform_All); // choose the record structure
|
|||
rec->ts = mPayload[iv->id].ts; |
|||
mPayload[iv->id].gotFragment = true; |
|||
|
|||
/*
|
|||
Polling the device software and hardware version number command |
|||
start byte Command word routing address target address User data check end byte |
|||
byte[0] byte[1] byte[2] byte[3] byte[4] byte[5] byte[6] byte[7] byte[8] byte[9] byte[10] byte[11] byte[12] |
|||
0x7e 0x0f xx xx xx xx YY YY YY YY 0x00 CRC 0x7f |
|||
Command Receipt - First Frame |
|||
start byte Command word target address routing address Multi-frame marking User data User data User data User data User data User data User data User data User data User data User data User data User data User data User data User data check end byte |
|||
byte[0] byte[1] byte[2] byte[3] byte[4] byte[5] byte[6] byte[7] byte[8] byte[9] byte[10] byte[11] byte[12] byte[13] byte[14] byte[15] byte[16] byte[17] byte[18] byte[19] byte[20] byte[21] byte[22] byte[23] byte[24] byte[25] byte[26] byte[27] byte[28] |
|||
0x7e 0x8f YY YY YY YY xx xx xx xx 0x00 USFWBuild_VER APPFWBuild_VER APPFWBuild_YYYY APPFWBuild_MMDD APPFWBuild_HHMM APPFW_PN HW_VER CRC 0x7f |
|||
Command Receipt - Second Frame |
|||
start byte Command word target address routing address Multi-frame marking User data User data User data User data User data User data User data User data User data User data User data User data User data User data User data User data check end byte |
|||
byte[0] byte[1] byte[2] byte[3] byte[4] byte[5] byte[6] byte[7] byte[8] byte[9] byte[10] byte[11] byte[12] byte[13] byte[14] byte[15] byte[16] byte[17] byte[18] byte[19] byte[20] byte[21] byte[22] byte[23] byte[24] byte[25] byte[26] byte[27] byte[28] |
|||
0x7e 0x8f YY YY YY YY xx xx xx xx 0x01 HW_PN HW_FB_TLmValue HW_FB_ReSPRT HW_GridSamp_ResValule HW_ECapValue Matching_APPFW_PN CRC 0x7f |
|||
Command receipt - third frame |
|||
start byte Command word target address routing address Multi-frame marking User data User data User data User data User data User data User data User data check end byte |
|||
byte[0] byte[1] byte[2] byte[3] byte[4] byte[5] byte[6] byte[7] byte[8] byte[9] byte[10] byte[11] byte[12] byte[13] byte[14] byte[15] byte[16] byte[15] byte[16] byte[17] byte[18] |
|||
0x7e 0x8f YY YY YY YY xx xx xx xx 0x12 APPFW_MINVER HWInfoAddr PNInfoCRC_gusv PNInfoCRC_gusv CRC 0x7f |
|||
*/ |
|||
|
|||
/*
|
|||
case InverterDevInform_All: |
|||
rec->length = (uint8_t)(HMINFO_LIST_LEN); |
|||
rec->assign = (byteAssign_t *)InfoAssignment; |
|||
rec->pyldLen = HMINFO_PAYLOAD_LEN; |
|||
break; |
|||
const byteAssign_t InfoAssignment[] = { |
|||
{ FLD_FW_VERSION, UNIT_NONE, CH0, 0, 2, 1 }, |
|||
{ FLD_FW_BUILD_YEAR, UNIT_NONE, CH0, 2, 2, 1 }, |
|||
{ FLD_FW_BUILD_MONTH_DAY, UNIT_NONE, CH0, 4, 2, 1 }, |
|||
{ FLD_FW_BUILD_HOUR_MINUTE, UNIT_NONE, CH0, 6, 2, 1 }, |
|||
{ FLD_HW_ID, UNIT_NONE, CH0, 8, 2, 1 } |
|||
}; |
|||
*/ |
|||
|
|||
if ( p->packet[9] == 0x00 ) {//first frame
|
|||
//FLD_FW_VERSION
|
|||
for (uint8_t i = 0; i < 5; i++) { |
|||
iv->setValue(i, rec, (float) ((p->packet[(12+2*i)] << 8) + p->packet[(13+2*i)])/1); |
|||
} |
|||
iv->isConnected = true; |
|||
mPayload[iv->id].gotFragment = true; |
|||
if(mSerialDebug) { |
|||
DPRINT_IVID(DBG_INFO, iv->id); |
|||
DPRINT(DBG_INFO,F("HW_VER is ")); |
|||
DBGPRINTLN(String((p->packet[24] << 8) + p->packet[25])); |
|||
} |
|||
} else if ( p->packet[9] == 0x01 || p->packet[9] == 0x10 ) {//second frame for MI, 3rd gen. answers in 0x10
|
|||
DPRINT_IVID(DBG_INFO, iv->id); |
|||
if ( p->packet[9] == 0x01 ) { |
|||
DBGPRINTLN(F("got 2nd frame (hw info)")); |
|||
DPRINT(DBG_INFO,F("HW_PartNo ")); |
|||
DBGPRINTLN(String((uint32_t) (((p->packet[10] << 8) | p->packet[11]) << 8 | p->packet[12]) << 8 | p->packet[13])); |
|||
mPayload[iv->id].gotFragment = true; |
|||
iv->setValue(iv->getPosByChFld(0, FLD_YT, rec), rec, (float) ((p->packet[20] << 8) + p->packet[21])/1); |
|||
if(mSerialDebug) { |
|||
DPRINT(DBG_INFO,F("HW_FB_TLmValue ")); |
|||
DBGPRINTLN(String((p->packet[14] << 8) + p->packet[15])); |
|||
DPRINT(DBG_INFO,F("HW_FB_ReSPRT ")); |
|||
DBGPRINTLN(String((p->packet[16] << 8) + p->packet[17])); |
|||
DPRINT(DBG_INFO,F("HW_GridSamp_ResValule ")); |
|||
DBGPRINTLN(String((p->packet[18] << 8) + p->packet[19])); |
|||
DPRINT(DBG_INFO,F("HW_ECapValue ")); |
|||
DBGPRINTLN(String((p->packet[20] << 8) + p->packet[21])); |
|||
} |
|||
} else { |
|||
DBGPRINTLN(F("3rd gen. inverter!")); // see table in OpenDTU code, DevInfoParser.cpp devInfo[]
|
|||
} |
|||
|
|||
} else if ( p->packet[9] == 0x12 ) {//3rd frame
|
|||
DPRINT_IVID(DBG_INFO, iv->id); |
|||
DBGPRINTLN(F("got 3rd frame (hw info)")); |
|||
iv->setQueuedCmdFinished(); |
|||
mPayload[iv->id].complete = true; |
|||
mStat->rxSuccess++; |
|||
} |
|||
|
|||
} else if ( p->packet[0] == (TX_REQ_INFO + ALL_FRAMES) // response from get information command
|
|||
|| (p->packet[0] == 0xB6 && mPayload[iv->id].txCmd != 0x36)) { // strange short response from MI-1500 3rd gen; might be missleading!
|
|||
// atm, we just do nothing else than print out what we got...
|
|||
// for decoding see xls- Data collection instructions - #147ff
|
|||
//mPayload[iv->id].txId = p->packet[0];
|
|||
DPRINTLN(DBG_DEBUG, F("Response from info request received")); |
|||
uint8_t *pid = &p->packet[9]; |
|||
if (*pid == 0x00) { |
|||
DPRINT(DBG_DEBUG, F("fragment number zero received")); |
|||
iv->setQueuedCmdFinished(); |
|||
} else if (p->packet[9] == 0x81) { // might need some additional check, as this is only ment for short answers!
|
|||
DPRINT_IVID(DBG_WARN, iv->id); |
|||
DBGPRINTLN(F("seems to use 3rd gen. protocol - switching ivGen!")); |
|||
iv->ivGen = IV_HM; |
|||
iv->setQueuedCmdFinished(); |
|||
iv->clearCmdQueue(); |
|||
//DPRINTLN(DBG_DEBUG, "PID: 0x" + String(*pid, HEX));
|
|||
/* (old else-tree)
|
|||
if ((*pid & 0x7F) < MAX_PAYLOAD_ENTRIES) {^ |
|||
memcpy(mPayload[iv->id].data[(*pid & 0x7F) - 1], &p->packet[10], p->len - 11); |
|||
mPayload[iv->id].len[(*pid & 0x7F) - 1] = p->len - 11; |
|||
mPayload[iv->id].gotFragment = true; |
|||
} |
|||
if ((*pid & ALL_FRAMES) == ALL_FRAMES) { |
|||
// Last packet
|
|||
if (((*pid & 0x7f) > mPayload[iv->id].maxPackId) || (MAX_PAYLOAD_ENTRIES == mPayload[iv->id].maxPackId)) { |
|||
mPayload[iv->id].maxPackId = (*pid & 0x7f); |
|||
if (*pid > 0x81) |
|||
mPayload[iv->id].lastFound = true; |
|||
} |
|||
}*/ |
|||
} |
|||
//}
|
|||
} else if (p->packet[0] == (TX_REQ_DEVCONTROL + ALL_FRAMES ) // response from dev control command
|
|||
|| p->packet[0] == (TX_REQ_DEVCONTROL + ALL_FRAMES -1)) { // response from DRED instruction
|
|||
DPRINT_IVID(DBG_DEBUG, iv->id); |
|||
DBGPRINTLN(F("Response from devcontrol request received")); |
|||
|
|||
mPayload[iv->id].txId = p->packet[0]; |
|||
iv->clearDevControlRequest(); |
|||
|
|||
if ((p->packet[9] == 0x5a) && (p->packet[10] == 0x5a)) { |
|||
mApp->setMqttPowerLimitAck(iv); |
|||
iv->powerLimitAck = true; |
|||
DPRINT_IVID(DBG_INFO, iv->id); |
|||
DBGPRINT(F("has accepted power limit set point ")); |
|||
DBGPRINT(String(iv->powerLimit[0])); |
|||
DBGPRINT(F(" with PowerLimitControl ")); |
|||
DBGPRINTLN(String(iv->powerLimit[1])); |
|||
|
|||
iv->clearCmdQueue(); |
|||
iv->enqueCommand<InfoCommand>(SystemConfigPara); // read back power limit
|
|||
} |
|||
iv->devControlCmd = Init; |
|||
} else { // some other response; copied from hmPayload:process; might not be correct to do that here!!!
|
|||
DPRINT(DBG_INFO, F("procPyld: cmd: 0x")); |
|||
DBGHEXLN(mPayload[iv->id].txCmd); |
|||
DPRINT(DBG_INFO, F("procPyld: txid: 0x")); |
|||
DBGHEXLN(mPayload[iv->id].txId); |
|||
//DPRINT(DBG_DEBUG, F("procPyld: max: "));
|
|||
//DBGPRINTLN(String(mPayload[iv->id].maxPackId));
|
|||
record_t<> *rec = iv->getRecordStruct(mPayload[iv->id].txCmd); // choose the parser
|
|||
mPayload[iv->id].complete = true; |
|||
|
|||
uint8_t payload[128]; |
|||
uint8_t payloadLen = 0; |
|||
|
|||
memset(payload, 0, 128); |
|||
|
|||
/*for (uint8_t i = 0; i < (mPayload[iv->id].maxPackId); i++) {
|
|||
memcpy(&payload[payloadLen], mPayload[iv->id].data[i], (mPayload[iv->id].len[i])); |
|||
payloadLen += (mPayload[iv->id].len[i]); |
|||
yield(); |
|||
}*/ |
|||
payloadLen -= 2; |
|||
|
|||
if (mSerialDebug) { |
|||
DPRINT(DBG_INFO, F("Payload (")); |
|||
DBGPRINT(String(payloadLen)); |
|||
DBGPRINT("): "); |
|||
ah::dumpBuf(payload, payloadLen); |
|||
} |
|||
|
|||
if (NULL == rec) { |
|||
DPRINTLN(DBG_ERROR, F("record is NULL!")); |
|||
} else if ((rec->pyldLen == payloadLen) || (0 == rec->pyldLen)) { |
|||
if (mPayload[iv->id].txId == (TX_REQ_INFO + ALL_FRAMES)) |
|||
mStat->rxSuccess++; |
|||
|
|||
rec->ts = mPayload[iv->id].ts; |
|||
for (uint8_t i = 0; i < rec->length; i++) { |
|||
iv->addValue(i, payload, rec); |
|||
yield(); |
|||
} |
|||
iv->doCalculations(); |
|||
notify(mPayload[iv->id].txCmd, iv); |
|||
|
|||
if(AlarmData == mPayload[iv->id].txCmd) { |
|||
uint8_t i = 0; |
|||
while(1) { |
|||
if(0 == iv->parseAlarmLog(i++, payload, payloadLen)) |
|||
break; |
|||
if (NULL != mCbAlarm) |
|||
(mCbAlarm)(iv); |
|||
yield(); |
|||
} |
|||
} |
|||
} else { |
|||
DPRINTLN(DBG_ERROR, F("plausibility check failed, expected ") + String(rec->pyldLen) + F(" bytes")); |
|||
mStat->rxFail++; |
|||
} |
|||
|
|||
iv->setQueuedCmdFinished(); |
|||
} |
|||
} |
|||
|
|||
void process(bool retransmit) { |
|||
for (uint8_t id = 0; id < mSys->getNumInverters(); id++) { |
|||
Inverter<> *iv = mSys->getInverterByPos(id); |
|||
if (NULL == iv) |
|||
continue; // skip to next inverter
|
|||
|
|||
if (IV_HM == iv->ivGen) // only process MI inverters
|
|||
continue; // skip to next inverter
|
|||
|
|||
if ( !mPayload[iv->id].complete && |
|||
(mPayload[iv->id].txId != (TX_REQ_INFO + ALL_FRAMES)) && |
|||
(mPayload[iv->id].txId < (0x36 + ALL_FRAMES)) && |
|||
(mPayload[iv->id].txId > (0x39 + ALL_FRAMES)) && |
|||
(mPayload[iv->id].txId != (0x09 + ALL_FRAMES)) && |
|||
(mPayload[iv->id].txId != (0x11 + ALL_FRAMES)) && |
|||
(mPayload[iv->id].txId != (0x88)) && |
|||
(mPayload[iv->id].txId != (0x92)) && |
|||
(mPayload[iv->id].txId != 0 )) { |
|||
// no processing needed if txId is not one of 0x95, 0x88, 0x89, 0x91, 0x92 or resonse to 0x36ff
|
|||
mPayload[iv->id].complete = true; |
|||
continue; // skip to next inverter
|
|||
} |
|||
|
|||
//delayed next message?
|
|||
//mPayload[iv->id].skipfirstrepeat++;
|
|||
/*if (mPayload[iv->id].skipfirstrepeat) {
|
|||
mPayload[iv->id].skipfirstrepeat = 0; //reset counter
|
|||
continue; // skip to next inverter
|
|||
}*/ |
|||
|
|||
if (!mPayload[iv->id].complete) { |
|||
//DPRINTLN(DBG_INFO, F("Pyld incompl code")); //info for testing only
|
|||
bool crcPass, pyldComplete; |
|||
crcPass = build(iv->id, &pyldComplete); |
|||
if (!crcPass && !pyldComplete) { // payload not complete
|
|||
if ((mPayload[iv->id].requested) && (retransmit)) { |
|||
if (iv->devControlCmd == Restart || iv->devControlCmd == CleanState_LockAndAlarm) { |
|||
// This is required to prevent retransmissions without answer.
|
|||
DPRINT_IVID(DBG_INFO, iv->id); |
|||
DBGPRINTLN(F("Prevent retransmit on Restart / CleanState_LockAndAlarm...")); |
|||
mPayload[iv->id].retransmits = mMaxRetrans; |
|||
} else if(iv->devControlCmd == ActivePowerContr) { |
|||
DPRINT_IVID(DBG_INFO, iv->id); |
|||
DBGPRINTLN(F("retransmit power limit")); |
|||
mRadio->sendControlPacket(iv->radioId.u64, iv->devControlCmd, iv->powerLimit, true, false); |
|||
} else { |
|||
uint8_t cmd = mPayload[iv->id].txCmd; |
|||
if (mPayload[iv->id].retransmits < mMaxRetrans) { |
|||
mPayload[iv->id].retransmits++; |
|||
if( !mPayload[iv->id].gotFragment ) { |
|||
DPRINT_IVID(DBG_INFO, iv->id); |
|||
DBGPRINTLN(F("nothing received")); |
|||
mPayload[iv->id].retransmits = mMaxRetrans; |
|||
} else if ( cmd == 0x0f ) { |
|||
//hard/firmware request
|
|||
mRadio->sendCmdPacket(iv->radioId.u64, 0x0f, 0x00, true, false); |
|||
//iv->setQueuedCmdFinished();
|
|||
//cmd = iv->getQueuedCmd();
|
|||
} else { |
|||
bool change = false; |
|||
if ( cmd >= 0x36 && cmd < 0x39 ) { // MI-1500 Data command
|
|||
if (cmd > 0x36 && mPayload[iv->id].retransmits==1) // first request for the upper channels
|
|||
change = true; |
|||
} else if ( cmd == 0x09 ) {//MI single or dual channel device
|
|||
if ( mPayload[iv->id].dataAB[CH1] && iv->type == INV_TYPE_2CH ) { |
|||
if (!mPayload[iv->id].stsAB[CH1] && mPayload[iv->id].retransmits<2) {} |
|||
//first try to get missing sts for first channel a second time
|
|||
else if (!mPayload[iv->id].stsAB[CH2] || !mPayload[iv->id].dataAB[CH2] ) { |
|||
cmd = 0x11; |
|||
change = true; |
|||
mPayload[iv->id].retransmits = 0; //reset counter
|
|||
} |
|||
} |
|||
} else if ( cmd == 0x11) { |
|||
if ( mPayload[iv->id].dataAB[CH2] ) { // data + status ch2 are there?
|
|||
if (mPayload[iv->id].stsAB[CH2] && (!mPayload[iv->id].stsAB[CH1] || !mPayload[iv->id].dataAB[CH1])) { |
|||
cmd = 0x09; |
|||
change = true; |
|||
} |
|||
} |
|||
} |
|||
DPRINT_IVID(DBG_INFO, iv->id); |
|||
if (change) { |
|||
DBGPRINT(F("next request is")); |
|||
//mPayload[iv->id].skipfirstrepeat = 0;
|
|||
mPayload[iv->id].txCmd = cmd; |
|||
} else { |
|||
DBGPRINT(F("sth.")); |
|||
DBGPRINT(F(" missing: Request Retransmit")); |
|||
} |
|||
DBGPRINT(F(" 0x")); |
|||
DBGHEXLN(cmd); |
|||
mRadio->sendCmdPacket(iv->radioId.u64, cmd, cmd, true, false); |
|||
yield(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} else if(!crcPass && pyldComplete) { // crc error on complete Payload
|
|||
if (mPayload[iv->id].retransmits < mMaxRetrans) { |
|||
mPayload[iv->id].retransmits++; |
|||
DPRINT_IVID(DBG_WARN, iv->id); |
|||
DBGPRINTLN(F("CRC Error: Request Complete Retransmit")); |
|||
mPayload[iv->id].txCmd = iv->getQueuedCmd(); |
|||
DPRINT_IVID(DBG_INFO, iv->id); |
|||
|
|||
DBGPRINT(F("prepareDevInformCmd 0x")); |
|||
DBGHEXLN(mPayload[iv->id].txCmd); |
|||
mRadio->sendCmdPacket(iv->radioId.u64, mPayload[iv->id].txCmd, mPayload[iv->id].txCmd, false, false); |
|||
} |
|||
} |
|||
|
|||
} |
|||
yield(); |
|||
} |
|||
} |
|||
|
|||
private: |
|||
void notify(uint8_t val, Inverter<> *iv) { |
|||
if(NULL != mCbMiPayload) |
|||
(mCbMiPayload)(val, iv); |
|||
} |
|||
|
|||
void miStsDecode(Inverter<> *iv, packet_t *p, uint8_t stschan = CH1) { |
|||
//DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") status msg 0x") + String(p->packet[0], HEX));
|
|||
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); // choose the record structure
|
|||
rec->ts = mPayload[iv->id].ts; |
|||
mPayload[iv->id].gotFragment = true; |
|||
mPayload[iv->id].txId = p->packet[0]; |
|||
miStsConsolidate(iv, stschan, rec, p->packet[10], p->packet[12], p->packet[9], p->packet[11]); |
|||
mPayload[iv->id].stsAB[stschan] = true; |
|||
if (mPayload[iv->id].stsAB[CH1] && mPayload[iv->id].stsAB[CH2]) |
|||
mPayload[iv->id].stsAB[CH0] = true; |
|||
//mPayload[iv->id].skipfirstrepeat = 1;
|
|||
if (mPayload[iv->id].stsAB[CH0] && mPayload[iv->id].dataAB[CH0] && !mPayload[iv->id].complete) { |
|||
miComplete(iv); |
|||
} |
|||
} |
|||
|
|||
void miStsConsolidate(Inverter<> *iv, uint8_t stschan, record_t<> *rec, uint8_t uState, uint8_t uEnum, uint8_t lState = 0, uint8_t lEnum = 0) { |
|||
//uint8_t status = (p->packet[11] << 8) + p->packet[12];
|
|||
uint16_t statusMi = 3; // regular status for MI, change to 1 later?
|
|||
if ( uState == 2 ) { |
|||
statusMi = 5050 + stschan; //first approach, needs review!
|
|||
if (lState) |
|||
statusMi += lState*10; |
|||
} else if ( uState > 3 ) { |
|||
statusMi = uState*1000 + uEnum*10; |
|||
if (lState) |
|||
statusMi += lState*100; //needs review, esp. for 4ch-8310 state!
|
|||
//if (lEnum)
|
|||
statusMi += lEnum; |
|||
if (uEnum < 6) { |
|||
statusMi += stschan; |
|||
} |
|||
if (statusMi == 8000) |
|||
statusMi = 8310; //trick?
|
|||
} |
|||
|
|||
uint16_t prntsts = statusMi == 3 ? 1 : statusMi; |
|||
if ( statusMi != mPayload[iv->id].sts[stschan] ) { //sth.'s changed?
|
|||
mPayload[iv->id].sts[stschan] = statusMi; |
|||
DPRINT(DBG_WARN, F("Status change for CH")); |
|||
DBGPRINT(String(stschan)); DBGPRINT(F(" (")); |
|||
DBGPRINT(String(prntsts)); DBGPRINT(F("): ")); |
|||
DBGPRINTLN(iv->getAlarmStr(prntsts)); |
|||
} |
|||
|
|||
if ( !mPayload[iv->id].sts[0] || prntsts < mPayload[iv->id].sts[0] ) { |
|||
mPayload[iv->id].sts[0] = prntsts; |
|||
iv->setValue(iv->getPosByChFld(0, FLD_EVT, rec), rec, prntsts); |
|||
} |
|||
|
|||
if (iv->alarmMesIndex < rec->record[iv->getPosByChFld(0, FLD_EVT, rec)]){ |
|||
iv->alarmMesIndex = rec->record[iv->getPosByChFld(0, FLD_EVT, rec)]; // seems there's no status per channel in 3rd gen. models?!?
|
|||
|
|||
DPRINT_IVID(DBG_INFO, iv->id); |
|||
DBGPRINT(F("alarm ID incremented to ")); |
|||
DBGPRINTLN(String(iv->alarmMesIndex)); |
|||
} |
|||
/*if(AlarmData == mPayload[iv->id].txCmd) {
|
|||
uint8_t i = 0; |
|||
uint16_t code; |
|||
uint32_t start, end; |
|||
while(1) { |
|||
code = iv->parseAlarmLog(i++, payload, payloadLen, &start, &end); |
|||
if(0 == code) |
|||
break; |
|||
if (NULL != mCbAlarm) |
|||
(mCbAlarm)(code, start, end); |
|||
yield(); |
|||
} |
|||
}*/ |
|||
} |
|||
|
|||
void miDataDecode(Inverter<> *iv, packet_t *p) { |
|||
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); // choose the parser
|
|||
rec->ts = mPayload[iv->id].ts; |
|||
mPayload[iv->id].gotFragment = true; |
|||
|
|||
uint8_t datachan = ( p->packet[0] == 0x89 || p->packet[0] == (0x36 + ALL_FRAMES) ) ? CH1 : |
|||
( p->packet[0] == 0x91 || p->packet[0] == (0x37 + ALL_FRAMES) ) ? CH2 : |
|||
p->packet[0] == (0x38 + ALL_FRAMES) ? CH3 : |
|||
CH4; |
|||
// count in RF_communication_protocol.xlsx is with offset = -1
|
|||
iv->setValue(iv->getPosByChFld(datachan, FLD_UDC, rec), rec, (float)((p->packet[9] << 8) + p->packet[10])/10); |
|||
yield(); |
|||
iv->setValue(iv->getPosByChFld(datachan, FLD_IDC, rec), rec, (float)((p->packet[11] << 8) + p->packet[12])/10); |
|||
yield(); |
|||
iv->setValue(iv->getPosByChFld(0, FLD_UAC, rec), rec, (float)((p->packet[13] << 8) + p->packet[14])/10); |
|||
yield(); |
|||
iv->setValue(iv->getPosByChFld(0, FLD_F, rec), rec, (float) ((p->packet[15] << 8) + p->packet[16])/100); |
|||
iv->setValue(iv->getPosByChFld(datachan, FLD_PDC, rec), rec, (float)((p->packet[17] << 8) + p->packet[18])/10); |
|||
yield(); |
|||
iv->setValue(iv->getPosByChFld(datachan, FLD_YD, rec), rec, (float)((p->packet[19] << 8) + p->packet[20])/1); |
|||
yield(); |
|||
iv->setValue(iv->getPosByChFld(0, FLD_T, rec), rec, (float) ((int16_t)(p->packet[21] << 8) + p->packet[22])/10); |
|||
iv->setValue(iv->getPosByChFld(0, FLD_IRR, rec), rec, (float) (calcIrradiation(iv, datachan))); |
|||
|
|||
if ( datachan < 3 ) { |
|||
mPayload[iv->id].dataAB[datachan] = true; |
|||
} |
|||
if ( !mPayload[iv->id].dataAB[CH0] && mPayload[iv->id].dataAB[CH1] && mPayload[iv->id].dataAB[CH2] ) { |
|||
mPayload[iv->id].dataAB[CH0] = true; |
|||
} |
|||
|
|||
if (p->packet[0] >= (0x36 + ALL_FRAMES) ) { |
|||
|
|||
/*For MI1500:
|
|||
if (MI1500) { |
|||
STAT = (uint8_t)(p->packet[25] ); |
|||
FCNT = (uint8_t)(p->packet[26]); |
|||
FCODE = (uint8_t)(p->packet[27]); |
|||
}*/ |
|||
|
|||
/*uint16_t status = (uint8_t)(p->packet[23]);
|
|||
mPayload[iv->id].sts[datachan] = status; |
|||
if ( !mPayload[iv->id].sts[0] || status < mPayload[iv->id].sts[0]) { |
|||
mPayload[iv->id].sts[0] = status; |
|||
iv->setValue(iv->getPosByChFld(0, FLD_EVT, rec), rec, status); |
|||
}*/ |
|||
miStsConsolidate(iv, datachan, rec, p->packet[23], p->packet[24]); |
|||
|
|||
if (p->packet[0] < (0x39 + ALL_FRAMES) ) { |
|||
mPayload[iv->id].txCmd++; |
|||
mPayload[iv->id].retransmits = 0; // reserve retransmissions for each response
|
|||
mPayload[iv->id].complete = false; |
|||
} |
|||
} |
|||
|
|||
/*
|
|||
if(AlarmData == mPayload[iv->id].txCmd) { |
|||
uint8_t i = 0; |
|||
uint16_t code; |
|||
uint32_t start, end; |
|||
while(1) { |
|||
code = iv->parseAlarmLog(i++, payload, payloadLen, &start, &end); |
|||
if(0 == code) |
|||
break; |
|||
if (NULL != mCbAlarm) |
|||
(mCbAl { FLD_YT, UNIT_KWH, CH0, CALC_YT_CH0, 0, CMD_CALC }, |
|||
|
|||
}*/ |
|||
|
|||
//if ( mPayload[iv->id].complete || //4ch device
|
|||
if ( p->packet[0] == (0x39 + ALL_FRAMES) || //4ch device - last message
|
|||
(iv->type != INV_TYPE_4CH //other devices
|
|||
&& mPayload[iv->id].dataAB[CH0] |
|||
&& mPayload[iv->id].stsAB[CH0])) { |
|||
miComplete(iv); |
|||
} |
|||
} |
|||
|
|||
void miComplete(Inverter<> *iv) { |
|||
if ( mPayload[iv->id].complete ) |
|||
return; //if we got second message as well in repreated attempt
|
|||
mPayload[iv->id].complete = true; |
|||
DPRINT_IVID(DBG_INFO, iv->id); |
|||
DBGPRINTLN(F("got all msgs")); |
|||
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); |
|||
iv->setValue(iv->getPosByChFld(0, FLD_YD, rec), rec, calcYieldDayCh0(iv,0)); |
|||
|
|||
//preliminary AC calculation...
|
|||
float ac_pow = 0; |
|||
for(uint8_t i = 1; i <= iv->channels; i++) { |
|||
if (mPayload[iv->id].sts[i] == 3) { |
|||
uint8_t pos = iv->getPosByChFld(i, FLD_PDC, rec); |
|||
ac_pow += iv->getValue(pos, rec); |
|||
} |
|||
} |
|||
ac_pow = (int) (ac_pow*9.5); |
|||
iv->setValue(iv->getPosByChFld(0, FLD_PAC, rec), rec, (float) ac_pow/10); |
|||
|
|||
iv->doCalculations(); |
|||
// update status state-machine,
|
|||
iv->isProducing(); |
|||
|
|||
iv->setQueuedCmdFinished(); |
|||
mStat->rxSuccess++; |
|||
yield(); |
|||
notify(RealTimeRunData_Debug, iv); |
|||
} |
|||
|
|||
bool build(uint8_t id, bool *complete) { |
|||
DPRINTLN(DBG_VERBOSE, F("build")); |
|||
// check if all messages are there
|
|||
|
|||
*complete = mPayload[id].complete; |
|||
uint8_t txCmd = mPayload[id].txCmd; |
|||
|
|||
if(!*complete) { |
|||
DPRINTLN(DBG_VERBOSE, F("incomlete, txCmd is 0x") + String(txCmd, HEX)); |
|||
//DBGHEXLN(txCmd);
|
|||
if (txCmd == 0x09 || txCmd == 0x11 || (txCmd >= 0x36 && txCmd <= 0x39)) |
|||
return false; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
/* uint16_t mParseAlarmLog(uint8_t id, uint8_t pyld[], uint8_t len, uint32_t *start, uint32_t *endTime) {
|
|||
uint8_t startOff = 2 + id * ALARM_LOG_ENTRY_SIZE; |
|||
if((startOff + ALARM_LOG_ENTRY_SIZE) > len) |
|||
return 0; |
|||
|
|||
uint16_t wCode = ((uint16_t)pyld[startOff]) << 8 | pyld[startOff+1]; |
|||
uint32_t startTimeOffset = 0, endTimeOffset = 0; |
|||
|
|||
if (((wCode >> 13) & 0x01) == 1) // check if is AM or PM
|
|||
startTimeOffset = 12 * 60 * 60; |
|||
if (((wCode >> 12) & 0x01) == 1) // check if is AM or PM
|
|||
endTimeOffset = 12 * 60 * 60; |
|||
|
|||
*start = (((uint16_t)pyld[startOff + 4] << 8) | ((uint16_t)pyld[startOff + 5])) + startTimeOffset; |
|||
*endTime = (((uint16_t)pyld[startOff + 6] << 8) | ((uint16_t)pyld[startOff + 7])) + endTimeOffset; |
|||
|
|||
DPRINTLN(DBG_INFO, "Alarm #" + String(pyld[startOff+1]) + " '" + String(getAlarmStr(pyld[startOff+1])) + "' start: " + ah::getTimeStr(*start) + ", end: " + ah::getTimeStr(*endTime)); |
|||
return pyld[startOff+1]; |
|||
} |
|||
*/ |
|||
|
|||
void reset(uint8_t id, bool clrSts = false) { |
|||
DPRINT_IVID(DBG_INFO, id); |
|||
DBGPRINTLN(F("resetPayload")); |
|||
memset(mPayload[id].len, 0, MAX_PAYLOAD_ENTRIES); |
|||
mPayload[id].gotFragment = false; |
|||
/*mPayload[id].maxPackId = MAX_PAYLOAD_ENTRIES;
|
|||
mPayload[id].lastFound = false;*/ |
|||
mPayload[id].retransmits = 0; |
|||
mPayload[id].complete = false; |
|||
mPayload[id].dataAB[CH0] = true; //required for 1CH and 2CH devices
|
|||
mPayload[id].dataAB[CH1] = true; //required for 1CH and 2CH devices
|
|||
mPayload[id].dataAB[CH2] = true; //only required for 2CH devices
|
|||
mPayload[id].stsAB[CH0] = true; //required for 1CH and 2CH devices
|
|||
mPayload[id].stsAB[CH1] = true; //required for 1CH and 2CH devices
|
|||
mPayload[id].stsAB[CH2] = true; //only required for 2CH devices
|
|||
mPayload[id].txCmd = 0; |
|||
//mPayload[id].skipfirstrepeat = 0;
|
|||
mPayload[id].requested = false; |
|||
mPayload[id].ts = *mTimestamp; |
|||
mPayload[id].sts[0] = 0; |
|||
if (clrSts) { // only clear channel states at startup
|
|||
mPayload[id].sts[CH1] = 0; |
|||
mPayload[id].sts[CH2] = 0; |
|||
mPayload[id].sts[CH3] = 0; |
|||
mPayload[id].sts[CH4] = 0; |
|||
mPayload[id].sts[5] = 0; //remember last summarized state
|
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
IApp *mApp; |
|||
HMSYSTEM *mSys; |
|||
HMRADIO *mRadio; |
|||
statistics_t *mStat; |
|||
uint8_t mMaxRetrans; |
|||
uint32_t *mTimestamp; |
|||
miPayload_t mPayload[MAX_NUM_INVERTERS]; |
|||
bool mSerialDebug; |
|||
|
|||
Inverter<> *mHighPrioIv; |
|||
alarmListenerType mCbAlarm; |
|||
payloadListenerType mCbMiPayload; |
|||
}; |
|||
|
|||
#endif /*__MI_PAYLOAD_H__*/ |
|||
@ -0,0 +1,218 @@ |
|||
//-----------------------------------------------------------------------------
|
|||
// 2023 Ahoy, https://www.mikrocontroller.net/topic/525778
|
|||
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
|
|||
//-----------------------------------------------------------------------------
|
|||
|
|||
#ifndef __NRF_HAL_H__ |
|||
#define __NRF_HAL_H__ |
|||
|
|||
#pragma once |
|||
|
|||
#include "../utils/spiPatcher.h" |
|||
|
|||
#include <esp_rom_gpio.h> |
|||
#include <RF24_hal.h> |
|||
|
|||
#define NRF_MAX_TRANSFER_SZ 64 |
|||
#define NRF_DEFAULT_SPI_SPEED 10000000 // 10 MHz
|
|||
|
|||
class nrfHal: public RF24_hal, public SpiPatcherHandle { |
|||
public: |
|||
nrfHal() { |
|||
mSpiPatcher = SpiPatcher::getInstance(SPI2_HOST); |
|||
} |
|||
|
|||
void patch() override { |
|||
esp_rom_gpio_connect_out_signal(mPinMosi, spi_periph_signal[mHostDevice].spid_out, false, false); |
|||
esp_rom_gpio_connect_in_signal(mPinMiso, spi_periph_signal[mHostDevice].spiq_in, false); |
|||
esp_rom_gpio_connect_out_signal(mPinClk, spi_periph_signal[mHostDevice].spiclk_out, false, false); |
|||
} |
|||
|
|||
void unpatch() override { |
|||
esp_rom_gpio_connect_out_signal(mPinMosi, SIG_GPIO_OUT_IDX, false, false); |
|||
esp_rom_gpio_connect_in_signal(mPinMiso, GPIO_MATRIX_CONST_ZERO_INPUT, false); |
|||
esp_rom_gpio_connect_out_signal(mPinClk, SIG_GPIO_OUT_IDX, false, false); |
|||
} |
|||
|
|||
void init(int8_t mosi, int8_t miso, int8_t sclk, int8_t cs, int8_t en, int32_t speed = NRF_DEFAULT_SPI_SPEED) { |
|||
mPinMosi = static_cast<gpio_num_t>(mosi); |
|||
mPinMiso = static_cast<gpio_num_t>(miso); |
|||
mPinClk = static_cast<gpio_num_t>(sclk); |
|||
mPinCs = static_cast<gpio_num_t>(cs); |
|||
mPinEn = static_cast<gpio_num_t>(en); |
|||
mSpiSpeed = speed; |
|||
|
|||
mHostDevice = mSpiPatcher->getDevice(); |
|||
|
|||
gpio_reset_pin(mPinMosi); |
|||
gpio_set_direction(mPinMosi, GPIO_MODE_OUTPUT); |
|||
gpio_set_level(mPinMosi, 1); |
|||
|
|||
gpio_reset_pin(mPinMiso); |
|||
gpio_set_direction(mPinMiso, GPIO_MODE_INPUT); |
|||
|
|||
gpio_reset_pin(mPinClk); |
|||
gpio_set_direction(mPinClk, GPIO_MODE_OUTPUT); |
|||
gpio_set_level(mPinClk, 0); |
|||
|
|||
gpio_reset_pin(mPinCs); |
|||
spi_device_interface_config_t devcfg = { |
|||
.command_bits = 0, |
|||
.address_bits = 0, |
|||
.dummy_bits = 0, |
|||
.mode = 0, |
|||
.duty_cycle_pos = 0, |
|||
.cs_ena_pretrans = 0, |
|||
.cs_ena_posttrans = 0, |
|||
.clock_speed_hz = mSpiSpeed, |
|||
.input_delay_ns = 0, |
|||
.spics_io_num = mPinCs, |
|||
.flags = 0, |
|||
.queue_size = 1, |
|||
.pre_cb = nullptr, |
|||
.post_cb = nullptr |
|||
}; |
|||
ESP_ERROR_CHECK(spi_bus_add_device(mHostDevice, &devcfg, &spi)); |
|||
|
|||
gpio_reset_pin(mPinEn); |
|||
gpio_set_direction(mPinEn, GPIO_MODE_OUTPUT); |
|||
gpio_set_level(mPinEn, 0); |
|||
} |
|||
|
|||
|
|||
bool begin() override { |
|||
return true; |
|||
} |
|||
|
|||
void end() override {} |
|||
|
|||
void ce(bool level) override { |
|||
gpio_set_level(mPinEn, level); |
|||
} |
|||
|
|||
uint8_t write(uint8_t cmd, const uint8_t* buf, uint8_t len) override { |
|||
uint8_t data[NRF_MAX_TRANSFER_SZ]; |
|||
data[0] = cmd; |
|||
std::copy(&buf[0], &buf[len], &data[1]); |
|||
|
|||
request_spi(); |
|||
|
|||
size_t spiLen = (static_cast<size_t>(len) + 1u) << 3; |
|||
spi_transaction_t t = { |
|||
.flags = 0, |
|||
.cmd = 0, |
|||
.addr = 0, |
|||
.length = spiLen, |
|||
.rxlength = spiLen, |
|||
.user = NULL, |
|||
.tx_buffer = data, |
|||
.rx_buffer = data |
|||
}; |
|||
ESP_ERROR_CHECK(spi_device_polling_transmit(spi, &t)); |
|||
|
|||
release_spi(); |
|||
|
|||
return data[0]; // status
|
|||
} |
|||
|
|||
uint8_t write(uint8_t cmd, const uint8_t* buf, uint8_t data_len, uint8_t blank_len) override { |
|||
uint8_t data[NRF_MAX_TRANSFER_SZ]; |
|||
data[0] = cmd; |
|||
memset(&data[1], 0, (NRF_MAX_TRANSFER_SZ-1)); |
|||
std::copy(&buf[0], &buf[data_len], &data[1]); |
|||
|
|||
request_spi(); |
|||
|
|||
size_t spiLen = (static_cast<size_t>(data_len) + static_cast<size_t>(blank_len) + 1u) << 3; |
|||
spi_transaction_t t = { |
|||
.flags = 0, |
|||
.cmd = 0, |
|||
.addr = 0, |
|||
.length = spiLen, |
|||
.rxlength = spiLen, |
|||
.user = NULL, |
|||
.tx_buffer = data, |
|||
.rx_buffer = data |
|||
}; |
|||
ESP_ERROR_CHECK(spi_device_polling_transmit(spi, &t)); |
|||
|
|||
release_spi(); |
|||
|
|||
return data[0]; // status
|
|||
} |
|||
|
|||
uint8_t read(uint8_t cmd, uint8_t* buf, uint8_t len) override { |
|||
uint8_t data[NRF_MAX_TRANSFER_SZ]; |
|||
data[0] = cmd; |
|||
memset(&data[1], 0xff, len); |
|||
|
|||
request_spi(); |
|||
|
|||
size_t spiLen = (static_cast<size_t>(len) + 1u) << 3; |
|||
spi_transaction_t t = { |
|||
.flags = 0, |
|||
.cmd = 0, |
|||
.addr = 0, |
|||
.length = spiLen, |
|||
.rxlength = spiLen, |
|||
.user = NULL, |
|||
.tx_buffer = data, |
|||
.rx_buffer = data |
|||
}; |
|||
ESP_ERROR_CHECK(spi_device_polling_transmit(spi, &t)); |
|||
|
|||
release_spi(); |
|||
|
|||
std::copy(&data[1], &data[len+1], buf); |
|||
return data[0]; // status
|
|||
} |
|||
|
|||
uint8_t read(uint8_t cmd, uint8_t* buf, uint8_t data_len, uint8_t blank_len) override { |
|||
uint8_t data[NRF_MAX_TRANSFER_SZ]; |
|||
data[0] = cmd; |
|||
memset(&data[1], 0xff, (data_len + blank_len)); |
|||
|
|||
request_spi(); |
|||
|
|||
size_t spiLen = (static_cast<size_t>(data_len) + static_cast<size_t>(blank_len) + 1u) << 3; |
|||
spi_transaction_t t = { |
|||
.flags = 0, |
|||
.cmd = 0, |
|||
.addr = 0, |
|||
.length = spiLen, |
|||
.rxlength = spiLen, |
|||
.user = NULL, |
|||
.tx_buffer = data, |
|||
.rx_buffer = data |
|||
}; |
|||
ESP_ERROR_CHECK(spi_device_polling_transmit(spi, &t)); |
|||
|
|||
release_spi(); |
|||
|
|||
std::copy(&data[1], &data[data_len+1], buf); |
|||
return data[0]; // status
|
|||
} |
|||
|
|||
private: |
|||
inline void request_spi() { |
|||
mSpiPatcher->request(this); |
|||
} |
|||
|
|||
inline void release_spi() { |
|||
mSpiPatcher->release(); |
|||
} |
|||
|
|||
private: |
|||
gpio_num_t mPinMosi = GPIO_NUM_NC; |
|||
gpio_num_t mPinMiso = GPIO_NUM_NC; |
|||
gpio_num_t mPinClk = GPIO_NUM_NC; |
|||
gpio_num_t mPinCs = GPIO_NUM_NC; |
|||
gpio_num_t mPinEn = GPIO_NUM_NC; |
|||
int32_t mSpiSpeed = NRF_DEFAULT_SPI_SPEED; |
|||
|
|||
spi_host_device_t mHostDevice; |
|||
spi_device_handle_t spi; |
|||
SpiPatcher *mSpiPatcher; |
|||
}; |
|||
|
|||
#endif /*__NRF_HAL_H__*/ |
|||
@ -0,0 +1,117 @@ |
|||
//-----------------------------------------------------------------------------
|
|||
// 2023 Ahoy, https://github.com/lumpapu/ahoy
|
|||
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/4.0/deed
|
|||
//-----------------------------------------------------------------------------
|
|||
|
|||
#ifndef __RADIO_H__ |
|||
#define __RADIO_H__ |
|||
|
|||
#define TX_REQ_INFO 0x15 |
|||
#define TX_REQ_DEVCONTROL 0x51 |
|||
#define ALL_FRAMES 0x80 |
|||
#define SINGLE_FRAME 0x81 |
|||
|
|||
#include "../utils/dbg.h" |
|||
#include "../utils/crc.h" |
|||
|
|||
// forward declaration of class
|
|||
template <class REC_TYP=float> |
|||
class Inverter; |
|||
|
|||
// abstract radio interface
|
|||
class Radio { |
|||
public: |
|||
virtual void sendControlPacket(Inverter<> *iv, uint8_t cmd, uint16_t *data, bool isRetransmit) = 0; |
|||
virtual bool switchFrequency(Inverter<> *iv, uint32_t fromkHz, uint32_t tokHz) { return true; } |
|||
virtual bool switchFrequencyCh(Inverter<> *iv, uint8_t fromCh, uint8_t toCh) { return true; } |
|||
virtual bool isChipConnected(void) { return false; } |
|||
|
|||
virtual void loop(void) {}; |
|||
|
|||
void handleIntr(void) { |
|||
mIrqRcvd = true; |
|||
} |
|||
|
|||
void sendCmdPacket(Inverter<> *iv, uint8_t mid, uint8_t pid, bool isRetransmit, bool appendCrc16=true) { |
|||
initPacket(getIvId(iv), mid, pid); |
|||
sendPacket(iv, 10, isRetransmit, appendCrc16); |
|||
} |
|||
|
|||
void prepareDevInformCmd(Inverter<> *iv, uint8_t cmd, uint32_t ts, uint16_t alarmMesId, bool isRetransmit, uint8_t reqfld=TX_REQ_INFO) { // might not be necessary to add additional arg.
|
|||
if(IV_MI == getIvGen(iv)) { |
|||
DPRINT(DBG_DEBUG, F("legacy cmd 0x")); |
|||
DPRINTLN(DBG_DEBUG,String(cmd, HEX)); |
|||
sendCmdPacket(iv, cmd, cmd, false, false); |
|||
return; |
|||
} |
|||
|
|||
if(*mSerialDebug) { |
|||
DPRINT(DBG_DEBUG, F("prepareDevInformCmd 0x")); |
|||
DPRINTLN(DBG_DEBUG,String(cmd, HEX)); |
|||
} |
|||
initPacket(getIvId(iv), reqfld, ALL_FRAMES); |
|||
mTxBuf[10] = cmd; |
|||
CP_U32_LittleEndian(&mTxBuf[12], ts); |
|||
if (cmd == AlarmData ) { //cmd == RealTimeRunData_Debug ||
|
|||
mTxBuf[18] = (alarmMesId >> 8) & 0xff; |
|||
mTxBuf[19] = (alarmMesId ) & 0xff; |
|||
} |
|||
sendPacket(iv, 24, isRetransmit); |
|||
} |
|||
|
|||
uint32_t getDTUSn(void) { |
|||
return mDtuSn; |
|||
} |
|||
|
|||
public: |
|||
std::queue<packet_t> mBufCtrl; |
|||
|
|||
protected: |
|||
virtual void sendPacket(Inverter<> *iv, uint8_t len, bool isRetransmit, bool appendCrc16=true) = 0; |
|||
virtual uint64_t getIvId(Inverter<> *iv) = 0; |
|||
virtual uint8_t getIvGen(Inverter<> *iv) = 0; |
|||
|
|||
void initPacket(uint64_t ivId, uint8_t mid, uint8_t pid) { |
|||
mTxBuf[0] = mid; |
|||
CP_U32_BigEndian(&mTxBuf[1], ivId >> 8); |
|||
CP_U32_LittleEndian(&mTxBuf[5], mDtuSn); |
|||
mTxBuf[9] = pid; |
|||
memset(&mTxBuf[10], 0x00, (MAX_RF_PAYLOAD_SIZE-10)); |
|||
} |
|||
|
|||
void updateCrcs(uint8_t *len, bool appendCrc16=true) { |
|||
// append crc's
|
|||
if (appendCrc16 && ((*len) > 10)) { |
|||
// crc control data
|
|||
uint16_t crc = ah::crc16(&mTxBuf[10], (*len) - 10); |
|||
mTxBuf[(*len)++] = (crc >> 8) & 0xff; |
|||
mTxBuf[(*len)++] = (crc ) & 0xff; |
|||
} |
|||
// crc over all
|
|||
mTxBuf[*len] = ah::crc8(mTxBuf, *len); |
|||
(*len)++; |
|||
} |
|||
|
|||
void generateDtuSn(void) { |
|||
uint32_t chipID = 0; |
|||
#ifdef ESP32 |
|||
uint64_t MAC = ESP.getEfuseMac(); |
|||
chipID = ((MAC >> 8) & 0xFF0000) | ((MAC >> 24) & 0xFF00) | ((MAC >> 40) & 0xFF); |
|||
#else |
|||
chipID = ESP.getChipId(); |
|||
#endif |
|||
mDtuSn = 0x80000000; // the first digit is an 8 for DTU production year 2022, the rest is filled with the ESP chipID in decimal
|
|||
for(int i = 0; i < 7; i++) { |
|||
mDtuSn |= (chipID % 10) << (i * 4); |
|||
chipID /= 10; |
|||
} |
|||
} |
|||
|
|||
uint32_t mDtuSn; |
|||
volatile bool mIrqRcvd; |
|||
bool *mSerialDebug, *mPrivacyMode, *mPrintWholeTrace; |
|||
uint8_t mTxBuf[MAX_RF_PAYLOAD_SIZE]; |
|||
|
|||
}; |
|||
|
|||
#endif /*__RADIO_H__*/ |
|||
@ -0,0 +1,196 @@ |
|||
//-----------------------------------------------------------------------------
|
|||
// 2023 Ahoy, https://www.mikrocontroller.net/topic/525778
|
|||
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
|
|||
//-----------------------------------------------------------------------------
|
|||
|
|||
#ifndef __CMT_HAL_H__ |
|||
#define __CMT_HAL_H__ |
|||
|
|||
#pragma once |
|||
|
|||
#include "../utils/spiPatcher.h" |
|||
|
|||
#include <driver/gpio.h> |
|||
|
|||
#define CMT_DEFAULT_SPI_SPEED 4000000 // 4 MHz
|
|||
|
|||
class cmtHal : public SpiPatcherHandle { |
|||
public: |
|||
cmtHal() { |
|||
mSpiPatcher = SpiPatcher::getInstance(SPI2_HOST); |
|||
} |
|||
|
|||
void patch() override { |
|||
esp_rom_gpio_connect_out_signal(mPinSdio, spi_periph_signal[mHostDevice].spid_out, false, false); |
|||
esp_rom_gpio_connect_in_signal(mPinSdio, spi_periph_signal[mHostDevice].spid_in, false); |
|||
esp_rom_gpio_connect_out_signal(mPinClk, spi_periph_signal[mHostDevice].spiclk_out, false, false); |
|||
} |
|||
|
|||
void unpatch() override { |
|||
esp_rom_gpio_connect_out_signal(mPinSdio, SIG_GPIO_OUT_IDX, false, false); |
|||
esp_rom_gpio_connect_in_signal(mPinSdio, GPIO_MATRIX_CONST_ZERO_INPUT, false); |
|||
esp_rom_gpio_connect_out_signal(mPinClk, SIG_GPIO_OUT_IDX, false, false); |
|||
} |
|||
|
|||
void init(int8_t sdio, int8_t clk, int8_t cs, int8_t fcs, int32_t speed = CMT_DEFAULT_SPI_SPEED) { |
|||
mPinSdio = static_cast<gpio_num_t>(sdio); |
|||
mPinClk = static_cast<gpio_num_t>(clk); |
|||
mPinCs = static_cast<gpio_num_t>(cs); |
|||
mPinFcs = static_cast<gpio_num_t>(fcs); |
|||
mSpiSpeed = speed; |
|||
|
|||
mHostDevice = mSpiPatcher->getDevice(); |
|||
|
|||
gpio_reset_pin(mPinSdio); |
|||
gpio_set_direction(mPinSdio, GPIO_MODE_INPUT_OUTPUT); |
|||
gpio_set_level(mPinSdio, 1); |
|||
|
|||
gpio_reset_pin(mPinClk); |
|||
gpio_set_direction(mPinClk, GPIO_MODE_OUTPUT); |
|||
gpio_set_level(mPinClk, 0); |
|||
|
|||
gpio_reset_pin(mPinCs); |
|||
spi_device_interface_config_t devcfg_reg = { |
|||
.command_bits = 1, |
|||
.address_bits = 7, |
|||
.dummy_bits = 0, |
|||
.mode = 0, |
|||
.duty_cycle_pos = 0, |
|||
.cs_ena_pretrans = 1, |
|||
.cs_ena_posttrans = 1, |
|||
.clock_speed_hz = mSpiSpeed, |
|||
.input_delay_ns = 0, |
|||
.spics_io_num = mPinCs, |
|||
.flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_3WIRE, |
|||
.queue_size = 1, |
|||
.pre_cb = nullptr, |
|||
.post_cb = nullptr |
|||
}; |
|||
ESP_ERROR_CHECK(spi_bus_add_device(mHostDevice, &devcfg_reg, &spi_reg)); |
|||
|
|||
gpio_reset_pin(mPinFcs); |
|||
spi_device_interface_config_t devcfg_fifo = { |
|||
.command_bits = 0, |
|||
.address_bits = 0, |
|||
.dummy_bits = 0, |
|||
.mode = 0, |
|||
.duty_cycle_pos = 0, |
|||
.cs_ena_pretrans = 2, |
|||
.cs_ena_posttrans = static_cast<uint8_t>(2 * mSpiSpeed / 1000000), // >2 us
|
|||
.clock_speed_hz = mSpiSpeed, |
|||
.input_delay_ns = 0, |
|||
.spics_io_num = mPinFcs, |
|||
.flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_3WIRE, |
|||
.queue_size = 1, |
|||
.pre_cb = nullptr, |
|||
.post_cb = nullptr |
|||
}; |
|||
ESP_ERROR_CHECK(spi_bus_add_device(mHostDevice, &devcfg_fifo, &spi_fifo)); |
|||
} |
|||
|
|||
uint8_t readReg(uint8_t addr) { |
|||
uint8_t data; |
|||
|
|||
request_spi(); |
|||
|
|||
spi_transaction_t t = { |
|||
.flags = 0, |
|||
.cmd = 1, |
|||
.addr = addr, |
|||
.length = 0, |
|||
.rxlength = 8, |
|||
.user = NULL, |
|||
.tx_buffer = NULL, |
|||
.rx_buffer = &data |
|||
}; |
|||
ESP_ERROR_CHECK(spi_device_polling_transmit(spi_reg, &t)); |
|||
|
|||
release_spi(); |
|||
|
|||
return data; |
|||
} |
|||
|
|||
void writeReg(uint8_t addr, uint8_t data) { |
|||
request_spi(); |
|||
|
|||
spi_transaction_t t = { |
|||
.flags = 0, |
|||
.cmd = 0, |
|||
.addr = addr, |
|||
.length = 8, |
|||
.rxlength = 0, |
|||
.user = NULL, |
|||
.tx_buffer = &data, |
|||
.rx_buffer = NULL |
|||
}; |
|||
ESP_ERROR_CHECK(spi_device_polling_transmit(spi_reg, &t)); |
|||
|
|||
release_spi(); |
|||
} |
|||
|
|||
void readFifo(uint8_t buf[], uint8_t *len, uint8_t maxlen) { |
|||
request_spi(); |
|||
|
|||
spi_transaction_t t = { |
|||
.flags = 0, |
|||
.cmd = 0, |
|||
.addr = 0, |
|||
.length = 0, |
|||
.rxlength = 8, |
|||
.user = NULL, |
|||
.tx_buffer = NULL, |
|||
.rx_buffer = NULL |
|||
}; |
|||
for (uint8_t i = 0; i < maxlen; i++) { |
|||
if(0 == i) |
|||
t.rx_buffer = len; |
|||
else |
|||
t.rx_buffer = buf + i - 1; |
|||
ESP_ERROR_CHECK(spi_device_polling_transmit(spi_fifo, &t)); |
|||
} |
|||
|
|||
release_spi(); |
|||
} |
|||
void writeFifo(const uint8_t buf[], uint16_t len) { |
|||
request_spi(); |
|||
|
|||
spi_transaction_t t = { |
|||
.flags = 0, |
|||
.cmd = 0, |
|||
.addr = 0, |
|||
.length = 8, |
|||
.rxlength = 0, |
|||
.user = NULL, |
|||
.tx_buffer = NULL, |
|||
.rx_buffer = NULL |
|||
}; |
|||
for (uint16_t i = 0; i < len; i++) { |
|||
t.tx_buffer = buf + i; |
|||
ESP_ERROR_CHECK(spi_device_polling_transmit(spi_fifo, &t)); |
|||
} |
|||
|
|||
release_spi(); |
|||
} |
|||
|
|||
private: |
|||
inline void request_spi() { |
|||
mSpiPatcher->request(this); |
|||
} |
|||
|
|||
inline void release_spi() { |
|||
mSpiPatcher->release(); |
|||
} |
|||
|
|||
private: |
|||
gpio_num_t mPinSdio = GPIO_NUM_NC; |
|||
gpio_num_t mPinClk = GPIO_NUM_NC; |
|||
gpio_num_t mPinCs = GPIO_NUM_NC; |
|||
gpio_num_t mPinFcs = GPIO_NUM_NC; |
|||
int32_t mSpiSpeed = CMT_DEFAULT_SPI_SPEED; |
|||
|
|||
spi_host_device_t mHostDevice; |
|||
spi_device_handle_t spi_reg, spi_fifo; |
|||
SpiPatcher *mSpiPatcher; |
|||
}; |
|||
|
|||
#endif /*__CMT_HAL_H__*/ |
|||
@ -1,406 +0,0 @@ |
|||
//-----------------------------------------------------------------------------
|
|||
// 2023 Ahoy, https://ahoydtu.de
|
|||
// Creative Commons - https://creativecommons.org/licenses/by-nc-sa/4.0/deed
|
|||
//-----------------------------------------------------------------------------
|
|||
|
|||
#ifndef __HMS_PAYLOAD_H__ |
|||
#define __HMS_PAYLOAD_H__ |
|||
|
|||
#include "../utils/dbg.h" |
|||
#include "../utils/crc.h" |
|||
#include "../config/config.h" |
|||
#include <Arduino.h> |
|||
|
|||
#define HMS_TIMEOUT_SEC 30 // 30s * 1000
|
|||
|
|||
typedef struct { |
|||
uint8_t txCmd; |
|||
uint8_t txId; |
|||
//uint8_t invId;
|
|||
uint32_t ts; |
|||
uint8_t data[MAX_PAYLOAD_ENTRIES][MAX_RF_PAYLOAD_SIZE]; |
|||
uint8_t len[MAX_PAYLOAD_ENTRIES]; |
|||
bool complete; |
|||
uint8_t maxPackId; |
|||
bool lastFound; |
|||
uint8_t retransmits; |
|||
bool requested; |
|||
bool gotFragment; |
|||
} hmsPayload_t; |
|||
|
|||
|
|||
typedef std::function<void(uint8_t, Inverter<> *)> payloadListenerType; |
|||
typedef std::function<void(Inverter<> *)> alarmListenerType; |
|||
|
|||
|
|||
template<class HMSYSTEM, class RADIO> |
|||
class HmsPayload { |
|||
public: |
|||
HmsPayload() {} |
|||
|
|||
void setup(IApp *app, HMSYSTEM *sys, RADIO *radio, statistics_t *stat, uint8_t maxRetransmits, uint32_t *timestamp) { |
|||
mApp = app; |
|||
mSys = sys; |
|||
mRadio = radio; |
|||
mStat = stat; |
|||
mMaxRetrans = maxRetransmits; |
|||
mTimestamp = timestamp; |
|||
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) { |
|||
reset(i); |
|||
mIvCmd56Cnt[i] = 0; |
|||
} |
|||
mSerialDebug = false; |
|||
mHighPrioIv = NULL; |
|||
mCbAlarm = NULL; |
|||
mCbPayload = NULL; |
|||
//mLastRx = 0;
|
|||
} |
|||
|
|||
void enableSerialDebug(bool enable) { |
|||
mSerialDebug = enable; |
|||
} |
|||
|
|||
void addPayloadListener(payloadListenerType cb) { |
|||
mCbPayload = cb; |
|||
} |
|||
|
|||
void addAlarmListener(alarmListenerType cb) { |
|||
mCbAlarm = cb; |
|||
} |
|||
|
|||
void loop() { |
|||
if(NULL != mHighPrioIv) { |
|||
ivSend(mHighPrioIv, true); |
|||
mHighPrioIv = NULL; |
|||
} |
|||
} |
|||
|
|||
void ivSendHighPrio(Inverter<> *iv) { |
|||
mHighPrioIv = iv; |
|||
} |
|||
|
|||
void ivSend(Inverter<> *iv, bool highPrio = false) { |
|||
if ((IV_HMS != iv->ivGen) && (IV_HMT != iv->ivGen)) // only process HMS inverters
|
|||
return; |
|||
|
|||
if(!highPrio) { |
|||
if (mPayload[iv->id].requested) { |
|||
if (!mPayload[iv->id].complete) |
|||
process(false); // no retransmit
|
|||
|
|||
if (!mPayload[iv->id].complete) { |
|||
if (MAX_PAYLOAD_ENTRIES == mPayload[iv->id].maxPackId) |
|||
mStat->rxFailNoAnser++; // got nothing
|
|||
else |
|||
mStat->rxFail++; // got fragments but not complete response
|
|||
|
|||
iv->setQueuedCmdFinished(); // command failed
|
|||
if (mSerialDebug) |
|||
DPRINTLN(DBG_INFO, F("enqueued cmd failed/timeout")); |
|||
/*if (mSerialDebug) {
|
|||
DPRINT(DBG_INFO, F("(#")); |
|||
DBGPRINT(String(iv->id)); |
|||
DBGPRINT(F(") no Payload received! (retransmits: ")); |
|||
DBGPRINT(String(mPayload[iv->id].retransmits)); |
|||
DBGPRINTLN(F(")")); |
|||
}*/ |
|||
} |
|||
} |
|||
} |
|||
|
|||
reset(iv->id); |
|||
mPayload[iv->id].requested = true; |
|||
|
|||
yield(); |
|||
if (mSerialDebug) { |
|||
DPRINT_IVID(DBG_INFO, iv->id); |
|||
DBGPRINT(F("Requesting Inv SN ")); |
|||
DBGPRINTLN(String(iv->config->serial.u64, HEX)); |
|||
} |
|||
|
|||
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); |
|||
if (iv->getDevControlRequest()) { |
|||
if (mSerialDebug) { |
|||
DPRINT_IVID(DBG_INFO, iv->id); |
|||
DBGPRINT(F("Devcontrol request 0x")); |
|||
DBGPRINT(String(iv->devControlCmd, HEX)); |
|||
DBGPRINT(F(" power limit ")); |
|||
DBGPRINTLN(String(iv->powerLimit[0])); |
|||
} |
|||
iv->powerLimitAck = false; |
|||
mRadio->sendControlPacket(&iv->radioId.u64, iv->devControlCmd, iv->powerLimit, false); |
|||
mPayload[iv->id].txCmd = iv->devControlCmd; |
|||
//iv->clearCmdQueue();
|
|||
//iv->enqueCommand<InfoCommand>(SystemConfigPara); // read back power limit
|
|||
} else if(((rec->ts + HMS_TIMEOUT_SEC) < *mTimestamp) && (mIvCmd56Cnt[iv->id] < 3)) { |
|||
mRadio->switchFrequency(&iv->radioId.u64, HOY_BOOT_FREQ_KHZ, WORK_FREQ_KHZ); |
|||
mIvCmd56Cnt[iv->id]++; |
|||
} else { |
|||
if(++mIvCmd56Cnt[iv->id] == 10) |
|||
mIvCmd56Cnt[iv->id] = 0; |
|||
uint8_t cmd = iv->getQueuedCmd(); |
|||
DPRINT_IVID(DBG_INFO, iv->id); |
|||
DBGPRINT(F("prepareDevInformCmd 0x")); |
|||
DBGHEXLN(cmd); |
|||
mRadio->prepareDevInformCmd(&iv->radioId.u64, cmd, mPayload[iv->id].ts, iv->alarmMesIndex, false); |
|||
mPayload[iv->id].txCmd = cmd; |
|||
} |
|||
} |
|||
|
|||
void add(Inverter<> *iv, hmsPacket_t *p) { |
|||
if (p->data[1] == (TX_REQ_INFO + ALL_FRAMES)) { // response from get information command
|
|||
mPayload[iv->id].txId = p->data[1]; |
|||
DPRINTLN(DBG_DEBUG, F("Response from info request received")); |
|||
uint8_t *pid = &p->data[10]; |
|||
if (*pid == 0x00) { |
|||
DPRINT(DBG_DEBUG, F("fragment number zero received and ignored")); |
|||
} else { |
|||
DPRINTLN(DBG_DEBUG, "PID: 0x" + String(*pid, HEX)); |
|||
if ((*pid & 0x7F) < MAX_PAYLOAD_ENTRIES) { |
|||
memcpy(mPayload[iv->id].data[(*pid & 0x7F) - 1], &p->data[11], p->data[0] - 11); |
|||
mPayload[iv->id].len[(*pid & 0x7F) - 1] = p->data[0] -11; |
|||
mPayload[iv->id].gotFragment = true; |
|||
} |
|||
|
|||
if ((*pid & ALL_FRAMES) == ALL_FRAMES) { |
|||
// Last packet
|
|||
if (((*pid & 0x7f) > mPayload[iv->id].maxPackId) || (MAX_PAYLOAD_ENTRIES == mPayload[iv->id].maxPackId)) { |
|||
mPayload[iv->id].maxPackId = (*pid & 0x7f); |
|||
if (*pid > 0x81) |
|||
mPayload[iv->id].lastFound = true; |
|||
} |
|||
} |
|||
} |
|||
} else if (p->data[1] == (TX_REQ_DEVCONTROL + ALL_FRAMES)) { // response from dev control command
|
|||
DPRINTLN(DBG_DEBUG, F("Response from devcontrol request received")); |
|||
|
|||
mPayload[iv->id].txId = p->data[1]; |
|||
iv->clearDevControlRequest(); |
|||
|
|||
if ((p->data[13] == ActivePowerContr) && (p->data[14] == 0x00)) { |
|||
bool ok = true; |
|||
if((p->data[11] == 0x00) && (p->data[12] == 0x00)) { |
|||
mApp->setMqttPowerLimitAck(iv); |
|||
iv->powerLimitAck = true; |
|||
} else |
|||
ok = false; |
|||
DPRINT_IVID(DBG_INFO, iv->id); |
|||
DBGPRINT(F(" has ")); |
|||
if(!ok) DBGPRINT(F("not ")); |
|||
DBGPRINT(F("accepted power limit set point ")); |
|||
DBGPRINT(String(iv->powerLimit[0])); |
|||
DBGPRINT(F(" with PowerLimitControl ")); |
|||
DBGPRINTLN(String(iv->powerLimit[1])); |
|||
|
|||
iv->clearCmdQueue(); |
|||
iv->enqueCommand<InfoCommand>(SystemConfigPara); // read back power limit
|
|||
if(mHighPrioIv == NULL) // do it immediately if possible
|
|||
mHighPrioIv = iv; |
|||
} |
|||
iv->devControlCmd = Init; |
|||
} |
|||
} |
|||
|
|||
void process(bool retransmit) { |
|||
for (uint8_t id = 0; id < mSys->getNumInverters(); id++) { |
|||
Inverter<> *iv = mSys->getInverterByPos(id); |
|||
if (NULL == iv) |
|||
continue; // skip to next inverter
|
|||
|
|||
if ((IV_HMS != iv->ivGen) && (IV_HMT != iv->ivGen)) // only process HMS inverters
|
|||
continue; // skip to next inverter
|
|||
|
|||
if ((mPayload[iv->id].txId != (TX_REQ_INFO + ALL_FRAMES)) && (0 != mPayload[iv->id].txId)) { |
|||
// no processing needed if txId is not 0x95
|
|||
mPayload[iv->id].complete = true; |
|||
continue; // skip to next inverter
|
|||
} |
|||
|
|||
if (!mPayload[iv->id].complete) { |
|||
bool crcPass, pyldComplete; |
|||
crcPass = build(iv->id, &pyldComplete); |
|||
if (!crcPass && !pyldComplete) { // payload not complete
|
|||
if ((mPayload[iv->id].requested) && (retransmit)) { |
|||
if (mPayload[iv->id].retransmits < mMaxRetrans) { |
|||
mPayload[iv->id].retransmits++; |
|||
if (iv->devControlCmd == Restart || iv->devControlCmd == CleanState_LockAndAlarm) { |
|||
// This is required to prevent retransmissions without answer.
|
|||
DPRINTLN(DBG_INFO, F("Prevent retransmit on Restart / CleanState_LockAndAlarm...")); |
|||
mPayload[iv->id].retransmits = mMaxRetrans; |
|||
} else if(iv->devControlCmd == ActivePowerContr) { |
|||
DPRINTLN(DBG_INFO, F("retransmit power limit")); |
|||
mRadio->sendControlPacket(&iv->radioId.u64, iv->devControlCmd, iv->powerLimit, true); |
|||
} else { |
|||
if(false == mPayload[iv->id].gotFragment) { |
|||
|
|||
//DPRINTLN(DBG_WARN, F("nothing received: Request Complete Retransmit"));
|
|||
//mPayload[iv->id].txCmd = iv->getQueuedCmd();
|
|||
//DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") prepareDevInformCmd 0x") + String(mPayload[iv->id].txCmd, HEX));
|
|||
//mRadio->prepareDevInformCmd(iv->radioId.u64, mPayload[iv->id].txCmd, mPayload[iv->id].ts, iv->alarmMesIndex, true);
|
|||
|
|||
DPRINT_IVID(DBG_INFO, iv->id); |
|||
DBGPRINTLN(F("nothing received")); |
|||
mPayload[iv->id].retransmits = mMaxRetrans; |
|||
} else { |
|||
for (uint8_t i = 0; i < (mPayload[iv->id].maxPackId - 1); i++) { |
|||
if (mPayload[iv->id].len[i] == 0) { |
|||
DPRINT(DBG_WARN, F("Frame ")); |
|||
DBGPRINT(String(i + 1)); |
|||
DBGPRINTLN(F(" missing: Request Retransmit")); |
|||
//mRadio->sendCmdPacket(iv->radioId.u64, TX_REQ_INFO, (SINGLE_FRAME + i), true);
|
|||
break; // only request retransmit one frame per loop
|
|||
} |
|||
yield(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} /*else if(!crcPass && pyldComplete) { // crc error on complete Payload
|
|||
if (mPayload[iv->id].retransmits < mMaxRetrans) { |
|||
mPayload[iv->id].retransmits++; |
|||
DPRINTLN(DBG_WARN, F("CRC Error: Request Complete Retransmit")); |
|||
mPayload[iv->id].txCmd = iv->getQueuedCmd(); |
|||
DPRINT(DBG_INFO, F("(#")); |
|||
DBGPRINT(String(iv->id)); |
|||
DBGPRINT(F(") prepareDevInformCmd 0x")); |
|||
DBGPRINTLN(String(mPayload[iv->id].txCmd, HEX)); |
|||
mRadio->prepareDevInformCmd(iv->radioId.u64, mPayload[iv->id].txCmd, mPayload[iv->id].ts, iv->alarmMesIndex, true); |
|||
} |
|||
}*/ else { // payload complete
|
|||
DPRINT(DBG_INFO, F("procPyld: cmd: 0x")); |
|||
DBGPRINTLN(String(mPayload[iv->id].txCmd, HEX)); |
|||
DPRINT(DBG_INFO, F("procPyld: txid: 0x")); |
|||
DBGPRINTLN(String(mPayload[iv->id].txId, HEX)); |
|||
DPRINTLN(DBG_DEBUG, F("procPyld: max: ") + String(mPayload[iv->id].maxPackId)); |
|||
record_t<> *rec = iv->getRecordStruct(mPayload[iv->id].txCmd); // choose the parser
|
|||
mPayload[iv->id].complete = true; |
|||
|
|||
uint8_t payload[150]; |
|||
uint8_t payloadLen = 0; |
|||
|
|||
memset(payload, 0, 150); |
|||
|
|||
for (uint8_t i = 0; i < (mPayload[iv->id].maxPackId); i++) { |
|||
if((mPayload[iv->id].len[i] + payloadLen) > 150) { |
|||
DPRINTLN(DBG_ERROR, F("payload buffer to small!")); |
|||
break; |
|||
} |
|||
memcpy(&payload[payloadLen], mPayload[iv->id].data[i], (mPayload[iv->id].len[i])); |
|||
payloadLen += (mPayload[iv->id].len[i]); |
|||
yield(); |
|||
} |
|||
payloadLen -= 2; |
|||
|
|||
if (mSerialDebug) { |
|||
DPRINT(DBG_INFO, F("Payload (")); |
|||
DBGPRINT(String(payloadLen)); |
|||
DBGPRINT(F("): ")); |
|||
ah::dumpBuf(payload, payloadLen); |
|||
} |
|||
|
|||
if (NULL == rec) { |
|||
DPRINTLN(DBG_ERROR, F("record is NULL!")); |
|||
} else if ((rec->pyldLen == payloadLen) || (0 == rec->pyldLen)) { |
|||
if (mPayload[iv->id].txId == (TX_REQ_INFO + ALL_FRAMES)) |
|||
mStat->rxSuccess++; |
|||
|
|||
rec->ts = mPayload[iv->id].ts; |
|||
for (uint8_t i = 0; i < rec->length; i++) { |
|||
iv->addValue(i, payload, rec); |
|||
yield(); |
|||
} |
|||
iv->doCalculations(); |
|||
notify(mPayload[iv->id].txCmd, iv); |
|||
|
|||
if(AlarmData == mPayload[iv->id].txCmd) { |
|||
uint8_t i = 0; |
|||
uint32_t start, end; |
|||
while(1) { |
|||
if(0 == iv->parseAlarmLog(i++, payload, payloadLen)) |
|||
break; |
|||
if (NULL != mCbAlarm) |
|||
(mCbAlarm)(iv); |
|||
yield(); |
|||
} |
|||
} |
|||
} else { |
|||
DPRINT(DBG_ERROR, F("plausibility check failed, expected ")); |
|||
DBGPRINT(String(rec->pyldLen)); |
|||
DBGPRINTLN(F(" bytes")); |
|||
mStat->rxFail++; |
|||
} |
|||
|
|||
iv->setQueuedCmdFinished(); |
|||
} |
|||
} |
|||
yield(); |
|||
} |
|||
} |
|||
|
|||
private: |
|||
void notify(uint8_t val, Inverter<> *iv) { |
|||
if(NULL != mCbPayload) |
|||
(mCbPayload)(val, iv); |
|||
} |
|||
|
|||
bool build(uint8_t id, bool *complete) { |
|||
DPRINTLN(DBG_VERBOSE, F("build")); |
|||
uint16_t crc = 0xffff, crcRcv = 0x0000; |
|||
if (mPayload[id].maxPackId > MAX_PAYLOAD_ENTRIES) |
|||
mPayload[id].maxPackId = MAX_PAYLOAD_ENTRIES; |
|||
|
|||
// check if all fragments are there
|
|||
*complete = true; |
|||
for (uint8_t i = 0; i < mPayload[id].maxPackId; i++) { |
|||
if(mPayload[id].len[i] == 0) |
|||
*complete = false; |
|||
} |
|||
if(!*complete) |
|||
return false; |
|||
|
|||
for (uint8_t i = 0; i < mPayload[id].maxPackId; i++) { |
|||
if (mPayload[id].len[i] > 0) { |
|||
if (i == (mPayload[id].maxPackId - 1)) { |
|||
crc = ah::crc16(mPayload[id].data[i], mPayload[id].len[i] - 1, crc); |
|||
crcRcv = (mPayload[id].data[i][mPayload[id].len[i] - 2] << 8) | (mPayload[id].data[i][mPayload[id].len[i] - 1]); |
|||
} else |
|||
crc = ah::crc16(mPayload[id].data[i], mPayload[id].len[i], crc); |
|||
} |
|||
yield(); |
|||
} |
|||
|
|||
return (crc == crcRcv) ? true : false; |
|||
} |
|||
|
|||
void reset(uint8_t id) { |
|||
DPRINT(DBG_INFO, "resetPayload: id: "); |
|||
DBGPRINTLN(String(id)); |
|||
memset(&mPayload[id], 0, sizeof(hmsPayload_t)); |
|||
mPayload[id].txCmd = 0; |
|||
mPayload[id].gotFragment = false; |
|||
//mPayload[id].retransmits = 0;
|
|||
mPayload[id].maxPackId = MAX_PAYLOAD_ENTRIES; |
|||
mPayload[id].lastFound = false; |
|||
mPayload[id].complete = false; |
|||
mPayload[id].requested = false; |
|||
mPayload[id].ts = *mTimestamp; |
|||
} |
|||
|
|||
IApp *mApp; |
|||
HMSYSTEM *mSys; |
|||
RADIO *mRadio; |
|||
statistics_t *mStat; |
|||
uint8_t mMaxRetrans; |
|||
uint32_t *mTimestamp; |
|||
//uint32_t mLastRx;
|
|||
hmsPayload_t mPayload[MAX_NUM_INVERTERS]; |
|||
uint8_t mIvCmd56Cnt[MAX_NUM_INVERTERS]; |
|||
bool mSerialDebug; |
|||
Inverter<> *mHighPrioIv; |
|||
|
|||
alarmListenerType mCbAlarm; |
|||
payloadListenerType mCbPayload; |
|||
}; |
|||
|
|||
#endif /*__HMS_PAYLOAD_H__*/ |
|||
@ -0,0 +1,22 @@ |
|||
#include "../../utils/helper.h" |
|||
|
|||
#ifndef __DISPLAY_DATA__ |
|||
#define __DISPLAY_DATA__ |
|||
|
|||
struct DisplayData { |
|||
const char *version=nullptr; |
|||
float totalPower=0.0f; // indicate current power (W)
|
|||
float totalYieldDay=0.0f; // indicate day yield (Wh)
|
|||
float totalYieldTotal=0.0f; // indicate total yield (kWh)
|
|||
uint32_t utcTs=0; // indicate absolute timestamp (utc unix time). 0 = time is not synchonized
|
|||
uint8_t nrProducing=0; // indicate number of producing inverters
|
|||
uint8_t nrSleeping=0; // indicate number of sleeping inverters
|
|||
bool WifiSymbol = false; // indicate if WiFi is connected
|
|||
bool RadioSymbol = false; // indicate if radio module is connecting and working
|
|||
bool MQTTSymbol = false; // indicate if MQTT is connected
|
|||
int8_t WifiRSSI=SCHAR_MIN; // indicate RSSI value for WiFi
|
|||
int8_t RadioRSSI=SCHAR_MIN; // indicate RSSI value for radio
|
|||
IPAddress ipAddress; // indicate ip adress of ahoy
|
|||
}; |
|||
|
|||
#endif /*__DISPLAY_DATA__*/ |
|||
@ -0,0 +1,9 @@ |
|||
//-----------------------------------------------------------------------------
|
|||
// 2023 Ahoy, https://www.mikrocontroller.net/topic/525778
|
|||
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
|
|||
//-----------------------------------------------------------------------------
|
|||
|
|||
#if defined(ESP32) |
|||
#include "spiPatcher.h" |
|||
SpiPatcher *SpiPatcher::mInstance = nullptr; |
|||
#endif |
|||
@ -0,0 +1,88 @@ |
|||
//-----------------------------------------------------------------------------
|
|||
// 2023 Ahoy, https://www.mikrocontroller.net/topic/525778
|
|||
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
|
|||
//-----------------------------------------------------------------------------
|
|||
|
|||
#ifndef __SPI_PATCHER_H__ |
|||
#define __SPI_PATCHER_H__ |
|||
#pragma once |
|||
|
|||
#if defined(ESP32) |
|||
|
|||
#include "spiPatcherHandle.h" |
|||
|
|||
#include <driver/spi_master.h> |
|||
#include <freertos/semphr.h> |
|||
|
|||
class SpiPatcher { |
|||
protected: |
|||
SpiPatcher(spi_host_device_t dev) : |
|||
mHostDevice(dev), mCurHandle(nullptr) { |
|||
// Use binary semaphore instead of mutex for performance reasons
|
|||
mutex = xSemaphoreCreateBinaryStatic(&mutex_buffer); |
|||
xSemaphoreGive(mutex); |
|||
|
|||
spi_bus_config_t buscfg = { |
|||
.mosi_io_num = -1, |
|||
.miso_io_num = -1, |
|||
.sclk_io_num = -1, |
|||
.quadwp_io_num = -1, |
|||
.quadhd_io_num = -1, |
|||
.data4_io_num = -1, |
|||
.data5_io_num = -1, |
|||
.data6_io_num = -1, |
|||
.data7_io_num = -1, |
|||
.max_transfer_sz = SOC_SPI_MAXIMUM_BUFFER_SIZE, |
|||
.flags = 0, |
|||
.intr_flags = 0 |
|||
}; |
|||
ESP_ERROR_CHECK(spi_bus_initialize(mHostDevice, &buscfg, SPI_DMA_DISABLED)); |
|||
} |
|||
|
|||
public: |
|||
SpiPatcher(SpiPatcher &other) = delete; |
|||
void operator=(const SpiPatcher &) = delete; |
|||
|
|||
static SpiPatcher* getInstance(spi_host_device_t dev) { |
|||
if(nullptr == mInstance) |
|||
mInstance = new SpiPatcher(dev); |
|||
return mInstance; |
|||
} |
|||
|
|||
~SpiPatcher() { vSemaphoreDelete(mutex); } |
|||
|
|||
spi_host_device_t getDevice() { |
|||
return mHostDevice; |
|||
} |
|||
|
|||
inline void request(SpiPatcherHandle* handle) { |
|||
xSemaphoreTake(mutex, portMAX_DELAY); |
|||
|
|||
if (mCurHandle != handle) { |
|||
if (mCurHandle) { |
|||
mCurHandle->unpatch(); |
|||
} |
|||
mCurHandle = handle; |
|||
if (mCurHandle) { |
|||
mCurHandle->patch(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
inline void release() { |
|||
xSemaphoreGive(mutex); |
|||
} |
|||
|
|||
protected: |
|||
static SpiPatcher *mInstance; |
|||
|
|||
private: |
|||
const spi_host_device_t mHostDevice; |
|||
SpiPatcherHandle* mCurHandle; |
|||
SemaphoreHandle_t mutex; |
|||
StaticSemaphore_t mutex_buffer; |
|||
}; |
|||
|
|||
#endif /*ESP32*/ |
|||
|
|||
#endif /*__SPI_PATCHER_H__*/ |
|||
@ -0,0 +1,17 @@ |
|||
//-----------------------------------------------------------------------------
|
|||
// 2023 Ahoy, https://www.mikrocontroller.net/topic/525778
|
|||
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
|
|||
//-----------------------------------------------------------------------------
|
|||
|
|||
#ifndef __SPI_PATCHER_HANDLE_H__ |
|||
#define __SPI_PATCHER_HANDLE_H__ |
|||
#pragma once |
|||
|
|||
class SpiPatcherHandle { |
|||
public: |
|||
virtual ~SpiPatcherHandle() {} |
|||
virtual void patch() = 0; |
|||
virtual void unpatch() = 0; |
|||
}; |
|||
|
|||
#endif /*__SPI_PATCHER_HANDLE_H__*/ |
|||
@ -0,0 +1,99 @@ |
|||
#include <algorithm> |
|||
#include "syslog.h" |
|||
|
|||
#ifdef ENABLE_SYSLOG |
|||
|
|||
#define SYSLOG_MAX_PACKET_SIZE 256 |
|||
|
|||
|
|||
//-----------------------------------------------------------------------------
|
|||
void DbgSyslog::setup(settings_t *config) { |
|||
mConfig = config; |
|||
|
|||
// Syslog callback overrides web-serial callback
|
|||
registerDebugCb(std::bind(&DbgSyslog::syslogCb, this, std::placeholders::_1)); // dbg.h
|
|||
} |
|||
|
|||
//-----------------------------------------------------------------------------
|
|||
void DbgSyslog::syslogCb (String msg) |
|||
{ |
|||
if (!mSyslogIP.isSet()) { |
|||
// use WiFi.hostByName to DNS lookup for IPAddress of syslog server
|
|||
if (WiFi.status() == WL_CONNECTED) { |
|||
WiFi.hostByName(SYSLOG_HOST,mSyslogIP); |
|||
} |
|||
} |
|||
if (!mSyslogIP.isSet()) { |
|||
return; |
|||
} |
|||
uint16_t msgLength = msg.length(); |
|||
uint16_t msgPos = 0; |
|||
|
|||
do { |
|||
uint16_t charsToCopy = std::min(msgLength-msgPos,SYSLOG_BUF_SIZE - mSyslogBufFill); |
|||
|
|||
while (charsToCopy > 0) { |
|||
mSyslogBuffer[mSyslogBufFill] = msg[msgPos]; |
|||
msgPos++; |
|||
mSyslogBufFill++; |
|||
charsToCopy--; |
|||
} |
|||
mSyslogBuffer[mSyslogBufFill] = '\0'; |
|||
|
|||
bool isBufferFull = (mSyslogBufFill == SYSLOG_BUF_SIZE); |
|||
bool isEolFound = false; |
|||
if (mSyslogBufFill >= 2) { |
|||
isEolFound = (mSyslogBuffer[mSyslogBufFill-2] == '\r' && mSyslogBuffer[mSyslogBufFill-1] == '\n'); |
|||
} |
|||
// Get severity from input message
|
|||
if (msgLength >= 2) { |
|||
if (':' == msg[1]) { |
|||
switch(msg[0]) { |
|||
case 'E': mSyslogSeverity = PRI_ERROR; break; |
|||
case 'W': mSyslogSeverity = PRI_WARNING; break; |
|||
case 'I': mSyslogSeverity = PRI_INFO; break; |
|||
case 'D': mSyslogSeverity = PRI_DEBUG; break; |
|||
default: mSyslogSeverity = PRI_NOTICE; break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (isBufferFull || isEolFound) { |
|||
// Send mSyslogBuffer in chunks because mSyslogBuffer is larger than syslog packet size
|
|||
int packetStart = 0; |
|||
int packetSize = 122; // syslog payload depends also on hostname and app
|
|||
char saveChar; |
|||
if (isEolFound) { |
|||
mSyslogBuffer[mSyslogBufFill-2]=0; // skip \r\n
|
|||
} |
|||
while(packetStart < mSyslogBufFill) { |
|||
saveChar = mSyslogBuffer[packetStart+packetSize]; |
|||
mSyslogBuffer[packetStart+packetSize] = 0; |
|||
log(mConfig->sys.deviceName,SYSLOG_FACILITY, mSyslogSeverity, &mSyslogBuffer[packetStart]); |
|||
mSyslogBuffer[packetStart+packetSize] = saveChar; |
|||
packetStart += packetSize; |
|||
} |
|||
mSyslogBufFill = 0; |
|||
} |
|||
|
|||
} while (msgPos < msgLength); // Message not completely processed
|
|||
|
|||
} |
|||
|
|||
//-----------------------------------------------------------------------------
|
|||
void DbgSyslog::log(const char *hostname, uint8_t facility, uint8_t severity, char* msg) { |
|||
// The PRI value is an integer number which calculates by the following metric:
|
|||
uint8_t priority = (8 * facility) + severity; |
|||
|
|||
// This is a unit8 instead of a char because that's what udp.write() wants
|
|||
uint8_t buffer[SYSLOG_MAX_PACKET_SIZE]; |
|||
int len = snprintf((char*)buffer, SYSLOG_MAX_PACKET_SIZE, "<%d>%s %s: %s", priority, hostname, SYSLOG_APP, msg); |
|||
//printf("syslog::log %s\n",mSyslogIP.toString().c_str());
|
|||
//printf("syslog::log %d %s\n",len,buffer);
|
|||
// Send the raw UDP packet
|
|||
mSyslogUdp.beginPacket(mSyslogIP, SYSLOG_PORT); |
|||
mSyslogUdp.write(buffer, len); |
|||
mSyslogUdp.endPacket(); |
|||
} |
|||
|
|||
#endif |
|||
@ -0,0 +1,54 @@ |
|||
|
|||
#ifndef __SYSLOG_H__ |
|||
#define __SYSLOG_H__ |
|||
|
|||
#ifdef ESP8266 |
|||
#include <ESP8266WiFi.h> |
|||
#elif defined(ESP32) |
|||
#include <WiFi.h> |
|||
#endif |
|||
#include <WiFiUdp.h> |
|||
#include "../config/config.h" |
|||
#include "../config/settings.h" |
|||
|
|||
#ifdef ENABLE_SYSLOG |
|||
|
|||
#define SYSLOG_BUF_SIZE 255 |
|||
|
|||
#define PRI_EMERGENCY 0 |
|||
#define PRI_ALERT 1 |
|||
#define PRI_CRITICAL 2 |
|||
#define PRI_ERROR 3 |
|||
#define PRI_WARNING 4 |
|||
#define PRI_NOTICE 5 |
|||
#define PRI_INFO 6 |
|||
#define PRI_DEBUG 7 |
|||
|
|||
#define FAC_USER 1 |
|||
#define FAC_LOCAL0 16 |
|||
#define FAC_LOCAL1 17 |
|||
#define FAC_LOCAL2 18 |
|||
#define FAC_LOCAL3 19 |
|||
#define FAC_LOCAL4 20 |
|||
#define FAC_LOCAL5 21 |
|||
#define FAC_LOCAL6 22 |
|||
#define FAC_LOCAL7 23 |
|||
|
|||
class DbgSyslog { |
|||
public: |
|||
void setup (settings_t *config); |
|||
void syslogCb(String msg); |
|||
void log(const char *hostname, uint8_t facility, uint8_t severity, char* msg); |
|||
|
|||
private: |
|||
WiFiUDP mSyslogUdp; |
|||
IPAddress mSyslogIP; |
|||
settings_t *mConfig; |
|||
char mSyslogBuffer[SYSLOG_BUF_SIZE+1]; |
|||
uint16_t mSyslogBufFill = 0; |
|||
int mSyslogSeverity = PRI_NOTICE; |
|||
}; |
|||
|
|||
#endif /*ENABLE_SYSLOG*/ |
|||
|
|||
#endif /*__SYSLOG_H__*/ |
|||
@ -0,0 +1,126 @@ |
|||
//-----------------------------------------------------------
|
|||
// You69Man, 2023
|
|||
//
|
|||
// This program is free software; you can redistribute it and/or
|
|||
// modify it under the terms of the GNU General Public License
|
|||
// version 2 as published by the Free Software Foundation.
|
|||
//-----------------------------------------------------------
|
|||
|
|||
/**
|
|||
* @file timemonitor.h |
|||
* |
|||
* Class declaration for TimeMonitor |
|||
*/ |
|||
|
|||
#ifndef __TIMEMONITOR_H__ |
|||
#define __TIMEMONITOR_H__ |
|||
|
|||
#include <Arduino.h> |
|||
|
|||
class TimeMonitor { |
|||
public: |
|||
/**
|
|||
* A constructor for initializing a TimeMonitor |
|||
* @note TimeMonitor witch default constructor is stopped |
|||
*/ |
|||
TimeMonitor(void) {} |
|||
|
|||
/**
|
|||
* A constructor for initializing a TimeMonitor |
|||
* @param timeout timeout in ms |
|||
* @param start (optional) if true, start TimeMonitor immediately |
|||
* @note TimeMonitor witch default constructor is stopped |
|||
*/ |
|||
TimeMonitor(uint32_t timeout, bool start = false) { |
|||
if (start) |
|||
startTimeMonitor(timeout); |
|||
else |
|||
configureTimeMonitor(timeout); |
|||
} |
|||
|
|||
/**
|
|||
* Start the TimeMonitor with new timeout configuration |
|||
* @param timeout timout in ms |
|||
*/ |
|||
void startTimeMonitor(uint32_t timeout) { |
|||
mStartTime = millis(); |
|||
mTimeout = timeout; |
|||
mStarted = true; |
|||
} |
|||
|
|||
/**
|
|||
* Restart the TimeMonitor with already set timeout configuration |
|||
* @note returns nothing |
|||
*/ |
|||
void reStartTimeMonitor(void) { |
|||
mStartTime = millis(); |
|||
mStarted = true; |
|||
} |
|||
|
|||
/**
|
|||
* Configure the TimeMonitor to new timeout configuration |
|||
* @param timeout timeout in ms |
|||
* @note This doesn't restart an already running TimeMonitor. |
|||
* If timer is already running and new timeout is longer that current timeout runtime of the running timer is expanded |
|||
* If timer is already running and new timeout is shorter that current timeout this can immediately lead to a timeout |
|||
*/ |
|||
void configureTimeMonitor(uint32_t timeout) { |
|||
mTimeout = timeout; |
|||
} |
|||
|
|||
/**
|
|||
* Stop the TimeMonitor |
|||
*/ |
|||
void stopTimeMonitor(void) { |
|||
mStarted = false; |
|||
} |
|||
|
|||
/**
|
|||
* Get timeout status of the TimeMonitor |
|||
* @return bool |
|||
* true: TimeMonitor already timed out |
|||
* false: TimeMonitor still in time or TimeMonitor was stopped |
|||
*/ |
|||
bool isTimeout(void) { |
|||
if ((mStarted) && (millis() - mStartTime >= mTimeout)) |
|||
return true; |
|||
else |
|||
return false; |
|||
} |
|||
|
|||
/**
|
|||
* Get timeout configuration of the TimeMonitor |
|||
* @return uint32_t timeout value in ms |
|||
*/ |
|||
uint32_t getTimeout(void) const { |
|||
return mTimeout; |
|||
} |
|||
|
|||
/**
|
|||
* Get residual time of the TimeMonitor until timeout |
|||
* @return uint32_t residual time until timeout in ms |
|||
* @note in case of a stopped TimeMonitor residual time is always 0xFFFFFFFFUL |
|||
* in case of a timed out TimeMonitor residual time is always 0UL (zero) |
|||
*/ |
|||
uint32_t getResidualTime(void) const { |
|||
uint32_t delayed = millis() - mStartTime; |
|||
return(mStarted ? (delayed < mTimeout ? mTimeout - delayed : 0UL) : 0xFFFFFFFFUL); |
|||
} |
|||
|
|||
/**
|
|||
* Get overall run time of the TimeMonitor |
|||
* @return uint32_t residual time until timeout in ms |
|||
* @note in case of a stopped TimeMonitor residual time is always 0xFFFFFFFFUL |
|||
* in case of a timed out TimeMonitor residual time is always 0UL (zero) |
|||
*/ |
|||
uint32_t getRunTime(void) const { |
|||
return(mStarted ? millis() - mStartTime : 0UL); |
|||
} |
|||
|
|||
private: |
|||
uint32_t mStartTime = 0UL; // start time of the TimeMonitor
|
|||
uint32_t mTimeout = 0UL; // timeout configuration of the TimeMonitor
|
|||
bool mStarted = false; // start/stop state of the TimeMonitor
|
|||
}; |
|||
|
|||
#endif |
|||
@ -0,0 +1,765 @@ |
|||
{ |
|||
"type": [ |
|||
{"0x0300": "DE_VDE4105_2018"}, |
|||
{"0x0a00": "DE NF_EN_50549-1:2019"}, |
|||
{"0x0c00": "AT_TOR_Erzeuger_default"}, |
|||
{"0x0d04": "France NF_EN_50549-1:2019"}, |
|||
{"0x1200": "2.0.4 (EU_EN50438)"}, |
|||
{"0x3700": "2.0.0 (CH_NA EEA-NE7–CH2020)"} |
|||
], |
|||
"grp_codes": [ |
|||
{"0x00": "Voltage H/LVRT"}, |
|||
{"0x10": "Frequency H/LFRT"}, |
|||
{"0x20": "Islanding Detection"}, |
|||
{"0x30": "Reconnection"}, |
|||
{"0x40": "Ramp Rates"}, |
|||
{"0x50": "Frequency Watt"}, |
|||
{"0x60": "Volt Watt"}, |
|||
{"0x70": "Active Power Control"}, |
|||
{"0x80": "Volt Var"}, |
|||
{"0x90": "Specified Power Factor"}, |
|||
{"0xa0": "Reactive Power Control"}, |
|||
{"0xb0": "Watt Power Factor"} |
|||
], |
|||
"group": [ |
|||
{ |
|||
"0x0003": [ |
|||
{ |
|||
"name": "Nominal Voltage", |
|||
"div": 10, |
|||
"def": 230, |
|||
"unit": "V" |
|||
}, |
|||
{ |
|||
"name": "Low Voltage 1", |
|||
"div": 10, |
|||
"min": 180, |
|||
"max": 207, |
|||
"def": 184, |
|||
"unit": "V" |
|||
}, |
|||
{ |
|||
"name": "LV1 Maximum Trip Time", |
|||
"div": 10, |
|||
"def": 1.5, |
|||
"unit": "s" |
|||
}, |
|||
{ |
|||
"name": "High Voltage 1", |
|||
"div": 10, |
|||
"min": 250, |
|||
"max": 270, |
|||
"def": 253, |
|||
"unit": "V" |
|||
}, |
|||
{ |
|||
"name": "HV1 Maximum Trip Time", |
|||
"div": 10, |
|||
"min": 0.1, |
|||
"max": 100, |
|||
"def": 0.1, |
|||
"unit": "s" |
|||
}, |
|||
{ |
|||
"name": "Low Voltage 2", |
|||
"div": 10, |
|||
"min": 80, |
|||
"max": 161, |
|||
"def": 104, |
|||
"unit": "V" |
|||
}, |
|||
{ |
|||
"name": "LV2 Maximum Trip Time", |
|||
"div": 100, |
|||
"min": 0.1, |
|||
"max": 5, |
|||
"def": 0.3, |
|||
"unit": "s" |
|||
}, |
|||
{ |
|||
"name": "High Voltage 2", |
|||
"div": 10, |
|||
"min": 230, |
|||
"max": 299, |
|||
"def": 276, |
|||
"unit": "V" |
|||
}, |
|||
{ |
|||
"name": "HV2 Maximum Trip Time", |
|||
"div": 100, |
|||
"min": 0, |
|||
"max": 5, |
|||
"def": 0.05, |
|||
"unit": "s" |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"0x000a": [ |
|||
{ |
|||
"name": "Nominal Voltage", |
|||
"div": 10, |
|||
"def": 230, |
|||
"unit": "V" |
|||
}, |
|||
{ |
|||
"name": "Low Voltage 1", |
|||
"div": 10, |
|||
"min": 160, |
|||
"max": 195.5, |
|||
"def": 184, |
|||
"unit": "V" |
|||
}, |
|||
{ |
|||
"name": "LV1 Maximum Trip Time", |
|||
"div": 10, |
|||
"def": 3, |
|||
"unit": "s" |
|||
}, |
|||
{ |
|||
"name": "High Voltage 1", |
|||
"div": 10, |
|||
"min": 270, |
|||
"max": 287.5, |
|||
"def": 287.5, |
|||
"unit": "V" |
|||
}, |
|||
{ |
|||
"name": "HV1 Maximum Trip Time", |
|||
"div": 10, |
|||
"def": 0.1, |
|||
"unit": "s" |
|||
}, |
|||
{ |
|||
"name": "Low Voltage 2", |
|||
"div": 10, |
|||
"max": 150, |
|||
"min": 100, |
|||
"def": 103.5, |
|||
"unit": "V" |
|||
}, |
|||
{ |
|||
"name": "LV2 Maximum Trip Time", |
|||
"div": 100, |
|||
"def": 0.3, |
|||
"unit": "s" |
|||
}, |
|||
{ |
|||
"name": "10 mins Average High Voltage", |
|||
"div": 10, |
|||
"min": 250, |
|||
"max": 270, |
|||
"def": 253, |
|||
"unit": "V" |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"0x000c": [ |
|||
{ |
|||
"name": "Nominal Voltage", |
|||
"div": 10, |
|||
"def": 230, |
|||
"unit": "V" |
|||
}, |
|||
{ |
|||
"name": "Low Voltage 1", |
|||
"div": 10, |
|||
"min": 180, |
|||
"max": 207, |
|||
"def": 184, |
|||
"unit": "V" |
|||
}, |
|||
{ |
|||
"name": "LV1 Maximum Trip Time", |
|||
"div": 10, |
|||
"min": 0.1, |
|||
"max": 5, |
|||
"def": 1.5, |
|||
"unit": "s" |
|||
}, |
|||
{ |
|||
"name": "High Voltage 1", |
|||
"div": 10, |
|||
"min": 250, |
|||
"max": 270, |
|||
"def": 253, |
|||
"unit": "V" |
|||
}, |
|||
{ |
|||
"name": "HV1 Maximum Trip Time", |
|||
"div": 10, |
|||
"min": 0.1, |
|||
"max": 100, |
|||
"def": 3, |
|||
"unit": "s" |
|||
}, |
|||
{ |
|||
"name": "Low Voltage 2", |
|||
"div": 10, |
|||
"min": 80, |
|||
"max": 161, |
|||
"def": 161, |
|||
"unit": "V" |
|||
}, |
|||
{ |
|||
"name": "LV2 Maximum Trip Time", |
|||
"div": 100, |
|||
"min": 0.1, |
|||
"max": 5, |
|||
"def": 0.2, |
|||
"unit": "s" |
|||
}, |
|||
{ |
|||
"name": "High Voltage 2", |
|||
"div": 10, |
|||
"min": 230, |
|||
"max": 299, |
|||
"def": 264.5, |
|||
"unit": "V" |
|||
}, |
|||
{ |
|||
"name": "HV2 Maximum Trip Time", |
|||
"div": 100, |
|||
"min": 0.1, |
|||
"max": 5, |
|||
"def": 0.2, |
|||
"unit": "s" |
|||
}, |
|||
{ |
|||
"name": "High Voltage 3", |
|||
"div": 10, |
|||
"def": 276, |
|||
"unit": "V" |
|||
}, |
|||
{ |
|||
"name": "HV3 Maximum Trip Time", |
|||
"div": 100, |
|||
"min": 0.1, |
|||
"max": 10, |
|||
"def": 0.1, |
|||
"unit": "s" |
|||
}, |
|||
{ |
|||
"name": "10 mins Average High Voltage", |
|||
"div": 10, |
|||
"def": 253, |
|||
"unit": "V" |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"0x1000": [ |
|||
{ |
|||
"name": "Nominal Frequency", |
|||
"div": 100, |
|||
"def": 50, |
|||
"unit": "Hz" |
|||
}, |
|||
{ |
|||
"name": "Low Frequency 1", |
|||
"div": 100, |
|||
"min": 47.5, |
|||
"max": 49.9, |
|||
"def": 47.5, |
|||
"unit": "Hz" |
|||
}, |
|||
{ |
|||
"name": "LF1 Maximum Trip Time", |
|||
"div": 10, |
|||
"def": 0.1, |
|||
"unit": "s" |
|||
}, |
|||
{ |
|||
"name": "High Frequency 1", |
|||
"div": 100, |
|||
"min": 50.1, |
|||
"max": 51.5, |
|||
"def": 51.5, |
|||
"unit": "Hz" |
|||
}, |
|||
{ |
|||
"name": "HF1 Maximum Trip Time", |
|||
"div": 10, |
|||
"def": 0.1, |
|||
"unit": "s" |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"0x1003": [ |
|||
{ |
|||
"name": "Nominal Frequency", |
|||
"div": 100, |
|||
"def": 50, |
|||
"unit": "Hz" |
|||
}, |
|||
{ |
|||
"name": "Low Frequency 1", |
|||
"div": 100, |
|||
"min": 45, |
|||
"max": 49.9, |
|||
"def": 48, |
|||
"unit": "Hz" |
|||
}, |
|||
{ |
|||
"name": "LF1 Maximum Trip Time", |
|||
"div": 100, |
|||
"min": 0.1, |
|||
"max": 20, |
|||
"def": 2, |
|||
"unit": "s" |
|||
}, |
|||
{ |
|||
"name": "High Frequency 1", |
|||
"div": 100, |
|||
"min": 50, |
|||
"max": 53, |
|||
"def": 51, |
|||
"unit": "Hz" |
|||
}, |
|||
{ |
|||
"name": "HF1 Maximum Trip time", |
|||
"div": 10, |
|||
"min": 0.1, |
|||
"max": 20, |
|||
"def": 2, |
|||
"unit": "s" |
|||
}, |
|||
{ |
|||
"name": "Low Frequency 2", |
|||
"div": 100, |
|||
"min": 45, |
|||
"max": 50, |
|||
"def": 47.5, |
|||
"unit": "Hz" |
|||
}, |
|||
{ |
|||
"name": "LF2 Maximum Trip Time", |
|||
"div": 10, |
|||
"min": 0.1, |
|||
"max": 5, |
|||
"def": 0.5, |
|||
"unit": "s" |
|||
}, |
|||
{ |
|||
"name": "High Frequency 2", |
|||
"div": 100, |
|||
"min": 50, |
|||
"max": 52, |
|||
"def": 52, |
|||
"unit": "Hz" |
|||
}, |
|||
{ |
|||
"name": "HF2 Maximum Trip time", |
|||
"div": 10, |
|||
"min": 0.1, |
|||
"max": 5, |
|||
"def": 0.5, |
|||
"unit": "s" |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"0x2000": [ |
|||
{ |
|||
"name": "Island Detection Activated", |
|||
"div": 1, |
|||
"min": 0, |
|||
"max": 1, |
|||
"def": 1 |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"0x3003": [ |
|||
{ |
|||
"name": "Reconnect Time", |
|||
"div": 10, |
|||
"min": 10, |
|||
"max": 300, |
|||
"def": 60, |
|||
"unit": "s" |
|||
}, |
|||
{ |
|||
"name": "Reconnect High Voltage", |
|||
"div": 10, |
|||
"min": 240, |
|||
"max": 276, |
|||
"def": 253, |
|||
"unit": "V" |
|||
}, |
|||
{ |
|||
"name": "Reconnect Low Voltage", |
|||
"div": 10, |
|||
"min": 195.5, |
|||
"max": 210, |
|||
"def": 195.5, |
|||
"unit": "V" |
|||
}, |
|||
{ |
|||
"name": "Reconnect High Frequency", |
|||
"div": 100, |
|||
"max": 50.9, |
|||
"min": 50.1, |
|||
"def": 50.2, |
|||
"unit": "Hz" |
|||
}, |
|||
{ |
|||
"name": "Reconnect Low Frequency", |
|||
"div": 100, |
|||
"min": 47.5, |
|||
"max": 49.9, |
|||
"def": 49.5, |
|||
"unit": "Hz" |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"0x4000": [ |
|||
{ |
|||
"name": "Normal Ramp up Rate", |
|||
"div": 100, |
|||
"min": 0.1, |
|||
"max": 100, |
|||
"def": 20, |
|||
"unit": "Rated%/s" |
|||
}, |
|||
{ |
|||
"name": "Soft Start Ramp up Rate ", |
|||
"div": 100, |
|||
"min": 0.1, |
|||
"max": 10, |
|||
"def": 0.16, |
|||
"unit": "Rated%/s" |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"0x5001": [ |
|||
{ |
|||
"name": "FW Function Activated", |
|||
"div": 1, |
|||
"min": 0, |
|||
"max": 1, |
|||
"def": 1 |
|||
}, |
|||
{ |
|||
"name": "Start of Frequency Watt Droop", |
|||
"div": 100, |
|||
"min": 50.2, |
|||
"max": 53, |
|||
"def": 50.2, |
|||
"unit": "Hz" |
|||
}, |
|||
{ |
|||
"name": "FW Droop Slope", |
|||
"div": 10, |
|||
"min": 16.7, |
|||
"max": 100, |
|||
"def": 40, |
|||
"unit": "Pn%/Hz" |
|||
}, |
|||
{ |
|||
"name": "Recovery Ramp Rate", |
|||
"div": 100, |
|||
"min": 0.1, |
|||
"max": 100, |
|||
"def": 0.16, |
|||
"unit": "Pn%/s" |
|||
|
|||
}, |
|||
{ |
|||
"name": "FW Setting Time", |
|||
"div": 10, |
|||
"min": 0, |
|||
"max": 2, |
|||
"def": 0, |
|||
"unit": "s" |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"0x5008": [ |
|||
{ |
|||
"name": "FW Function Activated", |
|||
"div": 1, |
|||
"min": 0, |
|||
"max": 1, |
|||
"def": 1 |
|||
}, |
|||
{ |
|||
"name": "Start of Frequency Watt Droop", |
|||
"div": 100, |
|||
"min": 50.2, |
|||
"max": 52, |
|||
"def": 50.2, |
|||
"unit": "Hz" |
|||
}, |
|||
{ |
|||
"name": "FW Droop Slope", |
|||
"div": 10, |
|||
"min": 16.7, |
|||
"max": 100, |
|||
"def": 40, |
|||
"unit": "Pn%/Hz" |
|||
}, |
|||
{ |
|||
"name": "Recovery Ramp Rate", |
|||
"div": 100, |
|||
"min": 0.1, |
|||
"max": 50, |
|||
"def": 0.16, |
|||
"unit": "Pn%/s" |
|||
}, |
|||
{ |
|||
"name": "Recovery High Frequency", |
|||
"div": 10, |
|||
"min": 50.1, |
|||
"max": 52, |
|||
"def": 50.2, |
|||
"unit": "Hz" |
|||
}, |
|||
{ |
|||
"name": "Recovery Low Frequency", |
|||
"div": 100, |
|||
"min": 49, |
|||
"max": 49.9, |
|||
"def": 49.8, |
|||
"unit": "Hz" |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"0x6000": [ |
|||
{ |
|||
"name": "VW Function Activated", |
|||
"div": 1, |
|||
"min": 0, |
|||
"max": 1, |
|||
"def": 1 |
|||
}, |
|||
{ |
|||
"name": "Start of Voltage Watt Droop", |
|||
"div": 10, |
|||
"def": 253, |
|||
"unit": "V" |
|||
}, |
|||
{ |
|||
"name": "End of Voltage Watt Droop", |
|||
"div": 10, |
|||
"min": 258, |
|||
"max": 270, |
|||
"def": 265, |
|||
"unit": "V" |
|||
}, |
|||
{ |
|||
"name": "VW Droop Slope", |
|||
"div": 100, |
|||
"min": 5, |
|||
"max": 18, |
|||
"def": 5.33, |
|||
"unit": "Pn%/V" |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"0x7002": [ |
|||
{ |
|||
"name": "APC Function Activated", |
|||
"div": 1, |
|||
"min": 0, |
|||
"max": 1, |
|||
"def": 1 |
|||
}, |
|||
{ |
|||
"name": "Power Ramp Rate", |
|||
"div": 100, |
|||
"min": 0.33, |
|||
"max": 100, |
|||
"def": 100, |
|||
"unit": "Pn%/s" |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"0x8000": [ |
|||
{ |
|||
"name": "VV Function Activated", |
|||
"div": 1, |
|||
"min": 0, |
|||
"max": 1, |
|||
"def": 0 |
|||
}, |
|||
{ |
|||
"name": "Voltage Set Point V1", |
|||
"div": 10, |
|||
"min": 184, |
|||
"max": 230, |
|||
"def": 213.9, |
|||
"unit": "V" |
|||
}, |
|||
{ |
|||
"name": "Reactive Set Point Q1", |
|||
"div": 10, |
|||
"min": 0, |
|||
"max": 100, |
|||
"def": 30, |
|||
"unit": "%Pn" |
|||
}, |
|||
{ |
|||
"name": "Voltage Set Point V2", |
|||
"div": 10, |
|||
"min": 210, |
|||
"max": 240, |
|||
"def": 223.1, |
|||
"unit": "V" |
|||
}, |
|||
{ |
|||
"name": "Voltage Set Point V3", |
|||
"div": 10, |
|||
"min": 220, |
|||
"max": 240, |
|||
"def": 236.9, |
|||
"unit": "V" |
|||
}, |
|||
{ |
|||
"name": "Voltage Set Point V4", |
|||
"div": 10, |
|||
"min": 230, |
|||
"max": 253, |
|||
"def": 246.1, |
|||
"unit": "V" |
|||
}, |
|||
{ |
|||
"name": "Reactive Set Point Q4", |
|||
"div": 10, |
|||
"min": 0, |
|||
"max": 100, |
|||
"def": 30, |
|||
"unit": "%Pn" |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"0x8001": [ |
|||
{ |
|||
"name": "VV Function Activated", |
|||
"div": 1, |
|||
"min": 0, |
|||
"max": 1, |
|||
"def": 0 |
|||
}, |
|||
{ |
|||
"name": "Voltage Set Point V1", |
|||
"div": 10, |
|||
"def": 213.9, |
|||
"unit": "V" |
|||
}, |
|||
{ |
|||
"name": "Reactive Set Point Q1", |
|||
"div": 10, |
|||
"min": 0, |
|||
"max": 100, |
|||
"def": 30, |
|||
"unit": "%Pn" |
|||
}, |
|||
{ |
|||
"name": "Voltage Set Point V2", |
|||
"div": 10, |
|||
"def": 223.1, |
|||
"unit": "V" |
|||
}, |
|||
{ |
|||
"name": "Voltage Set Point V3", |
|||
"div": 10, |
|||
"def": 236.9, |
|||
"unit": "V" |
|||
}, |
|||
{ |
|||
"name": "Voltage Set Point V4", |
|||
"div": 10, |
|||
"def": 246.1, |
|||
"unit": "V" |
|||
}, |
|||
{ |
|||
"name": "Reactive Set Point Q4", |
|||
"div": 10, |
|||
"min": 0, |
|||
"max": 100, |
|||
"def": 30, |
|||
"unit": "%Pn" |
|||
}, |
|||
{ |
|||
"name": "VV Setting Time", |
|||
"div": 10, |
|||
"min": 0, |
|||
"max": 60, |
|||
"def": 10, |
|||
"unit": "s" |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"0x9000": [ |
|||
{ |
|||
"name": "Specified Power Factor Activated", |
|||
"div": 1, |
|||
"min": 0, |
|||
"max": 1, |
|||
"def": 0 |
|||
}, |
|||
{ |
|||
"name": "Power Factor", |
|||
"div": 100, |
|||
"min": 0.9, |
|||
"max": 1, |
|||
"def": 0.95 |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"0xa002": [ |
|||
{ |
|||
"name": "RPC Function Activated", |
|||
"div": 1, |
|||
"min": 0, |
|||
"max": 1, |
|||
"def": 0 |
|||
}, |
|||
{ |
|||
"name": "Reactive Power", |
|||
"div": 100, |
|||
"min": 0, |
|||
"max": 50, |
|||
"def": 0, |
|||
"unit": "%Sn" |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"0xb000": [ |
|||
{ |
|||
"name": "WPF Function Activated", |
|||
"div": 1, |
|||
"min": 0, |
|||
"max": 1, |
|||
"def": 0 |
|||
}, |
|||
{ |
|||
"name": "Start of Power of WPF", |
|||
"div": 10, |
|||
"def": 50, |
|||
"unit": "%Pn" |
|||
}, |
|||
{ |
|||
"name": "Power Factor ar Rated Power", |
|||
"div": 100, |
|||
"min": 0.8, |
|||
"max": 1, |
|||
"def": 0.95 |
|||
} |
|||
] |
|||
} |
|||
] |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
.\bdfconv\bdfconv_2_22.exe -v -f 1 -m "32-137" u8g2_font_5x8_symbols_ahoy.bdf -o u8g2_font_5x8_symbols_ahoy.c_ -n u8g2_font_5x8_symbols_ahoy |
|||
|
|||
.\bdfconv\bdfconv_2_22.exe -v -f 1 -m "65-75" u8g2_font_ncenB10_symbols10_ahoy.bdf -o u8g2_font_ncenB10_symbols10_ahoy.c_ -n u8g2_font_ncenB10_symbols10_ahoy |
|||
.\bdfconv\bdfconv_2_22.exe -v -f 1 -m "65-75" u8g2_font_ncenB08_symbols8_ahoy.bdf -o u8g2_font_ncenB08_symbols8_ahoy.c_ -n u8g2_font_ncenB08_symbols8_ahoy |
|||
|
|||
pause |
|||
@ -0,0 +1,11 @@ |
|||
Useful sources to edit u8g2 fonts: |
|||
|
|||
bdf font files for u8g2 font library: |
|||
https://github.com/olikraus/u8g2/tree/master/tools/font/bdf |
|||
|
|||
Tool to edit bdf files: |
|||
https://github.com/olikraus/u8g2/tree/master/tools/font/fony |
|||
|
|||
Tool to convert bdf font files to u8g2 source code: |
|||
https://github.com/olikraus/u8g2/tree/master/tools/font/bdfconv |
|||
|
|||
File diff suppressed because it is too large
@ -0,0 +1,40 @@ |
|||
/* |
|||
Fontname: u8g2_font_5x8_symbols_ahoy |
|||
Copyright: Public domain font. Share and enjoy. |
|||
Glyphs: 106/106 |
|||
BBX Build Mode: 0 |
|||
*/ |
|||
const uint8_t u8g2_font_5x8_symbols_ahoy[1049] U8G2_FONT_SECTION("u8g2_font_5x8_symbols_ahoy") = |
|||
"j\0\3\2\4\4\3\4\5\10\10\0\377\6\377\6\0\1\61\2b\4\0 \5\0\304\11!\7a\306" |
|||
"\212!\11\42\7\63\335\212\304\22#\16u\304\232R\222\14JePJI\2$\14u\304\252l\251m" |
|||
"I\262E\0%\10S\315\212(\351\24&\13t\304\232(i\252\64%\1'\6\61\336\212\1(\7b" |
|||
"\305\32\245))\11b\305\212(\251(\0*\13T\304\212(Q\206D\211\2+\12U\304\252\60\32\244" |
|||
"\60\2,\7\63\275\32\245\4-\6\24\324\212!.\6\42\305\212!/\10d\304\272R[\6\60\14d" |
|||
"\304\32%R\206DJ\24\0\61\10c\305\232Dj\31\62\13d\304\32%\312\22%\33\2\63\13d\304" |
|||
"\212!\212D)Q\0\64\13d\304\252H\251\14Q\226\0\65\12d\304\212A\33\245D\1\66\13d\304" |
|||
"\32%[\42)Q\0\67\13d\304\212!\213\262(\213\0\70\14d\304\32%J\224HJ\24\0\71\13" |
|||
"d\304\32%\222\222-Q\0:\10R\305\212!\32\2;\10c\275\32\243R\2<\10c\305\252\244\224" |
|||
"\25=\10\64\314\212!\34\2>\11c\305\212\254\224\224\0?\11c\305\232\246$M\0@\15\205\274*" |
|||
")\222\226DI\244\252\2A\12d\304\32%\222\206I\12B\14d\304\212%\32\222H\32\22\0C\12" |
|||
"d\304\32%\322J\211\2D\12d\304\212%r\32\22\0E\12d\304\212A[\262l\10F\12d\304" |
|||
"\212A[\262\32\0G\13d\304\32%\322\222)Q\0H\12d\304\212H\32&S\0I\10c\305\212" |
|||
"%j\31J\12d\304\232)\253\224\42\0K\13d\304\212HI\244\244S\0L\10d\304\212\254\333\20" |
|||
"M\12d\304\212h\70D\246\0N\12d\304\212h\31\226I\12O\12d\304\32%rJ\24\0P\13" |
|||
"d\304\212%\222\206$\313\0Q\12t\274\32%\222\26\307\0R\13d\304\212%\222\206$\222\2S\14" |
|||
"d\304\32%J\302$J\24\0T\11e\304\212A\12;\1U\11d\304\212\310S\242\0V\12d\304" |
|||
"\212\310)\221\24\0W\12d\304\212\310\64\34\242\0X\13d\304\212HJ$%\222\2Y\12e\304\212" |
|||
"LKja\11Z\12d\304\212!\213\332\206\0[\10c\305\212!j\32\134\10d\304\212,l\13]" |
|||
"\10c\305\212\251i\10^\6#\345\232\6_\6\24\274\212!`\6\42\345\212(a\11D\304\232!\222" |
|||
"\222\1b\13d\304\212,[\42iH\0c\7C\305\232)\23d\12d\304\272\312\20I\311\0e\11" |
|||
"D\304\32%\31\262\1f\12d\304\252Ji\312\42\0g\12T\274\32%J\266D\1h\12d\304\212" |
|||
",[\42S\0i\10c\305\232P\252\14j\12s\275\252\64\212\224\12\0k\12d\304\212\254\64$\221" |
|||
"\24l\10c\305\12\251\313\0m\12E\304\12\245EI\224\2n\10D\304\212%\62\5o\11D\304\32" |
|||
"%\222\22\5p\12T\274\212%\32\222,\3q\11T\274\232!J\266\2r\11D\304\212$\261e\0" |
|||
"s\10C\305\232![\0t\13d\304\232,\232\262$J\0u\10D\304\212\310\224\14v\10C\305\212" |
|||
"\304R\1w\12E\304\212LI\224.\0x\11D\304\212(\221\224(y\13T\274\212HJ\206(Q" |
|||
"\0z\11D\304\212!*\15\1{\12t\304*%L\304(\24|\6a\306\212\3}\13t\304\12\61" |
|||
"\12\225\60\221\0~\10$\344\232DI\0\5\0\304\12\200\13u\274\212K\242T\266\260\4\201\14f" |
|||
"D\233!\11#-\312!\11\202\15hD<\65\12\243,\214\302$\16\203\15w<\214C\22F\71\220" |
|||
"\26\207A\204\13u\274\212\244\232t\313\241\10\205\17\206<\213\60\31\22\311\66D\245!\11\3\206\20\210" |
|||
"<\254\342\20]\302(L\246C\30E\0\207\15wD\334X\25\267\341\20\15\21\0\210\16w<\214\203" |
|||
"RQ\25I\212\324a\20\211\15f\304\213)\213\244,\222\222\245\0\0\0\0"; |
|||
Binary file not shown.
@ -0,0 +1,166 @@ |
|||
STARTFONT 2.1 |
|||
COMMENT Exported by Fony v1.4.7 |
|||
FONT u8g2_font_ncenB08_symbols8_ahoy |
|||
SIZE 12 100 100 |
|||
FONTBOUNDINGBOX 9 11 0 -2 |
|||
STARTPROPERTIES 6 |
|||
COPYRIGHT "" |
|||
RESOLUTION_X 100 |
|||
RESOLUTION_Y 100 |
|||
FONT_ASCENT 10 |
|||
FONT_DESCENT 2 |
|||
DEFAULT_CHAR 0 |
|||
ENDPROPERTIES |
|||
CHARS 11 |
|||
STARTCHAR 065 |
|||
ENCODING 65 |
|||
SWIDTH 576 0 |
|||
DWIDTH 8 0 |
|||
BBX 7 8 0 0 |
|||
BITMAP |
|||
FE |
|||
92 |
|||
D6 |
|||
38 |
|||
10 |
|||
10 |
|||
10 |
|||
10 |
|||
ENDCHAR |
|||
STARTCHAR 066 |
|||
ENCODING 66 |
|||
SWIDTH 648 0 |
|||
DWIDTH 9 0 |
|||
BBX 8 9 1 0 |
|||
BITMAP |
|||
3C |
|||
42 |
|||
99 |
|||
24 |
|||
42 |
|||
18 |
|||
24 |
|||
00 |
|||
18 |
|||
ENDCHAR |
|||
STARTCHAR 067 |
|||
ENCODING 67 |
|||
SWIDTH 576 0 |
|||
DWIDTH 8 0 |
|||
BBX 8 8 0 0 |
|||
BITMAP |
|||
18 |
|||
24 |
|||
24 |
|||
42 |
|||
42 |
|||
42 |
|||
42 |
|||
81 |
|||
ENDCHAR |
|||
STARTCHAR 068 |
|||
ENCODING 68 |
|||
SWIDTH 648 0 |
|||
DWIDTH 9 0 |
|||
BBX 8 8 0 0 |
|||
BITMAP |
|||
FF |
|||
41 |
|||
20 |
|||
10 |
|||
10 |
|||
20 |
|||
41 |
|||
FF |
|||
ENDCHAR |
|||
STARTCHAR 069 |
|||
ENCODING 69 |
|||
SWIDTH 576 0 |
|||
DWIDTH 8 0 |
|||
BBX 7 8 0 0 |
|||
BITMAP |
|||
FE |
|||
D6 |
|||
FE |
|||
38 |
|||
10 |
|||
38 |
|||
54 |
|||
92 |
|||
ENDCHAR |
|||
STARTCHAR 070 |
|||
ENCODING 70 |
|||
SWIDTH 648 0 |
|||
DWIDTH 9 0 |
|||
BBX 8 9 1 0 |
|||
BITMAP |
|||
BD |
|||
42 |
|||
BD |
|||
3C |
|||
5A |
|||
18 |
|||
24 |
|||
42 |
|||
99 |
|||
ENDCHAR |
|||
STARTCHAR 071 |
|||
ENCODING 71 |
|||
SWIDTH 648 0 |
|||
DWIDTH 9 0 |
|||
BBX 8 8 0 0 |
|||
BITMAP |
|||
24 |
|||
3C |
|||
E7 |
|||
42 |
|||
42 |
|||
E7 |
|||
3C |
|||
24 |
|||
ENDCHAR |
|||
STARTCHAR 072 |
|||
ENCODING 72 |
|||
SWIDTH 576 0 |
|||
DWIDTH 8 0 |
|||
BBX 7 7 0 1 |
|||
BITMAP |
|||
04 |
|||
06 |
|||
06 |
|||
0E |
|||
1E |
|||
FC |
|||
78 |
|||
ENDCHAR |
|||
STARTCHAR 073 |
|||
ENCODING 73 |
|||
SWIDTH 576 0 |
|||
DWIDTH 8 0 |
|||
BBX 7 9 0 0 |
|||
BITMAP |
|||
44 |
|||
FE |
|||
82 |
|||
92 |
|||
B2 |
|||
92 |
|||
92 |
|||
82 |
|||
FE |
|||
ENDCHAR |
|||
STARTCHAR 074 |
|||
ENCODING 74 |
|||
SWIDTH 504 0 |
|||
DWIDTH 7 0 |
|||
BBX 0 0 0 0 |
|||
BITMAP |
|||
ENDCHAR |
|||
STARTCHAR 075 |
|||
ENCODING 75 |
|||
SWIDTH 648 0 |
|||
DWIDTH 9 0 |
|||
BBX 0 0 0 0 |
|||
BITMAP |
|||
ENDCHAR |
|||
ENDFONT |
|||
@ -0,0 +1,13 @@ |
|||
/* |
|||
Fontname: u8g2_font_ncenB08_symbols8_ahoy |
|||
Copyright: |
|||
Glyphs: 11/11 |
|||
BBX Build Mode: 0 |
|||
*/ |
|||
const uint8_t u8g2_font_ncenB08_symbols8_ahoy[173] U8G2_FONT_SECTION("u8g2_font_ncenB08_symbols8_ahoy") = |
|||
"\13\0\3\2\4\4\2\2\5\11\11\0\0\10\0\10\0\0\0\0\0\0\224A\14\207\212q\220\242%\221" |
|||
"\326\270\15B\20\230\233\65da\22Ima\250F\71\254\1C\20\210\212\247Fa\224\205Q\30\205Q" |
|||
"\230\304\1D\16\210\232qP\322(Gr \256\16\7E\15\207\212\361\222\14\247\65\335\222\246\2F\25" |
|||
"\230\233\221\14I\61I\206$\32\262D\11\325(\13\223H\12G\17\210\232U\34\242K\30\205\311t\10" |
|||
"\243\10H\14w\216\33\253\342\66\34\242!\2I\21\227\212\223%\303\240J\221\42I\221\24\251\303 J" |
|||
"\5\0z\1K\5\0\232\1\0\0\0"; |
|||
Binary file not shown.
@ -0,0 +1,196 @@ |
|||
STARTFONT 2.1 |
|||
COMMENT Exported by Fony v1.4.7 |
|||
FONT u8g2_font_symbols10_ahoy |
|||
SIZE 16 100 100 |
|||
FONTBOUNDINGBOX 12 15 0 -3 |
|||
STARTPROPERTIES 6 |
|||
COPYRIGHT "" |
|||
RESOLUTION_X 100 |
|||
RESOLUTION_Y 100 |
|||
FONT_ASCENT 13 |
|||
FONT_DESCENT 3 |
|||
DEFAULT_CHAR 0 |
|||
ENDPROPERTIES |
|||
CHARS 11 |
|||
STARTCHAR 065 |
|||
ENCODING 65 |
|||
SWIDTH 576 0 |
|||
DWIDTH 8 0 |
|||
BBX 7 11 0 0 |
|||
BITMAP |
|||
FE |
|||
92 |
|||
92 |
|||
54 |
|||
38 |
|||
10 |
|||
10 |
|||
10 |
|||
10 |
|||
10 |
|||
10 |
|||
ENDCHAR |
|||
STARTCHAR 066 |
|||
ENCODING 66 |
|||
SWIDTH 648 0 |
|||
DWIDTH 9 0 |
|||
BBX 8 9 0 1 |
|||
BITMAP |
|||
3C |
|||
42 |
|||
99 |
|||
24 |
|||
42 |
|||
18 |
|||
24 |
|||
00 |
|||
18 |
|||
ENDCHAR |
|||
STARTCHAR 067 |
|||
ENCODING 67 |
|||
SWIDTH 792 0 |
|||
DWIDTH 11 0 |
|||
BBX 10 11 0 0 |
|||
BITMAP |
|||
0C00 |
|||
1200 |
|||
2100 |
|||
2100 |
|||
2100 |
|||
2180 |
|||
4080 |
|||
4080 |
|||
4080 |
|||
4080 |
|||
8040 |
|||
ENDCHAR |
|||
STARTCHAR 068 |
|||
ENCODING 68 |
|||
SWIDTH 720 0 |
|||
DWIDTH 10 0 |
|||
BBX 9 11 0 0 |
|||
BITMAP |
|||
FF80 |
|||
6080 |
|||
3000 |
|||
1800 |
|||
0C00 |
|||
0C00 |
|||
1800 |
|||
3000 |
|||
6000 |
|||
C080 |
|||
FF80 |
|||
ENDCHAR |
|||
STARTCHAR 069 |
|||
ENCODING 69 |
|||
SWIDTH 576 0 |
|||
DWIDTH 8 0 |
|||
BBX 7 11 0 0 |
|||
BITMAP |
|||
AA |
|||
00 |
|||
92 |
|||
00 |
|||
38 |
|||
00 |
|||
10 |
|||
00 |
|||
10 |
|||
00 |
|||
10 |
|||
ENDCHAR |
|||
STARTCHAR 070 |
|||
ENCODING 70 |
|||
SWIDTH 648 0 |
|||
DWIDTH 9 0 |
|||
BBX 8 10 1 0 |
|||
BITMAP |
|||
BD |
|||
42 |
|||
BD |
|||
24 |
|||
5A |
|||
18 |
|||
24 |
|||
24 |
|||
5A |
|||
81 |
|||
ENDCHAR |
|||
STARTCHAR 071 |
|||
ENCODING 71 |
|||
SWIDTH 864 0 |
|||
DWIDTH 12 0 |
|||
BBX 11 11 0 0 |
|||
BITMAP |
|||
1100 |
|||
1100 |
|||
0E00 |
|||
D160 |
|||
2080 |
|||
2080 |
|||
2080 |
|||
D160 |
|||
0E00 |
|||
1100 |
|||
1100 |
|||
ENDCHAR |
|||
STARTCHAR 072 |
|||
ENCODING 72 |
|||
SWIDTH 792 0 |
|||
DWIDTH 11 0 |
|||
BBX 10 11 0 0 |
|||
BITMAP |
|||
0080 |
|||
0080 |
|||
00C0 |
|||
00C0 |
|||
01C0 |
|||
01C0 |
|||
03C0 |
|||
0780 |
|||
1F80 |
|||
FF00 |
|||
3C00 |
|||
ENDCHAR |
|||
STARTCHAR 073 |
|||
ENCODING 73 |
|||
SWIDTH 720 0 |
|||
DWIDTH 10 0 |
|||
BBX 9 11 0 0 |
|||
BITMAP |
|||
DD80 |
|||
FF80 |
|||
8080 |
|||
8880 |
|||
9880 |
|||
8880 |
|||
8880 |
|||
8880 |
|||
8880 |
|||
8080 |
|||
FF80 |
|||
ENDCHAR |
|||
STARTCHAR 074 |
|||
ENCODING 74 |
|||
SWIDTH 648 0 |
|||
DWIDTH 9 0 |
|||
BBX 8 8 0 0 |
|||
BITMAP |
|||
F9 |
|||
04 |
|||
F2 |
|||
09 |
|||
E5 |
|||
15 |
|||
D5 |
|||
D5 |
|||
ENDCHAR |
|||
STARTCHAR 075 |
|||
ENCODING 75 |
|||
SWIDTH 576 0 |
|||
DWIDTH 8 0 |
|||
BBX 0 0 0 0 |
|||
BITMAP |
|||
ENDCHAR |
|||
ENDFONT |
|||
@ -0,0 +1,14 @@ |
|||
/* |
|||
Fontname: u8g2_font_symbols10_ahoy |
|||
Copyright: |
|||
Glyphs: 11/11 |
|||
BBX Build Mode: 0 |
|||
*/ |
|||
const uint8_t u8g2_font_ncenB10_symbols10_ahoy[217] U8G2_FONT_SECTION("u8g2_font_ncenB10_symbols10_ahoy") = |
|||
"\13\0\3\2\4\4\2\2\5\13\13\0\0\13\0\13\0\0\0\0\0\0\300A\15\267\212q\220\42\251\322" |
|||
"\266\306\275\1B\20\230\236\65da\22Ima\250F\71\254\1C\23\272\272\251\3Q\32\366Q\212\243" |
|||
"\70\212\243\70\311\221\0D\20\271\252\361\242F:\242#: {\36\16\1E\17\267\212\221\264\3Q\35" |
|||
"\332\321\34\316\341\14F\25\250\233\221\14I\61I\206$\252%J\250Fa\224%J\71G\30\273\312W" |
|||
"\316r`T\262DJ\303\64L#%K\304\35\310\342,\3H\27\272\272\217\344P\16\351\210\16\354\300" |
|||
"<\244C\70,\303 \16!\0I\24\271\252\241\34\336\1-\223\64-\323\62-\323\62\35x\10J\22" |
|||
"\210\232\61Hi\64Di\64DI\226$KeiK\5\0\212\1\0\0\0"; |
|||
Binary file not shown.
@ -0,0 +1,16 @@ |
|||
Display_Mono_64x48: |
|||
u8g2_font_fur11_tr |
|||
u8g2_font_6x10_tf |
|||
u8g2_font_4x6_tr |
|||
|
|||
Display_Mono_128x32: |
|||
u8g2_font_9x15_tr |
|||
u8g2_font_tom_thumb_4x6_tr |
|||
|
|||
Display_Mono_84x48: |
|||
u8g2_font_5x8_symbols_ahoy |
|||
u8g2_font_logisoso16_tr |
|||
|
|||
Display_Mono_128x64: |
|||
u8g2_font_ncenB08_symbols8_ahoy |
|||
u8g2_font_ncenB10_symbols10_ahoy |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue