From 0a297954a0403e7ad37f8dba18b6c9705833dcfe Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Tue, 21 Jan 2020 14:47:13 +0100 Subject: [PATCH 001/106] initial --- lib/SDS011/src/SDS011.h | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 lib/SDS011/src/SDS011.h diff --git a/lib/SDS011/src/SDS011.h b/lib/SDS011/src/SDS011.h new file mode 100644 index 00000000..f8a013d5 --- /dev/null +++ b/lib/SDS011/src/SDS011.h @@ -0,0 +1,40 @@ +// SDS011 dust sensor PM2.5 and PM10 +// --------------------------------- +// +// By R. Zschiegner (rz@madavi.de) +// April 2016 +// +// Documentation: +// - The iNovaFitness SDS011 datasheet +// + +#if ARDUINO >= 100 + #include "Arduino.h" +#else + #include "WProgram.h" +#endif + +#include + +// Definition SDS011 sensor 'commands' +#define SDS_START_CMD 1 +#define SDS_STOP_CMD 2 +#define SDS_CONTINUOUS_MODE_CMD 3 +#define SDS_VERSION_DATE_CMD 4 + +class SDS011 { + public: + SDS011(void); + void begin(uint8_t pin_rx, uint8_t pin_tx); + void begin(HardwareSerial* serial); + void begin(SoftwareSerial* serial); + int read(float *p25, float *p10); + void sleep(); + void wakeup(); + void contmode( int ); + private: + void SDS_cmd(const uint8_t); + uint8_t calcChecksum( byte *); + uint8_t _pin_rx, _pin_tx; + Stream *sds_data; +}; From 4edff01a37667e39b17e2db05ec22e3d1a74e9eb Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Tue, 21 Jan 2020 14:51:41 +0100 Subject: [PATCH 002/106] initial --- lib/SDS011/src/SDS011.cpp | 191 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 lib/SDS011/src/SDS011.cpp diff --git a/lib/SDS011/src/SDS011.cpp b/lib/SDS011/src/SDS011.cpp new file mode 100644 index 00000000..31efdea4 --- /dev/null +++ b/lib/SDS011/src/SDS011.cpp @@ -0,0 +1,191 @@ +// SDS011 dust sensor PM2.5 and PM10 +// --------------------- +// +// By R. Zschiegner (rz@madavi.de) +// April 2016 +// +// Documentation: +// - The iNovaFitness SDS011 datasheet +// +// modified by AQ - 2018-11-18 +// + +#include "SDS011.h" + +static const byte SDS_SLEEP[] = { + 0xAA, // head + 0xB4, // command id + 0x06, // data byte 1 + 0x01, // data byte 2 (set mode) + 0x00, // data byte 3 (sleep) + 0x00, // data byte 4 + 0x00, // data byte 5 + 0x00, // data byte 6 + 0x00, // data byte 7 + 0x00, // data byte 8 + 0x00, // data byte 9 + 0x00, // data byte 10 + 0x00, // data byte 11 + 0x00, // data byte 12 + 0x00, // data byte 13 + 0xFF, // data byte 14 (device id byte 1) + 0xFF, // data byte 15 (device id byte 2) + 0x05, // checksum + 0xAB // tail +}; + +static const byte SDS_START[] = { + 0xAA, 0xB4, 0x06, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x06, 0xAB}; + +static const byte SDS_CONT_MODE[] = { + 0xAA, 0xB4, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x07, 0xAB}; + +static const byte SDS_VERSION[] = { + 0xAA, 0xB4, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x05, 0xAB}; + +const uint8_t SDS_cmd_len = 19; + +SDS011::SDS011(void) { + +} + +// -------------------------------------------------------- +// SDS011:read +// -------------------------------------------------------- +int SDS011::read(float *p25, float *p10) { + byte buffer; + int value; + int len = 0; + int pm10_serial = 0; + int pm25_serial = 0; + int checksum_is; + int checksum_ok = 0; + int error = 1; + + while ((sds_data->available() > 0) && (sds_data->available() >= (10-len))) { + buffer = sds_data->read(); + value = int(buffer); + switch (len) { + case (0): if (value != 170) { len = -1; }; break; + case (1): if (value != 192) { len = -1; }; break; + case (2): pm25_serial = value; checksum_is = value; break; + case (3): pm25_serial += (value << 8); checksum_is += value; break; + case (4): pm10_serial = value; checksum_is += value; break; + case (5): pm10_serial += (value << 8); checksum_is += value; break; + case (6): checksum_is += value; break; + case (7): checksum_is += value; break; + case (8): if (value == (checksum_is % 256)) { checksum_ok = 1; } else { len = -1; }; break; + case (9): if (value != 171) { len = -1; }; break; + } + len++; + if (len == 10 && checksum_ok == 1) { + *p10 = (float)pm10_serial/10.0; + *p25 = (float)pm25_serial/10.0; + len = 0; checksum_ok = 0; pm10_serial = 0.0; pm25_serial = 0.0; checksum_is = 0; + error = 0; + } + yield(); + } + return error; +} + +// -------------------------------------------------------- +// SDS011:sleep +// -------------------------------------------------------- +void SDS011::sleep() { + SDS_cmd(SDS_STOP_CMD); +} + +// -------------------------------------------------------- +// SDS011:wakeup +// -------------------------------------------------------- +void SDS011::wakeup() { + SDS_cmd(SDS_START_CMD); +} + +// -------------------------------------------------------- +// SDS011:continous mode +// -------------------------------------------------------- +void SDS011::contmode(int noOfMinutes) +{ + byte buffer[SDS_cmd_len]; + memcpy(buffer, SDS_CONT_MODE, SDS_cmd_len); + buffer[4] = (byte) noOfMinutes; + buffer[17] = calcChecksum( buffer ); + for (uint8_t i = 0; i < SDS_cmd_len; i++) { + sds_data->write(buffer[i]); + } + sds_data->flush(); + while (sds_data->available() > 0) { + sds_data->read(); + } +// SDS_cmd(SDS_CONTINUOUS_MODE_CMD); +} + +/***************************************************************** + * send SDS011 command (start, stop, continuous mode, version * + *****************************************************************/ +void SDS011::SDS_cmd(const uint8_t cmd) +{ + byte buf[SDS_cmd_len]; + switch (cmd) { + case SDS_START_CMD: + memcpy(buf, SDS_START, SDS_cmd_len); + break; + case SDS_STOP_CMD: + memcpy(buf, SDS_SLEEP, SDS_cmd_len); + break; + case SDS_CONTINUOUS_MODE_CMD: + memcpy(buf, SDS_CONT_MODE, SDS_cmd_len); + break; + case SDS_VERSION_DATE_CMD: + memcpy(buf, SDS_VERSION, SDS_cmd_len); + break; + default: + return; + } + for (uint8_t i = 0; i < SDS_cmd_len; i++) { + sds_data->write(buf[i]); + } + sds_data->flush(); + while (sds_data->available() > 0) { + sds_data->read(); + } + +} + +// -------------------------------------------------------- +// SDS011: calculate checksum +// -------------------------------------------------------- +uint8_t SDS011::calcChecksum( byte *buffer ) +{ + uint8_t value = 0; + + for (uint8_t i = 2; i < 17; i++ ) + { + value += buffer[i]; + value &= 0xff; + } + return value; +} + +void SDS011::begin(uint8_t pin_rx, uint8_t pin_tx) { + _pin_rx = pin_rx; + _pin_tx = pin_tx; + + SoftwareSerial *softSerial = new SoftwareSerial(_pin_rx, _pin_tx); + softSerial->begin(9600); + + sds_data = softSerial; +} + +void SDS011::begin(HardwareSerial* serial) { + Serial.println("SDS011::begin"); +// serial->begin(9600); // why do I have to remove this line? + sds_data = serial; +} + +void SDS011::begin(SoftwareSerial* serial) { + serial->begin(9600); + sds_data = serial; +} From c545136c063754844f2dac3f86119aac09d982cc Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Tue, 21 Jan 2020 14:54:35 +0100 Subject: [PATCH 003/106] plus modifications for SDS011 --- src/sdcard.cpp | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/sdcard.cpp b/src/sdcard.cpp index 0633151f..8e45ea7c 100644 --- a/src/sdcard.cpp +++ b/src/sdcard.cpp @@ -6,6 +6,12 @@ static const char TAG[] = __FILE__; #include "sdcard.h" +#if (HAS_SDS011) +#include +// the results of the sensor: +extern float pm25; +extern float pm10; +#endif static bool useSDCard; @@ -35,7 +41,13 @@ void sdcardWriteData(uint16_t noWifi, uint16_t noBle) { sprintf(tempBuffer, "%02d:%02d:%02d,", hour(t), minute(t), second(t)); fileSDCard.print(tempBuffer); sprintf(tempBuffer, "%d,%d", noWifi, noBle); - fileSDCard.println(tempBuffer); + fileSDCard.print( tempBuffer); +#if (HAS_SDS011) + ESP_LOGD(TAG, "fine-dust-values: %5.1f,%4.1f", pm10, pm25); + sprintf(tempBuffer, ",%5.1f,%4.1f", pm10, pm25); + fileSDCard.print( tempBuffer); +#endif + fileSDCard.println( ); if (++counterWrites > 2) { // force writing to SD-card @@ -58,8 +70,12 @@ void createFile(void) { ESP_LOGD(TAG, "SD: file does not exist: opening"); fileSDCard = SD.open(bufferFilename, FILE_WRITE); if (fileSDCard) { - ESP_LOGD(TAG, "SD: name opended: <%s>", bufferFilename); - fileSDCard.println(SDCARD_FILE_HEADER); + ESP_LOGD(TAG, "SD: name opened: <%s>", bufferFilename); + fileSDCard.print( SDCARD_FILE_HEADER ); +#if (HAS_SDS011) + fileSDCard.print( SDCARD_FILE_HEADER_SDS011 ); +#endif + fileSDCard.println(); useSDCard = true; break; } @@ -68,4 +84,4 @@ void createFile(void) { return; } -#endif // (HAS_SDCARD) \ No newline at end of file +#endif // (HAS_SDCARD) From aacadda01d9727b6e98e7a119f5b7a4e380f2aab Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Tue, 21 Jan 2020 14:56:02 +0100 Subject: [PATCH 004/106] initial --- src/sds011read.cpp | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/sds011read.cpp diff --git a/src/sds011read.cpp b/src/sds011read.cpp new file mode 100644 index 00000000..c55b5741 --- /dev/null +++ b/src/sds011read.cpp @@ -0,0 +1,36 @@ +// routines for fetching data from the SDS011-sensor + +// Local logging tag +static const char TAG[] = __FILE__; + +#include + +// UART(2) is unused in this project +static HardwareSerial sdsSerial(2); // so we use it here +static SDS011 sdsSensor; // fine dust sensor + +// the results of the sensor: +float pm25; +float pm10; + +// init +bool sds011_init() +{ + pm25 = pm10 = 0.0; + sdsSerial.begin(9600, SERIAL_8N1, ESP_PIN_RX, ESP_PIN_TX); + sdsSensor.begin (&sdsSerial); + sdsSensor.contmode(0); // for safety: wakeup/sleep - if we want it we do it by ourselves + sdsSensor.wakeup(); // always wake up + return true; +} +// reading data: +void sds011_loop() +{ + pm25 = pm10 = 0.0; + int sdsErrorCode = sdsSensor.read(&pm25, &pm10); + if (!sdsErrorCode) + { + ESP_LOGD(TAG, "SDS011 error: %d", sdsErrorCode); + } + return; +} From 788ff354dc998f8b604c693d061faf36e4cccb82 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Tue, 21 Jan 2020 15:00:35 +0100 Subject: [PATCH 005/106] plus code for SDS011 --- src/senddata.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/senddata.cpp b/src/senddata.cpp index f4f6b085..db0a4e20 100644 --- a/src/senddata.cpp +++ b/src/senddata.cpp @@ -13,6 +13,10 @@ void SendPayload(uint8_t port, sendprio_t prio) { MessageBuffer_t SendBuffer; // contains MessageSize, MessagePort, MessagePrio, Message[] +#if (HAS_SDS011) + sds011_loop(); +#endif + SendBuffer.MessageSize = payload.getSize(); SendBuffer.MessagePrio = prio; @@ -172,4 +176,4 @@ void flushQueues() { #ifdef HAS_SPI spi_queuereset(); #endif -} \ No newline at end of file +} From dbaf2dbf6dced909f8941278cf85833053f6aa71 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Tue, 21 Jan 2020 15:03:07 +0100 Subject: [PATCH 006/106] plus code for SDS011 --- src/main.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index 5e761b0e..19e45152 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -328,6 +328,12 @@ void setup() { strcat_P(features, " SD"); #endif +#if (HAS_SDS011) +// ESP_LOGI(TAG, "init fine-dust-sensor"); + if ( sds011_init() ) + strcat_P(features, " SDS"); +#endif + #if (VENDORFILTER) strcat_P(features, " FILTER"); #endif From 804cbfb879ee7b44ebf995883eac98b756c8a0e5 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Wed, 22 Jan 2020 15:43:13 +0100 Subject: [PATCH 007/106] added support for SDS011 --- include/senddata.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/senddata.h b/include/senddata.h index 5022800c..324f2e1f 100644 --- a/include/senddata.h +++ b/include/senddata.h @@ -16,6 +16,10 @@ #include "sdcard.h" #endif +#if (HAS_SDS011) +#include "sds011read.h" +#endif + extern Ticker sendcycler; void SendPayload(uint8_t port, sendprio_t prio); From 7a465f7c752b0a8b8fbc7ab6bcc8bda3e6f22090 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Wed, 22 Jan 2020 15:44:54 +0100 Subject: [PATCH 008/106] added support for SDS011 --- include/payload.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/include/payload.h b/include/payload.h index b41bb113..b2d5683f 100644 --- a/include/payload.h +++ b/include/payload.h @@ -3,6 +3,10 @@ #include "paxcounter.conf" +#if (HAS_SDS011) +#include "sds011read.h" +#endif + // MyDevices CayenneLPP 1.0 channels for Synamic sensor payload format // all payload goes out on LoRa FPort 1 #if (PAYLOAD_ENCODER == 3) @@ -55,6 +59,9 @@ public: void addButton(uint8_t value); void addSensor(uint8_t[]); void addTime(time_t value); + void addPM10(float value); + void addPM25(float value); + void addChars( char* string, int len); #if (PAYLOAD_ENCODER == 1) // format plain @@ -95,4 +102,4 @@ private: extern PayloadConvert payload; -#endif // _PAYLOAD_H_ \ No newline at end of file +#endif // _PAYLOAD_H_ From c1411e5c1b475213fe0e5066e65a9791bd93fd58 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Wed, 22 Jan 2020 15:49:07 +0100 Subject: [PATCH 009/106] added support for SDS011 --- src/senddata.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/senddata.cpp b/src/senddata.cpp index f4f6b085..367a5c68 100644 --- a/src/senddata.cpp +++ b/src/senddata.cpp @@ -3,6 +3,11 @@ Ticker sendcycler; +#if (HAS_SDS011) +extern float pm10; +extern float pm25; +#endif + void sendcycle() { xTaskNotifyFromISR(irqHandlerTask, SENDCYCLE_IRQ, eSetBits, NULL); } @@ -13,6 +18,10 @@ void SendPayload(uint8_t port, sendprio_t prio) { MessageBuffer_t SendBuffer; // contains MessageSize, MessagePort, MessagePrio, Message[] +#if (HAS_SDS011) + sds011_loop(); +#endif + SendBuffer.MessageSize = payload.getSize(); SendBuffer.MessagePrio = prio; @@ -94,6 +103,10 @@ void sendData() { payload.addCount(macs_wifi, MAC_SNIFF_WIFI); if (cfg.blescan) payload.addCount(macs_ble, MAC_SNIFF_BLE); +#endif +#if (HAS_SDS011) + payload.addPM10(pm10); + payload.addPM25(pm25); #endif SendPayload(COUNTERPORT, prio_normal); // clear counter if not in cumulative counter mode @@ -172,4 +185,4 @@ void flushQueues() { #ifdef HAS_SPI spi_queuereset(); #endif -} \ No newline at end of file +} From b5250a21edf33f1bf86361804ba367ab1969e73e Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Wed, 22 Jan 2020 15:51:49 +0100 Subject: [PATCH 010/106] added support for SDS011 --- src/payload.cpp | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/payload.cpp b/src/payload.cpp index 323e402b..2624c138 100644 --- a/src/payload.cpp +++ b/src/payload.cpp @@ -489,5 +489,41 @@ void PayloadConvert::addTime(time_t value) { buffer[cursor++] = (byte)((tx_period & 0x000000FF)); #endif } +#endif // PAYLOAD_ENCODER -#endif \ No newline at end of file +void PayloadConvert::addPM10( float value) { +#if (HAS_SDS011) +#if (PAYLOAD_ENCODER == 1) // plain + char tempBuffer[10+1]; + sprintf( tempBuffer, ",%5.1f", value); + addChars(tempBuffer, strlen(tempBuffer)); +#elif (PAYLOAD_ENCODER == 2 ) // packed + writeUint16( (uint16_t) (value*10) ); +#elif (PAYLOAD_ENCODER == 3 ) // Cayenne LPP dynamic + // TODO +#elif (PAYLOAD_ENCODER == 4 ) // Cayenne LPP packed + // TODO +#endif +#endif // HAS_SDS011 +} + +void PayloadConvert::addPM25( float value) { +#if (HAS_SDS011) +#if (PAYLOAD_ENCODER == 1) // plain + char tempBuffer[10+1]; + sprintf( tempBuffer, ",%5.1f", value); + addChars(tempBuffer, strlen(tempBuffer)); +#elif (PAYLOAD_ENCODER == 2 ) // packed + writeUint16( (uint16_t) (value*10) ); +#elif (PAYLOAD_ENCODER == 3 ) // Cayenne LPP dynamic + // TODO +#elif (PAYLOAD_ENCODER == 4 ) // Cayenne LPP packed + // TODO +#endif +#endif // HAS_SDS011 +} + +void PayloadConvert::addChars( char * string, int len) { + for (int i=0; i < len; i++) + addByte(string[i]); +} From 0bc7afe02380c1989989e1c4e1d1a005984b0407 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Mon, 3 Feb 2020 15:22:33 +0100 Subject: [PATCH 011/106] Update sdcard.h --- include/sdcard.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/sdcard.h b/include/sdcard.h index 051baa19..99e784cb 100644 --- a/include/sdcard.h +++ b/include/sdcard.h @@ -10,7 +10,7 @@ #define SDCARD_FILE_NAME "paxcount.%02d" #define SDCARD_FILE_HEADER "date, time, wifi, bluet" -bool sdcardInit( void ); +bool sdcard_init( void ); void sdcardWriteData( uint16_t, uint16_t); -#endif \ No newline at end of file +#endif From 6eccb475e352e2d0cb50b70086d46d85caf94f9c Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Mon, 3 Feb 2020 15:23:13 +0100 Subject: [PATCH 012/106] Update cyclic.h --- include/cyclic.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/cyclic.h b/include/cyclic.h index 4b402360..ccadbb65 100644 --- a/include/cyclic.h +++ b/include/cyclic.h @@ -18,6 +18,15 @@ #include "display.h" #endif +#if (HAS_SDS011) +#include "sds011read.h" +#endif + +#if (HAS_SDCARD) +#include "sdcard.h" +#endif + + extern Ticker housekeeper; void housekeeping(void); From 1ec0f8f3f7fd50d4d6e43ba13ec50e7b8d983b16 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Mon, 3 Feb 2020 15:24:07 +0100 Subject: [PATCH 013/106] Update payload.h --- include/payload.h | 56 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/include/payload.h b/include/payload.h index b2d5683f..328c0b4e 100644 --- a/include/payload.h +++ b/include/payload.h @@ -61,6 +61,62 @@ public: void addTime(time_t value); void addPM10(float value); void addPM25(float value); +private: + void addChars( char* string, int len); + +#if (PAYLOAD_ENCODER == 1) // format plain + +private: + uint8_t *buffer; + uint8_t cursor; + +#elif (PAYLOAD_ENCODER == 2) // format packed + +private: + uint8_t *buffer; + uint8_t cursor; + void uintToBytes(uint64_t i, uint8_t byteSize); + void writeUptime(uint64_t unixtime); + void writeLatLng(double latitude, double longitude); + void writeUint64(uint64_t i); + void writeUint32(uint32_t i); + void writeUint16(uint16_t i); + void writeUint8(uint8_t i); + void writeFloat(float value); + void writeUFloat(float value); + void writePressure(float value); + void writeVersion(char *version); + void writeBitmap(bool a, bool b, bool c, bool d, bool e, bool f, bool g, + bool h); + +#elif ((PAYLOAD_ENCODER == 3) || (PAYLOAD_ENCODER == 4)) // format cayenne lpp + +private: + uint8_t *buffer; + uint8_t maxsize; + uint8_t cursor; + +#else +#error No valid payload converter defined! +#endif +}; + +extern PayloadConvert payload; + +#endif // _PAYLOAD_H_ + void addCount(uint16_t value, uint8_t sniffytpe); + void addConfig(configData_t value); + void addStatus(uint16_t voltage, uint64_t uptime, float cputemp, uint32_t mem, + uint8_t reset1, uint8_t reset2); + void addAlarm(int8_t rssi, uint8_t message); + void addVoltage(uint16_t value); + void addGPS(gpsStatus_t value); + void addBME(bmeStatus_t value); + void addButton(uint8_t value); + void addSensor(uint8_t[]); + void addTime(time_t value); + void addPM10(float value); + void addPM25(float value); void addChars( char* string, int len); #if (PAYLOAD_ENCODER == 1) // format plain From acf42ef27b31ea716160df3b5930221597c876d4 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Mon, 3 Feb 2020 15:24:49 +0100 Subject: [PATCH 014/106] Update sds011read.h --- include/sds011read.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/sds011read.h b/include/sds011read.h index 734395a7..d278a5fd 100644 --- a/include/sds011read.h +++ b/include/sds011read.h @@ -11,5 +11,7 @@ bool sds011_init(); void sds011_loop(); +void sds011_sleep(void); +void sds011_wakeup(void); #endif // _SDS011READ_H From 8fe69a9130ca6fa3e28e0f9b6bc803a7bd00ae21 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Mon, 3 Feb 2020 15:26:57 +0100 Subject: [PATCH 016/106] Update sds011read.cpp --- src/sds011read.cpp | 42 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/src/sds011read.cpp b/src/sds011read.cpp index c55b5741..cf6dccdf 100644 --- a/src/sds011read.cpp +++ b/src/sds011read.cpp @@ -6,12 +6,16 @@ static const char TAG[] = __FILE__; #include // UART(2) is unused in this project +#if (HAS_IF482) +#error cannot use IF482 together with SDS011 (both use UART#2) +#endif static HardwareSerial sdsSerial(2); // so we use it here static SDS011 sdsSensor; // fine dust sensor // the results of the sensor: float pm25; float pm10; +boolean isSDS011Active; // init bool sds011_init() @@ -19,18 +23,40 @@ bool sds011_init() pm25 = pm10 = 0.0; sdsSerial.begin(9600, SERIAL_8N1, ESP_PIN_RX, ESP_PIN_TX); sdsSensor.begin (&sdsSerial); - sdsSensor.contmode(0); // for safety: wakeup/sleep - if we want it we do it by ourselves - sdsSensor.wakeup(); // always wake up + sdsSensor.contmode(0); // for safety: no wakeup/sleep by the sensor + sds011_sleep(); // we do it by ourselves return true; } // reading data: void sds011_loop() { - pm25 = pm10 = 0.0; - int sdsErrorCode = sdsSensor.read(&pm25, &pm10); - if (!sdsErrorCode) - { - ESP_LOGD(TAG, "SDS011 error: %d", sdsErrorCode); - } + if ( isSDS011Active ) { + int sdsErrorCode = sdsSensor.read(&pm25, &pm10); + if (sdsErrorCode) { + pm25 = pm10 = 0.0; + ESP_LOGI(TAG, "SDS011 error: %d", sdsErrorCode); + } + else { + ESP_LOGI(TAG, "fine-dust-values: %5.1f,%4.1f", pm10, pm25); + } + sds011_sleep(); + } return; } + +// putting the SDS-sensor to sleep +void sds011_sleep(void) +{ + sdsSensor.sleep(); + isSDS011Active = false; +} + +// start the SDS-sensor +// needs 30 seconds for warming up +void sds011_wakeup() +{ + if ( !isSDS011Active ) { + sdsSensor.wakeup(); + isSDS011Active = true; + } +} From 14baacc4bf1bc3b39a7cafb82373366e9e67b359 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Mon, 3 Feb 2020 15:27:49 +0100 Subject: [PATCH 017/106] Update sdcard.cpp --- src/sdcard.cpp | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/src/sdcard.cpp b/src/sdcard.cpp index 8e45ea7c..1c1256a4 100644 --- a/src/sdcard.cpp +++ b/src/sdcard.cpp @@ -19,7 +19,7 @@ static void createFile(void); File fileSDCard; -bool sdcardInit() { +bool sdcard_init() { ESP_LOGD(TAG, "looking for SD-card..."); useSDCard = SD.begin(SDCARD_CS, SDCARD_MOSI, SDCARD_MISO, SDCARD_SCLK); if (useSDCard) @@ -43,7 +43,6 @@ void sdcardWriteData(uint16_t noWifi, uint16_t noBle) { sprintf(tempBuffer, "%d,%d", noWifi, noBle); fileSDCard.print( tempBuffer); #if (HAS_SDS011) - ESP_LOGD(TAG, "fine-dust-values: %5.1f,%4.1f", pm10, pm25); sprintf(tempBuffer, ",%5.1f,%4.1f", pm10, pm25); fileSDCard.print( tempBuffer); #endif @@ -84,4 +83,41 @@ void createFile(void) { return; } +#endif // (HAS_SDCARD) + + if (++counterWrites > 2) { + // force writing to SD-card + ESP_LOGD(TAG, "flushing data to card"); + fileSDCard.flush(); + counterWrites = 0; + } +} + +void createFile(void) { + char bufferFilename[8 + 1 + 3 + 1]; + + useSDCard = false; + + for (int i = 0; i < 100; i++) { + sprintf(bufferFilename, SDCARD_FILE_NAME, i); + ESP_LOGD(TAG, "SD: looking for file <%s>", bufferFilename); + bool fileExists = SD.exists(bufferFilename); + if (!fileExists) { + ESP_LOGD(TAG, "SD: file does not exist: opening"); + fileSDCard = SD.open(bufferFilename, FILE_WRITE); + if (fileSDCard) { + ESP_LOGD(TAG, "SD: name opened: <%s>", bufferFilename); + fileSDCard.print( SDCARD_FILE_HEADER ); +#if (HAS_SDS011) + fileSDCard.print( SDCARD_FILE_HEADER_SDS011 ); +#endif + fileSDCard.println(); + useSDCard = true; + break; + } + } + } + return; +} + #endif // (HAS_SDCARD) From 9e1cb5fbc0d70fbdc78da322c25564eee4dcbdcd Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Mon, 3 Feb 2020 15:28:45 +0100 Subject: [PATCH 018/106] Update timekeeper.cpp --- src/timekeeper.cpp | 273 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 273 insertions(+) diff --git a/src/timekeeper.cpp b/src/timekeeper.cpp index 85f62864..c6540286 100644 --- a/src/timekeeper.cpp +++ b/src/timekeeper.cpp @@ -16,6 +16,9 @@ static const char TAG[] = __FILE__; const char timeSetSymbols[] = {'G', 'R', 'L', '?'}; #ifdef HAS_IF482 +#if (HAS_SDS011) +#error cannot use IF482 together with SDS011 (both use UART#2) +#endif HardwareSerial IF482(2); // use UART #2 (#1 may be in use for serial GPS) #endif @@ -317,4 +320,274 @@ void clock_loop(void *taskparameter) { // ClockTask } // for } // clock_loop() +#endif // HAS_IF482 || defined HAS_DCF77 + if (t) { + timeSource = _rtc; + goto finish; + } +#endif + + goto finish; + +finish: + + setMyTime((uint32_t)t, t_msec, timeSource); // set time + +} // calibrateTime() + +// adjust system time, calibrate RTC and RTC_INT pps +void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec, + timesource_t mytimesource) { + + // called with invalid timesource? + if (mytimesource == _unsynced) + return; + + // increment t_sec only if t_msec > 1000 + time_t time_to_set = (time_t)(t_sec + t_msec / 1000); + + // do we have a valid time? + if (timeIsValid(time_to_set)) { + + // if we have msec fraction, then wait until top of second with + // millisecond precision + if (t_msec % 1000) { + time_to_set++; + vTaskDelay(pdMS_TO_TICKS(1000 - t_msec % 1000)); + } + + ESP_LOGD(TAG, "[%0.3f] UTC epoch time: %d.%03d sec", millis() / 1000.0, + time_to_set, t_msec % 1000); + +// if we have got an external timesource, set RTC time and shift RTC_INT pulse +// to top of second +#ifdef HAS_RTC + if ((mytimesource == _gps) || (mytimesource == _lora)) + set_rtctime(time_to_set); +#endif + +// if we have a software pps timer, shift it to top of second +#if (!defined GPS_INT && !defined RTC_INT) + timerWrite(ppsIRQ, 0); // reset pps timer + CLOCKIRQ(); // fire clock pps, this advances time 1 sec +#endif + + setTime(time_to_set); // set the time on top of second + + timeSource = mytimesource; // set global variable + timesyncer.attach(TIME_SYNC_INTERVAL * 60, timeSync); + ESP_LOGI(TAG, "[%0.3f] Timesync finished, time was set | source: %c", + millis() / 1000.0, timeSetSymbols[timeSource]); + } else { + timesyncer.attach(TIME_SYNC_INTERVAL_RETRY * 60, timeSync); + ESP_LOGI(TAG, "[%0.3f] Timesync failed, invalid time fetched | source: %c", + millis() / 1000.0, timeSetSymbols[timeSource]); + } +} + +// helper function to setup a pulse per second for time synchronisation +uint8_t timepulse_init() { + +// use time pulse from GPS as time base with fixed 1Hz frequency +#ifdef GPS_INT + + // setup external interupt pin for rising edge GPS INT + pinMode(GPS_INT, INPUT_PULLDOWN); + // setup external rtc 1Hz clock as pulse per second clock + ESP_LOGI(TAG, "Timepulse: external (GPS)"); + return 1; // success + +// use pulse from on board RTC chip as time base with fixed frequency +#elif defined RTC_INT + + // setup external interupt pin for falling edge RTC INT + pinMode(RTC_INT, INPUT_PULLUP); + + // setup external rtc 1Hz clock as pulse per second clock + if (I2C_MUTEX_LOCK()) { + Rtc.SetSquareWavePinClockFrequency(DS3231SquareWaveClock_1Hz); + Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeClock); + I2C_MUTEX_UNLOCK(); + ESP_LOGI(TAG, "Timepulse: external (RTC)"); + return 1; // success + } else { + ESP_LOGE(TAG, "RTC initialization error, I2C bus busy"); + return 0; // failure + } + return 1; // success + +#else + // use ESP32 hardware timer as time base with adjustable frequency + ppsIRQ = timerBegin(1, 8000, true); // set 80 MHz prescaler to 1/10000 sec + timerAlarmWrite(ppsIRQ, 10000, true); // 1000ms + ESP_LOGI(TAG, "Timepulse: internal (ESP32 hardware timer)"); + return 1; // success + +#endif +} // timepulse_init + +void timepulse_start(void) { + +#ifdef GPS_INT // start external clock gps pps line + attachInterrupt(digitalPinToInterrupt(GPS_INT), CLOCKIRQ, RISING); +#elif defined RTC_INT // start external clock rtc + attachInterrupt(digitalPinToInterrupt(RTC_INT), CLOCKIRQ, FALLING); +#else // start internal clock esp32 hardware timer + timerAttachInterrupt(ppsIRQ, &CLOCKIRQ, true); + timerAlarmEnable(ppsIRQ); +#endif + + // start cyclic time sync + timeSync(); // init systime by RTC or GPS or LORA + timesyncer.attach(TIME_SYNC_INTERVAL * 60, timeSync); +} + +// interrupt service routine triggered by either pps or esp32 hardware timer +void IRAM_ATTR CLOCKIRQ(void) { + + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + SyncToPPS(); // advance systime, see microTime.h + +// advance wall clock, if we have +#if (defined HAS_IF482 || defined HAS_DCF77) + xTaskNotifyFromISR(ClockTask, uint32_t(now()), eSetBits, + &xHigherPriorityTaskWoken); +#endif + +// flip time pulse ticker, if needed +#ifdef HAS_DISPLAY +#if (defined GPS_INT || defined RTC_INT) + TimePulseTick = !TimePulseTick; // flip pulse ticker +#endif +#endif + + // yield only if we should + if (xHigherPriorityTaskWoken) + portYIELD_FROM_ISR(); +} + +// helper function to check plausibility of a time +time_t timeIsValid(time_t const t) { + // is it a time in the past? we use compile date to guess + return (t >= compiledUTC() ? t : 0); +} + +// helper function to convert compile time to UTC time +time_t compiledUTC(void) { + static time_t t = myTZ.toUTC(RtcDateTime(__DATE__, __TIME__).Epoch32Time()); + return t; +} + +// helper function to calculate serial transmit time +TickType_t tx_Ticks(uint32_t framesize, unsigned long baud, uint32_t config, + int8_t rxPin, int8_t txPins) { + + uint32_t databits = ((config & 0x0c) >> 2) + 5; + uint32_t stopbits = ((config & 0x20) >> 5) + 1; + uint32_t txTime = (databits + stopbits + 1) * framesize * 1000.0 / baud; + // +1 for the startbit + + return round(txTime); +} + +#if (defined HAS_IF482 || defined HAS_DCF77) + +#if (defined HAS_DCF77 && defined HAS_IF482) +#error You must define at most one of IF482 or DCF77! +#endif + +void clock_init(void) { + +// setup clock output interface +#ifdef HAS_IF482 + IF482.begin(HAS_IF482); +#elif defined HAS_DCF77 + pinMode(HAS_DCF77, OUTPUT); +#endif + + userUTCTime = now(); + + xTaskCreatePinnedToCore(clock_loop, // task function + "clockloop", // name of task + 2048, // stack size of task + (void *)&userUTCTime, // start time as task parameter + 4, // priority of the task + &ClockTask, // task handle + 1); // CPU core + + assert(ClockTask); // has clock task started? +} // clock_init + +void clock_loop(void *taskparameter) { // ClockTask + + // caveat: don't use now() in this task, it will cause a race condition + // due to concurrent access to i2c bus when reading/writing from/to rtc chip! + +#define nextmin(t) (t + DCF77_FRAME_SIZE + 1) // next minute + +#ifdef HAS_TWO_LED + static bool led1_state = false; +#endif + uint32_t printtime; + time_t t = *((time_t *)taskparameter), last_printtime = 0; // UTC time seconds + +#ifdef HAS_DCF77 + uint8_t *DCFpulse; // pointer on array with DCF pulse bits + DCFpulse = DCF77_Frame(nextmin(t)); // load first DCF frame before start +#elif defined HAS_IF482 + static TickType_t txDelay = pdMS_TO_TICKS(1000 - IF482_SYNC_FIXUP) - + tx_Ticks(IF482_FRAME_SIZE, HAS_IF482); +#endif + + // output the next second's pulse/telegram after pps arrived + for (;;) { + + // wait for timepulse and store UTC time in seconds got + xTaskNotifyWait(0x00, ULONG_MAX, &printtime, portMAX_DELAY); + t = time_t(printtime); + + // no confident or no recent time -> suppress clock output + if ((timeStatus() == timeNotSet) || !(timeIsValid(t)) || + (t == last_printtime)) + continue; + +#if defined HAS_IF482 + + // wait until moment to fire. Normally we won't get notified during this + // timespan, except when next pps pulse arrives while waiting, because pps + // was adjusted by recent time sync + if (xTaskNotifyWait(0x00, ULONG_MAX, &printtime, txDelay) == pdTRUE) + t = time_t(printtime); // new adjusted UTC time seconds + + // send IF482 telegram + IF482.print(IF482_Frame(t + 1)); // note: telegram is for *next* second + +#elif defined HAS_DCF77 + + if (second(t) == DCF77_FRAME_SIZE - 1) // is it time to load new frame? + DCFpulse = DCF77_Frame(nextmin(t)); // generate frame for next minute + + if (minute(nextmin(t)) == // do we still have a recent frame? + DCFpulse[DCF77_FRAME_SIZE]) // (timepulses could be missed!) + DCF77_Pulse(t, DCFpulse); // then output current second's pulse + + // else we have no recent frame, thus suppressing clock output + +#endif + +// pps blink on secondary LED if we have one +#ifdef HAS_TWO_LED + if (led1_state) + switch_LED1(LED_OFF); + else + switch_LED1(LED_ON); + led1_state = !led1_state; +#endif + + last_printtime = t; + + } // for +} // clock_loop() + #endif // HAS_IF482 || defined HAS_DCF77 From 766ef6c8885f5cfb895bd330cbcf7f9747fd6946 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Mon, 3 Feb 2020 15:29:32 +0100 Subject: [PATCH 019/106] Update cyclic.cpp --- src/cyclic.cpp | 116 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 114 insertions(+), 2 deletions(-) diff --git a/src/cyclic.cpp b/src/cyclic.cpp index 89f8e3e2..6a13a217 100644 --- a/src/cyclic.cpp +++ b/src/cyclic.cpp @@ -9,8 +9,14 @@ static const char TAG[] = __FILE__; Ticker housekeeper; +#if (HAS_SDS011) +extern boolean isSDS011Active; +#endif + void housekeeping() { + static int counter = 0; xTaskNotifyFromISR(irqHandlerTask, CYCLIC_IRQ, eSetBits, NULL); +ESP_LOGI( TAG, "in Housekeeping(): %d", counter++); } // do all housekeeping @@ -18,7 +24,6 @@ void doHousekeeping() { // update uptime counter uptime(); - // check if update mode trigger switch was set if (RTC_runmode == RUNMODE_UPDATE) { // check battery status if we can before doing ota @@ -112,6 +117,113 @@ void doHousekeeping() { } #endif +#if (HAS_SDS011) + if ( isSDS011Active ) { + ESP_LOGD(TAG, "SDS011: go to sleep"); + sds011_loop(); + } + else { + ESP_LOGD(TAG, "SDS011: wakeup"); + sds011_wakeup(); + } +#endif + +} // doHousekeeping() + +// uptime counter 64bit to prevent millis() rollover after 49 days +uint64_t uptime() { + static uint32_t low32, high32; + uint32_t new_low32 = millis(); + if (new_low32 < low32) + high32++; + low32 = new_low32; + return (uint64_t)high32 << 32 | low32; +} + +uint32_t getFreeRAM() { +#ifndef BOARD_HAS_PSRAM + return ESP.getFreeHeap(); +#else + return ESP.getFreePsram(); +#endif +} + +void reset_counters() { +#if ((WIFICOUNTER) || (BLECOUNTER)) + macs.clear(); // clear all macs container + macs_total = 0; // reset all counters + macs_wifi = 0; + macs_ble = 0; +#ifdef HAS_DISPLAY + oledPlotCurve(0, true); +#endif + +#endif +} +#endif + +#if (defined HAS_DCF77 || defined HAS_IF482) + ESP_LOGD(TAG, "Clockloop %d bytes left | Taskstate = %d", + uxTaskGetStackHighWaterMark(ClockTask), eTaskGetState(ClockTask)); +#endif + +#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED) + ESP_LOGD(TAG, "LEDloop %d bytes left | Taskstate = %d", + uxTaskGetStackHighWaterMark(ledLoopTask), + eTaskGetState(ledLoopTask)); +#endif + +// read battery voltage into global variable +#if (defined BAT_MEASURE_ADC || defined HAS_PMU) + batt_voltage = read_voltage(); + if (batt_voltage == 0xffff) + ESP_LOGI(TAG, "Battery: external power"); + else + ESP_LOGI(TAG, "Battery: %dmV", batt_voltage); +#ifdef HAS_PMU + AXP192_showstatus(); +#endif +#endif + +// display BME680/280 sensor data +#if (HAS_BME) +#ifdef HAS_BME680 + ESP_LOGI(TAG, "BME680 Temp: %.2f°C | IAQ: %.2f | IAQacc: %d", + bme_status.temperature, bme_status.iaq, bme_status.iaq_accuracy); +#elif defined HAS_BME280 + ESP_LOGI(TAG, "BME280 Temp: %.2f°C | Humidity: %.2f | Pressure: %.0f", + bme_status.temperature, bme_status.humidity, bme_status.pressure); +#elif defined HAS_BMP180 + ESP_LOGI(TAG, "BMP180 Temp: %.2f°C | Pressure: %.0f", bme_status.temperature, + bme_status.pressure); +#endif +#endif + + // check free heap memory + if (ESP.getMinFreeHeap() <= MEM_LOW) { + ESP_LOGI(TAG, + "Memory full, counter cleared (heap low water mark = %d Bytes / " + "free heap = %d bytes)", + ESP.getMinFreeHeap(), ESP.getFreeHeap()); + reset_counters(); // clear macs container and reset all counters + get_salt(); // get new salt for salting hashes + + if (ESP.getMinFreeHeap() <= MEM_LOW) // check again + do_reset(true); // memory leak, reset device + } + +// check free PSRAM memory +#ifdef BOARD_HAS_PSRAM + if (ESP.getMinFreePsram() <= MEM_LOW) { + ESP_LOGI(TAG, "PSRAM full, counter cleared"); + reset_counters(); // clear macs container and reset all counters + get_salt(); // get new salt for salting hashes + + if (ESP.getMinFreePsram() <= MEM_LOW) // check again + do_reset(true); // memory leak, reset device + } +#endif + } // doHousekeeping() // uptime counter 64bit to prevent millis() rollover after 49 days @@ -143,4 +255,4 @@ void reset_counters() { #endif #endif -} \ No newline at end of file +} From f35014cab84003eeaee66491b5424e0eff3b3b8f Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Mon, 3 Feb 2020 15:30:15 +0100 Subject: [PATCH 020/106] Update senddata.cpp --- src/senddata.cpp | 147 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 144 insertions(+), 3 deletions(-) diff --git a/src/senddata.cpp b/src/senddata.cpp index 367a5c68..f89710ca 100644 --- a/src/senddata.cpp +++ b/src/senddata.cpp @@ -9,7 +9,10 @@ extern float pm25; #endif void sendcycle() { + static int counter = 0; xTaskNotifyFromISR(irqHandlerTask, SENDCYCLE_IRQ, eSetBits, NULL); +ESP_LOGI( TAG, "in sendcycle(): %d", counter++); + } // put data to send in RTos Queues used for transmit over channels Lora and SPI @@ -18,9 +21,9 @@ void SendPayload(uint8_t port, sendprio_t prio) { MessageBuffer_t SendBuffer; // contains MessageSize, MessagePort, MessagePrio, Message[] -#if (HAS_SDS011) - sds011_loop(); -#endif +//#if (HAS_SDS011) +// sds011_loop(); +//#endif SendBuffer.MessageSize = payload.getSize(); SendBuffer.MessagePrio = prio; @@ -178,6 +181,144 @@ void sendData() { } // sendData() +void flushQueues() { +#if (HAS_LORA) + lora_queuereset(); +#endif +#ifdef HAS_SPI + spi_queuereset(); +#endif +} + SendBuffer.MessagePort = port; + } + memcpy(SendBuffer.Message, payload.getBuffer(), SendBuffer.MessageSize); + +// enqueue message in device's send queues +#if (HAS_LORA) + lora_enqueuedata(&SendBuffer); +#endif +#ifdef HAS_SPI + spi_enqueuedata(&SendBuffer); +#endif + +// write data to sdcard, if present +#ifdef HAS_SDCARD + sdcardWriteData(macs_wifi, macs_ble); +#endif + +} // SendPayload + +// interrupt triggered function to prepare payload to send +void sendData() { + + uint8_t bitmask = cfg.payloadmask; + uint8_t mask = 1; + #if (HAS_GPS) + gpsStatus_t gps_status; + #endif + + while (bitmask) { + switch (bitmask & mask) { + +#if ((WIFICOUNTER) || (BLECOUNTER)) + case COUNT_DATA: + payload.reset(); +#if !(PAYLOAD_OPENSENSEBOX) + if (cfg.wifiscan) + payload.addCount(macs_wifi, MAC_SNIFF_WIFI); + if (cfg.blescan) + payload.addCount(macs_ble, MAC_SNIFF_BLE); +#endif +#if (HAS_GPS) + if (GPSPORT == COUNTERPORT) { + // send GPS position only if we have a fix + if (gps_hasfix()) { + gps_storelocation(&gps_status); + payload.addGPS(gps_status); + } else + ESP_LOGD(TAG, "No valid GPS position"); + } +#endif +#if (PAYLOAD_OPENSENSEBOX) + if (cfg.wifiscan) + payload.addCount(macs_wifi, MAC_SNIFF_WIFI); + if (cfg.blescan) + payload.addCount(macs_ble, MAC_SNIFF_BLE); +#endif +#if (HAS_SDS011) + payload.addPM10(pm10); + payload.addPM25(pm25); +#endif + SendPayload(COUNTERPORT, prio_normal); + // clear counter if not in cumulative counter mode + if (cfg.countermode != 1) { + reset_counters(); // clear macs container and reset all counters + get_salt(); // get new salt for salting hashes + ESP_LOGI(TAG, "Counter cleared"); + } +#ifdef HAS_DISPLAY + else + oledPlotCurve(macs.size(), true); +#endif + break; +#endif + +#if (HAS_BME) + case MEMS_DATA: + payload.reset(); + payload.addBME(bme_status); + SendPayload(BMEPORT, prio_normal); + break; +#endif + +#if (HAS_GPS) + case GPS_DATA: + if (GPSPORT != COUNTERPORT) { + // send GPS position only if we have a fix + if (gps_hasfix()) { + gps_storelocation(&gps_status); + payload.reset(); + payload.addGPS(gps_status); + SendPayload(GPSPORT, prio_high); + } else + ESP_LOGD(TAG, "No valid GPS position"); + } + break; +#endif + +#if (HAS_SENSORS) + case SENSOR1_DATA: + payload.reset(); + payload.addSensor(sensor_read(1)); + SendPayload(SENSOR1PORT, prio_normal); + break; + case SENSOR2_DATA: + payload.reset(); + payload.addSensor(sensor_read(2)); + SendPayload(SENSOR2PORT, prio_normal); + break; + case SENSOR3_DATA: + payload.reset(); + payload.addSensor(sensor_read(3)); + SendPayload(SENSOR3PORT, prio_normal); + break; +#endif + +#if (defined BAT_MEASURE_ADC || defined HAS_PMU) + case BATT_DATA: + payload.reset(); + payload.addVoltage(read_voltage()); + SendPayload(BATTPORT, prio_normal); + break; +#endif + + } // switch + bitmask &= ~mask; + mask <<= 1; + } // while (bitmask) + +} // sendData() + void flushQueues() { #if (HAS_LORA) lora_queuereset(); From 3621cfcc52db19f8965999aa2ff866b201e17f7b Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Mon, 3 Feb 2020 15:31:06 +0100 Subject: [PATCH 021/106] Update payload.cpp --- src/payload.cpp | 468 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 468 insertions(+) diff --git a/src/payload.cpp b/src/payload.cpp index 2624c138..05924897 100644 --- a/src/payload.cpp +++ b/src/payload.cpp @@ -491,6 +491,474 @@ void PayloadConvert::addTime(time_t value) { } #endif // PAYLOAD_ENCODER +void PayloadConvert::addPM10( float value) { +#if (HAS_SDS011) +#if (PAYLOAD_ENCODER == 1) // plain + char tempBuffer[10+1]; + sprintf( tempBuffer, ",%5.1f", value); + addChars(tempBuffer, strlen(tempBuffer)); +#elif (PAYLOAD_ENCODER == 2 ) // packed + writeUint16( (uint16_t) (value*10) ); +#elif (PAYLOAD_ENCODER == 3 ) // Cayenne LPP dynamic +#error not implemented yet +#elif (PAYLOAD_ENCODER == 4 ) // Cayenne LPP packed +#error not implemented yet +#endif +#endif // HAS_SDS011 +} + +void PayloadConvert::addPM25( float value) { +#if (HAS_SDS011) +#if (PAYLOAD_ENCODER == 1) // plain + char tempBuffer[10+1]; + sprintf( tempBuffer, ",%5.1f", value); + addChars(tempBuffer, strlen(tempBuffer)); +#elif (PAYLOAD_ENCODER == 2 ) // packed + writeUint16( (uint16_t) (value*10) ); +#elif (PAYLOAD_ENCODER == 3 ) // Cayenne LPP dynamic +#error not implemented yet +#elif (PAYLOAD_ENCODER == 4 ) // Cayenne LPP packed +#error not implemented yet +#endif +#endif // HAS_SDS011 +} + +void PayloadConvert::addChars( char * string, int len) { + for (int i=0; i < len; i++) + addByte(string[i]); +} + + buffer[cursor++] = highByte(voltage); + buffer[cursor++] = lowByte(voltage); + buffer[cursor++] = (byte)((uptime & 0xFF00000000000000) >> 56); + buffer[cursor++] = (byte)((uptime & 0x00FF000000000000) >> 48); + buffer[cursor++] = (byte)((uptime & 0x0000FF0000000000) >> 40); + buffer[cursor++] = (byte)((uptime & 0x000000FF00000000) >> 32); + buffer[cursor++] = (byte)((uptime & 0x00000000FF000000) >> 24); + buffer[cursor++] = (byte)((uptime & 0x0000000000FF0000) >> 16); + buffer[cursor++] = (byte)((uptime & 0x000000000000FF00) >> 8); + buffer[cursor++] = (byte)((uptime & 0x00000000000000FF)); + buffer[cursor++] = (byte)(cputemp); + buffer[cursor++] = (byte)((mem & 0xFF000000) >> 24); + buffer[cursor++] = (byte)((mem & 0x00FF0000) >> 16); + buffer[cursor++] = (byte)((mem & 0x0000FF00) >> 8); + buffer[cursor++] = (byte)((mem & 0x000000FF)); + buffer[cursor++] = (byte)(reset1); + buffer[cursor++] = (byte)(reset2); +} + +void PayloadConvert::addGPS(gpsStatus_t value) { +#if(HAS_GPS) + buffer[cursor++] = (byte)((value.latitude & 0xFF000000) >> 24); + buffer[cursor++] = (byte)((value.latitude & 0x00FF0000) >> 16); + buffer[cursor++] = (byte)((value.latitude & 0x0000FF00) >> 8); + buffer[cursor++] = (byte)((value.latitude & 0x000000FF)); + buffer[cursor++] = (byte)((value.longitude & 0xFF000000) >> 24); + buffer[cursor++] = (byte)((value.longitude & 0x00FF0000) >> 16); + buffer[cursor++] = (byte)((value.longitude & 0x0000FF00) >> 8); + buffer[cursor++] = (byte)((value.longitude & 0x000000FF)); +#if (!PAYLOAD_OPENSENSEBOX) + buffer[cursor++] = value.satellites; + buffer[cursor++] = highByte(value.hdop); + buffer[cursor++] = lowByte(value.hdop); + buffer[cursor++] = highByte(value.altitude); + buffer[cursor++] = lowByte(value.altitude); +#endif +#endif +} + +void PayloadConvert::addSensor(uint8_t buf[]) { +#if(HAS_SENSORS) + uint8_t length = buf[0]; + memcpy(buffer, buf + 1, length); + cursor += length; // length of buffer +#endif +} + +void PayloadConvert::addBME(bmeStatus_t value) { +#if(HAS_BME) + int16_t temperature = (int16_t)(value.temperature); // float -> int + uint16_t humidity = (uint16_t)(value.humidity); // float -> int + uint16_t pressure = (uint16_t)(value.pressure); // float -> int + uint16_t iaq = (uint16_t)(value.iaq); // float -> int + buffer[cursor++] = highByte(temperature); + buffer[cursor++] = lowByte(temperature); + buffer[cursor++] = highByte(pressure); + buffer[cursor++] = lowByte(pressure); + buffer[cursor++] = highByte(humidity); + buffer[cursor++] = lowByte(humidity); + buffer[cursor++] = highByte(iaq); + buffer[cursor++] = lowByte(iaq); +#endif +} + +void PayloadConvert::addButton(uint8_t value) { +#ifdef HAS_BUTTON + buffer[cursor++] = value; +#endif +} + +void PayloadConvert::addTime(time_t value) { + uint32_t time = (uint32_t)value; + buffer[cursor++] = (byte)((time & 0xFF000000) >> 24); + buffer[cursor++] = (byte)((time & 0x00FF0000) >> 16); + buffer[cursor++] = (byte)((time & 0x0000FF00) >> 8); + buffer[cursor++] = (byte)((time & 0x000000FF)); +} + +/* ---------------- packed format with LoRa serialization Encoder ---------- + */ +// derived from +// https://github.com/thesolarnomad/lora-serialization/blob/master/src/LoraEncoder.cpp + +#elif (PAYLOAD_ENCODER == 2) + +void PayloadConvert::addByte(uint8_t value) { writeUint8(value); } + +void PayloadConvert::addCount(uint16_t value, uint8_t snifftype) { + writeUint16(value); +} + +void PayloadConvert::addAlarm(int8_t rssi, uint8_t msg) { + writeUint8(rssi); + writeUint8(msg); +} + +void PayloadConvert::addVoltage(uint16_t value) { writeUint16(value); } + +void PayloadConvert::addConfig(configData_t value) { + writeUint8(value.loradr); + writeUint8(value.txpower); + writeUint16(value.rssilimit); + writeUint8(value.sendcycle); + writeUint8(value.wifichancycle); + writeUint8(value.blescantime); + writeUint8(value.rgblum); + writeBitmap(value.adrmode ? true : false, value.screensaver ? true : false, + value.screenon ? true : false, value.countermode ? true : false, + value.blescan ? true : false, value.wifiant ? true : false, + value.vendorfilter ? true : false, + value.monitormode ? true : false); + writeBitmap(value.payloadmask && GPS_DATA ? true : false, + value.payloadmask && ALARM_DATA ? true : false, + value.payloadmask && MEMS_DATA ? true : false, + value.payloadmask && COUNT_DATA ? true : false, + value.payloadmask && SENSOR1_DATA ? true : false, + value.payloadmask && SENSOR2_DATA ? true : false, + value.payloadmask && SENSOR3_DATA ? true : false, + value.payloadmask && BATT_DATA ? true : false); + writeVersion(value.version); +} + +void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime, float cputemp, + uint32_t mem, uint8_t reset1, uint8_t reset2) { + writeUint16(voltage); + writeUptime(uptime); + writeUint8((byte)cputemp); + writeUint32(mem); + writeUint8(reset1); + writeUint8(reset2); +} + +void PayloadConvert::addGPS(gpsStatus_t value) { +#if(HAS_GPS) + writeLatLng(value.latitude, value.longitude); +#if (!PAYLOAD_OPENSENSEBOX) + writeUint8(value.satellites); + writeUint16(value.hdop); + writeUint16(value.altitude); +#endif +#endif +} + +void PayloadConvert::addSensor(uint8_t buf[]) { +#if(HAS_SENSORS) + uint8_t length = buf[0]; + memcpy(buffer, buf + 1, length); + cursor += length; // length of buffer +#endif +} + +void PayloadConvert::addBME(bmeStatus_t value) { +#if(HAS_BME) + writeFloat(value.temperature); + writePressure(value.pressure); + writeUFloat(value.humidity); + writeUFloat(value.iaq); +#endif +} + +void PayloadConvert::addButton(uint8_t value) { +#ifdef HAS_BUTTON + writeUint8(value); +#endif +} + +void PayloadConvert::addTime(time_t value) { + uint32_t time = (uint32_t)value; + writeUint32(time); +} + +void PayloadConvert::uintToBytes(uint64_t value, uint8_t byteSize) { + for (uint8_t x = 0; x < byteSize; x++) { + byte next = 0; + if (sizeof(value) > x) { + next = static_cast((value >> (x * 8)) & 0xFF); + } + buffer[cursor] = next; + ++cursor; + } +} + +void PayloadConvert::writeUptime(uint64_t uptime) { + writeUint64(uptime); +} + +void PayloadConvert::writeVersion(char *version) { + memcpy(buffer + cursor, version, 10); + cursor += 10; +} + +void PayloadConvert::writeLatLng(double latitude, double longitude) { + // Tested to at least work with int32_t, which are processed correctly. + writeUint32(latitude); + writeUint32(longitude); +} + +void PayloadConvert::writeUint64(uint64_t i) { uintToBytes(i, 8); } + +void PayloadConvert::writeUint32(uint32_t i) { uintToBytes(i, 4); } + +void PayloadConvert::writeUint16(uint16_t i) { uintToBytes(i, 2); } + +void PayloadConvert::writeUint8(uint8_t i) { uintToBytes(i, 1); } + +void PayloadConvert::writeUFloat(float value) { + writeUint16(value * 100); +} + +void PayloadConvert::writePressure(float value) { + writeUint16(value * 10); +} + +/** + * Uses a 16bit two's complement with two decimals, so the range is + * -327.68 to +327.67 degrees + */ +void PayloadConvert::writeFloat(float value) { + int16_t t = (int16_t)(value * 100); + if (value < 0) { + t = ~-t; + t = t + 1; + } + buffer[cursor++] = (byte)((t >> 8) & 0xFF); + buffer[cursor++] = (byte)t & 0xFF; +} + +void PayloadConvert::writeBitmap(bool a, bool b, bool c, bool d, bool e, bool f, + bool g, bool h) { + uint8_t bitmap = 0; + // LSB first + bitmap |= (a & 1) << 7; + bitmap |= (b & 1) << 6; + bitmap |= (c & 1) << 5; + bitmap |= (d & 1) << 4; + bitmap |= (e & 1) << 3; + bitmap |= (f & 1) << 2; + bitmap |= (g & 1) << 1; + bitmap |= (h & 1) << 0; + writeUint8(bitmap); +} + +/* ---------------- Cayenne LPP 2.0 format ---------- */ +// see specs +// http://community.mydevices.com/t/cayenne-lpp-2-0/7510 (LPP 2.0) +// https://github.com/myDevicesIoT/cayenne-docs/blob/master/docs/LORA.md +// (LPP 1.0) PAYLOAD_ENCODER == 3 -> Dynamic Sensor Payload, using channels -> +// FPort 1 PAYLOAD_ENCODER == 4 -> Packed Sensor Payload, not using channels -> +// FPort 2 + +#elif ((PAYLOAD_ENCODER == 3) || (PAYLOAD_ENCODER == 4)) + +void PayloadConvert::addByte(uint8_t value) { + /* + not implemented + */ } + +void PayloadConvert::addCount(uint16_t value, uint8_t snifftype) { + switch (snifftype) { + case MAC_SNIFF_WIFI: +#if (PAYLOAD_ENCODER == 3) + buffer[cursor++] = LPP_COUNT_WIFI_CHANNEL; +#endif + buffer[cursor++] = + LPP_LUMINOSITY; // workaround since cayenne has no data type meter + buffer[cursor++] = highByte(value); + buffer[cursor++] = lowByte(value); + break; + case MAC_SNIFF_BLE: +#if (PAYLOAD_ENCODER == 3) + buffer[cursor++] = LPP_COUNT_BLE_CHANNEL; +#endif + buffer[cursor++] = + LPP_LUMINOSITY; // workaround since cayenne has no data type meter + buffer[cursor++] = highByte(value); + buffer[cursor++] = lowByte(value); + break; + } +} + +void PayloadConvert::addAlarm(int8_t rssi, uint8_t msg) { +#if (PAYLOAD_ENCODER == 3) + buffer[cursor++] = LPP_ALARM_CHANNEL; +#endif + buffer[cursor++] = LPP_PRESENCE; + buffer[cursor++] = msg; +#if (PAYLOAD_ENCODER == 3) + buffer[cursor++] = LPP_MSG_CHANNEL; +#endif + buffer[cursor++] = LPP_ANALOG_INPUT; + buffer[cursor++] = rssi; +} + +void PayloadConvert::addVoltage(uint16_t value) { + uint16_t volt = value / 10; +#if (PAYLOAD_ENCODER == 3) + buffer[cursor++] = LPP_BATT_CHANNEL; +#endif + buffer[cursor++] = LPP_ANALOG_INPUT; + buffer[cursor++] = highByte(volt); + buffer[cursor++] = lowByte(volt); +} + +void PayloadConvert::addConfig(configData_t value) { +#if (PAYLOAD_ENCODER == 3) + buffer[cursor++] = LPP_ADR_CHANNEL; +#endif + buffer[cursor++] = LPP_DIGITAL_INPUT; + buffer[cursor++] = value.adrmode; +} + +void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime, float celsius, + uint32_t mem, uint8_t reset1, uint8_t reset2) { + uint16_t temp = celsius * 10; + uint16_t volt = voltage / 10; +#if (defined BAT_MEASURE_ADC || defined HAS_PMU) +#if (PAYLOAD_ENCODER == 3) + buffer[cursor++] = LPP_BATT_CHANNEL; +#endif + buffer[cursor++] = LPP_ANALOG_INPUT; + buffer[cursor++] = highByte(volt); + buffer[cursor++] = lowByte(volt); +#endif // BAT_MEASURE_ADC + +#if (PAYLOAD_ENCODER == 3) + buffer[cursor++] = LPP_TEMPERATURE_CHANNEL; +#endif + buffer[cursor++] = LPP_TEMPERATURE; + buffer[cursor++] = highByte(temp); + buffer[cursor++] = lowByte(temp); +} + +void PayloadConvert::addGPS(gpsStatus_t value) { +#if(HAS_GPS) + int32_t lat = value.latitude / 100; + int32_t lon = value.longitude / 100; + int32_t alt = value.altitude * 100; +#if (PAYLOAD_ENCODER == 3) + buffer[cursor++] = LPP_GPS_CHANNEL; +#endif + buffer[cursor++] = LPP_GPS; + buffer[cursor++] = (byte)((lat & 0xFF0000) >> 16); + buffer[cursor++] = (byte)((lat & 0x00FF00) >> 8); + buffer[cursor++] = (byte)((lat & 0x0000FF)); + buffer[cursor++] = (byte)((lon & 0xFF0000) >> 16); + buffer[cursor++] = (byte)((lon & 0x00FF00) >> 8); + buffer[cursor++] = (byte)(lon & 0x0000FF); + buffer[cursor++] = (byte)((alt & 0xFF0000) >> 16); + buffer[cursor++] = (byte)((alt & 0x00FF00) >> 8); + buffer[cursor++] = (byte)(alt & 0x0000FF); +#endif // HAS_GPS +} + +void PayloadConvert::addSensor(uint8_t buf[]) { +#if(HAS_SENSORS) +// to come +/* + uint8_t length = buf[0]; + memcpy(buffer, buf+1, length); + cursor += length; // length of buffer +*/ +#endif // HAS_SENSORS +} + +void PayloadConvert::addBME(bmeStatus_t value) { +#if(HAS_BME) + + // data value conversions to meet cayenne data type definition + // 0.1°C per bit => -3276,7 .. +3276,7 °C + int16_t temperature = (int16_t)(value.temperature * 10.0); + // 0.1 hPa per bit => 0 .. 6553,6 hPa + uint16_t pressure = (uint16_t)(value.pressure * 10); + // 0.5% per bit => 0 .. 128 %C + uint8_t humidity = (uint8_t)(value.humidity * 2.0); + int16_t iaq = (int16_t)(value.iaq); + +#if (PAYLOAD_ENCODER == 3) + buffer[cursor++] = LPP_TEMPERATURE_CHANNEL; +#endif + buffer[cursor++] = LPP_TEMPERATURE; // 2 bytes 0.1 °C Signed MSB + buffer[cursor++] = highByte(temperature); + buffer[cursor++] = lowByte(temperature); +#if (PAYLOAD_ENCODER == 3) + buffer[cursor++] = LPP_BAROMETER_CHANNEL; +#endif + buffer[cursor++] = LPP_BAROMETER; // 2 bytes 0.1 hPa Unsigned MSB + buffer[cursor++] = highByte(pressure); + buffer[cursor++] = lowByte(pressure); +#if (PAYLOAD_ENCODER == 3) + buffer[cursor++] = LPP_HUMIDITY_CHANNEL; +#endif + buffer[cursor++] = LPP_HUMIDITY; // 1 byte 0.5 % Unsigned + buffer[cursor++] = humidity; +#if (PAYLOAD_ENCODER == 3) + buffer[cursor++] = LPP_AIR_CHANNEL; +#endif + buffer[cursor++] = LPP_LUMINOSITY; // 2 bytes, 1.0 unsigned + buffer[cursor++] = highByte(iaq); + buffer[cursor++] = lowByte(iaq); +#endif // HAS_BME +} + +void PayloadConvert::addButton(uint8_t value) { +#ifdef HAS_BUTTON +#if (PAYLOAD_ENCODER == 3) + buffer[cursor++] = LPP_BUTTON_CHANNEL; +#endif + buffer[cursor++] = LPP_DIGITAL_INPUT; + buffer[cursor++] = value; +#endif // HAS_BUTTON +} + +void PayloadConvert::addTime(time_t value) { +#if (PAYLOAD_ENCODER == 4) + uint32_t t = (uint32_t)value; + uint32_t tx_period = (uint32_t)SENDCYCLE * 2; + buffer[cursor++] = 0x03; // set config mask to UTCTime + TXPeriod + // UTCTime in seconds + buffer[cursor++] = (byte)((t & 0xFF000000) >> 24); + buffer[cursor++] = (byte)((t & 0x00FF0000) >> 16); + buffer[cursor++] = (byte)((t & 0x0000FF00) >> 8); + buffer[cursor++] = (byte)((t & 0x000000FF)); + // TXPeriod in seconds + buffer[cursor++] = (byte)((tx_period & 0xFF000000) >> 24); + buffer[cursor++] = (byte)((tx_period & 0x00FF0000) >> 16); + buffer[cursor++] = (byte)((tx_period & 0x0000FF00) >> 8); + buffer[cursor++] = (byte)((tx_period & 0x000000FF)); +#endif +} +#endif // PAYLOAD_ENCODER + void PayloadConvert::addPM10( float value) { #if (HAS_SDS011) #if (PAYLOAD_ENCODER == 1) // plain From 1cfe16b33cfef273e8eb86cffcdf833e67bab267 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Mon, 3 Feb 2020 15:31:49 +0100 Subject: [PATCH 022/106] Update main.cpp --- src/main.cpp | 437 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 437 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index 19e45152..09aa66c4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -323,6 +323,443 @@ void setup() { assert(spi_init() == ESP_OK); #endif +#ifdef HAS_SDCARD + if (sdcard_init()) + strcat_P(features, " SD"); +#endif + +#if (HAS_SDS011) + ESP_LOGI(TAG, "init fine-dust-sensor"); + if ( sds011_init() ) + strcat_P(features, " SDS"); +#endif + +#if (VENDORFILTER) + strcat_P(features, " FILTER"); +#endif + +// initialize matrix display +#ifdef HAS_MATRIX_DISPLAY + strcat_P(features, " LED_MATRIX"); + MatrixDisplayIsOn = cfg.screenon; + init_matrix_display(); // note: blocking call +#endif + +// show payload encoder +#if PAYLOAD_ENCODER == 1 + strcat_P(features, " PLAIN"); +#elif PAYLOAD_ENCODER == 2 + strcat_P(features, " PACKED"); +#elif PAYLOAD_ENCODER == 3 + strcat_P(features, " LPPDYN"); +#elif PAYLOAD_ENCODER == 4 + strcat_P(features, " LPPPKD"); +#endif + +// initialize RTC +#ifdef HAS_RTC + strcat_P(features, " RTC"); + assert(rtc_init()); +#endif + +#if defined HAS_DCF77 + strcat_P(features, " DCF77"); +#endif + +#if defined HAS_IF482 + strcat_P(features, " IF482"); +#endif + +#if (WIFICOUNTER) + strcat_P(features, " WIFI"); + // start wifi in monitor mode and start channel rotation timer + ESP_LOGI(TAG, "Starting Wifi..."); + wifi_sniffer_init(); +#else + // switch off wifi + esp_wifi_deinit(); +#endif + + // initialize salt value using esp_random() called by random() in + // arduino-esp32 core. Note: do this *after* wifi has started, since + // function gets it's seed from RF noise + get_salt(); // get new 16bit for salting hashes + + // start state machine + ESP_LOGI(TAG, "Starting Interrupt Handler..."); + xTaskCreatePinnedToCore(irqHandler, // task function + "irqhandler", // name of task + 4096, // stack size of task + (void *)1, // parameter of the task + 2, // priority of the task + &irqHandlerTask, // task handle + 1); // CPU core + +// initialize BME sensor (BME280/BME680) +#if (HAS_BME) +#ifdef HAS_BME680 + strcat_P(features, " BME680"); +#elif defined HAS_BME280 + strcat_P(features, " BME280"); +#elif defined HAS_BMP180 + strcat_P(features, " BMP180"); +#endif + if (bme_init()) + ESP_LOGI(TAG, "Starting BME sensor..."); +#endif + + // starting timers and interrupts + assert(irqHandlerTask != NULL); // has interrupt handler task started? + ESP_LOGI(TAG, "Starting Timers..."); + +// display interrupt +#ifdef HAS_DISPLAY + // https://techtutorialsx.com/2017/10/07/esp32-arduino-timer-interrupts/ + // prescaler 80 -> divides 80 MHz CPU freq to 1 MHz, timer 0, count up + displayIRQ = timerBegin(0, 80, true); + timerAttachInterrupt(displayIRQ, &DisplayIRQ, true); + timerAlarmWrite(displayIRQ, DISPLAYREFRESH_MS * 1000, true); + timerAlarmEnable(displayIRQ); +#endif + +// LED Matrix display interrupt +#ifdef HAS_MATRIX_DISPLAY + // https://techtutorialsx.com/2017/10/07/esp32-arduino-timer-interrupts/ + // prescaler 80 -> divides 80 MHz CPU freq to 1 MHz, timer 3, count up + matrixDisplayIRQ = timerBegin(3, 80, true); + timerAttachInterrupt(matrixDisplayIRQ, &MatrixDisplayIRQ, true); + timerAlarmWrite(matrixDisplayIRQ, MATRIX_DISPLAY_SCAN_US, true); + timerAlarmEnable(matrixDisplayIRQ); +#endif + +// initialize button +#ifdef HAS_BUTTON + strcat_P(features, " BTN_"); +#ifdef BUTTON_PULLUP + strcat_P(features, "PU"); +#else + strcat_P(features, "PD"); +#endif // BUTTON_PULLUP + button_init(HAS_BUTTON); +#endif // HAS_BUTTON + + // cyclic function interrupts + sendcycler.attach(SENDCYCLE * 2, sendcycle); + housekeeper.attach(HOMECYCLE, housekeeping); + +#if (TIME_SYNC_INTERVAL) + +#if (!(TIME_SYNC_LORAWAN) && !(TIME_SYNC_LORASERVER) && !defined HAS_GPS && \ + !defined HAS_RTC) +#warning you did not specify a time source, time will not be synched +#endif + +// initialize gps time +#if (HAS_GPS) + fetch_gpsTime(); +#endif + +#if (defined HAS_IF482 || defined HAS_DCF77) + ESP_LOGI(TAG, "Starting Clock Controller..."); + clock_init(); +#endif + +#if (TIME_SYNC_LORASERVER) + timesync_init(); // create loraserver time sync task +#endif + + ESP_LOGI(TAG, "Starting Timekeeper..."); + assert(timepulse_init()); // setup pps timepulse + timepulse_start(); // starts pps and cyclic time sync + +#endif // TIME_SYNC_INTERVAL + + // show compiled features + ESP_LOGI(TAG, "Features:%s", features); + + // set runmode to normal + RTC_runmode = RUNMODE_NORMAL; + + vTaskDelete(NULL); + +} // setup() + +void loop() { vTaskDelete(NULL); } +3 MatrixDisplayIRQ -> matrix mux cycle -> 0,5ms (MATRIX_DISPLAY_SCAN_US) + + +// Interrupt routines +------------------------------------------------------------------------------- + +fired by hardware +DisplayIRQ -> esp32 timer 0 -> irqHandlerTask (Core 1) +CLOCKIRQ -> esp32 timer 1 -> ClockTask (Core 1) +ButtonIRQ -> external gpio -> irqHandlerTask (Core 1) +PMUIRQ -> PMU chip gpio -> irqHandlerTask (Core 1) + +fired by software (Ticker.h) +TIMESYNC_IRQ -> timeSync() -> irqHandlerTask (Core 1) +CYCLIC_IRQ -> housekeeping() -> irqHandlerTask (Core 1) +SENDCYCLE_IRQ -> sendcycle() -> irqHandlerTask (Core 1) +BME_IRQ -> bmecycle() -> irqHandlerTask (Core 1) + + +// External RTC timer (if present) +------------------------------------------------------------------------------- +triggers pps 1 sec impulse + +*/ + +// Basic Config +#include "main.h" + +configData_t cfg; // struct holds current device configuration +char lmic_event_msg[LMIC_EVENTMSG_LEN]; // display buffer for LMIC event message +uint8_t volatile channel = 0; // channel rotation counter +uint16_t volatile macs_total = 0, macs_wifi = 0, macs_ble = 0, + batt_voltage = 0; // globals for display + +hw_timer_t *ppsIRQ = NULL, *displayIRQ = NULL, *matrixDisplayIRQ = NULL; + +TaskHandle_t irqHandlerTask = NULL, ClockTask = NULL; +SemaphoreHandle_t I2Caccess; +bool volatile TimePulseTick = false; +time_t userUTCTime = 0; +timesource_t timeSource = _unsynced; + +// container holding unique MAC address hashes with Memory Alloctor using PSRAM, +// if present +std::set, Mallocator> macs; + +// initialize payload encoder +PayloadConvert payload(PAYLOAD_BUFFER_SIZE); + +// set Time Zone for user setting from paxcounter.conf +TimeChangeRule myDST = DAYLIGHT_TIME; +TimeChangeRule mySTD = STANDARD_TIME; +Timezone myTZ(myDST, mySTD); + +// local Tag for logging +static const char TAG[] = __FILE__; + +void setup() { + + char features[100] = ""; + + // create some semaphores for syncing / mutexing tasks + I2Caccess = xSemaphoreCreateMutex(); // for access management of i2c bus + assert(I2Caccess != NULL); + I2C_MUTEX_UNLOCK(); + + // disable brownout detection +#ifdef DISABLE_BROWNOUT + // register with brownout is at address DR_REG_RTCCNTL_BASE + 0xd4 + (*((uint32_t volatile *)ETS_UNCACHED_ADDR((DR_REG_RTCCNTL_BASE + 0xd4)))) = 0; +#endif + + // setup debug output or silence device +#if (VERBOSE) + Serial.begin(115200); + esp_log_level_set("*", ESP_LOG_VERBOSE); +#else + // mute logs completely by redirecting them to silence function + esp_log_level_set("*", ESP_LOG_NONE); +#endif + + do_after_reset(rtc_get_reset_reason(0)); + + // print chip information on startup if in verbose mode after coldstart +#if (VERBOSE) + + if (RTC_runmode == RUNMODE_POWERCYCLE) { + esp_chip_info_t chip_info; + esp_chip_info(&chip_info); + ESP_LOGI(TAG, + "This is ESP32 chip with %d CPU cores, WiFi%s%s, silicon revision " + "%d, %dMB %s Flash", + chip_info.cores, + (chip_info.features & CHIP_FEATURE_BT) ? "/BT" : "", + (chip_info.features & CHIP_FEATURE_BLE) ? "/BLE" : "", + chip_info.revision, spi_flash_get_chip_size() / (1024 * 1024), + (chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" + : "external"); + ESP_LOGI(TAG, "Internal Total heap %d, internal Free Heap %d", + ESP.getHeapSize(), ESP.getFreeHeap()); +#ifdef BOARD_HAS_PSRAM + ESP_LOGI(TAG, "SPIRam Total heap %d, SPIRam Free Heap %d", + ESP.getPsramSize(), ESP.getFreePsram()); +#endif + ESP_LOGI(TAG, "ChipRevision %d, Cpu Freq %d, SDK Version %s", + ESP.getChipRevision(), ESP.getCpuFreqMHz(), ESP.getSdkVersion()); + ESP_LOGI(TAG, "Flash Size %d, Flash Speed %d", ESP.getFlashChipSize(), + ESP.getFlashChipSpeed()); + ESP_LOGI(TAG, "Wifi/BT software coexist version %s", + esp_coex_version_get()); + +#if (HAS_LORA) + ESP_LOGI(TAG, "IBM LMIC version %d.%d.%d", LMIC_VERSION_MAJOR, + LMIC_VERSION_MINOR, LMIC_VERSION_BUILD); + ESP_LOGI(TAG, "Arduino LMIC version %d.%d.%d.%d", + ARDUINO_LMIC_VERSION_GET_MAJOR(ARDUINO_LMIC_VERSION), + ARDUINO_LMIC_VERSION_GET_MINOR(ARDUINO_LMIC_VERSION), + ARDUINO_LMIC_VERSION_GET_PATCH(ARDUINO_LMIC_VERSION), + ARDUINO_LMIC_VERSION_GET_LOCAL(ARDUINO_LMIC_VERSION)); + showLoraKeys(); +#endif // HAS_LORA + +#if (HAS_GPS) + ESP_LOGI(TAG, "TinyGPS+ version %s", TinyGPSPlus::libraryVersion()); +#endif + } +#endif // VERBOSE + + // open i2c bus + i2c_init(); + +// setup power on boards with power management logic +#ifdef EXT_POWER_SW + pinMode(EXT_POWER_SW, OUTPUT); + digitalWrite(EXT_POWER_SW, EXT_POWER_ON); + strcat_P(features, " VEXT"); +#endif +#ifdef HAS_PMU + AXP192_init(); + strcat_P(features, " PMU"); +#endif + + // read (and initialize on first run) runtime settings from NVRAM + loadConfig(); // includes initialize if necessary + +// initialize display +#ifdef HAS_DISPLAY + strcat_P(features, " OLED"); + DisplayIsOn = cfg.screenon; + // display verbose info only after a coldstart (note: blocking call!) + init_display(RTC_runmode == RUNMODE_POWERCYCLE ? true : false); +#endif + + // scan i2c bus for devices + i2c_scan(); + +#ifdef BOARD_HAS_PSRAM + assert(psramFound()); + ESP_LOGI(TAG, "PSRAM found and initialized"); + strcat_P(features, " PSRAM"); +#endif + +#ifdef BAT_MEASURE_EN + pinMode(BAT_MEASURE_EN, OUTPUT); +#endif + +// initialize leds +#if (HAS_LED != NOT_A_PIN) + pinMode(HAS_LED, OUTPUT); + strcat_P(features, " LED"); + +#ifdef LED_POWER_SW + pinMode(LED_POWER_SW, OUTPUT); + digitalWrite(LED_POWER_SW, LED_POWER_ON); +#endif + +#ifdef HAS_TWO_LED + pinMode(HAS_TWO_LED, OUTPUT); + strcat_P(features, " LED1"); +#endif + +// use LED for power display if we have additional RGB LED, else for status +#ifdef HAS_RGB_LED + switch_LED(LED_ON); + strcat_P(features, " RGB"); + rgb_set_color(COLOR_PINK); +#endif + +#endif // HAS_LED + +#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED) + // start led loop + ESP_LOGI(TAG, "Starting LED Controller..."); + xTaskCreatePinnedToCore(ledLoop, // task function + "ledloop", // name of task + 1024, // stack size of task + (void *)1, // parameter of the task + 3, // priority of the task + &ledLoopTask, // task handle + 0); // CPU core +#endif + +// initialize wifi antenna +#ifdef HAS_ANTENNA_SWITCH + strcat_P(features, " ANT"); + antenna_init(); + antenna_select(cfg.wifiant); +#endif + +// initialize battery status +#if (defined BAT_MEASURE_ADC || defined HAS_PMU) + strcat_P(features, " BATT"); + calibrate_voltage(); + batt_voltage = read_voltage(); +#endif + +#if (USE_OTA) + strcat_P(features, " OTA"); + // reboot to firmware update mode if ota trigger switch is set + if (RTC_runmode == RUNMODE_UPDATE) + start_ota_update(); +#endif + +// start BLE scan callback if BLE function is enabled in NVRAM configuration +// or switch off bluetooth, if not compiled +#if (BLECOUNTER) + strcat_P(features, " BLE"); + if (cfg.blescan) { + ESP_LOGI(TAG, "Starting Bluetooth..."); + start_BLEscan(); + } else + btStop(); +#else + // remove bluetooth stack to gain more free memory + btStop(); + ESP_ERROR_CHECK(esp_bt_mem_release(ESP_BT_MODE_BTDM)); + ESP_ERROR_CHECK(esp_coex_preference_set( + ESP_COEX_PREFER_WIFI)); // configure Wifi/BT coexist lib +#endif + +// initialize gps +#if (HAS_GPS) + strcat_P(features, " GPS"); + if (gps_init()) { + ESP_LOGI(TAG, "Starting GPS Feed..."); + xTaskCreatePinnedToCore(gps_loop, // task function + "gpsloop", // name of task + 2048, // stack size of task + (void *)1, // parameter of the task + 1, // priority of the task + &GpsTask, // task handle + 1); // CPU core + } +#endif + +// initialize sensors +#if (HAS_SENSORS) + strcat_P(features, " SENS"); + sensor_init(); +#endif + +// initialize LoRa +#if (HAS_LORA) + strcat_P(features, " LORA"); + // kick off join, except we come from sleep + assert(lora_stack_init(RTC_runmode == RUNMODE_WAKEUP ? false : true) == + ESP_OK); +#endif + +// initialize SPI +#ifdef HAS_SPI + strcat_P(features, " SPI"); + assert(spi_init() == ESP_OK); +#endif + #ifdef HAS_SDCARD if (sdcardInit()) strcat_P(features, " SD"); From 5aefaf2b2f1ed1da3ca5977412da968603dc28d7 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Wed, 5 Feb 2020 15:54:06 +0100 Subject: [PATCH 023/106] Update payload.h copy+paste-error corrected --- include/payload.h | 55 ----------------------------------------------- 1 file changed, 55 deletions(-) diff --git a/include/payload.h b/include/payload.h index 328c0b4e..719d59ec 100644 --- a/include/payload.h +++ b/include/payload.h @@ -103,59 +103,4 @@ private: extern PayloadConvert payload; -#endif // _PAYLOAD_H_ - void addCount(uint16_t value, uint8_t sniffytpe); - void addConfig(configData_t value); - void addStatus(uint16_t voltage, uint64_t uptime, float cputemp, uint32_t mem, - uint8_t reset1, uint8_t reset2); - void addAlarm(int8_t rssi, uint8_t message); - void addVoltage(uint16_t value); - void addGPS(gpsStatus_t value); - void addBME(bmeStatus_t value); - void addButton(uint8_t value); - void addSensor(uint8_t[]); - void addTime(time_t value); - void addPM10(float value); - void addPM25(float value); - void addChars( char* string, int len); - -#if (PAYLOAD_ENCODER == 1) // format plain - -private: - uint8_t *buffer; - uint8_t cursor; - -#elif (PAYLOAD_ENCODER == 2) // format packed - -private: - uint8_t *buffer; - uint8_t cursor; - void uintToBytes(uint64_t i, uint8_t byteSize); - void writeUptime(uint64_t unixtime); - void writeLatLng(double latitude, double longitude); - void writeUint64(uint64_t i); - void writeUint32(uint32_t i); - void writeUint16(uint16_t i); - void writeUint8(uint8_t i); - void writeFloat(float value); - void writeUFloat(float value); - void writePressure(float value); - void writeVersion(char *version); - void writeBitmap(bool a, bool b, bool c, bool d, bool e, bool f, bool g, - bool h); - -#elif ((PAYLOAD_ENCODER == 3) || (PAYLOAD_ENCODER == 4)) // format cayenne lpp - -private: - uint8_t *buffer; - uint8_t maxsize; - uint8_t cursor; - -#else -#error No valid payload converter defined! -#endif -}; - -extern PayloadConvert payload; - #endif // _PAYLOAD_H_ From f5faf31a2a4b43199f408c2131ca634cff0d5394 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Wed, 5 Feb 2020 15:55:54 +0100 Subject: [PATCH 024/106] Update cyclic.cpp copy+paste-error corrected --- src/cyclic.cpp | 96 -------------------------------------------------- 1 file changed, 96 deletions(-) diff --git a/src/cyclic.cpp b/src/cyclic.cpp index 6a13a217..2fd00037 100644 --- a/src/cyclic.cpp +++ b/src/cyclic.cpp @@ -160,99 +160,3 @@ void reset_counters() { #endif } -#endif - -#if (defined HAS_DCF77 || defined HAS_IF482) - ESP_LOGD(TAG, "Clockloop %d bytes left | Taskstate = %d", - uxTaskGetStackHighWaterMark(ClockTask), eTaskGetState(ClockTask)); -#endif - -#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED) - ESP_LOGD(TAG, "LEDloop %d bytes left | Taskstate = %d", - uxTaskGetStackHighWaterMark(ledLoopTask), - eTaskGetState(ledLoopTask)); -#endif - -// read battery voltage into global variable -#if (defined BAT_MEASURE_ADC || defined HAS_PMU) - batt_voltage = read_voltage(); - if (batt_voltage == 0xffff) - ESP_LOGI(TAG, "Battery: external power"); - else - ESP_LOGI(TAG, "Battery: %dmV", batt_voltage); -#ifdef HAS_PMU - AXP192_showstatus(); -#endif -#endif - -// display BME680/280 sensor data -#if (HAS_BME) -#ifdef HAS_BME680 - ESP_LOGI(TAG, "BME680 Temp: %.2f°C | IAQ: %.2f | IAQacc: %d", - bme_status.temperature, bme_status.iaq, bme_status.iaq_accuracy); -#elif defined HAS_BME280 - ESP_LOGI(TAG, "BME280 Temp: %.2f°C | Humidity: %.2f | Pressure: %.0f", - bme_status.temperature, bme_status.humidity, bme_status.pressure); -#elif defined HAS_BMP180 - ESP_LOGI(TAG, "BMP180 Temp: %.2f°C | Pressure: %.0f", bme_status.temperature, - bme_status.pressure); -#endif -#endif - - // check free heap memory - if (ESP.getMinFreeHeap() <= MEM_LOW) { - ESP_LOGI(TAG, - "Memory full, counter cleared (heap low water mark = %d Bytes / " - "free heap = %d bytes)", - ESP.getMinFreeHeap(), ESP.getFreeHeap()); - reset_counters(); // clear macs container and reset all counters - get_salt(); // get new salt for salting hashes - - if (ESP.getMinFreeHeap() <= MEM_LOW) // check again - do_reset(true); // memory leak, reset device - } - -// check free PSRAM memory -#ifdef BOARD_HAS_PSRAM - if (ESP.getMinFreePsram() <= MEM_LOW) { - ESP_LOGI(TAG, "PSRAM full, counter cleared"); - reset_counters(); // clear macs container and reset all counters - get_salt(); // get new salt for salting hashes - - if (ESP.getMinFreePsram() <= MEM_LOW) // check again - do_reset(true); // memory leak, reset device - } -#endif - -} // doHousekeeping() - -// uptime counter 64bit to prevent millis() rollover after 49 days -uint64_t uptime() { - static uint32_t low32, high32; - uint32_t new_low32 = millis(); - if (new_low32 < low32) - high32++; - low32 = new_low32; - return (uint64_t)high32 << 32 | low32; -} - -uint32_t getFreeRAM() { -#ifndef BOARD_HAS_PSRAM - return ESP.getFreeHeap(); -#else - return ESP.getFreePsram(); -#endif -} - -void reset_counters() { -#if ((WIFICOUNTER) || (BLECOUNTER)) - macs.clear(); // clear all macs container - macs_total = 0; // reset all counters - macs_wifi = 0; - macs_ble = 0; -#ifdef HAS_DISPLAY - oledPlotCurve(0, true); -#endif - -#endif -} From d6e4ba16e70b0537f28a35a0be919e0e9c1a3b14 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Wed, 5 Feb 2020 15:57:33 +0100 Subject: [PATCH 025/106] Update payload.cpp copy+paste-error corrected --- src/payload.cpp | 468 ------------------------------------------------ 1 file changed, 468 deletions(-) diff --git a/src/payload.cpp b/src/payload.cpp index 05924897..57428234 100644 --- a/src/payload.cpp +++ b/src/payload.cpp @@ -523,474 +523,6 @@ void PayloadConvert::addPM25( float value) { #endif // HAS_SDS011 } -void PayloadConvert::addChars( char * string, int len) { - for (int i=0; i < len; i++) - addByte(string[i]); -} - - buffer[cursor++] = highByte(voltage); - buffer[cursor++] = lowByte(voltage); - buffer[cursor++] = (byte)((uptime & 0xFF00000000000000) >> 56); - buffer[cursor++] = (byte)((uptime & 0x00FF000000000000) >> 48); - buffer[cursor++] = (byte)((uptime & 0x0000FF0000000000) >> 40); - buffer[cursor++] = (byte)((uptime & 0x000000FF00000000) >> 32); - buffer[cursor++] = (byte)((uptime & 0x00000000FF000000) >> 24); - buffer[cursor++] = (byte)((uptime & 0x0000000000FF0000) >> 16); - buffer[cursor++] = (byte)((uptime & 0x000000000000FF00) >> 8); - buffer[cursor++] = (byte)((uptime & 0x00000000000000FF)); - buffer[cursor++] = (byte)(cputemp); - buffer[cursor++] = (byte)((mem & 0xFF000000) >> 24); - buffer[cursor++] = (byte)((mem & 0x00FF0000) >> 16); - buffer[cursor++] = (byte)((mem & 0x0000FF00) >> 8); - buffer[cursor++] = (byte)((mem & 0x000000FF)); - buffer[cursor++] = (byte)(reset1); - buffer[cursor++] = (byte)(reset2); -} - -void PayloadConvert::addGPS(gpsStatus_t value) { -#if(HAS_GPS) - buffer[cursor++] = (byte)((value.latitude & 0xFF000000) >> 24); - buffer[cursor++] = (byte)((value.latitude & 0x00FF0000) >> 16); - buffer[cursor++] = (byte)((value.latitude & 0x0000FF00) >> 8); - buffer[cursor++] = (byte)((value.latitude & 0x000000FF)); - buffer[cursor++] = (byte)((value.longitude & 0xFF000000) >> 24); - buffer[cursor++] = (byte)((value.longitude & 0x00FF0000) >> 16); - buffer[cursor++] = (byte)((value.longitude & 0x0000FF00) >> 8); - buffer[cursor++] = (byte)((value.longitude & 0x000000FF)); -#if (!PAYLOAD_OPENSENSEBOX) - buffer[cursor++] = value.satellites; - buffer[cursor++] = highByte(value.hdop); - buffer[cursor++] = lowByte(value.hdop); - buffer[cursor++] = highByte(value.altitude); - buffer[cursor++] = lowByte(value.altitude); -#endif -#endif -} - -void PayloadConvert::addSensor(uint8_t buf[]) { -#if(HAS_SENSORS) - uint8_t length = buf[0]; - memcpy(buffer, buf + 1, length); - cursor += length; // length of buffer -#endif -} - -void PayloadConvert::addBME(bmeStatus_t value) { -#if(HAS_BME) - int16_t temperature = (int16_t)(value.temperature); // float -> int - uint16_t humidity = (uint16_t)(value.humidity); // float -> int - uint16_t pressure = (uint16_t)(value.pressure); // float -> int - uint16_t iaq = (uint16_t)(value.iaq); // float -> int - buffer[cursor++] = highByte(temperature); - buffer[cursor++] = lowByte(temperature); - buffer[cursor++] = highByte(pressure); - buffer[cursor++] = lowByte(pressure); - buffer[cursor++] = highByte(humidity); - buffer[cursor++] = lowByte(humidity); - buffer[cursor++] = highByte(iaq); - buffer[cursor++] = lowByte(iaq); -#endif -} - -void PayloadConvert::addButton(uint8_t value) { -#ifdef HAS_BUTTON - buffer[cursor++] = value; -#endif -} - -void PayloadConvert::addTime(time_t value) { - uint32_t time = (uint32_t)value; - buffer[cursor++] = (byte)((time & 0xFF000000) >> 24); - buffer[cursor++] = (byte)((time & 0x00FF0000) >> 16); - buffer[cursor++] = (byte)((time & 0x0000FF00) >> 8); - buffer[cursor++] = (byte)((time & 0x000000FF)); -} - -/* ---------------- packed format with LoRa serialization Encoder ---------- - */ -// derived from -// https://github.com/thesolarnomad/lora-serialization/blob/master/src/LoraEncoder.cpp - -#elif (PAYLOAD_ENCODER == 2) - -void PayloadConvert::addByte(uint8_t value) { writeUint8(value); } - -void PayloadConvert::addCount(uint16_t value, uint8_t snifftype) { - writeUint16(value); -} - -void PayloadConvert::addAlarm(int8_t rssi, uint8_t msg) { - writeUint8(rssi); - writeUint8(msg); -} - -void PayloadConvert::addVoltage(uint16_t value) { writeUint16(value); } - -void PayloadConvert::addConfig(configData_t value) { - writeUint8(value.loradr); - writeUint8(value.txpower); - writeUint16(value.rssilimit); - writeUint8(value.sendcycle); - writeUint8(value.wifichancycle); - writeUint8(value.blescantime); - writeUint8(value.rgblum); - writeBitmap(value.adrmode ? true : false, value.screensaver ? true : false, - value.screenon ? true : false, value.countermode ? true : false, - value.blescan ? true : false, value.wifiant ? true : false, - value.vendorfilter ? true : false, - value.monitormode ? true : false); - writeBitmap(value.payloadmask && GPS_DATA ? true : false, - value.payloadmask && ALARM_DATA ? true : false, - value.payloadmask && MEMS_DATA ? true : false, - value.payloadmask && COUNT_DATA ? true : false, - value.payloadmask && SENSOR1_DATA ? true : false, - value.payloadmask && SENSOR2_DATA ? true : false, - value.payloadmask && SENSOR3_DATA ? true : false, - value.payloadmask && BATT_DATA ? true : false); - writeVersion(value.version); -} - -void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime, float cputemp, - uint32_t mem, uint8_t reset1, uint8_t reset2) { - writeUint16(voltage); - writeUptime(uptime); - writeUint8((byte)cputemp); - writeUint32(mem); - writeUint8(reset1); - writeUint8(reset2); -} - -void PayloadConvert::addGPS(gpsStatus_t value) { -#if(HAS_GPS) - writeLatLng(value.latitude, value.longitude); -#if (!PAYLOAD_OPENSENSEBOX) - writeUint8(value.satellites); - writeUint16(value.hdop); - writeUint16(value.altitude); -#endif -#endif -} - -void PayloadConvert::addSensor(uint8_t buf[]) { -#if(HAS_SENSORS) - uint8_t length = buf[0]; - memcpy(buffer, buf + 1, length); - cursor += length; // length of buffer -#endif -} - -void PayloadConvert::addBME(bmeStatus_t value) { -#if(HAS_BME) - writeFloat(value.temperature); - writePressure(value.pressure); - writeUFloat(value.humidity); - writeUFloat(value.iaq); -#endif -} - -void PayloadConvert::addButton(uint8_t value) { -#ifdef HAS_BUTTON - writeUint8(value); -#endif -} - -void PayloadConvert::addTime(time_t value) { - uint32_t time = (uint32_t)value; - writeUint32(time); -} - -void PayloadConvert::uintToBytes(uint64_t value, uint8_t byteSize) { - for (uint8_t x = 0; x < byteSize; x++) { - byte next = 0; - if (sizeof(value) > x) { - next = static_cast((value >> (x * 8)) & 0xFF); - } - buffer[cursor] = next; - ++cursor; - } -} - -void PayloadConvert::writeUptime(uint64_t uptime) { - writeUint64(uptime); -} - -void PayloadConvert::writeVersion(char *version) { - memcpy(buffer + cursor, version, 10); - cursor += 10; -} - -void PayloadConvert::writeLatLng(double latitude, double longitude) { - // Tested to at least work with int32_t, which are processed correctly. - writeUint32(latitude); - writeUint32(longitude); -} - -void PayloadConvert::writeUint64(uint64_t i) { uintToBytes(i, 8); } - -void PayloadConvert::writeUint32(uint32_t i) { uintToBytes(i, 4); } - -void PayloadConvert::writeUint16(uint16_t i) { uintToBytes(i, 2); } - -void PayloadConvert::writeUint8(uint8_t i) { uintToBytes(i, 1); } - -void PayloadConvert::writeUFloat(float value) { - writeUint16(value * 100); -} - -void PayloadConvert::writePressure(float value) { - writeUint16(value * 10); -} - -/** - * Uses a 16bit two's complement with two decimals, so the range is - * -327.68 to +327.67 degrees - */ -void PayloadConvert::writeFloat(float value) { - int16_t t = (int16_t)(value * 100); - if (value < 0) { - t = ~-t; - t = t + 1; - } - buffer[cursor++] = (byte)((t >> 8) & 0xFF); - buffer[cursor++] = (byte)t & 0xFF; -} - -void PayloadConvert::writeBitmap(bool a, bool b, bool c, bool d, bool e, bool f, - bool g, bool h) { - uint8_t bitmap = 0; - // LSB first - bitmap |= (a & 1) << 7; - bitmap |= (b & 1) << 6; - bitmap |= (c & 1) << 5; - bitmap |= (d & 1) << 4; - bitmap |= (e & 1) << 3; - bitmap |= (f & 1) << 2; - bitmap |= (g & 1) << 1; - bitmap |= (h & 1) << 0; - writeUint8(bitmap); -} - -/* ---------------- Cayenne LPP 2.0 format ---------- */ -// see specs -// http://community.mydevices.com/t/cayenne-lpp-2-0/7510 (LPP 2.0) -// https://github.com/myDevicesIoT/cayenne-docs/blob/master/docs/LORA.md -// (LPP 1.0) PAYLOAD_ENCODER == 3 -> Dynamic Sensor Payload, using channels -> -// FPort 1 PAYLOAD_ENCODER == 4 -> Packed Sensor Payload, not using channels -> -// FPort 2 - -#elif ((PAYLOAD_ENCODER == 3) || (PAYLOAD_ENCODER == 4)) - -void PayloadConvert::addByte(uint8_t value) { - /* - not implemented - */ } - -void PayloadConvert::addCount(uint16_t value, uint8_t snifftype) { - switch (snifftype) { - case MAC_SNIFF_WIFI: -#if (PAYLOAD_ENCODER == 3) - buffer[cursor++] = LPP_COUNT_WIFI_CHANNEL; -#endif - buffer[cursor++] = - LPP_LUMINOSITY; // workaround since cayenne has no data type meter - buffer[cursor++] = highByte(value); - buffer[cursor++] = lowByte(value); - break; - case MAC_SNIFF_BLE: -#if (PAYLOAD_ENCODER == 3) - buffer[cursor++] = LPP_COUNT_BLE_CHANNEL; -#endif - buffer[cursor++] = - LPP_LUMINOSITY; // workaround since cayenne has no data type meter - buffer[cursor++] = highByte(value); - buffer[cursor++] = lowByte(value); - break; - } -} - -void PayloadConvert::addAlarm(int8_t rssi, uint8_t msg) { -#if (PAYLOAD_ENCODER == 3) - buffer[cursor++] = LPP_ALARM_CHANNEL; -#endif - buffer[cursor++] = LPP_PRESENCE; - buffer[cursor++] = msg; -#if (PAYLOAD_ENCODER == 3) - buffer[cursor++] = LPP_MSG_CHANNEL; -#endif - buffer[cursor++] = LPP_ANALOG_INPUT; - buffer[cursor++] = rssi; -} - -void PayloadConvert::addVoltage(uint16_t value) { - uint16_t volt = value / 10; -#if (PAYLOAD_ENCODER == 3) - buffer[cursor++] = LPP_BATT_CHANNEL; -#endif - buffer[cursor++] = LPP_ANALOG_INPUT; - buffer[cursor++] = highByte(volt); - buffer[cursor++] = lowByte(volt); -} - -void PayloadConvert::addConfig(configData_t value) { -#if (PAYLOAD_ENCODER == 3) - buffer[cursor++] = LPP_ADR_CHANNEL; -#endif - buffer[cursor++] = LPP_DIGITAL_INPUT; - buffer[cursor++] = value.adrmode; -} - -void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime, float celsius, - uint32_t mem, uint8_t reset1, uint8_t reset2) { - uint16_t temp = celsius * 10; - uint16_t volt = voltage / 10; -#if (defined BAT_MEASURE_ADC || defined HAS_PMU) -#if (PAYLOAD_ENCODER == 3) - buffer[cursor++] = LPP_BATT_CHANNEL; -#endif - buffer[cursor++] = LPP_ANALOG_INPUT; - buffer[cursor++] = highByte(volt); - buffer[cursor++] = lowByte(volt); -#endif // BAT_MEASURE_ADC - -#if (PAYLOAD_ENCODER == 3) - buffer[cursor++] = LPP_TEMPERATURE_CHANNEL; -#endif - buffer[cursor++] = LPP_TEMPERATURE; - buffer[cursor++] = highByte(temp); - buffer[cursor++] = lowByte(temp); -} - -void PayloadConvert::addGPS(gpsStatus_t value) { -#if(HAS_GPS) - int32_t lat = value.latitude / 100; - int32_t lon = value.longitude / 100; - int32_t alt = value.altitude * 100; -#if (PAYLOAD_ENCODER == 3) - buffer[cursor++] = LPP_GPS_CHANNEL; -#endif - buffer[cursor++] = LPP_GPS; - buffer[cursor++] = (byte)((lat & 0xFF0000) >> 16); - buffer[cursor++] = (byte)((lat & 0x00FF00) >> 8); - buffer[cursor++] = (byte)((lat & 0x0000FF)); - buffer[cursor++] = (byte)((lon & 0xFF0000) >> 16); - buffer[cursor++] = (byte)((lon & 0x00FF00) >> 8); - buffer[cursor++] = (byte)(lon & 0x0000FF); - buffer[cursor++] = (byte)((alt & 0xFF0000) >> 16); - buffer[cursor++] = (byte)((alt & 0x00FF00) >> 8); - buffer[cursor++] = (byte)(alt & 0x0000FF); -#endif // HAS_GPS -} - -void PayloadConvert::addSensor(uint8_t buf[]) { -#if(HAS_SENSORS) -// to come -/* - uint8_t length = buf[0]; - memcpy(buffer, buf+1, length); - cursor += length; // length of buffer -*/ -#endif // HAS_SENSORS -} - -void PayloadConvert::addBME(bmeStatus_t value) { -#if(HAS_BME) - - // data value conversions to meet cayenne data type definition - // 0.1°C per bit => -3276,7 .. +3276,7 °C - int16_t temperature = (int16_t)(value.temperature * 10.0); - // 0.1 hPa per bit => 0 .. 6553,6 hPa - uint16_t pressure = (uint16_t)(value.pressure * 10); - // 0.5% per bit => 0 .. 128 %C - uint8_t humidity = (uint8_t)(value.humidity * 2.0); - int16_t iaq = (int16_t)(value.iaq); - -#if (PAYLOAD_ENCODER == 3) - buffer[cursor++] = LPP_TEMPERATURE_CHANNEL; -#endif - buffer[cursor++] = LPP_TEMPERATURE; // 2 bytes 0.1 °C Signed MSB - buffer[cursor++] = highByte(temperature); - buffer[cursor++] = lowByte(temperature); -#if (PAYLOAD_ENCODER == 3) - buffer[cursor++] = LPP_BAROMETER_CHANNEL; -#endif - buffer[cursor++] = LPP_BAROMETER; // 2 bytes 0.1 hPa Unsigned MSB - buffer[cursor++] = highByte(pressure); - buffer[cursor++] = lowByte(pressure); -#if (PAYLOAD_ENCODER == 3) - buffer[cursor++] = LPP_HUMIDITY_CHANNEL; -#endif - buffer[cursor++] = LPP_HUMIDITY; // 1 byte 0.5 % Unsigned - buffer[cursor++] = humidity; -#if (PAYLOAD_ENCODER == 3) - buffer[cursor++] = LPP_AIR_CHANNEL; -#endif - buffer[cursor++] = LPP_LUMINOSITY; // 2 bytes, 1.0 unsigned - buffer[cursor++] = highByte(iaq); - buffer[cursor++] = lowByte(iaq); -#endif // HAS_BME -} - -void PayloadConvert::addButton(uint8_t value) { -#ifdef HAS_BUTTON -#if (PAYLOAD_ENCODER == 3) - buffer[cursor++] = LPP_BUTTON_CHANNEL; -#endif - buffer[cursor++] = LPP_DIGITAL_INPUT; - buffer[cursor++] = value; -#endif // HAS_BUTTON -} - -void PayloadConvert::addTime(time_t value) { -#if (PAYLOAD_ENCODER == 4) - uint32_t t = (uint32_t)value; - uint32_t tx_period = (uint32_t)SENDCYCLE * 2; - buffer[cursor++] = 0x03; // set config mask to UTCTime + TXPeriod - // UTCTime in seconds - buffer[cursor++] = (byte)((t & 0xFF000000) >> 24); - buffer[cursor++] = (byte)((t & 0x00FF0000) >> 16); - buffer[cursor++] = (byte)((t & 0x0000FF00) >> 8); - buffer[cursor++] = (byte)((t & 0x000000FF)); - // TXPeriod in seconds - buffer[cursor++] = (byte)((tx_period & 0xFF000000) >> 24); - buffer[cursor++] = (byte)((tx_period & 0x00FF0000) >> 16); - buffer[cursor++] = (byte)((tx_period & 0x0000FF00) >> 8); - buffer[cursor++] = (byte)((tx_period & 0x000000FF)); -#endif -} -#endif // PAYLOAD_ENCODER - -void PayloadConvert::addPM10( float value) { -#if (HAS_SDS011) -#if (PAYLOAD_ENCODER == 1) // plain - char tempBuffer[10+1]; - sprintf( tempBuffer, ",%5.1f", value); - addChars(tempBuffer, strlen(tempBuffer)); -#elif (PAYLOAD_ENCODER == 2 ) // packed - writeUint16( (uint16_t) (value*10) ); -#elif (PAYLOAD_ENCODER == 3 ) // Cayenne LPP dynamic - // TODO -#elif (PAYLOAD_ENCODER == 4 ) // Cayenne LPP packed - // TODO -#endif -#endif // HAS_SDS011 -} - -void PayloadConvert::addPM25( float value) { -#if (HAS_SDS011) -#if (PAYLOAD_ENCODER == 1) // plain - char tempBuffer[10+1]; - sprintf( tempBuffer, ",%5.1f", value); - addChars(tempBuffer, strlen(tempBuffer)); -#elif (PAYLOAD_ENCODER == 2 ) // packed - writeUint16( (uint16_t) (value*10) ); -#elif (PAYLOAD_ENCODER == 3 ) // Cayenne LPP dynamic - // TODO -#elif (PAYLOAD_ENCODER == 4 ) // Cayenne LPP packed - // TODO -#endif -#endif // HAS_SDS011 -} - void PayloadConvert::addChars( char * string, int len) { for (int i=0; i < len; i++) addByte(string[i]); From 18a0c5f54705172577fc41713f5145c0664e7616 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Wed, 5 Feb 2020 15:58:32 +0100 Subject: [PATCH 026/106] Update sdcard.cpp copy+paste-error corrected --- src/sdcard.cpp | 36 ------------------------------------ 1 file changed, 36 deletions(-) diff --git a/src/sdcard.cpp b/src/sdcard.cpp index 1c1256a4..780efef9 100644 --- a/src/sdcard.cpp +++ b/src/sdcard.cpp @@ -85,39 +85,3 @@ void createFile(void) { #endif // (HAS_SDCARD) - if (++counterWrites > 2) { - // force writing to SD-card - ESP_LOGD(TAG, "flushing data to card"); - fileSDCard.flush(); - counterWrites = 0; - } -} - -void createFile(void) { - char bufferFilename[8 + 1 + 3 + 1]; - - useSDCard = false; - - for (int i = 0; i < 100; i++) { - sprintf(bufferFilename, SDCARD_FILE_NAME, i); - ESP_LOGD(TAG, "SD: looking for file <%s>", bufferFilename); - bool fileExists = SD.exists(bufferFilename); - if (!fileExists) { - ESP_LOGD(TAG, "SD: file does not exist: opening"); - fileSDCard = SD.open(bufferFilename, FILE_WRITE); - if (fileSDCard) { - ESP_LOGD(TAG, "SD: name opened: <%s>", bufferFilename); - fileSDCard.print( SDCARD_FILE_HEADER ); -#if (HAS_SDS011) - fileSDCard.print( SDCARD_FILE_HEADER_SDS011 ); -#endif - fileSDCard.println(); - useSDCard = true; - break; - } - } - } - return; -} - -#endif // (HAS_SDCARD) From c80e7aa7a5cc698eccc04a3afcaf29d497a48a5d Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Wed, 5 Feb 2020 16:02:04 +0100 Subject: [PATCH 027/106] Update main.cpp copy+paste-error corrected --- src/main.cpp | 437 --------------------------------------------------- 1 file changed, 437 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 09aa66c4..c8f3af3d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -485,440 +485,3 @@ void setup() { } // setup() void loop() { vTaskDelete(NULL); } -3 MatrixDisplayIRQ -> matrix mux cycle -> 0,5ms (MATRIX_DISPLAY_SCAN_US) - - -// Interrupt routines -------------------------------------------------------------------------------- - -fired by hardware -DisplayIRQ -> esp32 timer 0 -> irqHandlerTask (Core 1) -CLOCKIRQ -> esp32 timer 1 -> ClockTask (Core 1) -ButtonIRQ -> external gpio -> irqHandlerTask (Core 1) -PMUIRQ -> PMU chip gpio -> irqHandlerTask (Core 1) - -fired by software (Ticker.h) -TIMESYNC_IRQ -> timeSync() -> irqHandlerTask (Core 1) -CYCLIC_IRQ -> housekeeping() -> irqHandlerTask (Core 1) -SENDCYCLE_IRQ -> sendcycle() -> irqHandlerTask (Core 1) -BME_IRQ -> bmecycle() -> irqHandlerTask (Core 1) - - -// External RTC timer (if present) -------------------------------------------------------------------------------- -triggers pps 1 sec impulse - -*/ - -// Basic Config -#include "main.h" - -configData_t cfg; // struct holds current device configuration -char lmic_event_msg[LMIC_EVENTMSG_LEN]; // display buffer for LMIC event message -uint8_t volatile channel = 0; // channel rotation counter -uint16_t volatile macs_total = 0, macs_wifi = 0, macs_ble = 0, - batt_voltage = 0; // globals for display - -hw_timer_t *ppsIRQ = NULL, *displayIRQ = NULL, *matrixDisplayIRQ = NULL; - -TaskHandle_t irqHandlerTask = NULL, ClockTask = NULL; -SemaphoreHandle_t I2Caccess; -bool volatile TimePulseTick = false; -time_t userUTCTime = 0; -timesource_t timeSource = _unsynced; - -// container holding unique MAC address hashes with Memory Alloctor using PSRAM, -// if present -std::set, Mallocator> macs; - -// initialize payload encoder -PayloadConvert payload(PAYLOAD_BUFFER_SIZE); - -// set Time Zone for user setting from paxcounter.conf -TimeChangeRule myDST = DAYLIGHT_TIME; -TimeChangeRule mySTD = STANDARD_TIME; -Timezone myTZ(myDST, mySTD); - -// local Tag for logging -static const char TAG[] = __FILE__; - -void setup() { - - char features[100] = ""; - - // create some semaphores for syncing / mutexing tasks - I2Caccess = xSemaphoreCreateMutex(); // for access management of i2c bus - assert(I2Caccess != NULL); - I2C_MUTEX_UNLOCK(); - - // disable brownout detection -#ifdef DISABLE_BROWNOUT - // register with brownout is at address DR_REG_RTCCNTL_BASE + 0xd4 - (*((uint32_t volatile *)ETS_UNCACHED_ADDR((DR_REG_RTCCNTL_BASE + 0xd4)))) = 0; -#endif - - // setup debug output or silence device -#if (VERBOSE) - Serial.begin(115200); - esp_log_level_set("*", ESP_LOG_VERBOSE); -#else - // mute logs completely by redirecting them to silence function - esp_log_level_set("*", ESP_LOG_NONE); -#endif - - do_after_reset(rtc_get_reset_reason(0)); - - // print chip information on startup if in verbose mode after coldstart -#if (VERBOSE) - - if (RTC_runmode == RUNMODE_POWERCYCLE) { - esp_chip_info_t chip_info; - esp_chip_info(&chip_info); - ESP_LOGI(TAG, - "This is ESP32 chip with %d CPU cores, WiFi%s%s, silicon revision " - "%d, %dMB %s Flash", - chip_info.cores, - (chip_info.features & CHIP_FEATURE_BT) ? "/BT" : "", - (chip_info.features & CHIP_FEATURE_BLE) ? "/BLE" : "", - chip_info.revision, spi_flash_get_chip_size() / (1024 * 1024), - (chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" - : "external"); - ESP_LOGI(TAG, "Internal Total heap %d, internal Free Heap %d", - ESP.getHeapSize(), ESP.getFreeHeap()); -#ifdef BOARD_HAS_PSRAM - ESP_LOGI(TAG, "SPIRam Total heap %d, SPIRam Free Heap %d", - ESP.getPsramSize(), ESP.getFreePsram()); -#endif - ESP_LOGI(TAG, "ChipRevision %d, Cpu Freq %d, SDK Version %s", - ESP.getChipRevision(), ESP.getCpuFreqMHz(), ESP.getSdkVersion()); - ESP_LOGI(TAG, "Flash Size %d, Flash Speed %d", ESP.getFlashChipSize(), - ESP.getFlashChipSpeed()); - ESP_LOGI(TAG, "Wifi/BT software coexist version %s", - esp_coex_version_get()); - -#if (HAS_LORA) - ESP_LOGI(TAG, "IBM LMIC version %d.%d.%d", LMIC_VERSION_MAJOR, - LMIC_VERSION_MINOR, LMIC_VERSION_BUILD); - ESP_LOGI(TAG, "Arduino LMIC version %d.%d.%d.%d", - ARDUINO_LMIC_VERSION_GET_MAJOR(ARDUINO_LMIC_VERSION), - ARDUINO_LMIC_VERSION_GET_MINOR(ARDUINO_LMIC_VERSION), - ARDUINO_LMIC_VERSION_GET_PATCH(ARDUINO_LMIC_VERSION), - ARDUINO_LMIC_VERSION_GET_LOCAL(ARDUINO_LMIC_VERSION)); - showLoraKeys(); -#endif // HAS_LORA - -#if (HAS_GPS) - ESP_LOGI(TAG, "TinyGPS+ version %s", TinyGPSPlus::libraryVersion()); -#endif - } -#endif // VERBOSE - - // open i2c bus - i2c_init(); - -// setup power on boards with power management logic -#ifdef EXT_POWER_SW - pinMode(EXT_POWER_SW, OUTPUT); - digitalWrite(EXT_POWER_SW, EXT_POWER_ON); - strcat_P(features, " VEXT"); -#endif -#ifdef HAS_PMU - AXP192_init(); - strcat_P(features, " PMU"); -#endif - - // read (and initialize on first run) runtime settings from NVRAM - loadConfig(); // includes initialize if necessary - -// initialize display -#ifdef HAS_DISPLAY - strcat_P(features, " OLED"); - DisplayIsOn = cfg.screenon; - // display verbose info only after a coldstart (note: blocking call!) - init_display(RTC_runmode == RUNMODE_POWERCYCLE ? true : false); -#endif - - // scan i2c bus for devices - i2c_scan(); - -#ifdef BOARD_HAS_PSRAM - assert(psramFound()); - ESP_LOGI(TAG, "PSRAM found and initialized"); - strcat_P(features, " PSRAM"); -#endif - -#ifdef BAT_MEASURE_EN - pinMode(BAT_MEASURE_EN, OUTPUT); -#endif - -// initialize leds -#if (HAS_LED != NOT_A_PIN) - pinMode(HAS_LED, OUTPUT); - strcat_P(features, " LED"); - -#ifdef LED_POWER_SW - pinMode(LED_POWER_SW, OUTPUT); - digitalWrite(LED_POWER_SW, LED_POWER_ON); -#endif - -#ifdef HAS_TWO_LED - pinMode(HAS_TWO_LED, OUTPUT); - strcat_P(features, " LED1"); -#endif - -// use LED for power display if we have additional RGB LED, else for status -#ifdef HAS_RGB_LED - switch_LED(LED_ON); - strcat_P(features, " RGB"); - rgb_set_color(COLOR_PINK); -#endif - -#endif // HAS_LED - -#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED) - // start led loop - ESP_LOGI(TAG, "Starting LED Controller..."); - xTaskCreatePinnedToCore(ledLoop, // task function - "ledloop", // name of task - 1024, // stack size of task - (void *)1, // parameter of the task - 3, // priority of the task - &ledLoopTask, // task handle - 0); // CPU core -#endif - -// initialize wifi antenna -#ifdef HAS_ANTENNA_SWITCH - strcat_P(features, " ANT"); - antenna_init(); - antenna_select(cfg.wifiant); -#endif - -// initialize battery status -#if (defined BAT_MEASURE_ADC || defined HAS_PMU) - strcat_P(features, " BATT"); - calibrate_voltage(); - batt_voltage = read_voltage(); -#endif - -#if (USE_OTA) - strcat_P(features, " OTA"); - // reboot to firmware update mode if ota trigger switch is set - if (RTC_runmode == RUNMODE_UPDATE) - start_ota_update(); -#endif - -// start BLE scan callback if BLE function is enabled in NVRAM configuration -// or switch off bluetooth, if not compiled -#if (BLECOUNTER) - strcat_P(features, " BLE"); - if (cfg.blescan) { - ESP_LOGI(TAG, "Starting Bluetooth..."); - start_BLEscan(); - } else - btStop(); -#else - // remove bluetooth stack to gain more free memory - btStop(); - ESP_ERROR_CHECK(esp_bt_mem_release(ESP_BT_MODE_BTDM)); - ESP_ERROR_CHECK(esp_coex_preference_set( - ESP_COEX_PREFER_WIFI)); // configure Wifi/BT coexist lib -#endif - -// initialize gps -#if (HAS_GPS) - strcat_P(features, " GPS"); - if (gps_init()) { - ESP_LOGI(TAG, "Starting GPS Feed..."); - xTaskCreatePinnedToCore(gps_loop, // task function - "gpsloop", // name of task - 2048, // stack size of task - (void *)1, // parameter of the task - 1, // priority of the task - &GpsTask, // task handle - 1); // CPU core - } -#endif - -// initialize sensors -#if (HAS_SENSORS) - strcat_P(features, " SENS"); - sensor_init(); -#endif - -// initialize LoRa -#if (HAS_LORA) - strcat_P(features, " LORA"); - // kick off join, except we come from sleep - assert(lora_stack_init(RTC_runmode == RUNMODE_WAKEUP ? false : true) == - ESP_OK); -#endif - -// initialize SPI -#ifdef HAS_SPI - strcat_P(features, " SPI"); - assert(spi_init() == ESP_OK); -#endif - -#ifdef HAS_SDCARD - if (sdcardInit()) - strcat_P(features, " SD"); -#endif - -#if (HAS_SDS011) -// ESP_LOGI(TAG, "init fine-dust-sensor"); - if ( sds011_init() ) - strcat_P(features, " SDS"); -#endif - -#if (VENDORFILTER) - strcat_P(features, " FILTER"); -#endif - -// initialize matrix display -#ifdef HAS_MATRIX_DISPLAY - strcat_P(features, " LED_MATRIX"); - MatrixDisplayIsOn = cfg.screenon; - init_matrix_display(); // note: blocking call -#endif - -// show payload encoder -#if PAYLOAD_ENCODER == 1 - strcat_P(features, " PLAIN"); -#elif PAYLOAD_ENCODER == 2 - strcat_P(features, " PACKED"); -#elif PAYLOAD_ENCODER == 3 - strcat_P(features, " LPPDYN"); -#elif PAYLOAD_ENCODER == 4 - strcat_P(features, " LPPPKD"); -#endif - -// initialize RTC -#ifdef HAS_RTC - strcat_P(features, " RTC"); - assert(rtc_init()); -#endif - -#if defined HAS_DCF77 - strcat_P(features, " DCF77"); -#endif - -#if defined HAS_IF482 - strcat_P(features, " IF482"); -#endif - -#if (WIFICOUNTER) - strcat_P(features, " WIFI"); - // start wifi in monitor mode and start channel rotation timer - ESP_LOGI(TAG, "Starting Wifi..."); - wifi_sniffer_init(); -#else - // switch off wifi - esp_wifi_deinit(); -#endif - - // initialize salt value using esp_random() called by random() in - // arduino-esp32 core. Note: do this *after* wifi has started, since - // function gets it's seed from RF noise - get_salt(); // get new 16bit for salting hashes - - // start state machine - ESP_LOGI(TAG, "Starting Interrupt Handler..."); - xTaskCreatePinnedToCore(irqHandler, // task function - "irqhandler", // name of task - 4096, // stack size of task - (void *)1, // parameter of the task - 2, // priority of the task - &irqHandlerTask, // task handle - 1); // CPU core - -// initialize BME sensor (BME280/BME680) -#if (HAS_BME) -#ifdef HAS_BME680 - strcat_P(features, " BME680"); -#elif defined HAS_BME280 - strcat_P(features, " BME280"); -#elif defined HAS_BMP180 - strcat_P(features, " BMP180"); -#endif - if (bme_init()) - ESP_LOGI(TAG, "Starting BME sensor..."); -#endif - - // starting timers and interrupts - assert(irqHandlerTask != NULL); // has interrupt handler task started? - ESP_LOGI(TAG, "Starting Timers..."); - -// display interrupt -#ifdef HAS_DISPLAY - // https://techtutorialsx.com/2017/10/07/esp32-arduino-timer-interrupts/ - // prescaler 80 -> divides 80 MHz CPU freq to 1 MHz, timer 0, count up - displayIRQ = timerBegin(0, 80, true); - timerAttachInterrupt(displayIRQ, &DisplayIRQ, true); - timerAlarmWrite(displayIRQ, DISPLAYREFRESH_MS * 1000, true); - timerAlarmEnable(displayIRQ); -#endif - -// LED Matrix display interrupt -#ifdef HAS_MATRIX_DISPLAY - // https://techtutorialsx.com/2017/10/07/esp32-arduino-timer-interrupts/ - // prescaler 80 -> divides 80 MHz CPU freq to 1 MHz, timer 3, count up - matrixDisplayIRQ = timerBegin(3, 80, true); - timerAttachInterrupt(matrixDisplayIRQ, &MatrixDisplayIRQ, true); - timerAlarmWrite(matrixDisplayIRQ, MATRIX_DISPLAY_SCAN_US, true); - timerAlarmEnable(matrixDisplayIRQ); -#endif - -// initialize button -#ifdef HAS_BUTTON - strcat_P(features, " BTN_"); -#ifdef BUTTON_PULLUP - strcat_P(features, "PU"); -#else - strcat_P(features, "PD"); -#endif // BUTTON_PULLUP - button_init(HAS_BUTTON); -#endif // HAS_BUTTON - - // cyclic function interrupts - sendcycler.attach(SENDCYCLE * 2, sendcycle); - housekeeper.attach(HOMECYCLE, housekeeping); - -#if (TIME_SYNC_INTERVAL) - -#if (!(TIME_SYNC_LORAWAN) && !(TIME_SYNC_LORASERVER) && !defined HAS_GPS && \ - !defined HAS_RTC) -#warning you did not specify a time source, time will not be synched -#endif - -// initialize gps time -#if (HAS_GPS) - fetch_gpsTime(); -#endif - -#if (defined HAS_IF482 || defined HAS_DCF77) - ESP_LOGI(TAG, "Starting Clock Controller..."); - clock_init(); -#endif - -#if (TIME_SYNC_LORASERVER) - timesync_init(); // create loraserver time sync task -#endif - - ESP_LOGI(TAG, "Starting Timekeeper..."); - assert(timepulse_init()); // setup pps timepulse - timepulse_start(); // starts pps and cyclic time sync - -#endif // TIME_SYNC_INTERVAL - - // show compiled features - ESP_LOGI(TAG, "Features:%s", features); - - // set runmode to normal - RTC_runmode = RUNMODE_NORMAL; - - vTaskDelete(NULL); - -} // setup() - -void loop() { vTaskDelete(NULL); } From ddf06b491327c4e3daaf4d5e279acf838059574a Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Wed, 5 Feb 2020 16:03:29 +0100 Subject: [PATCH 028/106] Update timekeeper.cpp copy+paste-error corrected --- src/timekeeper.cpp | 270 --------------------------------------------- 1 file changed, 270 deletions(-) diff --git a/src/timekeeper.cpp b/src/timekeeper.cpp index c6540286..f95a97ab 100644 --- a/src/timekeeper.cpp +++ b/src/timekeeper.cpp @@ -320,274 +320,4 @@ void clock_loop(void *taskparameter) { // ClockTask } // for } // clock_loop() -#endif // HAS_IF482 || defined HAS_DCF77 - if (t) { - timeSource = _rtc; - goto finish; - } -#endif - - goto finish; - -finish: - - setMyTime((uint32_t)t, t_msec, timeSource); // set time - -} // calibrateTime() - -// adjust system time, calibrate RTC and RTC_INT pps -void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec, - timesource_t mytimesource) { - - // called with invalid timesource? - if (mytimesource == _unsynced) - return; - - // increment t_sec only if t_msec > 1000 - time_t time_to_set = (time_t)(t_sec + t_msec / 1000); - - // do we have a valid time? - if (timeIsValid(time_to_set)) { - - // if we have msec fraction, then wait until top of second with - // millisecond precision - if (t_msec % 1000) { - time_to_set++; - vTaskDelay(pdMS_TO_TICKS(1000 - t_msec % 1000)); - } - - ESP_LOGD(TAG, "[%0.3f] UTC epoch time: %d.%03d sec", millis() / 1000.0, - time_to_set, t_msec % 1000); - -// if we have got an external timesource, set RTC time and shift RTC_INT pulse -// to top of second -#ifdef HAS_RTC - if ((mytimesource == _gps) || (mytimesource == _lora)) - set_rtctime(time_to_set); -#endif - -// if we have a software pps timer, shift it to top of second -#if (!defined GPS_INT && !defined RTC_INT) - timerWrite(ppsIRQ, 0); // reset pps timer - CLOCKIRQ(); // fire clock pps, this advances time 1 sec -#endif - - setTime(time_to_set); // set the time on top of second - - timeSource = mytimesource; // set global variable - timesyncer.attach(TIME_SYNC_INTERVAL * 60, timeSync); - ESP_LOGI(TAG, "[%0.3f] Timesync finished, time was set | source: %c", - millis() / 1000.0, timeSetSymbols[timeSource]); - } else { - timesyncer.attach(TIME_SYNC_INTERVAL_RETRY * 60, timeSync); - ESP_LOGI(TAG, "[%0.3f] Timesync failed, invalid time fetched | source: %c", - millis() / 1000.0, timeSetSymbols[timeSource]); - } -} - -// helper function to setup a pulse per second for time synchronisation -uint8_t timepulse_init() { - -// use time pulse from GPS as time base with fixed 1Hz frequency -#ifdef GPS_INT - - // setup external interupt pin for rising edge GPS INT - pinMode(GPS_INT, INPUT_PULLDOWN); - // setup external rtc 1Hz clock as pulse per second clock - ESP_LOGI(TAG, "Timepulse: external (GPS)"); - return 1; // success - -// use pulse from on board RTC chip as time base with fixed frequency -#elif defined RTC_INT - - // setup external interupt pin for falling edge RTC INT - pinMode(RTC_INT, INPUT_PULLUP); - - // setup external rtc 1Hz clock as pulse per second clock - if (I2C_MUTEX_LOCK()) { - Rtc.SetSquareWavePinClockFrequency(DS3231SquareWaveClock_1Hz); - Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeClock); - I2C_MUTEX_UNLOCK(); - ESP_LOGI(TAG, "Timepulse: external (RTC)"); - return 1; // success - } else { - ESP_LOGE(TAG, "RTC initialization error, I2C bus busy"); - return 0; // failure - } - return 1; // success - -#else - // use ESP32 hardware timer as time base with adjustable frequency - ppsIRQ = timerBegin(1, 8000, true); // set 80 MHz prescaler to 1/10000 sec - timerAlarmWrite(ppsIRQ, 10000, true); // 1000ms - ESP_LOGI(TAG, "Timepulse: internal (ESP32 hardware timer)"); - return 1; // success - -#endif -} // timepulse_init - -void timepulse_start(void) { - -#ifdef GPS_INT // start external clock gps pps line - attachInterrupt(digitalPinToInterrupt(GPS_INT), CLOCKIRQ, RISING); -#elif defined RTC_INT // start external clock rtc - attachInterrupt(digitalPinToInterrupt(RTC_INT), CLOCKIRQ, FALLING); -#else // start internal clock esp32 hardware timer - timerAttachInterrupt(ppsIRQ, &CLOCKIRQ, true); - timerAlarmEnable(ppsIRQ); -#endif - - // start cyclic time sync - timeSync(); // init systime by RTC or GPS or LORA - timesyncer.attach(TIME_SYNC_INTERVAL * 60, timeSync); -} - -// interrupt service routine triggered by either pps or esp32 hardware timer -void IRAM_ATTR CLOCKIRQ(void) { - - BaseType_t xHigherPriorityTaskWoken = pdFALSE; - - SyncToPPS(); // advance systime, see microTime.h - -// advance wall clock, if we have -#if (defined HAS_IF482 || defined HAS_DCF77) - xTaskNotifyFromISR(ClockTask, uint32_t(now()), eSetBits, - &xHigherPriorityTaskWoken); -#endif - -// flip time pulse ticker, if needed -#ifdef HAS_DISPLAY -#if (defined GPS_INT || defined RTC_INT) - TimePulseTick = !TimePulseTick; // flip pulse ticker -#endif -#endif - - // yield only if we should - if (xHigherPriorityTaskWoken) - portYIELD_FROM_ISR(); -} - -// helper function to check plausibility of a time -time_t timeIsValid(time_t const t) { - // is it a time in the past? we use compile date to guess - return (t >= compiledUTC() ? t : 0); -} - -// helper function to convert compile time to UTC time -time_t compiledUTC(void) { - static time_t t = myTZ.toUTC(RtcDateTime(__DATE__, __TIME__).Epoch32Time()); - return t; -} - -// helper function to calculate serial transmit time -TickType_t tx_Ticks(uint32_t framesize, unsigned long baud, uint32_t config, - int8_t rxPin, int8_t txPins) { - - uint32_t databits = ((config & 0x0c) >> 2) + 5; - uint32_t stopbits = ((config & 0x20) >> 5) + 1; - uint32_t txTime = (databits + stopbits + 1) * framesize * 1000.0 / baud; - // +1 for the startbit - - return round(txTime); -} - -#if (defined HAS_IF482 || defined HAS_DCF77) - -#if (defined HAS_DCF77 && defined HAS_IF482) -#error You must define at most one of IF482 or DCF77! -#endif - -void clock_init(void) { - -// setup clock output interface -#ifdef HAS_IF482 - IF482.begin(HAS_IF482); -#elif defined HAS_DCF77 - pinMode(HAS_DCF77, OUTPUT); -#endif - - userUTCTime = now(); - - xTaskCreatePinnedToCore(clock_loop, // task function - "clockloop", // name of task - 2048, // stack size of task - (void *)&userUTCTime, // start time as task parameter - 4, // priority of the task - &ClockTask, // task handle - 1); // CPU core - - assert(ClockTask); // has clock task started? -} // clock_init - -void clock_loop(void *taskparameter) { // ClockTask - - // caveat: don't use now() in this task, it will cause a race condition - // due to concurrent access to i2c bus when reading/writing from/to rtc chip! - -#define nextmin(t) (t + DCF77_FRAME_SIZE + 1) // next minute - -#ifdef HAS_TWO_LED - static bool led1_state = false; -#endif - uint32_t printtime; - time_t t = *((time_t *)taskparameter), last_printtime = 0; // UTC time seconds - -#ifdef HAS_DCF77 - uint8_t *DCFpulse; // pointer on array with DCF pulse bits - DCFpulse = DCF77_Frame(nextmin(t)); // load first DCF frame before start -#elif defined HAS_IF482 - static TickType_t txDelay = pdMS_TO_TICKS(1000 - IF482_SYNC_FIXUP) - - tx_Ticks(IF482_FRAME_SIZE, HAS_IF482); -#endif - - // output the next second's pulse/telegram after pps arrived - for (;;) { - - // wait for timepulse and store UTC time in seconds got - xTaskNotifyWait(0x00, ULONG_MAX, &printtime, portMAX_DELAY); - t = time_t(printtime); - - // no confident or no recent time -> suppress clock output - if ((timeStatus() == timeNotSet) || !(timeIsValid(t)) || - (t == last_printtime)) - continue; - -#if defined HAS_IF482 - - // wait until moment to fire. Normally we won't get notified during this - // timespan, except when next pps pulse arrives while waiting, because pps - // was adjusted by recent time sync - if (xTaskNotifyWait(0x00, ULONG_MAX, &printtime, txDelay) == pdTRUE) - t = time_t(printtime); // new adjusted UTC time seconds - - // send IF482 telegram - IF482.print(IF482_Frame(t + 1)); // note: telegram is for *next* second - -#elif defined HAS_DCF77 - - if (second(t) == DCF77_FRAME_SIZE - 1) // is it time to load new frame? - DCFpulse = DCF77_Frame(nextmin(t)); // generate frame for next minute - - if (minute(nextmin(t)) == // do we still have a recent frame? - DCFpulse[DCF77_FRAME_SIZE]) // (timepulses could be missed!) - DCF77_Pulse(t, DCFpulse); // then output current second's pulse - - // else we have no recent frame, thus suppressing clock output - -#endif - -// pps blink on secondary LED if we have one -#ifdef HAS_TWO_LED - if (led1_state) - switch_LED1(LED_OFF); - else - switch_LED1(LED_ON); - led1_state = !led1_state; -#endif - - last_printtime = t; - - } // for -} // clock_loop() - #endif // HAS_IF482 || defined HAS_DCF77 From 090a1c6e3554c077e1849312c85adc9223a34e52 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Wed, 5 Feb 2020 16:25:25 +0100 Subject: [PATCH 029/106] Update senddata.cpp copy+paste-error corrected --- src/senddata.cpp | 138 ----------------------------------------------- 1 file changed, 138 deletions(-) diff --git a/src/senddata.cpp b/src/senddata.cpp index f89710ca..00986562 100644 --- a/src/senddata.cpp +++ b/src/senddata.cpp @@ -181,144 +181,6 @@ void sendData() { } // sendData() -void flushQueues() { -#if (HAS_LORA) - lora_queuereset(); -#endif -#ifdef HAS_SPI - spi_queuereset(); -#endif -} - SendBuffer.MessagePort = port; - } - memcpy(SendBuffer.Message, payload.getBuffer(), SendBuffer.MessageSize); - -// enqueue message in device's send queues -#if (HAS_LORA) - lora_enqueuedata(&SendBuffer); -#endif -#ifdef HAS_SPI - spi_enqueuedata(&SendBuffer); -#endif - -// write data to sdcard, if present -#ifdef HAS_SDCARD - sdcardWriteData(macs_wifi, macs_ble); -#endif - -} // SendPayload - -// interrupt triggered function to prepare payload to send -void sendData() { - - uint8_t bitmask = cfg.payloadmask; - uint8_t mask = 1; - #if (HAS_GPS) - gpsStatus_t gps_status; - #endif - - while (bitmask) { - switch (bitmask & mask) { - -#if ((WIFICOUNTER) || (BLECOUNTER)) - case COUNT_DATA: - payload.reset(); -#if !(PAYLOAD_OPENSENSEBOX) - if (cfg.wifiscan) - payload.addCount(macs_wifi, MAC_SNIFF_WIFI); - if (cfg.blescan) - payload.addCount(macs_ble, MAC_SNIFF_BLE); -#endif -#if (HAS_GPS) - if (GPSPORT == COUNTERPORT) { - // send GPS position only if we have a fix - if (gps_hasfix()) { - gps_storelocation(&gps_status); - payload.addGPS(gps_status); - } else - ESP_LOGD(TAG, "No valid GPS position"); - } -#endif -#if (PAYLOAD_OPENSENSEBOX) - if (cfg.wifiscan) - payload.addCount(macs_wifi, MAC_SNIFF_WIFI); - if (cfg.blescan) - payload.addCount(macs_ble, MAC_SNIFF_BLE); -#endif -#if (HAS_SDS011) - payload.addPM10(pm10); - payload.addPM25(pm25); -#endif - SendPayload(COUNTERPORT, prio_normal); - // clear counter if not in cumulative counter mode - if (cfg.countermode != 1) { - reset_counters(); // clear macs container and reset all counters - get_salt(); // get new salt for salting hashes - ESP_LOGI(TAG, "Counter cleared"); - } -#ifdef HAS_DISPLAY - else - oledPlotCurve(macs.size(), true); -#endif - break; -#endif - -#if (HAS_BME) - case MEMS_DATA: - payload.reset(); - payload.addBME(bme_status); - SendPayload(BMEPORT, prio_normal); - break; -#endif - -#if (HAS_GPS) - case GPS_DATA: - if (GPSPORT != COUNTERPORT) { - // send GPS position only if we have a fix - if (gps_hasfix()) { - gps_storelocation(&gps_status); - payload.reset(); - payload.addGPS(gps_status); - SendPayload(GPSPORT, prio_high); - } else - ESP_LOGD(TAG, "No valid GPS position"); - } - break; -#endif - -#if (HAS_SENSORS) - case SENSOR1_DATA: - payload.reset(); - payload.addSensor(sensor_read(1)); - SendPayload(SENSOR1PORT, prio_normal); - break; - case SENSOR2_DATA: - payload.reset(); - payload.addSensor(sensor_read(2)); - SendPayload(SENSOR2PORT, prio_normal); - break; - case SENSOR3_DATA: - payload.reset(); - payload.addSensor(sensor_read(3)); - SendPayload(SENSOR3PORT, prio_normal); - break; -#endif - -#if (defined BAT_MEASURE_ADC || defined HAS_PMU) - case BATT_DATA: - payload.reset(); - payload.addVoltage(read_voltage()); - SendPayload(BATTPORT, prio_normal); - break; -#endif - - } // switch - bitmask &= ~mask; - mask <<= 1; - } // while (bitmask) - -} // sendData() - void flushQueues() { #if (HAS_LORA) lora_queuereset(); From 8e8d2bf8eec51059c832cd0f54260b75c73f2aac Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Wed, 5 Feb 2020 16:34:37 +0100 Subject: [PATCH 030/106] Add files via upload --- EspSoftwareSerial/LICENSE | 502 +++++ EspSoftwareSerial/README.md | 124 ++ .../examples/loopback/loopback.ino | 263 +++ .../examples/onewiretest/onewiretest.ino | 48 + .../examples/repeater/repeater.ino | 183 ++ .../examples/servoTester/servoTester.ino | 115 ++ .../examples/swsertest/swsertest.ino | 47 + EspSoftwareSerial/keywords.txt | 43 + EspSoftwareSerial/library.json | 15 + EspSoftwareSerial/library.properties | 9 + EspSoftwareSerial/src/SoftwareSerial.cpp | 542 +++++ EspSoftwareSerial/src/SoftwareSerial.h | 255 +++ .../src/circular_queue/Delegate.h | 1786 +++++++++++++++++ .../src/circular_queue/MultiDelegate.h | 503 +++++ .../src/circular_queue/circular_queue.h | 399 ++++ .../src/circular_queue/circular_queue_mp.h | 200 ++ EspSoftwareSerial/src/circular_queue/ghostl.h | 92 + 17 files changed, 5126 insertions(+) create mode 100644 EspSoftwareSerial/LICENSE create mode 100644 EspSoftwareSerial/README.md create mode 100644 EspSoftwareSerial/examples/loopback/loopback.ino create mode 100644 EspSoftwareSerial/examples/onewiretest/onewiretest.ino create mode 100644 EspSoftwareSerial/examples/repeater/repeater.ino create mode 100644 EspSoftwareSerial/examples/servoTester/servoTester.ino create mode 100644 EspSoftwareSerial/examples/swsertest/swsertest.ino create mode 100644 EspSoftwareSerial/keywords.txt create mode 100644 EspSoftwareSerial/library.json create mode 100644 EspSoftwareSerial/library.properties create mode 100644 EspSoftwareSerial/src/SoftwareSerial.cpp create mode 100644 EspSoftwareSerial/src/SoftwareSerial.h create mode 100644 EspSoftwareSerial/src/circular_queue/Delegate.h create mode 100644 EspSoftwareSerial/src/circular_queue/MultiDelegate.h create mode 100644 EspSoftwareSerial/src/circular_queue/circular_queue.h create mode 100644 EspSoftwareSerial/src/circular_queue/circular_queue_mp.h create mode 100644 EspSoftwareSerial/src/circular_queue/ghostl.h diff --git a/EspSoftwareSerial/LICENSE b/EspSoftwareSerial/LICENSE new file mode 100644 index 00000000..f166cc57 --- /dev/null +++ b/EspSoftwareSerial/LICENSE @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! \ No newline at end of file diff --git a/EspSoftwareSerial/README.md b/EspSoftwareSerial/README.md new file mode 100644 index 00000000..2d14c9e6 --- /dev/null +++ b/EspSoftwareSerial/README.md @@ -0,0 +1,124 @@ +# EspSoftwareSerial + +## Implementation of the Arduino software serial library for the ESP8266 / ESP32 + +This fork implements interrupt service routine best practice. +In the receive interrupt, instead of blocking for whole bytes +at a time - voiding any near-realtime behavior of the CPU - only level +change and timestamp are recorded. The more time consuming phase +detection and byte assembly are done in the main code. + +Except at high bitrates, depending on other ongoing activity, +interrupts in particular, this software serial adapter +supports full duplex receive and send. At high bitrates (115200bps) +send bit timing can be improved at the expense of blocking concurrent +full duplex receives, with the ``SoftwareSerial::enableIntTx(false)`` function call. + +The same functionality is given as the corresponding AVR library but +several instances can be active at the same time. Speed up to 115200 baud +is supported. Besides a constructor compatible to the AVR SoftwareSerial class, +and updated constructor that takes no arguments exists, instead the ``begin()`` +function can handle the pin assignments and logic inversion. +It also has optional input buffer capacity arguments for byte buffer and ISR bit buffer. +This way, it is a better drop-in replacement for the hardware serial APIs on the ESP MCUs. + +Please note that due to the fact that the ESPs always have other activities +ongoing, there will be some inexactness in interrupt timings. This may +lead to inevitable, but few, bit errors when having heavy data traffic +at high baud rates. + +## Resource optimization + +The memory footprint can be optimized to just fit the amount of expected +incoming asynchronous data. +For this, the ``SoftwareSerial`` constructor provides two arguments. First, the +octet buffer capacity for assembled received octets can be set. Read calls are +satisfied from this buffer, freeing it in return. +Second, the signal edge detection buffer of 32bit fields can be resized. +One octet may require up to to 10 fields, but fewer may be needed, +depending on the bit pattern. Any read or write calls check this buffer +to assemble received octets, thus promoting completed octets to the octet +buffer, freeing fields in the edge detection buffer. + +Look at the swsertest.ino example. There, on reset, ASCII characters ' ' to 'z' +are sent. This happens not as a block write, but in a single write call per +character. As the example uses a local loopback wire, every outgoing bit is +immediately received back. Therefore, any single write call causes up to +10 fields - depending on the exact bit pattern - to be occupied in the signal +edge detection buffer. In turn, as explained before, each single write call +also causes received bit assembly to be performed, promoting these bits from +the signal edge detection buffer to the octet buffer as soon as possible. +Explaining by way of contrast, if during a a single write call, perhaps because +of using block writing, more than a single octet is received, there will be a +need for more than 10 fields in the signal edge detection buffer. +The necessary capacity of the octet buffer only depends on the amount of incoming +data until the next read call. + +For the swsertest.ino example, this results in the following optimized +constructor arguments to spend only the minimum RAM on buffers required: + +The octet buffer capacity (``bufCapacity``) is 93 (91 characters net plus two tolerance). +The signal edge detection buffer capacity (``isrBufCapacity``) is 10, as each octet has +10 bits on the wire, which are immediately received during the write, and each +write call causes the signal edge detection to promote the previously sent and +received bits to the octet buffer. + +In a more generalized scenario, calculate the bits (use message size in octets +times 10) that may be asynchronously received to determine the value for +``isrBufCapacity`` in the constructor. Also use the number of received octets +that must be buffered for reading as the value of ``bufCapacity``. +The more frequently your code calls write or read functions, the greater the +chances are that you can reduce the ``isrBufCapacity`` footprint without losing data, +and each time you call read to fetch from the octet buffer, you reduce the +need for space there. + +## SoftwareSerialConfig and parity +The configuration of the data stream is done via a ``SoftwareSerialConfig`` +argument to ``begin()``. Word lengths can be set to between 5 and 8 bits, parity +can be N(one), O(dd) or E(ven) and 1 or 2 stop bits can be used. The default is +``SWSERIAL_8N1`` using 8 bits, no parity and 1 stop bit but any combination can +be used, e.g. ``SWSERIAL_7E2``. If using EVEN or ODD parity, any parity errors +can be detected with the ``peekParityError()`` function. Note that parity +checking must be done before ``read()``, as the parity information is removed +from the buffer when reading the corresponding byte. + +To allow flexible 9-bit and data/addressing protocols, the additional parity +modes MARK and SPACE are also available. Furthermore, the parity mode can be +individually set in each call to ``write()``. + +This allows a simple implementation of protocols where the parity bit is used to +distinguish between data and addresses/commands ("9-bit" protocols). First set +up SoftwareSerial with parity mode SPACE, e.g. ``SWSERIAL_8S1``. This will add a +parity bit to every byte sent, setting it to logical zero (SPACE parity). + +To detect incoming bytes with the parity bit set (MARK parity), use the +``peekParityError()`` function. To send a byte with the parity bit set, just add +``MARK`` as the second argument when writing, e.g. ``write(ch, MARK)``. + +## Using and updating EspSoftwareSerial in the esp8266com/esp8266 Arduino build environment + +EspSoftwareSerial is both part of the BSP download for ESP8266 in Arduino, +and it is set up as a Git submodule in the esp8266 source tree, +specifically in ``.../esp8266/libraries/SoftwareSerial`` when using a Github +repository clone in your Arduino sketchbook hardware directory. +This supersedes any version of EspSoftwareSerial installed for instance via +the Arduino library manager, it is not required to install EspSoftwareSerial +for the ESP8266 separately at all, but doing so has ill effect. + +The responsible maintainer of the esp8266 repository has kindly shared the +following command line instructions to use, if one wishes to manually +update EspSoftwareSerial to a newer release than pulled in via the ESP8266 Arduino BSP: + +To update esp8266/arduino SoftwareSerial submodule to lastest master: + +Clean it (optional): +```shell +$ rm -rf libraries/SoftwareSerial +$ git submodule update --init +``` +Now update it: +```shell +$ cd libraries/SoftwareSerial +$ git checkout master +$ git pull +``` diff --git a/EspSoftwareSerial/examples/loopback/loopback.ino b/EspSoftwareSerial/examples/loopback/loopback.ino new file mode 100644 index 00000000..b612bdec --- /dev/null +++ b/EspSoftwareSerial/examples/loopback/loopback.ino @@ -0,0 +1,263 @@ +#include + +// On ESP8266: +// Local SoftwareSerial loopback, connect D5 (rx) and D6 (tx). +// For local hardware loopback, connect D5 to D8 (tx), D6 to D7 (rx). +// For hardware send/sink, connect D7 (rx) and D8 (tx). +// Hint: The logger is run at 9600bps such that enableIntTx(true) can remain unchanged. Blocking +// interrupts severely impacts the ability of the SoftwareSerial devices to operate concurrently +// and/or in duplex mode. +// Operating in software serial full duplex mode, runs at 19200bps and few errors (~2.5%). +// Operating in software serial half duplex mode (both loopback and repeater), +// runs at 57600bps with nearly no errors. +// Operating loopback in full duplex, and repeater in half duplex, runs at 38400bps with nearly no errors. +// On ESP32: +// For SoftwareSerial or hardware send/sink, connect D5 (rx) and D6 (tx). +// Hardware Serial2 defaults to D4 (rx), D3 (tx). +// For local hardware loopback, connect D5 (rx) to D3 (tx), D6 (tx) to D4 (rx). + +#if defined(ESP8266) && !defined(D5) +#define D5 (14) +#define D6 (12) +#define D7 (13) +#define D8 (15) +#define TX (1) +#endif + +// Pick only one of HWLOOPBACK, HWSOURCESWSINK, or HWSOURCESINK +//#define HWLOOPBACK 1 +//#define HWSOURCESWSINK 1 +//#define HWSOURCESINK 1 +#define HALFDUPLEX 1 + +#ifdef ESP32 +constexpr int IUTBITRATE = 19200; +#else +constexpr int IUTBITRATE = 19200; +#endif + +#if defined(ESP8266) +constexpr SoftwareSerialConfig swSerialConfig = SWSERIAL_8E1; +constexpr SerialConfig hwSerialConfig = SERIAL_8E1; +#elif defined(ESP32) +constexpr SoftwareSerialConfig swSerialConfig = SWSERIAL_8E1; +constexpr uint32_t hwSerialConfig = SERIAL_8E1; +#else +constexpr unsigned swSerialConfig = 3; +#endif +constexpr bool invert = false; + +constexpr int BLOCKSIZE = 16; // use fractions of 256 + +unsigned long start; +String effTxTxt("eff. tx: "); +String effRxTxt("eff. rx: "); +int txCount; +int rxCount; +int expected; +int rxErrors; +int rxParityErrors; +constexpr int ReportInterval = IUTBITRATE / 8; + +#if defined(ESP8266) +#if defined(HWLOOPBACK) || defined(HWSOURCESWSINK) +HardwareSerial& hwSerial(Serial); +SoftwareSerial serialIUT; +SoftwareSerial logger; +#elif defined(HWSOURCESINK) +HardwareSerial& serialIUT(Serial); +SoftwareSerial logger; +#else +SoftwareSerial serialIUT; +HardwareSerial& logger(Serial); +#endif +#elif defined(ESP32) +#if defined(HWLOOPBACK) || defined (HWSOURCESWSINK) +HardwareSerial& hwSerial(Serial2); +SoftwareSerial serialIUT; +#elif defined(HWSOURCESINK) +HardwareSerial& serialIUT(Serial2); +#else +SoftwareSerial serialIUT; +#endif +HardwareSerial& logger(Serial); +#else +SoftwareSerial serialIUT(14, 12); +HardwareSerial& logger(Serial); +#endif + +void setup() { +#if defined(ESP8266) +#if defined(HWLOOPBACK) || defined(HWSOURCESINK) || defined(HWSOURCESWSINK) + Serial.begin(IUTBITRATE, hwSerialConfig, SERIAL_FULL, 1, invert); + Serial.swap(); + Serial.setRxBufferSize(2 * BLOCKSIZE); + logger.begin(9600, SWSERIAL_8N1, -1, TX); +#else + logger.begin(9600); +#endif +#if !defined(HWSOURCESINK) + serialIUT.begin(IUTBITRATE, swSerialConfig, D5, D6, invert, 2 * BLOCKSIZE); +#ifdef HALFDUPLEX + serialIUT.enableIntTx(false); +#endif +#endif +#elif defined(ESP32) +#if defined(HWLOOPBACK) || defined(HWSOURCESWSINK) + Serial2.begin(IUTBITRATE, hwSerialConfig, D4, D3, invert); + Serial2.setRxBufferSize(2 * BLOCKSIZE); +#elif defined(HWSOURCESINK) + serialIUT.begin(IUTBITRATE, hwSerialConfig, D5, D6, invert); + serialIUT.setRxBufferSize(2 * BLOCKSIZE); +#endif +#if !defined(HWSOURCESINK) + serialIUT.begin(IUTBITRATE, swSerialConfig, D5, D6, invert, 2 * BLOCKSIZE); +#ifdef HALFDUPLEX + serialIUT.enableIntTx(false); +#endif +#endif + logger.begin(9600); +#else +#if !defined(HWSOURCESINK) + serialIUT.begin(IUTBITRATE); +#endif + logger.begin(9600); +#endif + + logger.println("Loopback example for EspSoftwareSerial"); + + start = micros(); + txCount = 0; + rxCount = 0; + rxErrors = 0; + rxParityErrors = 0; + expected = -1; +} + +unsigned char c = 0; + +void loop() { +#ifdef HALFDUPLEX + char block[BLOCKSIZE]; +#endif + char inBuf[BLOCKSIZE]; + for (int i = 0; i < BLOCKSIZE; ++i) { +#ifndef HALFDUPLEX +#ifdef HWSOURCESWSINK + hwSerial.write(c); +#else + serialIUT.write(c); +#endif +#ifdef HWLOOPBACK + int avail = hwSerial.available(); + while ((0 == (i % 8)) && avail > 0) { + int inCnt = hwSerial.read(inBuf, min(avail, min(BLOCKSIZE, hwSerial.availableForWrite()))); + hwSerial.write(inBuf, inCnt); + avail -= inCnt; + } +#endif +#else + block[i] = c; +#endif + c = (c + 1) % 256; + ++txCount; + } +#ifdef HALFDUPLEX +#ifdef HWSOURCESWSINK + hwSerial.write(block, BLOCKSIZE); +#else + serialIUT.write(block, BLOCKSIZE); +#endif +#endif +#ifdef HWSOURCESINK +#if defined(ESP8266) + if (serialIUT.hasOverrun()) { logger.println("serialIUT.overrun"); } +#endif +#else + if (serialIUT.overflow()) { logger.println("serialIUT.overflow"); } +#endif + + int inCnt; + uint32_t deadlineStart; + +#ifdef HWLOOPBACK + // starting deadline for the first bytes to become readable + deadlineStart = ESP.getCycleCount(); + inCnt = 0; + while ((ESP.getCycleCount() - deadlineStart) < (1000000UL * 12 * BLOCKSIZE) / IUTBITRATE * 24 * ESP.getCpuFreqMHz()) { + int avail = hwSerial.available(); + inCnt += hwSerial.read(&inBuf[inCnt], min(avail, min(BLOCKSIZE - inCnt, hwSerial.availableForWrite()))); + if (inCnt >= BLOCKSIZE) { break; } + // wait for more outstanding bytes to trickle in + if (avail) deadlineStart = ESP.getCycleCount(); + } + hwSerial.write(inBuf, inCnt); +#endif + + // starting deadline for the first bytes to come in + deadlineStart = ESP.getCycleCount(); + inCnt = 0; + while ((ESP.getCycleCount() - deadlineStart) < (1000000UL * 12 * BLOCKSIZE) / IUTBITRATE * 8 * ESP.getCpuFreqMHz()) { + int avail; + if (0 != (swSerialConfig & 070)) + avail = serialIUT.available(); + else + avail = serialIUT.read(inBuf, BLOCKSIZE); + for (int i = 0; i < avail; ++i) + { + unsigned char r; + if (0 != (swSerialConfig & 070)) + r = serialIUT.read(); + else + r = inBuf[i]; + if (expected == -1) { expected = r; } + else { + expected = (expected + 1) % (1UL << (5 + swSerialConfig % 4)); + } + if (r != expected) { + ++rxErrors; + expected = -1; + } +#ifndef HWSOURCESINK + if (serialIUT.readParity() != (static_cast(swSerialConfig & 010) ? serialIUT.parityOdd(r) : serialIUT.parityEven(r))) + { + ++rxParityErrors; + } +#endif + ++rxCount; + ++inCnt; + } + + if (inCnt >= BLOCKSIZE) { break; } + // wait for more outstanding bytes to trickle in + if (avail) deadlineStart = ESP.getCycleCount(); + } + + const uint32_t interval = micros() - start; + if (txCount >= ReportInterval && interval) { + uint8_t wordBits = (5 + swSerialConfig % 4) + static_cast(swSerialConfig & 070) + 1 + ((swSerialConfig & 0300) ? 1 : 0); + logger.println(String("tx/rx: ") + txCount + "/" + rxCount); + const long txCps = txCount * (1000000.0 / interval); + const long rxCps = rxCount * (1000000.0 / interval); + logger.print(effTxTxt + wordBits * txCps + "bps, " + + effRxTxt + wordBits * rxCps + "bps, " + + rxErrors + " errors (" + 100.0 * rxErrors / (!rxErrors ? 1 : rxCount) + "%)"); + if (0 != (swSerialConfig & 070)) + { + logger.print(" ("); logger.print(rxParityErrors); logger.println(" parity errors)"); + } + else + { + logger.println(); + } + txCount = 0; + rxCount = 0; + rxErrors = 0; + rxParityErrors = 0; + expected = -1; + // resync + delay(1000UL * 12 * BLOCKSIZE / IUTBITRATE * 16); + serialIUT.flush(); + start = micros(); + } +} diff --git a/EspSoftwareSerial/examples/onewiretest/onewiretest.ino b/EspSoftwareSerial/examples/onewiretest/onewiretest.ino new file mode 100644 index 00000000..3e96401b --- /dev/null +++ b/EspSoftwareSerial/examples/onewiretest/onewiretest.ino @@ -0,0 +1,48 @@ +#include +#include "SoftwareSerial.h" + +SoftwareSerial swSer1; +SoftwareSerial swSer2; + +void setup() { + delay(2000); + Serial.begin(115200); + Serial.println("\nOne Wire Half Duplex Serial Tester"); + swSer1.begin(115200, SWSERIAL_8N1, 12, 12, false, 256); + swSer1.enableIntTx(true); + swSer2.begin(115200, SWSERIAL_8N1, 14, 14, false, 256); + swSer2.enableIntTx(true); +} + +void loop() { + Serial.println("\n\nTesting on swSer1"); + Serial.print("Enter something to send using swSer1."); + checkSwSerial(&swSer1); + + Serial.println("\n\nTesting on swSer2"); + Serial.print("Enter something to send using swSer2."); + checkSwSerial(&swSer2); + +} + +void checkSwSerial(SoftwareSerial* ss) { + byte ch; + while (!Serial.available()); + ss->enableTx(true); + while (Serial.available()) { + ch = Serial.read(); + ss->write(ch); + } + ss->enableTx(false); + // wait 1 second for the reply from SOftwareSerial if any + delay(1000); + if (ss->available()) { + Serial.print("\nResult:"); + while (ss->available()) { + ch = (byte)ss->read(); + Serial.print(ch < 0x01 ? " 0" : " "); + Serial.print(ch, HEX); + } + Serial.println(); + } +} diff --git a/EspSoftwareSerial/examples/repeater/repeater.ino b/EspSoftwareSerial/examples/repeater/repeater.ino new file mode 100644 index 00000000..fa5566de --- /dev/null +++ b/EspSoftwareSerial/examples/repeater/repeater.ino @@ -0,0 +1,183 @@ +#include + +// On ESP8266: +// SoftwareSerial loopback for remote source (loopback.ino), or hardware loopback. +// Connect source D5 (rx) to local D8 (tx), source D6 (tx) to local D7 (rx). +// Hint: The logger is run at 9600bps such that enableIntTx(true) can remain unchanged. Blocking +// interrupts severely impacts the ability of the SoftwareSerial devices to operate concurrently +// and/or in duplex mode. +// On ESP32: +// For software or hardware loopback, connect source rx to local D8 (tx), source tx to local D7 (rx). + +#if defined(ESP8266) && !defined(D5) +#define D5 (14) +#define D6 (12) +#define D7 (13) +#define D8 (15) +#define TX (1) +#endif + +#define HWLOOPBACK 1 +#define HALFDUPLEX 1 + +#ifdef ESP32 +constexpr int IUTBITRATE = 19200; +#else +constexpr int IUTBITRATE = 19200; +#endif + +#if defined(ESP8266) +constexpr SoftwareSerialConfig swSerialConfig = SWSERIAL_8E1; +constexpr SerialConfig hwSerialConfig = SERIAL_8E1; +#elif defined(ESP32) +constexpr SoftwareSerialConfig swSerialConfig = SWSERIAL_8E1; +constexpr uint32_t hwSerialConfig = SERIAL_8E1; +#else +constexpr unsigned swSerialConfig = 3; +#endif +constexpr bool invert = false; + +constexpr int BLOCKSIZE = 16; // use fractions of 256 + +unsigned long start; +String bitRateTxt("Effective data rate: "); +int rxCount; +int seqErrors; +int parityErrors; +int expected; +constexpr int ReportInterval = IUTBITRATE / 8; + +#if defined(ESP8266) +#if defined(HWLOOPBACK) +HardwareSerial& repeater(Serial); +SoftwareSerial logger; +#else +SoftwareSerial repeater; +HardwareSerial& logger(Serial); +#endif +#elif defined(ESP32) +#if defined(HWLOOPBACK) +HardwareSerial& repeater(Serial2); +#else +SoftwareSerial repeater; +#endif +HardwareSerial& logger(Serial); +#else +SoftwareSerial repeater(14, 12); +HardwareSerial& logger(Serial); +#endif + +void setup() { +#if defined(ESP8266) +#if defined(HWLOOPBACK) + repeater.begin(IUTBITRATE, hwSerialConfig, SERIAL_FULL, 1, invert); + repeater.swap(); + repeater.setRxBufferSize(2 * BLOCKSIZE); + logger.begin(9600, SWSERIAL_8N1, -1, TX); +#else + repeater.begin(IUTBITRATE, swSerialConfig, D7, D8, invert, 4 * BLOCKSIZE); +#ifdef HALFDUPLEX + repeater.enableIntTx(false); +#endif + logger.begin(9600); +#endif +#elif defined(ESP32) +#if defined(HWLOOPBACK) + repeater.begin(IUTBITRATE, hwSerialConfig, D7, D8, invert); + repeater.setRxBufferSize(2 * BLOCKSIZE); +#else + repeater.begin(IUTBITRATE, swSerialConfig, D7, D8, invert, 4 * BLOCKSIZE); +#ifdef HALFDUPLEX + repeater.enableIntTx(false); +#endif +#endif + logger.begin(9600); +#else + repeater.begin(IUTBITRATE); + logger.begin(9600); +#endif + + logger.println("Repeater example for EspSoftwareSerial"); + start = micros(); + rxCount = 0; + seqErrors = 0; + parityErrors = 0; + expected = -1; +} + +void loop() { +#ifdef HWLOOPBACK +#if defined(ESP8266) + if (repeater.hasOverrun()) { logger.println("repeater.overrun"); } +#endif +#else + if (repeater.overflow()) { logger.println("repeater.overflow"); } +#endif + +#ifdef HALFDUPLEX + char block[BLOCKSIZE]; +#endif + // starting deadline for the first bytes to come in + uint32_t deadlineStart = ESP.getCycleCount(); + int inCnt = 0; + while ((ESP.getCycleCount() - deadlineStart) < (1000000UL * 12 * BLOCKSIZE) / IUTBITRATE * 24 * ESP.getCpuFreqMHz()) { + int avail = repeater.available(); + for (int i = 0; i < avail; ++i) + { + int r = repeater.read(); + if (r == -1) { logger.println("read() == -1"); } + if (expected == -1) { expected = r; } + else { + expected = (expected + 1) % (1UL << (5 + swSerialConfig % 4)); + } + if (r != expected) { + ++seqErrors; + expected = -1; + } +#ifndef HWLOOPBACK + if (repeater.readParity() != (static_cast(swSerialConfig & 010) ? repeater.parityOdd(r) : repeater.parityEven(r))) + { + ++parityErrors; + } +#endif + ++rxCount; +#ifdef HALFDUPLEX + block[inCnt] = r; +#else + repeater.write(r); +#endif + if (++inCnt >= BLOCKSIZE) { break; } + } + if (inCnt >= BLOCKSIZE) { break; } + // wait for more outstanding bytes to trickle in + if (avail) deadlineStart = ESP.getCycleCount(); + } + +#ifdef HALFDUPLEX + repeater.write(block, inCnt); +#endif + + if (rxCount >= ReportInterval) { + auto end = micros(); + unsigned long interval = end - start; + long cps = rxCount * (1000000.0 / interval); + long seqErrorsps = seqErrors * (1000000.0 / interval); + logger.print(bitRateTxt + 10 * cps + "bps, " + + seqErrorsps + "cps seq. errors (" + 100.0 * seqErrors / rxCount + "%)"); +#ifndef HWLOOPBACK + if (0 != (swSerialConfig & 070)) + { + logger.print(" ("); logger.print(parityErrors); logger.print(" parity errors)"); + } + else +#endif + { + logger.println(); + } + start = end; + rxCount = 0; + seqErrors = 0; + parityErrors = 0; + expected = -1; + } +} diff --git a/EspSoftwareSerial/examples/servoTester/servoTester.ino b/EspSoftwareSerial/examples/servoTester/servoTester.ino new file mode 100644 index 00000000..cbc784d8 --- /dev/null +++ b/EspSoftwareSerial/examples/servoTester/servoTester.ino @@ -0,0 +1,115 @@ +#include +#include + +SoftwareSerial swSer; + +byte buf[10] = { 0xFA, 0xAF,0x00,0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0xED }; +byte cmd[10] = { 0xFA, 0xAF,0x00,0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0xED }; +byte ver[10] = { 0xFC, 0xCF,0x00,0xAA,0x41, 0x16, 0x51, 0x01, 0x00, 0xED }; + + +void setup() { + delay(2000); + Serial.begin(115200); + Serial.println("\nAlpha 1S Servo Tester"); + swSer.begin(115200, SWSERIAL_8N1, 12, 12, false, 256); +} + +void loop() { + for (int i = 1; i <= 32; i++) { + GetVersion(i); + delay(100); + } + SetLED(1, 0); + GoPos(1, 0, 50); + delay(1000); + GoPos(1, 90, 50); + delay(1000); + GoPos(1, 100, 50); + delay(1000); + SetLED(1, 1); + delay(2000); +} + + + + +void GetVersion(byte id) { + memcpy(buf, cmd, 10); + buf[0] = 0xFC; + buf[1] = 0xCF; + buf[2] = id; + buf[3] = 0x01; + SendCommand(); +} + + +void GoPos(byte id, byte Pos, byte Time) { + memcpy(buf, cmd, 10); + buf[2] = id; + buf[3] = 0x01; + buf[4] = Pos; + buf[5] = Time; + buf[6] = 0x00; + buf[7] = Time; + SendCommand(); +} + +void GetPos(byte id) { + memcpy(buf, cmd, 10); + buf[2] = id; + buf[3] = 0x02; + SendCommand(); +} + + +void SetLED(byte id, byte mode) { + memcpy(buf, cmd, 10); + buf[2] = id; + buf[3] = 0x04; + buf[4] = mode; + SendCommand(); +} + +void SendCommand() { + SendCommand(true); +} + +void SendCommand(bool checkResult) { + byte sum = 0; + for (int i = 2; i < 8; i++) { + sum += buf[i]; + } + buf[8] = sum; + ShowCommand(); + swSer.flush(); + swSer.enableTx(true); + swSer.write(buf, 10); + swSer.enableTx(false); + if (checkResult) checkReturn(); +} + +void ShowCommand() { + Serial.print(millis()); + Serial.print(" OUT>>"); + for (int i = 0; i < 10; i++) { + Serial.print((buf[i] < 0x10 ? " 0" : " ")); + Serial.print(buf[i], HEX); + } + Serial.println(); +} + +void checkReturn() { + unsigned long startMs = millis(); + while (((millis() - startMs) < 500) && (!swSer.available())); + if (swSer.available()) { + Serial.print(millis()); + Serial.print(" IN>>>"); + while (swSer.available()) { + byte ch = (byte)swSer.read(); + Serial.print((ch < 0x10 ? " 0" : " ")); + Serial.print(ch, HEX); + } + Serial.println(); + } +} diff --git a/EspSoftwareSerial/examples/swsertest/swsertest.ino b/EspSoftwareSerial/examples/swsertest/swsertest.ino new file mode 100644 index 00000000..a047c1be --- /dev/null +++ b/EspSoftwareSerial/examples/swsertest/swsertest.ino @@ -0,0 +1,47 @@ +// On ESP8266: +// At 80MHz runs up 57600ps, and at 160MHz CPU frequency up to 115200bps with only negligible errors. +// Connect pin 12 to 14. + +#include + +#if defined(ESP8266) && !defined(D5) +#define D5 (14) +#define D6 (12) +#define D7 (13) +#define D8 (15) +#endif + +#ifdef ESP32 +#define BAUD_RATE 57600 +#else +#define BAUD_RATE 57600 +#endif + +// Reminder: the buffer size optimizations here, in particular the isrBufSize that only accommodates +// a single 8N1 word, are on the basis that any char written to the loopback SoftwareSerial adapter gets read +// before another write is performed. Block writes with a size greater than 1 would usually fail. +SoftwareSerial swSer; + +void setup() { + Serial.begin(115200); + swSer.begin(BAUD_RATE, SWSERIAL_8N1, D5, D6, false, 95, 11); + + Serial.println("\nSoftware serial test started"); + + for (char ch = ' '; ch <= 'z'; ch++) { + swSer.write(ch); + } + swSer.println(""); +} + +void loop() { + while (swSer.available() > 0) { + Serial.write(swSer.read()); + yield(); + } + while (Serial.available() > 0) { + swSer.write(Serial.read()); + yield(); + } + +} diff --git a/EspSoftwareSerial/keywords.txt b/EspSoftwareSerial/keywords.txt new file mode 100644 index 00000000..52d48ab3 --- /dev/null +++ b/EspSoftwareSerial/keywords.txt @@ -0,0 +1,43 @@ +####################################### +# Syntax Coloring Map for SoftwareSerial +# (esp8266) +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +SoftwareSerial KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +begin KEYWORD2 +baudRate KEYWORD2 +setTransmitEnablePin KEYWORD2 +enableIntTx KEYWORD2 +overflow KEYWORD2 +available KEYWORD2 +peek KEYWORD2 +read KEYWORD2 +flush KEYWORD2 +write KEYWORD2 +enableRx KEYWORD2 +enableTx KEYWORD2 +listen KEYWORD2 +end KEYWORD2 +isListening KEYWORD2 +stopListening KEYWORD2 +onReceive KEYWORD2 +perform_work KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### + +SW_SERIAL_UNUSED_PIN LITERAL1 +SWSERIAL_5N1 LITERAL1 +SWSERIAL_6N1 LITERAL1 +SWSERIAL_7N1 LITERAL1 +SWSERIAL_8N1 LITERAL1 diff --git a/EspSoftwareSerial/library.json b/EspSoftwareSerial/library.json new file mode 100644 index 00000000..3415b056 --- /dev/null +++ b/EspSoftwareSerial/library.json @@ -0,0 +1,15 @@ +{ + "name": "EspSoftwareSerial", + "version": "6.6.1", + "keywords": [ + "serial", "io", "softwareserial" + ], + "description": "Implementation of the Arduino software serial for ESP8266/ESP32.", + "repository": + { + "type": "git", + "url": "https://github.com/plerup/espsoftwareserial" + }, + "frameworks": "arduino", + "platforms": "*" +} diff --git a/EspSoftwareSerial/library.properties b/EspSoftwareSerial/library.properties new file mode 100644 index 00000000..bd052153 --- /dev/null +++ b/EspSoftwareSerial/library.properties @@ -0,0 +1,9 @@ +name=EspSoftwareSerial +version=6.6.1 +author=Peter Lerup, Dirk Kaar +maintainer=Peter Lerup +sentence=Implementation of the Arduino software serial for ESP8266/ESP32. +paragraph= +category=Signal Input/Output +url=https://github.com/plerup/espsoftwareserial/ +architectures=esp8266,esp32 diff --git a/EspSoftwareSerial/src/SoftwareSerial.cpp b/EspSoftwareSerial/src/SoftwareSerial.cpp new file mode 100644 index 00000000..0e7b8e08 --- /dev/null +++ b/EspSoftwareSerial/src/SoftwareSerial.cpp @@ -0,0 +1,542 @@ +/* + +SoftwareSerial.cpp - Implementation of the Arduino software serial for ESP8266/ESP32. +Copyright (c) 2015-2016 Peter Lerup. All rights reserved. +Copyright (c) 2018-2019 Dirk O. Kaar. All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "SoftwareSerial.h" +#include + +#ifdef ESP32 +#define xt_rsil(a) (a) +#define xt_wsr_ps(a) +#endif + +constexpr uint8_t BYTE_ALL_BITS_SET = ~static_cast(0); + +SoftwareSerial::SoftwareSerial() { + m_isrOverflow = false; +} + +SoftwareSerial::SoftwareSerial(int8_t rxPin, int8_t txPin, bool invert) +{ + m_isrOverflow = false; + m_rxPin = rxPin; + m_txPin = txPin; + m_invert = invert; +} + +SoftwareSerial::~SoftwareSerial() { + end(); +} + +bool SoftwareSerial::isValidGPIOpin(int8_t pin) { +#if defined(ESP8266) + return (pin >= 0 && pin <= 5) || (pin >= 12 && pin <= 15); +#elif defined(ESP32) + return pin == 0 || pin == 2 || (pin >= 4 && pin <= 5) || (pin >= 12 && pin <= 19) || + (pin >= 21 && pin <= 23) || (pin >= 25 && pin <= 27) || (pin >= 32 && pin <= 35); +#else + return true; +#endif +} + +void SoftwareSerial::begin(uint32_t baud, SoftwareSerialConfig config, + int8_t rxPin, int8_t txPin, + bool invert, int bufCapacity, int isrBufCapacity) { + if (-1 != rxPin) m_rxPin = rxPin; + if (-1 != txPin) m_txPin = txPin; + m_oneWire = (m_rxPin == m_txPin); + m_invert = invert; + m_dataBits = 5 + (config & 07); + m_parityMode = static_cast(config & 070); + m_stopBits = 1 + ((config & 0300) ? 1 : 0); + m_pduBits = m_dataBits + static_cast(m_parityMode) + m_stopBits; + m_bitCycles = (ESP.getCpuFreqMHz() * 1000000UL + baud / 2) / baud; + m_intTxEnabled = true; + if (isValidGPIOpin(m_rxPin)) { + std::unique_ptr > buffer(new circular_queue((bufCapacity > 0) ? bufCapacity : 64)); + m_buffer = move(buffer); + if (m_parityMode) + { + std::unique_ptr > parityBuffer(new circular_queue((bufCapacity > 0) ? (bufCapacity + 7) / 8 : 8)); + m_parityBuffer = move(parityBuffer); + m_parityInPos = m_parityOutPos = 1; + } + std::unique_ptr > isrBuffer(new circular_queue((isrBufCapacity > 0) ? isrBufCapacity : (sizeof(uint8_t) * 8 + 2) * bufCapacity)); + m_isrBuffer = move(isrBuffer); + if (m_buffer && (!m_parityMode || m_parityBuffer) && m_isrBuffer) { + m_rxValid = true; + pinMode(m_rxPin, INPUT_PULLUP); + } + } + if (isValidGPIOpin(m_txPin) +#ifdef ESP8266 + || ((m_txPin == 16) && !m_oneWire)) { +#else + ) { +#endif + m_txValid = true; + if (!m_oneWire) { + pinMode(m_txPin, OUTPUT); + digitalWrite(m_txPin, !m_invert); + } + } + if (!m_rxEnabled) { enableRx(true); } +} + +void SoftwareSerial::end() +{ + enableRx(false); + m_txValid = false; + if (m_buffer) { + m_buffer.reset(); + } + m_parityBuffer.reset(); + if (m_isrBuffer) { + m_isrBuffer.reset(); + } +} + +uint32_t SoftwareSerial::baudRate() { + return ESP.getCpuFreqMHz() * 1000000UL / m_bitCycles; +} + +void SoftwareSerial::setTransmitEnablePin(int8_t txEnablePin) { + if (isValidGPIOpin(txEnablePin)) { + m_txEnableValid = true; + m_txEnablePin = txEnablePin; + pinMode(m_txEnablePin, OUTPUT); + digitalWrite(m_txEnablePin, LOW); + } + else { + m_txEnableValid = false; + } +} + +void SoftwareSerial::enableIntTx(bool on) { + m_intTxEnabled = on; +} + +void SoftwareSerial::enableTx(bool on) { + if (m_txValid && m_oneWire) { + if (on) { + enableRx(false); + pinMode(m_txPin, OUTPUT); + digitalWrite(m_txPin, !m_invert); + } + else { + pinMode(m_rxPin, INPUT_PULLUP); + enableRx(true); + } + } +} + +void SoftwareSerial::enableRx(bool on) { + if (m_rxValid) { + if (on) { + m_rxCurBit = m_pduBits - 1; + // Init to stop bit level and current cycle + m_isrLastCycle = (ESP.getCycleCount() | 1) ^ m_invert; + if (m_bitCycles >= (ESP.getCpuFreqMHz() * 1000000UL) / 74880UL) + attachInterruptArg(digitalPinToInterrupt(m_rxPin), reinterpret_cast(rxBitISR), this, CHANGE); + else + attachInterruptArg(digitalPinToInterrupt(m_rxPin), reinterpret_cast(rxBitSyncISR), this, m_invert ? RISING : FALLING); + } + else { + detachInterrupt(digitalPinToInterrupt(m_rxPin)); + } + m_rxEnabled = on; + } +} + +int SoftwareSerial::read() { + if (!m_rxValid) { return -1; } + if (!m_buffer->available()) { + rxBits(); + if (!m_buffer->available()) { return -1; } + } + auto val = m_buffer->pop(); + if (m_parityBuffer) + { + m_lastReadParity = m_parityBuffer->peek() & m_parityOutPos; + m_parityOutPos <<= 1; + if (!m_parityOutPos) + { + m_parityOutPos = 1; + m_parityBuffer->pop(); + } + } + return val; +} + +size_t SoftwareSerial::read(uint8_t * buffer, size_t size) { + if (!m_rxValid) { return 0; } + size_t avail; + if (0 == (avail = m_buffer->pop_n(buffer, size))) { + rxBits(); + avail = m_buffer->pop_n(buffer, size); + } + if (!avail) return 0; + if (m_parityBuffer) { + uint32_t parityBits = avail; + while (m_parityOutPos >>= 1) ++parityBits; + m_parityOutPos = (1 << (parityBits % 8)); + m_parityBuffer->pop_n(nullptr, parityBits / 8); + } + return avail; +} + +size_t SoftwareSerial::readBytes(uint8_t * buffer, size_t size) { + if (!m_rxValid || !size) { return 0; } + size_t count = 0; + const auto start = millis(); + do { + count += read(&buffer[count], size - count); + if (count >= size) break; + yield(); + } while (millis() - start < _timeout); + return count; +} + +int SoftwareSerial::available() { + if (!m_rxValid) { return 0; } + rxBits(); + int avail = m_buffer->available(); + if (!avail) { + optimistic_yield(10000UL); + } + return avail; +} + +void ICACHE_RAM_ATTR SoftwareSerial::preciseDelay(bool sync) { + if (!sync) + { + // Reenable interrupts while delaying to avoid other tasks piling up + if (!m_intTxEnabled) { xt_wsr_ps(m_savedPS); } + auto expired = ESP.getCycleCount() - m_periodStart; + if (expired < m_periodDuration) + { + auto ms = (m_periodDuration - expired) / ESP.getCpuFreqMHz() / 1000UL; + if (ms) delay(ms); + } + while ((ESP.getCycleCount() - m_periodStart) < m_periodDuration) { optimistic_yield(10000); } + // Disable interrupts again + if (!m_intTxEnabled) { m_savedPS = xt_rsil(15); } + } + else + { + while ((ESP.getCycleCount() - m_periodStart) < m_periodDuration) {} + } + m_periodDuration = 0; + m_periodStart = ESP.getCycleCount(); +} + +void ICACHE_RAM_ATTR SoftwareSerial::writePeriod( + uint32_t dutyCycle, uint32_t offCycle, bool withStopBit) { + preciseDelay(true); + if (dutyCycle) + { + digitalWrite(m_txPin, HIGH); + m_periodDuration += dutyCycle; + if (offCycle || (withStopBit && !m_invert)) preciseDelay(!withStopBit || m_invert); + } + if (offCycle) + { + digitalWrite(m_txPin, LOW); + m_periodDuration += offCycle; + if (withStopBit && m_invert) preciseDelay(false); + } +} + +size_t SoftwareSerial::write(uint8_t byte) { + return write(&byte, 1); +} + +size_t SoftwareSerial::write(uint8_t byte, SoftwareSerialParity parity) { + return write(&byte, 1, parity); +} + +size_t SoftwareSerial::write(const uint8_t * buffer, size_t size) { + return write(buffer, size, m_parityMode); +} + +size_t ICACHE_RAM_ATTR SoftwareSerial::write(const uint8_t * buffer, size_t size, SoftwareSerialParity parity) { + if (m_rxValid) { rxBits(); } + if (!m_txValid) { return -1; } + + if (m_txEnableValid) { + digitalWrite(m_txEnablePin, HIGH); + } + // Stop bit: if inverted, LOW, otherwise HIGH + bool b = !m_invert; + uint32_t dutyCycle = 0; + uint32_t offCycle = 0; + if (!m_intTxEnabled) { + // Disable interrupts in order to get a clean transmit timing + m_savedPS = xt_rsil(15); + } + const uint32_t dataMask = ((1UL << m_dataBits) - 1); + bool withStopBit = true; + m_periodDuration = 0; + m_periodStart = ESP.getCycleCount(); + for (size_t cnt = 0; cnt < size; ++cnt) { + uint8_t byte = ~buffer[cnt] & dataMask; + // push LSB start-data-parity-stop bit pattern into uint32_t + // Stop bits: HIGH + uint32_t word = ~0UL; + // parity bit, if any + if (parity && m_parityMode) + { + uint32_t parityBit; + switch (parity) + { + case SWSERIAL_PARITY_EVEN: + // from inverted, so use odd parity + parityBit = byte; + parityBit ^= parityBit >> 4; + parityBit &= 0xf; + parityBit = (0x9669 >> parityBit) & 1; + break; + case SWSERIAL_PARITY_ODD: + // from inverted, so use even parity + parityBit = byte; + parityBit ^= parityBit >> 4; + parityBit &= 0xf; + parityBit = (0x6996 >> parityBit) & 1; + break; + case SWSERIAL_PARITY_MARK: + parityBit = false; + break; + case SWSERIAL_PARITY_SPACE: + // suppresses warning parityBit uninitialized + default: + parityBit = true; + break; + } + word ^= parityBit << m_dataBits; + } + word ^= byte; + // Stop bit: LOW + word <<= 1; + if (m_invert) word = ~word; + for (int i = 0; i <= m_pduBits; ++i) { + bool pb = b; + b = word & (1UL << i); + if (!pb && b) { + writePeriod(dutyCycle, offCycle, withStopBit); + withStopBit = false; + dutyCycle = offCycle = 0; + } + if (b) { + dutyCycle += m_bitCycles; + } + else { + offCycle += m_bitCycles; + } + } + withStopBit = true; + } + writePeriod(dutyCycle, offCycle, true); + if (!m_intTxEnabled) { + // restore the interrupt state + xt_wsr_ps(m_savedPS); + } + if (m_txEnableValid) { + digitalWrite(m_txEnablePin, LOW); + } + return size; +} + +void SoftwareSerial::flush() { + if (!m_rxValid) { return; } + m_buffer->flush(); + if (m_parityBuffer) + { + m_parityInPos = m_parityOutPos = 1; + m_parityBuffer->flush(); + } +} + +bool SoftwareSerial::overflow() { + bool res = m_overflow; + m_overflow = false; + return res; +} + +int SoftwareSerial::peek() { + if (!m_rxValid) { return -1; } + if (!m_buffer->available()) { + rxBits(); + if (!m_buffer->available()) return -1; + } + auto val = m_buffer->peek(); + if (m_parityBuffer) m_lastReadParity = m_parityBuffer->peek() & m_parityOutPos; + return val; +} + +void SoftwareSerial::rxBits() { + int isrAvail = m_isrBuffer->available(); +#ifdef ESP8266 + if (m_isrOverflow.load()) { + m_overflow = true; + m_isrOverflow.store(false); + } +#else + if (m_isrOverflow.exchange(false)) { + m_overflow = true; + } +#endif + + // stop bit can go undetected if leading data bits are at same level + // and there was also no next start bit yet, so one byte may be pending. + // low-cost check first + if (!isrAvail && m_rxCurBit >= -1 && m_rxCurBit < m_pduBits - m_stopBits) { + uint32_t detectionCycles = (m_pduBits - m_stopBits - m_rxCurBit) * m_bitCycles; + if (ESP.getCycleCount() - m_isrLastCycle > detectionCycles) { + // Produce faux stop bit level, prevents start bit maldetection + // cycle's LSB is repurposed for the level bit + rxBits(((m_isrLastCycle + detectionCycles) | 1) ^ m_invert); + } + } + + m_isrBuffer->for_each([this](const uint32_t& isrCycle) { rxBits(isrCycle); }); +} + +void SoftwareSerial::rxBits(const uint32_t & isrCycle) { + bool level = (m_isrLastCycle & 1) ^ m_invert; + + // error introduced by edge value in LSB of isrCycle is negligible + int32_t cycles = isrCycle - m_isrLastCycle; + m_isrLastCycle = isrCycle; + + uint8_t bits = cycles / m_bitCycles; + if (cycles % m_bitCycles > (m_bitCycles >> 1)) ++bits; + while (bits > 0) { + // start bit detection + if (m_rxCurBit >= (m_pduBits - 1)) { + // leading edge of start bit + if (level) break; + m_rxCurBit = -1; + --bits; + continue; + } + // data bits + if (m_rxCurBit >= -1 && m_rxCurBit < (m_dataBits - 1)) { + int8_t dataBits = min(bits, static_cast(m_dataBits - 1 - m_rxCurBit)); + m_rxCurBit += dataBits; + bits -= dataBits; + m_rxCurByte >>= dataBits; + if (level) { m_rxCurByte |= (BYTE_ALL_BITS_SET << (8 - dataBits)); } + continue; + } + // parity bit + if (m_parityMode && m_rxCurBit == (m_dataBits - 1)) { + ++m_rxCurBit; + --bits; + m_rxCurParity = level; + continue; + } + // stop bits + if (m_rxCurBit < (m_pduBits - m_stopBits - 1)) { + ++m_rxCurBit; + --bits; + continue; + } + if (m_rxCurBit == (m_pduBits - m_stopBits - 1)) { + // Store the received value in the buffer unless we have an overflow + // if not high stop bit level, discard word + if (level) + { + m_rxCurByte >>= (sizeof(uint8_t) * 8 - m_dataBits); + if (!m_buffer->push(m_rxCurByte)) { + m_overflow = true; + } + else { + if (m_parityBuffer) + { + if (m_rxCurParity) { + m_parityBuffer->pushpeek() |= m_parityInPos; + } + else { + m_parityBuffer->pushpeek() &= ~m_parityInPos; + } + m_parityInPos <<= 1; + if (!m_parityInPos) + { + m_parityBuffer->push(); + m_parityInPos = 1; + } + } + } + } + m_rxCurBit = m_pduBits; + // reset to 0 is important for masked bit logic + m_rxCurByte = 0; + m_rxCurParity = false; + break; + } + break; + } +} + +void ICACHE_RAM_ATTR SoftwareSerial::rxBitISR(SoftwareSerial * self) { + uint32_t curCycle = ESP.getCycleCount(); + bool level = digitalRead(self->m_rxPin); + + // Store level and cycle in the buffer unless we have an overflow + // cycle's LSB is repurposed for the level bit + if (!self->m_isrBuffer->push((curCycle | 1U) ^ !level)) self->m_isrOverflow.store(true); +} + +void ICACHE_RAM_ATTR SoftwareSerial::rxBitSyncISR(SoftwareSerial * self) { + uint32_t start = ESP.getCycleCount(); + uint32_t wait = self->m_bitCycles - 172U; + + bool level = self->m_invert; + // Store level and cycle in the buffer unless we have an overflow + // cycle's LSB is repurposed for the level bit + if (!self->m_isrBuffer->push(((start + wait) | 1U) ^ !level)) self->m_isrOverflow.store(true); + + for (uint32_t i = 0; i < self->m_pduBits; ++i) { + while (ESP.getCycleCount() - start < wait) {}; + wait += self->m_bitCycles; + + // Store level and cycle in the buffer unless we have an overflow + // cycle's LSB is repurposed for the level bit + if (digitalRead(self->m_rxPin) != level) + { + if (!self->m_isrBuffer->push(((start + wait) | 1U) ^ level)) self->m_isrOverflow.store(true); + level = !level; + } + } +} + +void SoftwareSerial::onReceive(Delegate handler) { + receiveHandler = handler; +} + +void SoftwareSerial::perform_work() { + if (!m_rxValid) { return; } + rxBits(); + if (receiveHandler) { + int avail = m_buffer->available(); + if (avail) { receiveHandler(avail); } + } +} diff --git a/EspSoftwareSerial/src/SoftwareSerial.h b/EspSoftwareSerial/src/SoftwareSerial.h new file mode 100644 index 00000000..371e3731 --- /dev/null +++ b/EspSoftwareSerial/src/SoftwareSerial.h @@ -0,0 +1,255 @@ +/* +SoftwareSerial.h + +SoftwareSerial.cpp - Implementation of the Arduino software serial for ESP8266/ESP32. +Copyright (c) 2015-2016 Peter Lerup. All rights reserved. +Copyright (c) 2018-2019 Dirk O. Kaar. All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef __SoftwareSerial_h +#define __SoftwareSerial_h + +#include "circular_queue/circular_queue.h" +#include + +enum SoftwareSerialParity : uint8_t { + SWSERIAL_PARITY_NONE = 000, + SWSERIAL_PARITY_EVEN = 020, + SWSERIAL_PARITY_ODD = 030, + SWSERIAL_PARITY_MARK = 040, + SWSERIAL_PARITY_SPACE = 070, +}; + +enum SoftwareSerialConfig { + SWSERIAL_5N1 = SWSERIAL_PARITY_NONE, + SWSERIAL_6N1, + SWSERIAL_7N1, + SWSERIAL_8N1, + SWSERIAL_5E1 = SWSERIAL_PARITY_EVEN, + SWSERIAL_6E1, + SWSERIAL_7E1, + SWSERIAL_8E1, + SWSERIAL_5O1 = SWSERIAL_PARITY_ODD, + SWSERIAL_6O1, + SWSERIAL_7O1, + SWSERIAL_8O1, + SWSERIAL_5M1 = SWSERIAL_PARITY_MARK, + SWSERIAL_6M1, + SWSERIAL_7M1, + SWSERIAL_8M1, + SWSERIAL_5S1 = SWSERIAL_PARITY_SPACE, + SWSERIAL_6S1, + SWSERIAL_7S1, + SWSERIAL_8S1, + SWSERIAL_5N2 = 0200 | SWSERIAL_PARITY_NONE, + SWSERIAL_6N2, + SWSERIAL_7N2, + SWSERIAL_8N2, + SWSERIAL_5E2 = 0200 | SWSERIAL_PARITY_EVEN, + SWSERIAL_6E2, + SWSERIAL_7E2, + SWSERIAL_8E2, + SWSERIAL_5O2 = 0200 | SWSERIAL_PARITY_ODD, + SWSERIAL_6O2, + SWSERIAL_7O2, + SWSERIAL_8O2, + SWSERIAL_5M2 = 0200 | SWSERIAL_PARITY_MARK, + SWSERIAL_6M2, + SWSERIAL_7M2, + SWSERIAL_8M2, + SWSERIAL_5S2 = 0200 | SWSERIAL_PARITY_SPACE, + SWSERIAL_6S2, + SWSERIAL_7S2, + SWSERIAL_8S2, +}; + +/// This class is compatible with the corresponding AVR one, however, +/// the constructor takes no arguments, for compatibility with the +/// HardwareSerial class. +/// Instead, the begin() function handles pin assignments and logic inversion. +/// It also has optional input buffer capacity arguments for byte buffer and ISR bit buffer. +/// Bitrates up to at least 115200 can be used. +class SoftwareSerial : public Stream { +public: + SoftwareSerial(); + /// Ctor to set defaults for pins. + /// @param rxPin the GPIO pin used for RX + /// @param txPin -1 for onewire protocol, GPIO pin used for twowire TX + SoftwareSerial(int8_t rxPin, int8_t txPin = -1, bool invert = false); + SoftwareSerial(const SoftwareSerial&) = delete; + SoftwareSerial& operator= (const SoftwareSerial&) = delete; + virtual ~SoftwareSerial(); + /// Configure the SoftwareSerial object for use. + /// @param baud the TX/RX bitrate + /// @param config sets databits, parity, and stop bit count + /// @param rxPin -1 or default: either no RX pin, or keeps the rxPin set in the ctor + /// @param txPin -1 or default: either no TX pin (onewire), or keeps the txPin set in the ctor + /// @param invert true: uses invert line level logic + /// @param bufCapacity the capacity for the received bytes buffer + /// @param isrBufCapacity 0: derived from bufCapacity. The capacity of the internal asynchronous + /// bit receive buffer, a suggested size is bufCapacity times the sum of + /// start, data, parity and stop bit count. + void begin(uint32_t baud, SoftwareSerialConfig config, + int8_t rxPin, int8_t txPin, bool invert, + int bufCapacity = 64, int isrBufCapacity = 0); + void begin(uint32_t baud, SoftwareSerialConfig config, + int8_t rxPin, int8_t txPin) { + begin(baud, config, rxPin, txPin, m_invert); + } + void begin(uint32_t baud, SoftwareSerialConfig config, + int8_t rxPin) { + begin(baud, config, rxPin, m_txPin, m_invert); + } + void begin(uint32_t baud, SoftwareSerialConfig config = SWSERIAL_8N1) { + begin(baud, config, m_rxPin, m_txPin, m_invert); + } + + uint32_t baudRate(); + /// Transmit control pin. + void setTransmitEnablePin(int8_t txEnablePin); + /// Enable or disable interrupts during tx. + void enableIntTx(bool on); + + bool overflow(); + + int available() override; + int availableForWrite() { + if (!m_txValid) return 0; + return 1; + } + int peek() override; + int read() override; + /// @returns The verbatim parity bit associated with the last read() or peek() call + bool readParity() + { + return m_lastReadParity; + } + /// @returns The calculated bit for even parity of the parameter byte + static bool parityEven(uint8_t byte) { + byte ^= byte >> 4; + byte &= 0xf; + return (0x6996 >> byte) & 1; + } + /// @returns The calculated bit for odd parity of the parameter byte + static bool parityOdd(uint8_t byte) { + byte ^= byte >> 4; + byte &= 0xf; + return (0x9669 >> byte) & 1; + } + /// The read(buffer, size) functions are non-blocking, the same as readBytes but without timeout + size_t read(uint8_t* buffer, size_t size); + /// The read(buffer, size) functions are non-blocking, the same as readBytes but without timeout + size_t read(char* buffer, size_t size) { + return read(reinterpret_cast(buffer), size); + } + /// @returns The number of bytes read into buffer, up to size. Times out if the limit set through + /// Stream::setTimeout() is reached. + size_t readBytes(uint8_t* buffer, size_t size) override; + /// @returns The number of bytes read into buffer, up to size. Times out if the limit set through + /// Stream::setTimeout() is reached. + size_t readBytes(char* buffer, size_t size) override { + return readBytes(reinterpret_cast(buffer), size); + } + void flush() override; + size_t write(uint8_t byte) override; + size_t write(uint8_t byte, SoftwareSerialParity parity); + size_t write(const uint8_t* buffer, size_t size) override; + size_t write(const char* buffer, size_t size) { + return write(reinterpret_cast(buffer), size); + } + size_t write(const uint8_t* buffer, size_t size, SoftwareSerialParity parity); + size_t write(const char* buffer, size_t size, SoftwareSerialParity parity) { + return write(reinterpret_cast(buffer), size, parity); + } + operator bool() const { return m_rxValid || m_txValid; } + + /// Disable or enable interrupts on the rx pin. + void enableRx(bool on); + /// One wire control. + void enableTx(bool on); + + // AVR compatibility methods. + bool listen() { enableRx(true); return true; } + void end(); + bool isListening() { return m_rxEnabled; } + bool stopListening() { enableRx(false); return true; } + + /// Set an event handler for received data. + void onReceive(Delegate handler); + + /// Run the internal processing and event engine. Can be iteratively called + /// from loop, or otherwise scheduled. + void perform_work(); + + using Print::write; + +private: + // If sync is false, it's legal to exceed the deadline, for instance, + // by enabling interrupts. + void preciseDelay(bool sync); + // If withStopBit is set, either cycle contains a stop bit. + // If dutyCycle == 0, the level is not forced to HIGH. + // If offCycle == 0, the level remains unchanged from dutyCycle. + void writePeriod( + uint32_t dutyCycle, uint32_t offCycle, bool withStopBit); + bool isValidGPIOpin(int8_t pin); + /* check m_rxValid that calling is safe */ + void rxBits(); + void rxBits(const uint32_t& isrCycle); + + static void rxBitISR(SoftwareSerial* self); + static void rxBitSyncISR(SoftwareSerial* self); + + // Member variables + int8_t m_rxPin = -1; + int8_t m_txPin = -1; + int8_t m_txEnablePin = -1; + uint8_t m_dataBits; + bool m_oneWire; + bool m_rxValid = false; + bool m_rxEnabled = false; + bool m_txValid = false; + bool m_txEnableValid = false; + bool m_invert; + /// PDU bits include data, parity and stop bits; the start bit is not counted. + uint8_t m_pduBits; + bool m_intTxEnabled; + SoftwareSerialParity m_parityMode; + uint8_t m_stopBits; + bool m_lastReadParity; + bool m_overflow = false; + uint32_t m_bitCycles; + uint8_t m_parityInPos; + uint8_t m_parityOutPos; + int8_t m_rxCurBit; // 0 thru (m_pduBits - m_stopBits - 1): data/parity bits. -1: start bit. (m_pduBits - 1): stop bit. + uint8_t m_rxCurByte = 0; + std::unique_ptr > m_buffer; + std::unique_ptr > m_parityBuffer; + uint32_t m_periodStart; + uint32_t m_periodDuration; + uint32_t m_savedPS = 0; + // the ISR stores the relative bit times in the buffer. The inversion corrected level is used as sign bit (2's complement): + // 1 = positive including 0, 0 = negative. + std::unique_ptr > m_isrBuffer; + std::atomic m_isrOverflow; + uint32_t m_isrLastCycle; + bool m_rxCurParity = false; + Delegate receiveHandler; +}; + +#endif // __SoftwareSerial_h diff --git a/EspSoftwareSerial/src/circular_queue/Delegate.h b/EspSoftwareSerial/src/circular_queue/Delegate.h new file mode 100644 index 00000000..bd19c66e --- /dev/null +++ b/EspSoftwareSerial/src/circular_queue/Delegate.h @@ -0,0 +1,1786 @@ +/* +Delegate.h - An efficient interchangeable C function ptr and C++ std::function delegate +Copyright (c) 2019 Dirk O. Kaar. All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __Delegate_h +#define __Delegate_h + +#if defined(ESP8266) +#include +#elif defined(ESP32) +#include +#else +#define ICACHE_RAM_ATTR +#define IRAM_ATTR +#endif + +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) +#include +#include +#else +#include "circular_queue/ghostl.h" +#endif + +namespace detail +{ + template + static R IRAM_ATTR vPtrToFunPtrExec(void* fn, P... args) + { + using target_type = R(P...); + return reinterpret_cast(fn)(std::forward(args...)); + } + +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + template + class DelegatePImpl { + public: + using target_type = R(P...); + protected: + using FunPtr = target_type*; + using FunAPtr = R(*)(A, P...); + using FunVPPtr = R(*)(void*, P...); + using FunctionType = std::function; + public: + DelegatePImpl() + { + kind = FP; + fn = nullptr; + } + + DelegatePImpl(std::nullptr_t) + { + kind = FP; + fn = nullptr; + } + + ~DelegatePImpl() + { + if (FUNC == kind) + functional.~FunctionType(); + else if (FPA == kind) + obj.~A(); + } + + DelegatePImpl(const DelegatePImpl& del) + { + kind = del.kind; + if (FUNC == del.kind) + { + new (&functional) FunctionType(del.functional); + } + else if (FPA == del.kind) + { + fnA = del.fnA; + new (&obj) A(del.obj); + } + else + { + fn = del.fn; + } + } + + DelegatePImpl(DelegatePImpl&& del) + { + kind = del.kind; + if (FUNC == del.kind) + { + new (&functional) FunctionType(std::move(del.functional)); + } + else if (FPA == del.kind) + { + fnA = del.fnA; + new (&obj) A(std::move(del.obj)); + } + else + { + fn = del.fn; + } + } + + DelegatePImpl(FunAPtr fnA, const A& obj) + { + kind = FPA; + DelegatePImpl::fnA = fnA; + new (&this->obj) A(obj); + } + + DelegatePImpl(FunAPtr fnA, A&& obj) + { + kind = FPA; + DelegatePImpl::fnA = fnA; + new (&this->obj) A(std::move(obj)); + } + + DelegatePImpl(FunPtr fn) + { + kind = FP; + DelegatePImpl::fn = fn; + } + + template DelegatePImpl(F functional) + { + kind = FUNC; + new (&this->functional) FunctionType(std::forward(functional)); + } + + DelegatePImpl& operator=(const DelegatePImpl& del) + { + if (this == &del) return *this; + if (kind != del.kind) + { + if (FUNC == kind) + { + functional.~FunctionType(); + } + else if (FPA == kind) + { + obj.~A(); + } + if (FUNC == del.kind) + { + new (&this->functional) FunctionType(); + } + else if (FPA == del.kind) + { + new (&obj) A; + } + kind = del.kind; + } + if (FUNC == del.kind) + { + functional = del.functional; + } + else if (FPA == del.kind) + { + fnA = del.fnA; + obj = del.obj; + } + else + { + fn = del.fn; + } + return *this; + } + + DelegatePImpl& operator=(DelegatePImpl&& del) + { + if (this == &del) return *this; + if (kind != del.kind) + { + if (FUNC == kind) + { + functional.~FunctionType(); + } + else if (FPA == kind) + { + obj.~A(); + } + if (FUNC == del.kind) + { + new (&this->functional) FunctionType(); + } + else if (FPA == del.kind) + { + new (&obj) A; + } + kind = del.kind; + } + if (FUNC == del.kind) + { + functional = std::move(del.functional); + } + else if (FPA == del.kind) + { + fnA = del.fnA; + obj = std::move(del.obj); + } + else + { + fn = del.fn; + } + return *this; + } + + DelegatePImpl& operator=(FunPtr fn) + { + if (FUNC == kind) + { + functional.~FunctionType(); + } + else if (FPA == kind) + { + obj.~A(); + } + kind = FP; + this->fn = fn; + return *this; + } + + DelegatePImpl& IRAM_ATTR operator=(std::nullptr_t) + { + if (FUNC == kind) + { + functional.~FunctionType(); + } + else if (FPA == kind) + { + obj.~A(); + } + kind = FP; + fn = nullptr; + return *this; + } + + operator bool() const + { + if (FP == kind) + { + return fn; + } + else if (FPA == kind) + { + return fnA; + } + else + { + return functional ? true : false; + } + } + + static R IRAM_ATTR vPtrToFunAPtrExec(void* self, P... args) + { + return static_cast(self)->fnA( + static_cast(self)->obj, + std::forward(args...)); + }; + + operator FunVPPtr() const + { + if (FP == kind) + { + return vPtrToFunPtrExec; + } + else if (FPA == kind) + { + return vPtrToFunAPtrExec; + } + else + { + return [](void* self, P... args) -> R + { + return static_cast(self)->functional(std::forward(args...)); + }; + } + } + + void* arg() const + { + if (FP == kind) + { + return reinterpret_cast(fn); + } + else + { + return const_cast(this); + } + } + + operator FunctionType() const + { + if (FP == kind) + { + return fn; + } + else if (FPA == kind) + { + return [this](P... args) { return fnA(obj, std::forward(args...)); }; + } + else + { + return functional; + } + } + + R IRAM_ATTR operator()(P... args) const + { + if (FP == kind) + { + return fn(std::forward(args...)); + } + else if (FPA == kind) + { + return fnA(obj, std::forward(args...)); + } + else + { + return functional(std::forward(args...)); + } + } + + protected: + enum { FUNC, FP, FPA } kind; + union { + FunctionType functional; + FunPtr fn; + struct { + FunAPtr fnA; + A obj; + }; + }; + }; +#else + template + class DelegatePImpl { + public: + using target_type = R(P...); + protected: + using FunPtr = target_type*; + using FunAPtr = R(*)(A, P...); + using FunVPPtr = R(*)(void*, P...); + public: + DelegatePImpl() + { + kind = FP; + fn = nullptr; + } + + DelegatePImpl(std::nullptr_t) + { + kind = FP; + fn = nullptr; + } + + DelegatePImpl(const DelegatePImpl& del) + { + kind = del.kind; + if (FPA == del.kind) + { + fnA = del.fnA; + obj = del.obj; + } + else + { + fn = del.fn; + } + } + + DelegatePImpl(DelegatePImpl&& del) + { + kind = del.kind; + if (FPA == del.kind) + { + fnA = del.fnA; + obj = std::move(del.obj); + } + else + { + fn = del.fn; + } + } + + DelegatePImpl(FunAPtr fnA, const A& obj) + { + kind = FPA; + DelegatePImpl::fnA = fnA; + this->obj = obj; + } + + DelegatePImpl(FunAPtr fnA, A&& obj) + { + kind = FPA; + DelegatePImpl::fnA = fnA; + this->obj = std::move(obj); + } + + DelegatePImpl(FunPtr fn) + { + kind = FP; + DelegatePImpl::fn = fn; + } + + template DelegatePImpl(F fn) + { + kind = FP; + DelegatePImpl::fn = std::forward(fn); + } + + DelegatePImpl& operator=(const DelegatePImpl& del) + { + if (this == &del) return *this; + if (kind != del.kind) + { + if (FPA == kind) + { + obj = {}; + } + kind = del.kind; + } + if (FPA == del.kind) + { + fnA = del.fnA; + obj = del.obj; + } + else + { + fn = del.fn; + } + return *this; + } + + DelegatePImpl& operator=(DelegatePImpl&& del) + { + if (this == &del) return *this; + if (kind != del.kind) + { + if (FPA == kind) + { + obj = {}; + } + kind = del.kind; + } + if (FPA == del.kind) + { + fnA = del.fnA; + obj = std::move(del.obj); + } + else + { + fn = del.fn; + } + return *this; + } + + DelegatePImpl& operator=(FunPtr fn) + { + if (FPA == kind) + { + obj = {}; + } + kind = FP; + this->fn = fn; + return *this; + } + + DelegatePImpl& IRAM_ATTR operator=(std::nullptr_t) + { + if (FPA == kind) + { + obj = {}; + } + kind = FP; + fn = nullptr; + return *this; + } + + operator bool() const + { + if (FP == kind) + { + return fn; + } + else + { + return fnA; + } + } + + static R IRAM_ATTR vPtrToFunAPtrExec(void* self, P... args) + { + return static_cast(self)->fnA( + static_cast(self)->obj, + std::forward(args...)); + }; + + operator FunVPPtr() const + { + if (FP == kind) + { + return vPtrToFunPtrExec; + } + else + { + return vPtrToFunAPtrExec; + } + } + + void* arg() const + { + if (FP == kind) + { + return reinterpret_cast(fn); + } + else + { + return const_cast(this); + } + } + + R IRAM_ATTR operator()(P... args) const + { + if (FP == kind) + { + return fn(std::forward(args...)); + } + else + { + return fnA(obj, std::forward(args...)); + } + } + + protected: + enum { FP, FPA } kind; + union { + FunPtr fn; + FunAPtr fnA; + }; + A obj; + }; +#endif + +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + template + class DelegatePImpl { + public: + using target_type = R(P...); + protected: + using FunPtr = target_type*; + using FunctionType = std::function; + using FunVPPtr = R(*)(void*, P...); + public: + DelegatePImpl() + { + kind = FP; + fn = nullptr; + } + + DelegatePImpl(std::nullptr_t) + { + kind = FP; + fn = nullptr; + } + + ~DelegatePImpl() + { + if (FUNC == kind) + functional.~FunctionType(); + } + + DelegatePImpl(const DelegatePImpl& del) + { + kind = del.kind; + if (FUNC == del.kind) + { + new (&functional) FunctionType(del.functional); + } + else + { + fn = del.fn; + } + } + + DelegatePImpl(DelegatePImpl&& del) + { + kind = del.kind; + if (FUNC == del.kind) + { + new (&functional) FunctionType(std::move(del.functional)); + } + else + { + fn = del.fn; + } + } + + DelegatePImpl(FunPtr fn) + { + kind = FP; + DelegatePImpl::fn = fn; + } + + template DelegatePImpl(F functional) + { + kind = FUNC; + new (&this->functional) FunctionType(std::forward(functional)); + } + + DelegatePImpl& operator=(const DelegatePImpl& del) + { + if (this == &del) return *this; + if (FUNC == kind && FUNC != del.kind) + { + functional.~FunctionType(); + } + else if (FUNC != kind && FUNC == del.kind) + { + new (&this->functional) FunctionType(); + } + kind = del.kind; + if (FUNC == del.kind) + { + functional = del.functional; + } + else + { + fn = del.fn; + } + return *this; + } + + DelegatePImpl& operator=(DelegatePImpl&& del) + { + if (this == &del) return *this; + if (FUNC == kind && FUNC != del.kind) + { + functional.~FunctionType(); + } + else if (FUNC != kind && FUNC == del.kind) + { + new (&this->functional) FunctionType(); + } + kind = del.kind; + if (FUNC == del.kind) + { + functional = std::move(del.functional); + } + else + { + fn = del.fn; + } + return *this; + } + + DelegatePImpl& operator=(FunPtr fn) + { + if (FUNC == kind) + { + functional.~FunctionType(); + kind = FP; + } + DelegatePImpl::fn = fn; + return *this; + } + + DelegatePImpl& IRAM_ATTR operator=(std::nullptr_t) + { + if (FUNC == kind) + { + functional.~FunctionType(); + } + kind = FP; + fn = nullptr; + return *this; + } + + operator bool() const + { + if (FP == kind) + { + return fn; + } + else + { + return functional ? true : false; + } + } + + operator FunVPPtr() const + { + if (FP == kind) + { + return vPtrToFunPtrExec; + } + else + { + return [](void* self, P... args) -> R + { + return static_cast(self)->functional(std::forward(args...)); + }; + } + } + + void* arg() const + { + if (FP == kind) + { + return reinterpret_cast(fn); + } + else + { + return const_cast(this); + } + } + + operator FunctionType() const + { + if (FP == kind) + { + return fn; + } + else + { + return functional; + } + } + + R IRAM_ATTR operator()(P... args) const + { + if (FP == kind) + { + return fn(std::forward(args...)); + } + else + { + return functional(std::forward(args...)); + } + } + + protected: + enum { FUNC, FP } kind; + union { + FunctionType functional; + FunPtr fn; + }; + }; +#else + template + class DelegatePImpl { + public: + using target_type = R(P...); + protected: + using FunPtr = target_type*; + using FunVPPtr = R(*)(void*, P...); + public: + DelegatePImpl() + { + fn = nullptr; + } + + DelegatePImpl(std::nullptr_t) + { + fn = nullptr; + } + + DelegatePImpl(const DelegatePImpl& del) + { + fn = del.fn; + } + + DelegatePImpl(DelegatePImpl&& del) + { + fn = std::move(del.fn); + } + + DelegatePImpl(FunPtr fn) + { + DelegatePImpl::fn = fn; + } + + template DelegatePImpl(F fn) + { + DelegatePImpl::fn = std::forward(fn); + } + + DelegatePImpl& operator=(const DelegatePImpl& del) + { + if (this == &del) return *this; + fn = del.fn; + return *this; + } + + DelegatePImpl& operator=(DelegatePImpl&& del) + { + if (this == &del) return *this; + fn = std::move(del.fn); + return *this; + } + + DelegatePImpl& operator=(FunPtr fn) + { + DelegatePImpl::fn = fn; + return *this; + } + + DelegatePImpl& IRAM_ATTR operator=(std::nullptr_t) + { + fn = nullptr; + return *this; + } + + operator bool() const + { + return fn; + } + + operator FunVPPtr() const + { + return vPtrToFunPtrExec; + } + + void* arg() const + { + return reinterpret_cast(fn); + } + + R IRAM_ATTR operator()(P... args) const + { + return fn(std::forward(args...)); + } + + protected: + FunPtr fn; + }; +#endif + +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + template + class DelegateImpl { + public: + using target_type = R(); + protected: + using FunPtr = target_type*; + using FunAPtr = R(*)(A); + using FunctionType = std::function; + using FunVPPtr = R(*)(void*); + public: + DelegateImpl() + { + kind = FP; + fn = nullptr; + } + + DelegateImpl(std::nullptr_t) + { + kind = FP; + fn = nullptr; + } + + ~DelegateImpl() + { + if (FUNC == kind) + functional.~FunctionType(); + else if (FPA == kind) + obj.~A(); + } + + DelegateImpl(const DelegateImpl& del) + { + kind = del.kind; + if (FUNC == del.kind) + { + new (&functional) FunctionType(del.functional); + } + else if (FPA == del.kind) + { + fnA = del.fnA; + new (&obj) A(del.obj); + } + else + { + fn = del.fn; + } + } + + DelegateImpl(DelegateImpl&& del) + { + kind = del.kind; + if (FUNC == del.kind) + { + new (&functional) FunctionType(std::move(del.functional)); + } + else if (FPA == del.kind) + { + fnA = del.fnA; + new (&obj) A(std::move(del.obj)); + } + else + { + fn = del.fn; + } + } + + DelegateImpl(FunAPtr fnA, const A& obj) + { + kind = FPA; + DelegateImpl::fnA = fnA; + new (&this->obj) A(obj); + } + + DelegateImpl(FunAPtr fnA, A&& obj) + { + kind = FPA; + DelegateImpl::fnA = fnA; + new (&this->obj) A(std::move(obj)); + } + + DelegateImpl(FunPtr fn) + { + kind = FP; + DelegateImpl::fn = fn; + } + + template DelegateImpl(F functional) + { + kind = FUNC; + new (&this->functional) FunctionType(std::forward(functional)); + } + + DelegateImpl& operator=(const DelegateImpl& del) + { + if (this == &del) return *this; + if (kind != del.kind) + { + if (FUNC == kind) + { + functional.~FunctionType(); + } + else if (FPA == kind) + { + obj.~A(); + } + if (FUNC == del.kind) + { + new (&this->functional) FunctionType(); + } + else if (FPA == del.kind) + { + new (&obj) A; + } + kind = del.kind; + } + if (FUNC == del.kind) + { + functional = del.functional; + } + else if (FPA == del.kind) + { + fnA = del.fnA; + obj = del.obj; + } + else + { + fn = del.fn; + } + return *this; + } + + DelegateImpl& operator=(DelegateImpl&& del) + { + if (this == &del) return *this; + if (kind != del.kind) + { + if (FUNC == kind) + { + functional.~FunctionType(); + } + else if (FPA == kind) + { + obj.~A(); + } + if (FUNC == del.kind) + { + new (&this->functional) FunctionType(); + } + else if (FPA == del.kind) + { + new (&obj) A; + } + kind = del.kind; + } + if (FUNC == del.kind) + { + functional = std::move(del.functional); + } + else if (FPA == del.kind) + { + fnA = del.fnA; + obj = std::move(del.obj); + } + else + { + fn = del.fn; + } + return *this; + } + + DelegateImpl& operator=(FunPtr fn) + { + if (FUNC == kind) + { + functional.~FunctionType(); + } + else if (FPA == kind) + { + obj.~A(); + } + kind = FP; + this->fn = fn; + return *this; + } + + DelegateImpl& IRAM_ATTR operator=(std::nullptr_t) + { + if (FUNC == kind) + { + functional.~FunctionType(); + } + else if (FPA == kind) + { + obj.~A(); + } + kind = FP; + fn = nullptr; + return *this; + } + + operator bool() const + { + if (FP == kind) + { + return fn; + } + else if (FPA == kind) + { + return fnA; + } + else + { + return functional ? true : false; + } + } + + static R IRAM_ATTR vPtrToFunAPtrExec(void* self) + { + return static_cast(self)->fnA( + static_cast(self)->obj); + }; + + operator FunVPPtr() const + { + if (FP == kind) + { + return reinterpret_cast(fn); + } + else if (FPA == kind) + { + return vPtrToFunAPtrExec; + } + else + { + return [](void* self) -> R + { + return static_cast(self)->functional(); + }; + } + } + + void* arg() const + { + if (FP == kind) + { + return nullptr; + } + else + { + return const_cast(this); + } + } + + operator FunctionType() const + { + if (FP == kind) + { + return fn; + } + else if (FPA == kind) + { + return [this]() { return fnA(obj); }; + } + else + { + return functional; + } + } + + R IRAM_ATTR operator()() const + { + if (FP == kind) + { + return fn(); + } + else if (FPA == kind) + { + return fnA(obj); + } + else + { + return functional(); + } + } + + protected: + enum { FUNC, FP, FPA } kind; + union { + FunctionType functional; + FunPtr fn; + struct { + FunAPtr fnA; + A obj; + }; + }; + }; +#else + template + class DelegateImpl { + public: + using target_type = R(); + protected: + using FunPtr = target_type*; + using FunAPtr = R(*)(A); + using FunVPPtr = R(*)(void*); + public: + DelegateImpl() + { + kind = FP; + fn = nullptr; + } + + DelegateImpl(std::nullptr_t) + { + kind = FP; + fn = nullptr; + } + + DelegateImpl(const DelegateImpl& del) + { + kind = del.kind; + if (FPA == del.kind) + { + fnA = del.fnA; + obj = del.obj; + } + else + { + fn = del.fn; + } + } + + DelegateImpl(DelegateImpl&& del) + { + kind = del.kind; + if (FPA == del.kind) + { + fnA = del.fnA; + obj = std::move(del.obj); + } + else + { + fn = del.fn; + } + } + + DelegateImpl(FunAPtr fnA, const A& obj) + { + kind = FPA; + DelegateImpl::fnA = fnA; + this->obj = obj; + } + + DelegateImpl(FunAPtr fnA, A&& obj) + { + kind = FPA; + DelegateImpl::fnA = fnA; + this->obj = std::move(obj); + } + + DelegateImpl(FunPtr fn) + { + kind = FP; + DelegateImpl::fn = fn; + } + + template DelegateImpl(F fn) + { + kind = FP; + DelegateImpl::fn = std::forward(fn); + } + + DelegateImpl& operator=(const DelegateImpl& del) + { + if (this == &del) return *this; + if (kind != del.kind) + { + if (FPA == kind) + { + obj = {}; + } + kind = del.kind; + } + if (FPA == del.kind) + { + fnA = del.fnA; + obj = del.obj; + } + else + { + fn = del.fn; + } + return *this; + } + + DelegateImpl& operator=(DelegateImpl&& del) + { + if (this == &del) return *this; + if (kind != del.kind) + { + if (FPA == kind) + { + obj = {}; + } + kind = del.kind; + } + if (FPA == del.kind) + { + fnA = del.fnA; + obj = std::move(del.obj); + } + else + { + fn = del.fn; + } + return *this; + } + + DelegateImpl& operator=(FunPtr fn) + { + if (FPA == kind) + { + obj = {}; + } + kind = FP; + this->fn = fn; + return *this; + } + + DelegateImpl& IRAM_ATTR operator=(std::nullptr_t) + { + if (FPA == kind) + { + obj = {}; + } + kind = FP; + fn = nullptr; + return *this; + } + + operator bool() const + { + if (FP == kind) + { + return fn; + } + else + { + return fnA; + } + } + + static R IRAM_ATTR vPtrToFunAPtrExec(void* self) + { + return static_cast(self)->fnA( + static_cast(self)->obj); + }; + + operator FunVPPtr() const + { + if (FP == kind) + { + return reinterpret_cast(fn); + } + else + { + return vPtrToFunAPtrExec; + } + } + + void* arg() const + { + if (FP == kind) + { + return nullptr; + } + else + { + return const_cast(this); + } + } + + R IRAM_ATTR operator()() const + { + if (FP == kind) + { + return fn(); + } + else + { + return fnA(obj); + } + } + + protected: + enum { FP, FPA } kind; + union { + FunPtr fn; + FunAPtr fnA; + }; + A obj; + }; +#endif + +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + template + class DelegateImpl { + public: + using target_type = R(); + protected: + using FunPtr = target_type*; + using FunctionType = std::function; + using FunVPPtr = R(*)(void*); + public: + DelegateImpl() + { + kind = FP; + fn = nullptr; + } + + DelegateImpl(std::nullptr_t) + { + kind = FP; + fn = nullptr; + } + + ~DelegateImpl() + { + if (FUNC == kind) + functional.~FunctionType(); + } + + DelegateImpl(const DelegateImpl& del) + { + kind = del.kind; + if (FUNC == del.kind) + { + new (&functional) FunctionType(del.functional); + } + else + { + fn = del.fn; + } + } + + DelegateImpl(DelegateImpl&& del) + { + kind = del.kind; + if (FUNC == del.kind) + { + new (&functional) FunctionType(std::move(del.functional)); + } + else + { + fn = del.fn; + } + } + + DelegateImpl(FunPtr fn) + { + kind = FP; + DelegateImpl::fn = fn; + } + + template DelegateImpl(F functional) + { + kind = FUNC; + new (&this->functional) FunctionType(std::forward(functional)); + } + + DelegateImpl& operator=(const DelegateImpl& del) + { + if (this == &del) return *this; + if (FUNC == kind && FUNC != del.kind) + { + functional.~FunctionType(); + } + else if (FUNC != kind && FUNC == del.kind) + { + new (&this->functional) FunctionType(); + } + kind = del.kind; + if (FUNC == del.kind) + { + functional = del.functional; + } + else + { + fn = del.fn; + } + return *this; + } + + DelegateImpl& operator=(DelegateImpl&& del) + { + if (this == &del) return *this; + if (FUNC == kind && FUNC != del.kind) + { + functional.~FunctionType(); + } + else if (FUNC != kind && FUNC == del.kind) + { + new (&this->functional) FunctionType(); + } + kind = del.kind; + if (FUNC == del.kind) + { + functional = std::move(del.functional); + } + else + { + fn = del.fn; + } + return *this; + } + + DelegateImpl& operator=(FunPtr fn) + { + if (FUNC == kind) + { + functional.~FunctionType(); + kind = FP; + } + DelegateImpl::fn = fn; + return *this; + } + + DelegateImpl& IRAM_ATTR operator=(std::nullptr_t) + { + if (FUNC == kind) + { + functional.~FunctionType(); + } + kind = FP; + fn = nullptr; + return *this; + } + + operator bool() const + { + if (FP == kind) + { + return fn; + } + else + { + return functional ? true : false; + } + } + + operator FunVPPtr() const + { + if (FP == kind) + { + return reinterpret_cast(fn); + } + else + { + return [](void* self) -> R + { + return static_cast(self)->functional(); + }; + } + } + + void* arg() const + { + if (FP == kind) + { + return nullptr; + } + else + { + return const_cast(this); + } + } + + operator FunctionType() const + { + if (FP == kind) + { + return fn; + } + else + { + return functional; + } + } + + R IRAM_ATTR operator()() const + { + if (FP == kind) + { + return fn(); + } + else + { + return functional(); + } + } + + protected: + enum { FUNC, FP } kind; + union { + FunctionType functional; + FunPtr fn; + }; + }; +#else + template + class DelegateImpl { + public: + using target_type = R(); + protected: + using FunPtr = target_type*; + using FunVPPtr = R(*)(void*); + public: + DelegateImpl() + { + fn = nullptr; + } + + DelegateImpl(std::nullptr_t) + { + fn = nullptr; + } + + DelegateImpl(const DelegateImpl& del) + { + fn = del.fn; + } + + DelegateImpl(DelegateImpl&& del) + { + fn = std::move(del.fn); + } + + DelegateImpl(FunPtr fn) + { + DelegateImpl::fn = fn; + } + + template DelegateImpl(F fn) + { + DelegateImpl::fn = std::forward(fn); + } + + DelegateImpl& operator=(const DelegateImpl& del) + { + if (this == &del) return *this; + fn = del.fn; + return *this; + } + + DelegateImpl& operator=(DelegateImpl&& del) + { + if (this == &del) return *this; + fn = std::move(del.fn); + return *this; + } + + DelegateImpl& operator=(FunPtr fn) + { + DelegateImpl::fn = fn; + return *this; + } + + DelegateImpl& IRAM_ATTR operator=(std::nullptr_t) + { + fn = nullptr; + return *this; + } + + operator bool() const + { + return fn; + } + + operator FunVPPtr() const + { + return reinterpret_cast(fn); + } + + void* arg() const + { + return nullptr; + } + + R IRAM_ATTR operator()() const + { + return fn(); + } + + protected: + FunPtr fn; + }; +#endif + + template + class Delegate : private detail::DelegatePImpl + { + private: + using typename detail::DelegatePImpl::FunVPPtr; +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + using typename detail::DelegatePImpl::FunctionType; +#endif + public: + using detail::DelegatePImpl::target_type; + using detail::DelegatePImpl::DelegatePImpl; + using detail::DelegatePImpl::operator=; + using detail::DelegatePImpl::operator bool; + using detail::DelegatePImpl::operator FunVPPtr; + using detail::DelegatePImpl::arg; +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + using detail::DelegatePImpl::operator FunctionType; +#endif + using detail::DelegatePImpl::operator(); + }; + + template + class Delegate : private detail::DelegatePImpl + { + private: + using typename detail::DelegatePImpl::FunVPPtr; +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + using typename detail::DelegatePImpl::FunctionType; +#endif + public: + using detail::DelegatePImpl::target_type; + using detail::DelegatePImpl::DelegatePImpl; + using detail::DelegatePImpl::operator=; + using detail::DelegatePImpl::operator bool; +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + using detail::DelegatePImpl::operator FunctionType; +#endif + using detail::DelegatePImpl::operator(); + operator FunVPPtr() const + { + if (detail::DelegatePImpl::FPA == detail::DelegatePImpl::kind) + { + return reinterpret_cast(detail::DelegatePImpl::fnA); + } + else + { + return detail::DelegatePImpl::operator FunVPPtr(); + } + } + void* arg() const + { + if (detail::DelegatePImpl::FPA == detail::DelegatePImpl::kind) + { + return detail::DelegatePImpl::obj; + } + else + { + return detail::DelegatePImpl::arg(); + } + } + }; + + template + class Delegate : private detail::DelegateImpl + { + private: + using typename detail::DelegateImpl::FunVPPtr; +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + using typename detail::DelegateImpl::FunctionType; +#endif + public: + using detail::DelegateImpl::target_type; + using detail::DelegateImpl::DelegateImpl; + using detail::DelegateImpl::operator=; + using detail::DelegateImpl::operator bool; + using detail::DelegateImpl::operator FunVPPtr; + using detail::DelegateImpl::arg; +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + using detail::DelegateImpl::operator FunctionType; +#endif + using detail::DelegateImpl::operator(); + }; + + template + class Delegate : private detail::DelegateImpl + { + private: + using typename detail::DelegateImpl::FunVPPtr; +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + using typename detail::DelegateImpl::FunctionType; +#endif + public: + using detail::DelegateImpl::target_type; + using detail::DelegateImpl::DelegateImpl; + using detail::DelegateImpl::operator=; + using detail::DelegateImpl::operator bool; +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + using detail::DelegateImpl::operator FunctionType; +#endif + using detail::DelegateImpl::operator(); + operator FunVPPtr() const + { + if (detail::DelegateImpl::FPA == detail::DelegateImpl::kind) + { + return reinterpret_cast(detail::DelegateImpl::fnA); + } + else + { + return detail::DelegateImpl::operator FunVPPtr(); + } + } + void* arg() const + { + if (detail::DelegateImpl::FPA == detail::DelegateImpl::kind) + { + return detail::DelegateImpl::obj; + } + else + { + return detail::DelegateImpl::arg(); + } + } + }; + +} + +template class Delegate; +template class Delegate : public detail::Delegate +{ +public: + using detail::Delegate::Delegate; +}; +template class Delegate : public detail::Delegate +{ +public: + using detail::Delegate::Delegate; +}; + +#endif // __Delegate_h diff --git a/EspSoftwareSerial/src/circular_queue/MultiDelegate.h b/EspSoftwareSerial/src/circular_queue/MultiDelegate.h new file mode 100644 index 00000000..1fd4188d --- /dev/null +++ b/EspSoftwareSerial/src/circular_queue/MultiDelegate.h @@ -0,0 +1,503 @@ +/* +MultiDelegate.h - A queue or event multiplexer based on the efficient Delegate +class +Copyright (c) 2019 Dirk O. Kaar. All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __MULTIDELEGATE_H +#define __MULTIDELEGATE_H + +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) +#include +#else +#include "circular_queue/ghostl.h" +#endif + +#if defined(ESP8266) +#include +using esp8266::InterruptLock; +#elif defined(ARDUINO) +class InterruptLock { +public: + InterruptLock() { + noInterrupts(); + } + ~InterruptLock() { + interrupts(); + } +}; +#else +#include +#endif + +namespace detail +{ + namespace + { + template< typename Delegate, typename R, bool ISQUEUE = false, typename... P> + struct CallP + { + static R execute(Delegate& del, P... args) + { + return del(std::forward(args...)) ? !ISQUEUE : ISQUEUE; + } + }; + + template< typename Delegate, bool ISQUEUE, typename... P> + struct CallP + { + static bool execute(Delegate& del, P... args) + { + del(std::forward(args...)); + return !ISQUEUE; + } + }; + + template< typename Delegate, typename R, bool ISQUEUE = false> + struct Call + { + static R execute(Delegate& del) + { + return del() ? !ISQUEUE : ISQUEUE; + } + }; + + template< typename Delegate, bool ISQUEUE> + struct Call + { + static bool execute(Delegate& del) + { + del(); + return !ISQUEUE; + } + }; + }; + + template< typename Delegate, typename R = void, bool ISQUEUE = false, uint32_t QUEUE_CAPACITY = 32, typename... P> + class MultiDelegatePImpl + { + public: + MultiDelegatePImpl() = default; + ~MultiDelegatePImpl() + { + *this = nullptr; + } + + MultiDelegatePImpl(const MultiDelegatePImpl&) = delete; + MultiDelegatePImpl& operator=(const MultiDelegatePImpl&) = delete; + + MultiDelegatePImpl(MultiDelegatePImpl&& md) + { + first = md.first; + last = md.last; + unused = md.unused; + nodeCount = md.nodeCount; + md.first = nullptr; + md.last = nullptr; + md.unused = nullptr; + md.nodeCount = 0; + } + + MultiDelegatePImpl(const Delegate& del) + { + add(del); + } + + MultiDelegatePImpl(Delegate&& del) + { + add(std::move(del)); + } + + MultiDelegatePImpl& operator=(MultiDelegatePImpl&& md) + { + first = md.first; + last = md.last; + unused = md.unused; + nodeCount = md.nodeCount; + md.first = nullptr; + md.last = nullptr; + md.unused = nullptr; + md.nodeCount = 0; + return *this; + } + + MultiDelegatePImpl& operator=(std::nullptr_t) + { + if (last) + last->mNext = unused; + if (first) + unused = first; + while (unused) + { + auto to_delete = unused; + unused = unused->mNext; + delete(to_delete); + } + return *this; + } + + MultiDelegatePImpl& operator+=(const Delegate& del) + { + add(del); + return *this; + } + + MultiDelegatePImpl& operator+=(Delegate&& del) + { + add(std::move(del)); + return *this; + } + + protected: + struct Node_t + { + ~Node_t() + { + mDelegate = nullptr; // special overload in Delegate + } + Node_t* mNext = nullptr; + Delegate mDelegate; + }; + + Node_t* first = nullptr; + Node_t* last = nullptr; + Node_t* unused = nullptr; + uint32_t nodeCount = 0; + + // Returns a pointer to an unused Node_t, + // or if none are available allocates a new one, + // or nullptr if limit is reached + Node_t* IRAM_ATTR get_node_unsafe() + { + Node_t* result = nullptr; + // try to get an item from unused items list + if (unused) + { + result = unused; + unused = unused->mNext; + } + // if no unused items, and count not too high, allocate a new one + else if (nodeCount < QUEUE_CAPACITY) + { +#if defined(ESP8266) || defined(ESP32) + result = new (std::nothrow) Node_t; +#else + result = new Node_t; +#endif + if (result) + ++nodeCount; + } + return result; + } + + void recycle_node_unsafe(Node_t* node) + { + node->mDelegate = nullptr; // special overload in Delegate + node->mNext = unused; + unused = node; + } + +#ifndef ARDUINO + std::mutex mutex_unused; +#endif + public: + const Delegate* IRAM_ATTR add(const Delegate& del) + { + return add(Delegate(del)); + } + + const Delegate* IRAM_ATTR add(Delegate&& del) + { + if (!del) + return nullptr; + +#ifdef ARDUINO + InterruptLock lockAllInterruptsInThisScope; +#else + std::lock_guard lock(mutex_unused); +#endif + + Node_t* item = ISQUEUE ? get_node_unsafe() : +#if defined(ESP8266) || defined(ESP32) + new (std::nothrow) Node_t; +#else + new Node_t; +#endif + if (!item) + return nullptr; + + item->mDelegate = std::move(del); + item->mNext = nullptr; + + if (last) + last->mNext = item; + else + first = item; + last = item; + + return &item->mDelegate; + } + + bool remove(const Delegate* del) + { + auto current = first; + if (!current) + return false; + + Node_t* prev = nullptr; + do + { + if (del == ¤t->mDelegate) + { + // remove callback from stack +#ifdef ARDUINO + InterruptLock lockAllInterruptsInThisScope; +#else + std::lock_guard lock(mutex_unused); +#endif + + auto to_recycle = current; + + // removing rLast + if (last == current) + last = prev; + + current = current->mNext; + if (prev) + { + prev->mNext = current; + } + else + { + first = current; + } + + if (ISQUEUE) + recycle_node_unsafe(to_recycle); + else + delete to_recycle; + return true; + } + else + { + prev = current; + current = current->mNext; + } + } while (current); + return false; + } + + void operator()(P... args) + { + auto current = first; + if (!current) + return; + + static std::atomic fence(false); + // prevent recursive calls +#if defined(ARDUINO) && !defined(ESP32) + if (fence.load()) return; + fence.store(true); +#else + if (fence.exchange(true)) return; +#endif + + Node_t* prev = nullptr; + // prevent execution of new callbacks during this run + auto stop = last; + + bool done; + do + { + done = current == stop; + if (!CallP::execute(current->mDelegate, args...)) + { + // remove callback from stack +#ifdef ARDUINO + InterruptLock lockAllInterruptsInThisScope; +#else + std::lock_guard lock(mutex_unused); +#endif + + auto to_recycle = current; + + // removing rLast + if (last == current) + last = prev; + + current = current->mNext; + if (prev) + { + prev->mNext = current; + } + else + { + first = current; + } + + if (ISQUEUE) + recycle_node_unsafe(to_recycle); + else + delete to_recycle; + } + else + { + prev = current; + current = current->mNext; + } + +#if defined(ESP8266) || defined(ESP32) + // running callbacks might last too long for watchdog etc. + optimistic_yield(10000); +#endif + } while (current && !done); + + fence.store(false); + } + }; + + template< typename Delegate, typename R = void, bool ISQUEUE = false, uint32_t QUEUE_CAPACITY = 32> + class MultiDelegateImpl : public MultiDelegatePImpl + { + protected: + using typename MultiDelegatePImpl::Node_t; + using MultiDelegatePImpl::first; + using MultiDelegatePImpl::last; + using MultiDelegatePImpl::unused; + using MultiDelegatePImpl::nodeCount; + using MultiDelegatePImpl::recycle_node_unsafe; +#ifndef ARDUINO + using MultiDelegatePImpl::mutex_unused; +#endif + + public: + using MultiDelegatePImpl::MultiDelegatePImpl; + + void operator()() + { + auto current = first; + if (!current) + return; + + static std::atomic fence(false); + // prevent recursive calls +#if defined(ARDUINO) && !defined(ESP32) + if (fence.load()) return; + fence.store(true); +#else + if (fence.exchange(true)) return; +#endif + + Node_t* prev = nullptr; + // prevent execution of new callbacks during this run + auto stop = last; + + bool done; + do + { + done = current == stop; + if (!Call::execute(current->mDelegate)) + { + // remove callback from stack +#ifdef ARDUINO + InterruptLock lockAllInterruptsInThisScope; +#else + std::lock_guard lock(mutex_unused); +#endif + + auto to_recycle = current; + + // removing rLast + if (last == current) + last = prev; + + current = current->mNext; + if (prev) + { + prev->mNext = current; + } + else + { + first = current; + } + + if (ISQUEUE) + recycle_node_unsafe(to_recycle); + else + delete to_recycle; + } + else + { + prev = current; + current = current->mNext; + } + +#if defined(ESP8266) || defined(ESP32) + // running callbacks might last too long for watchdog etc. + optimistic_yield(10000); +#endif + } while (current && !done); + + fence.store(false); + } + }; + + template< typename Delegate, typename R, bool ISQUEUE, uint32_t QUEUE_CAPACITY, typename... P> class MultiDelegate; + + template< typename Delegate, typename R, bool ISQUEUE, uint32_t QUEUE_CAPACITY, typename... P> + class MultiDelegate : public MultiDelegatePImpl + { + public: + using MultiDelegatePImpl::MultiDelegatePImpl; + }; + + template< typename Delegate, typename R, bool ISQUEUE, uint32_t QUEUE_CAPACITY> + class MultiDelegate : public MultiDelegateImpl + { + public: + using MultiDelegateImpl::MultiDelegateImpl; + }; +}; + +/** +The MultiDelegate class template can be specialized to either a queue or an event multiplexer. +It is designed to be used with Delegate, the efficient runtime wrapper for C function ptr and C++ std::function. +@tparam Delegate specifies the concrete type that MultiDelegate bases the queue or event multiplexer on. +@tparam ISQUEUE modifies the generated MultiDelegate class in subtle ways. In queue mode (ISQUEUE == true), + the value of QUEUE_CAPACITY enforces the maximum number of simultaneous items the queue can contain. + This is exploited to minimize the use of new and delete by reusing already allocated items, thus + reducing heap fragmentation. In event multiplexer mode (ISQUEUE = false), new and delete are + used for allocation of the event handler items. + If the result type of the function call operator of Delegate is void, calling a MultiDelegate queue + removes each item after calling it; a Multidelegate event multiplexer keeps event handlers until + explicitly removed. + If the result type of the function call operator of Delegate is non-void, the type-conversion to bool + of that result determines if the item is immediately removed or kept after each call: a Multidelegate + queue removes an item only if true is returned, but a Multidelegate event multiplexer removes event + handlers that return false. +@tparam QUEUE_CAPACITY is only used if ISQUEUE == true. Then, it sets the maximum capacity that the queue dynamically + allocates from the heap. Unused items are not returned to the heap, but are managed by the MultiDelegate + instance during its own lifetime for efficiency. +*/ +template< typename Delegate, bool ISQUEUE = false, uint32_t QUEUE_CAPACITY = 32> +class MultiDelegate : public detail::MultiDelegate +{ +public: + using detail::MultiDelegate::MultiDelegate; +}; + +#endif // __MULTIDELEGATE_H diff --git a/EspSoftwareSerial/src/circular_queue/circular_queue.h b/EspSoftwareSerial/src/circular_queue/circular_queue.h new file mode 100644 index 00000000..46e3f66e --- /dev/null +++ b/EspSoftwareSerial/src/circular_queue/circular_queue.h @@ -0,0 +1,399 @@ +/* +circular_queue.h - Implementation of a lock-free circular queue for EspSoftwareSerial. +Copyright (c) 2019 Dirk O. Kaar. All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __circular_queue_h +#define __circular_queue_h + +#ifdef ARDUINO +#include +#endif + +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) +#include +#include +#include +#include "Delegate.h" +using std::min; +#else +#include "ghostl.h" +#endif + +#if !defined(ESP32) && !defined(ESP8266) +#define ICACHE_RAM_ATTR +#define IRAM_ATTR +#endif + +/*! + @brief Instance class for a single-producer, single-consumer circular queue / ring buffer (FIFO). + This implementation is lock-free between producer and consumer for the available(), peek(), + pop(), and push() type functions. +*/ +template< typename T, typename ForEachArg = void > +class circular_queue +{ +public: + /*! + @brief Constructs a valid, but zero-capacity dummy queue. + */ + circular_queue() : m_bufSize(1) + { + m_inPos.store(0); + m_outPos.store(0); + } + /*! + @brief Constructs a queue of the given maximum capacity. + */ + circular_queue(const size_t capacity) : m_bufSize(capacity + 1), m_buffer(new T[m_bufSize]) + { + m_inPos.store(0); + m_outPos.store(0); + } + circular_queue(circular_queue&& cq) : + m_bufSize(cq.m_bufSize), m_buffer(cq.m_buffer), m_inPos(cq.m_inPos.load()), m_outPos(cq.m_outPos.load()) + {} + ~circular_queue() + { + m_buffer.reset(); + } + circular_queue(const circular_queue&) = delete; + circular_queue& operator=(circular_queue&& cq) + { + m_bufSize = cq.m_bufSize; + m_buffer = cq.m_buffer; + m_inPos.store(cq.m_inPos.load()); + m_outPos.store(cq.m_outPos.load()); + } + circular_queue& operator=(const circular_queue&) = delete; + + /*! + @brief Get the numer of elements the queue can hold at most. + */ + size_t capacity() const + { + return m_bufSize - 1; + } + + /*! + @brief Resize the queue. The available elements in the queue are preserved. + This is not lock-free and concurrent producer or consumer access + will lead to corruption. + @return True if the new capacity could accommodate the present elements in + the queue, otherwise nothing is done and false is returned. + */ + bool capacity(const size_t cap); + + /*! + @brief Discard all data in the queue. + */ + void flush() + { + m_outPos.store(m_inPos.load()); + } + + /*! + @brief Get a snapshot number of elements that can be retrieved by pop. + */ + size_t available() const + { + int avail = static_cast(m_inPos.load() - m_outPos.load()); + if (avail < 0) avail += m_bufSize; + return avail; + } + + /*! + @brief Get the remaining free elementes for pushing. + */ + size_t available_for_push() const + { + int avail = static_cast(m_outPos.load() - m_inPos.load()) - 1; + if (avail < 0) avail += m_bufSize; + return avail; + } + + /*! + @brief Peek at the next element pop will return without removing it from the queue. + @return An rvalue copy of the next element that can be popped. If the queue is empty, + return an rvalue copy of the element that is pending the next push. + */ + T peek() const + { + const auto outPos = m_outPos.load(std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_acquire); + return m_buffer[outPos]; + } + + /*! + @brief Peek at the next pending input value. + @return A reference to the next element that can be pushed. + */ + T& IRAM_ATTR pushpeek() + { + const auto inPos = m_inPos.load(std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_acquire); + return m_buffer[inPos]; + } + + /*! + @brief Release the next pending input value, accessible by pushpeek(), into the queue. + @return true if the queue accepted the value, false if the queue + was full. + */ + bool IRAM_ATTR push(); + + /*! + @brief Move the rvalue parameter into the queue. + @return true if the queue accepted the value, false if the queue + was full. + */ + bool IRAM_ATTR push(T&& val); + + /*! + @brief Push a copy of the parameter into the queue. + @return true if the queue accepted the value, false if the queue + was full. + */ + bool IRAM_ATTR push(const T& val) + { + return push(T(val)); + } + +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) + /*! + @brief Push copies of multiple elements from a buffer into the queue, + in order, beginning at buffer's head. + @return The number of elements actually copied into the queue, counted + from the buffer head. + */ + size_t push_n(const T* buffer, size_t size); +#endif + + /*! + @brief Pop the next available element from the queue. + @return An rvalue copy of the popped element, or a default + value of type T if the queue is empty. + */ + T pop(); + +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) + /*! + @brief Pop multiple elements in ordered sequence from the queue to a buffer. + If buffer is nullptr, simply discards up to size elements from the queue. + @return The number of elements actually popped from the queue to + buffer. + */ + size_t pop_n(T* buffer, size_t size); +#endif + + /*! + @brief Iterate over and remove each available element from queue, + calling back fun with an rvalue reference of every single element. + */ +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) + void for_each(const Delegate& fun); +#else + void for_each(Delegate fun); +#endif + + /*! + @brief In reverse order, iterate over, pop and optionally requeue each available element from the queue, + calling back fun with a reference of every single element. + Requeuing is dependent on the return boolean of the callback function. If it + returns true, the requeue occurs. + */ +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) + bool for_each_rev_requeue(const Delegate& fun); +#else + bool for_each_rev_requeue(Delegate fun); +#endif + +protected: + const T defaultValue = {}; + unsigned m_bufSize; +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) + std::unique_ptr m_buffer; +#else + std::unique_ptr m_buffer; +#endif + std::atomic m_inPos; + std::atomic m_outPos; +}; + +template< typename T, typename ForEachArg > +bool circular_queue::capacity(const size_t cap) +{ + if (cap + 1 == m_bufSize) return true; + else if (available() > cap) return false; + std::unique_ptr buffer(new T[cap + 1]); + const auto available = pop_n(buffer, cap); + m_buffer.reset(buffer); + m_bufSize = cap + 1; + std::atomic_thread_fence(std::memory_order_release); + m_inPos.store(available, std::memory_order_relaxed); + m_outPos.store(0, std::memory_order_release); + return true; +} + +template< typename T, typename ForEachArg > +bool IRAM_ATTR circular_queue::push() +{ + const auto inPos = m_inPos.load(std::memory_order_acquire); + const unsigned next = (inPos + 1) % m_bufSize; + if (next == m_outPos.load(std::memory_order_relaxed)) { + return false; + } + + std::atomic_thread_fence(std::memory_order_acquire); + + m_inPos.store(next, std::memory_order_release); + return true; +} + +template< typename T, typename ForEachArg > +bool IRAM_ATTR circular_queue::push(T&& val) +{ + const auto inPos = m_inPos.load(std::memory_order_acquire); + const unsigned next = (inPos + 1) % m_bufSize; + if (next == m_outPos.load(std::memory_order_relaxed)) { + return false; + } + + std::atomic_thread_fence(std::memory_order_acquire); + + m_buffer[inPos] = std::move(val); + + std::atomic_thread_fence(std::memory_order_release); + + m_inPos.store(next, std::memory_order_release); + return true; +} + +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) +template< typename T, typename ForEachArg > +size_t circular_queue::push_n(const T* buffer, size_t size) +{ + const auto inPos = m_inPos.load(std::memory_order_acquire); + const auto outPos = m_outPos.load(std::memory_order_relaxed); + + size_t blockSize = (outPos > inPos) ? outPos - 1 - inPos : (outPos == 0) ? m_bufSize - 1 - inPos : m_bufSize - inPos; + blockSize = min(size, blockSize); + if (!blockSize) return 0; + int next = (inPos + blockSize) % m_bufSize; + + std::atomic_thread_fence(std::memory_order_acquire); + + auto dest = m_buffer.get() + inPos; + std::copy_n(std::make_move_iterator(buffer), blockSize, dest); + size = min(size - blockSize, outPos > 1 ? static_cast(outPos - next - 1) : 0); + next += size; + dest = m_buffer.get(); + std::copy_n(std::make_move_iterator(buffer + blockSize), size, dest); + + std::atomic_thread_fence(std::memory_order_release); + + m_inPos.store(next, std::memory_order_release); + return blockSize + size; +} +#endif + +template< typename T, typename ForEachArg > +T circular_queue::pop() +{ + const auto outPos = m_outPos.load(std::memory_order_acquire); + if (m_inPos.load(std::memory_order_relaxed) == outPos) return defaultValue; + + std::atomic_thread_fence(std::memory_order_acquire); + + auto val = std::move(m_buffer[outPos]); + + std::atomic_thread_fence(std::memory_order_release); + + m_outPos.store((outPos + 1) % m_bufSize, std::memory_order_release); + return val; +} + +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) +template< typename T, typename ForEachArg > +size_t circular_queue::pop_n(T* buffer, size_t size) { + size_t avail = size = min(size, available()); + if (!avail) return 0; + const auto outPos = m_outPos.load(std::memory_order_acquire); + size_t n = min(avail, static_cast(m_bufSize - outPos)); + + std::atomic_thread_fence(std::memory_order_acquire); + + if (buffer) { + buffer = std::copy_n(std::make_move_iterator(m_buffer.get() + outPos), n, buffer); + avail -= n; + std::copy_n(std::make_move_iterator(m_buffer.get()), avail, buffer); + } + + std::atomic_thread_fence(std::memory_order_release); + + m_outPos.store((outPos + size) % m_bufSize, std::memory_order_release); + return size; +} +#endif + +template< typename T, typename ForEachArg > +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) +void circular_queue::for_each(const Delegate& fun) +#else +void circular_queue::for_each(Delegate fun) +#endif +{ + auto outPos = m_outPos.load(std::memory_order_acquire); + const auto inPos = m_inPos.load(std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_acquire); + while (outPos != inPos) + { + fun(std::move(m_buffer[outPos])); + std::atomic_thread_fence(std::memory_order_release); + outPos = (outPos + 1) % m_bufSize; + m_outPos.store(outPos, std::memory_order_release); + } +} + +template< typename T, typename ForEachArg > +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) +bool circular_queue::for_each_rev_requeue(const Delegate& fun) +#else +bool circular_queue::for_each_rev_requeue(Delegate fun) +#endif +{ + auto inPos0 = circular_queue::m_inPos.load(std::memory_order_acquire); + auto outPos = circular_queue::m_outPos.load(std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_acquire); + if (outPos == inPos0) return false; + auto pos = inPos0; + auto outPos1 = inPos0; + const auto posDecr = circular_queue::m_bufSize - 1; + do { + pos = (pos + posDecr) % circular_queue::m_bufSize; + T&& val = std::move(circular_queue::m_buffer[pos]); + if (fun(val)) + { + outPos1 = (outPos1 + posDecr) % circular_queue::m_bufSize; + if (outPos1 != pos) circular_queue::m_buffer[outPos1] = std::move(val); + } + } while (pos != outPos); + circular_queue::m_outPos.store(outPos1, std::memory_order_release); + return true; +} + +#endif // __circular_queue_h diff --git a/EspSoftwareSerial/src/circular_queue/circular_queue_mp.h b/EspSoftwareSerial/src/circular_queue/circular_queue_mp.h new file mode 100644 index 00000000..7024247a --- /dev/null +++ b/EspSoftwareSerial/src/circular_queue/circular_queue_mp.h @@ -0,0 +1,200 @@ +/* +circular_queue_mp.h - Implementation of a lock-free circular queue for EspSoftwareSerial. +Copyright (c) 2019 Dirk O. Kaar. All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __circular_queue_mp_h +#define __circular_queue_mp_h + +#include "circular_queue.h" + +#ifdef ESP8266 +#include "interrupts.h" +#else +#include +#endif + +/*! + @brief Instance class for a multi-producer, single-consumer circular queue / ring buffer (FIFO). + This implementation is lock-free between producers and consumer for the available(), peek(), + pop(), and push() type functions, but is guarded to safely allow only a single producer + at any instant. +*/ +template< typename T, typename ForEachArg = void > +class circular_queue_mp : protected circular_queue +{ +public: + circular_queue_mp() = default; + circular_queue_mp(const size_t capacity) : circular_queue(capacity) + {} + circular_queue_mp(circular_queue&& cq) : circular_queue(std::move(cq)) + {} + using circular_queue::operator=; + using circular_queue::capacity; + using circular_queue::flush; + using circular_queue::available; + using circular_queue::available_for_push; + using circular_queue::peek; + using circular_queue::pop; + using circular_queue::pop_n; + using circular_queue::for_each; + using circular_queue::for_each_rev_requeue; + + /*! + @brief Resize the queue. The available elements in the queue are preserved. + This is not lock-free, but safe, concurrent producer or consumer access + is guarded. + @return True if the new capacity could accommodate the present elements in + the queue, otherwise nothing is done and false is returned. + */ + bool capacity(const size_t cap) + { +#ifdef ESP8266 + esp8266::InterruptLock lock; +#else + std::lock_guard lock(m_pushMtx); +#endif + return circular_queue::capacity(cap); + } + + bool IRAM_ATTR push() = delete; + + /*! + @brief Move the rvalue parameter into the queue, guarded + for multiple concurrent producers. + @return true if the queue accepted the value, false if the queue + was full. + */ + bool IRAM_ATTR push(T&& val) + { +#ifdef ESP8266 + esp8266::InterruptLock lock; +#else + std::lock_guard lock(m_pushMtx); +#endif + return circular_queue::push(std::move(val)); + } + + /*! + @brief Push a copy of the parameter into the queue, guarded + for multiple concurrent producers. + @return true if the queue accepted the value, false if the queue + was full. + */ + bool IRAM_ATTR push(const T& val) + { +#ifdef ESP8266 + esp8266::InterruptLock lock; +#else + std::lock_guard lock(m_pushMtx); +#endif + return circular_queue::push(val); + } + + /*! + @brief Push copies of multiple elements from a buffer into the queue, + in order, beginning at buffer's head. This is guarded for + multiple producers, push_n() is atomic. + @return The number of elements actually copied into the queue, counted + from the buffer head. + */ + size_t push_n(const T* buffer, size_t size) + { +#ifdef ESP8266 + esp8266::InterruptLock lock; +#else + std::lock_guard lock(m_pushMtx); +#endif + return circular_queue::push_n(buffer, size); + } + + /*! + @brief Pops the next available element from the queue, requeues + it immediately. + @return A reference to the just requeued element, or the default + value of type T if the queue is empty. + */ + T& pop_requeue(); + + /*! + @brief Iterate over, pop and optionally requeue each available element from the queue, + calling back fun with a reference of every single element. + Requeuing is dependent on the return boolean of the callback function. If it + returns true, the requeue occurs. + */ + bool for_each_requeue(const Delegate& fun); + +#ifndef ESP8266 +protected: + std::mutex m_pushMtx; +#endif +}; + +template< typename T, typename ForEachArg > +T& circular_queue_mp::pop_requeue() +{ +#ifdef ESP8266 + esp8266::InterruptLock lock; +#else + std::lock_guard lock(m_pushMtx); +#endif + const auto outPos = circular_queue::m_outPos.load(std::memory_order_acquire); + const auto inPos = circular_queue::m_inPos.load(std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_acquire); + if (inPos == outPos) return circular_queue::defaultValue; + T& val = circular_queue::m_buffer[inPos] = std::move(circular_queue::m_buffer[outPos]); + const auto bufSize = circular_queue::m_bufSize; + std::atomic_thread_fence(std::memory_order_release); + circular_queue::m_outPos.store((outPos + 1) % bufSize, std::memory_order_relaxed); + circular_queue::m_inPos.store((inPos + 1) % bufSize, std::memory_order_release); + return val; +} + +template< typename T, typename ForEachArg > +bool circular_queue_mp::for_each_requeue(const Delegate& fun) +{ + auto inPos0 = circular_queue::m_inPos.load(std::memory_order_acquire); + auto outPos = circular_queue::m_outPos.load(std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_acquire); + if (outPos == inPos0) return false; + do { + T&& val = std::move(circular_queue::m_buffer[outPos]); + if (fun(val)) + { +#ifdef ESP8266 + esp8266::InterruptLock lock; +#else + std::lock_guard lock(m_pushMtx); +#endif + std::atomic_thread_fence(std::memory_order_release); + auto inPos = circular_queue::m_inPos.load(std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_acquire); + circular_queue::m_buffer[inPos] = std::move(val); + std::atomic_thread_fence(std::memory_order_release); + circular_queue::m_inPos.store((inPos + 1) % circular_queue::m_bufSize, std::memory_order_release); + } + else + { + std::atomic_thread_fence(std::memory_order_release); + } + outPos = (outPos + 1) % circular_queue::m_bufSize; + circular_queue::m_outPos.store(outPos, std::memory_order_release); + } while (outPos != inPos0); + return true; +} + +#endif // __circular_queue_mp_h diff --git a/EspSoftwareSerial/src/circular_queue/ghostl.h b/EspSoftwareSerial/src/circular_queue/ghostl.h new file mode 100644 index 00000000..11683805 --- /dev/null +++ b/EspSoftwareSerial/src/circular_queue/ghostl.h @@ -0,0 +1,92 @@ +/* +ghostl.h - Implementation of a bare-bones, mostly no-op, C++ STL shell + that allows building some Arduino ESP8266/ESP32 + libraries on Aruduino AVR. +Copyright (c) 2019 Dirk O. Kaar. All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __ghostl_h +#define __ghostl_h + +#if defined(ARDUINO_ARCH_SAMD) +#include +#endif + +namespace std +{ +#if !defined(ARDUINO_ARCH_SAMD) + typedef enum memory_order { + memory_order_relaxed, + memory_order_acquire, + memory_order_release, + memory_order_seq_cst + } memory_order; + template< typename T > class atomic { + private: + T value; + public: + atomic() {} + atomic(T desired) { value = desired; } + void store(T desired, std::memory_order = std::memory_order_seq_cst) volatile noexcept { value = desired; } + T load(std::memory_order = std::memory_order_seq_cst) const volatile noexcept { return value; } + }; + inline void atomic_thread_fence(std::memory_order order) noexcept {} + template< typename T > T&& move(T& t) noexcept { return static_cast(t); } +#endif + + template< typename T, unsigned long N > struct array + { + T _M_elems[N]; + decltype(sizeof(0)) size() const { return N; } + T& operator[](decltype(sizeof(0)) i) { return _M_elems[i]; } + const T& operator[](decltype(sizeof(0)) i) const { return _M_elems[i]; } + }; + + template< typename T > class unique_ptr + { + public: + using pointer = T*; + unique_ptr() noexcept : ptr(nullptr) {} + unique_ptr(pointer p) : ptr(p) {} + pointer operator->() const noexcept { return ptr; } + T& operator[](decltype(sizeof(0)) i) const { return ptr[i]; } + void reset(pointer p = pointer()) noexcept + { + delete ptr; + ptr = p; + } + T& operator*() const { return *ptr; } + private: + pointer ptr; + }; + + template< typename T > using function = T*; + using nullptr_t = decltype(nullptr); + + template + struct identity { + typedef T type; + }; + + template + inline T&& forward(typename identity::type& t) noexcept + { + return static_cast::type&&>(t); + } +} + +#endif // __ghostl_h From 714a39904a2f7e974b2440e3bf91170d05da49e1 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Wed, 5 Feb 2020 16:38:30 +0100 Subject: [PATCH 031/106] Create keywords.txt --- lib/EspSoftwareSerial/keywords.txt | 43 ++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 lib/EspSoftwareSerial/keywords.txt diff --git a/lib/EspSoftwareSerial/keywords.txt b/lib/EspSoftwareSerial/keywords.txt new file mode 100644 index 00000000..52d48ab3 --- /dev/null +++ b/lib/EspSoftwareSerial/keywords.txt @@ -0,0 +1,43 @@ +####################################### +# Syntax Coloring Map for SoftwareSerial +# (esp8266) +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +SoftwareSerial KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +begin KEYWORD2 +baudRate KEYWORD2 +setTransmitEnablePin KEYWORD2 +enableIntTx KEYWORD2 +overflow KEYWORD2 +available KEYWORD2 +peek KEYWORD2 +read KEYWORD2 +flush KEYWORD2 +write KEYWORD2 +enableRx KEYWORD2 +enableTx KEYWORD2 +listen KEYWORD2 +end KEYWORD2 +isListening KEYWORD2 +stopListening KEYWORD2 +onReceive KEYWORD2 +perform_work KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### + +SW_SERIAL_UNUSED_PIN LITERAL1 +SWSERIAL_5N1 LITERAL1 +SWSERIAL_6N1 LITERAL1 +SWSERIAL_7N1 LITERAL1 +SWSERIAL_8N1 LITERAL1 From a21bd9c4469a9cc45726002636e08cd1e0fb2b6a Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Wed, 5 Feb 2020 16:39:04 +0100 Subject: [PATCH 032/106] Add files via upload --- lib/EspSoftwareSerial/LICENSE | 502 +++++++++++++++++++++++ lib/EspSoftwareSerial/README.md | 124 ++++++ lib/EspSoftwareSerial/library.json | 15 + lib/EspSoftwareSerial/library.properties | 9 + 4 files changed, 650 insertions(+) create mode 100644 lib/EspSoftwareSerial/LICENSE create mode 100644 lib/EspSoftwareSerial/README.md create mode 100644 lib/EspSoftwareSerial/library.json create mode 100644 lib/EspSoftwareSerial/library.properties diff --git a/lib/EspSoftwareSerial/LICENSE b/lib/EspSoftwareSerial/LICENSE new file mode 100644 index 00000000..f166cc57 --- /dev/null +++ b/lib/EspSoftwareSerial/LICENSE @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! \ No newline at end of file diff --git a/lib/EspSoftwareSerial/README.md b/lib/EspSoftwareSerial/README.md new file mode 100644 index 00000000..2d14c9e6 --- /dev/null +++ b/lib/EspSoftwareSerial/README.md @@ -0,0 +1,124 @@ +# EspSoftwareSerial + +## Implementation of the Arduino software serial library for the ESP8266 / ESP32 + +This fork implements interrupt service routine best practice. +In the receive interrupt, instead of blocking for whole bytes +at a time - voiding any near-realtime behavior of the CPU - only level +change and timestamp are recorded. The more time consuming phase +detection and byte assembly are done in the main code. + +Except at high bitrates, depending on other ongoing activity, +interrupts in particular, this software serial adapter +supports full duplex receive and send. At high bitrates (115200bps) +send bit timing can be improved at the expense of blocking concurrent +full duplex receives, with the ``SoftwareSerial::enableIntTx(false)`` function call. + +The same functionality is given as the corresponding AVR library but +several instances can be active at the same time. Speed up to 115200 baud +is supported. Besides a constructor compatible to the AVR SoftwareSerial class, +and updated constructor that takes no arguments exists, instead the ``begin()`` +function can handle the pin assignments and logic inversion. +It also has optional input buffer capacity arguments for byte buffer and ISR bit buffer. +This way, it is a better drop-in replacement for the hardware serial APIs on the ESP MCUs. + +Please note that due to the fact that the ESPs always have other activities +ongoing, there will be some inexactness in interrupt timings. This may +lead to inevitable, but few, bit errors when having heavy data traffic +at high baud rates. + +## Resource optimization + +The memory footprint can be optimized to just fit the amount of expected +incoming asynchronous data. +For this, the ``SoftwareSerial`` constructor provides two arguments. First, the +octet buffer capacity for assembled received octets can be set. Read calls are +satisfied from this buffer, freeing it in return. +Second, the signal edge detection buffer of 32bit fields can be resized. +One octet may require up to to 10 fields, but fewer may be needed, +depending on the bit pattern. Any read or write calls check this buffer +to assemble received octets, thus promoting completed octets to the octet +buffer, freeing fields in the edge detection buffer. + +Look at the swsertest.ino example. There, on reset, ASCII characters ' ' to 'z' +are sent. This happens not as a block write, but in a single write call per +character. As the example uses a local loopback wire, every outgoing bit is +immediately received back. Therefore, any single write call causes up to +10 fields - depending on the exact bit pattern - to be occupied in the signal +edge detection buffer. In turn, as explained before, each single write call +also causes received bit assembly to be performed, promoting these bits from +the signal edge detection buffer to the octet buffer as soon as possible. +Explaining by way of contrast, if during a a single write call, perhaps because +of using block writing, more than a single octet is received, there will be a +need for more than 10 fields in the signal edge detection buffer. +The necessary capacity of the octet buffer only depends on the amount of incoming +data until the next read call. + +For the swsertest.ino example, this results in the following optimized +constructor arguments to spend only the minimum RAM on buffers required: + +The octet buffer capacity (``bufCapacity``) is 93 (91 characters net plus two tolerance). +The signal edge detection buffer capacity (``isrBufCapacity``) is 10, as each octet has +10 bits on the wire, which are immediately received during the write, and each +write call causes the signal edge detection to promote the previously sent and +received bits to the octet buffer. + +In a more generalized scenario, calculate the bits (use message size in octets +times 10) that may be asynchronously received to determine the value for +``isrBufCapacity`` in the constructor. Also use the number of received octets +that must be buffered for reading as the value of ``bufCapacity``. +The more frequently your code calls write or read functions, the greater the +chances are that you can reduce the ``isrBufCapacity`` footprint without losing data, +and each time you call read to fetch from the octet buffer, you reduce the +need for space there. + +## SoftwareSerialConfig and parity +The configuration of the data stream is done via a ``SoftwareSerialConfig`` +argument to ``begin()``. Word lengths can be set to between 5 and 8 bits, parity +can be N(one), O(dd) or E(ven) and 1 or 2 stop bits can be used. The default is +``SWSERIAL_8N1`` using 8 bits, no parity and 1 stop bit but any combination can +be used, e.g. ``SWSERIAL_7E2``. If using EVEN or ODD parity, any parity errors +can be detected with the ``peekParityError()`` function. Note that parity +checking must be done before ``read()``, as the parity information is removed +from the buffer when reading the corresponding byte. + +To allow flexible 9-bit and data/addressing protocols, the additional parity +modes MARK and SPACE are also available. Furthermore, the parity mode can be +individually set in each call to ``write()``. + +This allows a simple implementation of protocols where the parity bit is used to +distinguish between data and addresses/commands ("9-bit" protocols). First set +up SoftwareSerial with parity mode SPACE, e.g. ``SWSERIAL_8S1``. This will add a +parity bit to every byte sent, setting it to logical zero (SPACE parity). + +To detect incoming bytes with the parity bit set (MARK parity), use the +``peekParityError()`` function. To send a byte with the parity bit set, just add +``MARK`` as the second argument when writing, e.g. ``write(ch, MARK)``. + +## Using and updating EspSoftwareSerial in the esp8266com/esp8266 Arduino build environment + +EspSoftwareSerial is both part of the BSP download for ESP8266 in Arduino, +and it is set up as a Git submodule in the esp8266 source tree, +specifically in ``.../esp8266/libraries/SoftwareSerial`` when using a Github +repository clone in your Arduino sketchbook hardware directory. +This supersedes any version of EspSoftwareSerial installed for instance via +the Arduino library manager, it is not required to install EspSoftwareSerial +for the ESP8266 separately at all, but doing so has ill effect. + +The responsible maintainer of the esp8266 repository has kindly shared the +following command line instructions to use, if one wishes to manually +update EspSoftwareSerial to a newer release than pulled in via the ESP8266 Arduino BSP: + +To update esp8266/arduino SoftwareSerial submodule to lastest master: + +Clean it (optional): +```shell +$ rm -rf libraries/SoftwareSerial +$ git submodule update --init +``` +Now update it: +```shell +$ cd libraries/SoftwareSerial +$ git checkout master +$ git pull +``` diff --git a/lib/EspSoftwareSerial/library.json b/lib/EspSoftwareSerial/library.json new file mode 100644 index 00000000..3415b056 --- /dev/null +++ b/lib/EspSoftwareSerial/library.json @@ -0,0 +1,15 @@ +{ + "name": "EspSoftwareSerial", + "version": "6.6.1", + "keywords": [ + "serial", "io", "softwareserial" + ], + "description": "Implementation of the Arduino software serial for ESP8266/ESP32.", + "repository": + { + "type": "git", + "url": "https://github.com/plerup/espsoftwareserial" + }, + "frameworks": "arduino", + "platforms": "*" +} diff --git a/lib/EspSoftwareSerial/library.properties b/lib/EspSoftwareSerial/library.properties new file mode 100644 index 00000000..bd052153 --- /dev/null +++ b/lib/EspSoftwareSerial/library.properties @@ -0,0 +1,9 @@ +name=EspSoftwareSerial +version=6.6.1 +author=Peter Lerup, Dirk Kaar +maintainer=Peter Lerup +sentence=Implementation of the Arduino software serial for ESP8266/ESP32. +paragraph= +category=Signal Input/Output +url=https://github.com/plerup/espsoftwareserial/ +architectures=esp8266,esp32 From 8f322e536740c3908b4af9913c7723a452cc2a45 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Wed, 5 Feb 2020 16:39:42 +0100 Subject: [PATCH 033/106] Delete LICENSE --- EspSoftwareSerial/LICENSE | 502 -------------------------------------- 1 file changed, 502 deletions(-) delete mode 100644 EspSoftwareSerial/LICENSE diff --git a/EspSoftwareSerial/LICENSE b/EspSoftwareSerial/LICENSE deleted file mode 100644 index f166cc57..00000000 --- a/EspSoftwareSerial/LICENSE +++ /dev/null @@ -1,502 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! \ No newline at end of file From c7cf26185e43c2270f6eb0d08a9807a32a621d9f Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Wed, 5 Feb 2020 16:40:15 +0100 Subject: [PATCH 034/106] Delete README.md --- EspSoftwareSerial/README.md | 124 ------------------------------------ 1 file changed, 124 deletions(-) delete mode 100644 EspSoftwareSerial/README.md diff --git a/EspSoftwareSerial/README.md b/EspSoftwareSerial/README.md deleted file mode 100644 index 2d14c9e6..00000000 --- a/EspSoftwareSerial/README.md +++ /dev/null @@ -1,124 +0,0 @@ -# EspSoftwareSerial - -## Implementation of the Arduino software serial library for the ESP8266 / ESP32 - -This fork implements interrupt service routine best practice. -In the receive interrupt, instead of blocking for whole bytes -at a time - voiding any near-realtime behavior of the CPU - only level -change and timestamp are recorded. The more time consuming phase -detection and byte assembly are done in the main code. - -Except at high bitrates, depending on other ongoing activity, -interrupts in particular, this software serial adapter -supports full duplex receive and send. At high bitrates (115200bps) -send bit timing can be improved at the expense of blocking concurrent -full duplex receives, with the ``SoftwareSerial::enableIntTx(false)`` function call. - -The same functionality is given as the corresponding AVR library but -several instances can be active at the same time. Speed up to 115200 baud -is supported. Besides a constructor compatible to the AVR SoftwareSerial class, -and updated constructor that takes no arguments exists, instead the ``begin()`` -function can handle the pin assignments and logic inversion. -It also has optional input buffer capacity arguments for byte buffer and ISR bit buffer. -This way, it is a better drop-in replacement for the hardware serial APIs on the ESP MCUs. - -Please note that due to the fact that the ESPs always have other activities -ongoing, there will be some inexactness in interrupt timings. This may -lead to inevitable, but few, bit errors when having heavy data traffic -at high baud rates. - -## Resource optimization - -The memory footprint can be optimized to just fit the amount of expected -incoming asynchronous data. -For this, the ``SoftwareSerial`` constructor provides two arguments. First, the -octet buffer capacity for assembled received octets can be set. Read calls are -satisfied from this buffer, freeing it in return. -Second, the signal edge detection buffer of 32bit fields can be resized. -One octet may require up to to 10 fields, but fewer may be needed, -depending on the bit pattern. Any read or write calls check this buffer -to assemble received octets, thus promoting completed octets to the octet -buffer, freeing fields in the edge detection buffer. - -Look at the swsertest.ino example. There, on reset, ASCII characters ' ' to 'z' -are sent. This happens not as a block write, but in a single write call per -character. As the example uses a local loopback wire, every outgoing bit is -immediately received back. Therefore, any single write call causes up to -10 fields - depending on the exact bit pattern - to be occupied in the signal -edge detection buffer. In turn, as explained before, each single write call -also causes received bit assembly to be performed, promoting these bits from -the signal edge detection buffer to the octet buffer as soon as possible. -Explaining by way of contrast, if during a a single write call, perhaps because -of using block writing, more than a single octet is received, there will be a -need for more than 10 fields in the signal edge detection buffer. -The necessary capacity of the octet buffer only depends on the amount of incoming -data until the next read call. - -For the swsertest.ino example, this results in the following optimized -constructor arguments to spend only the minimum RAM on buffers required: - -The octet buffer capacity (``bufCapacity``) is 93 (91 characters net plus two tolerance). -The signal edge detection buffer capacity (``isrBufCapacity``) is 10, as each octet has -10 bits on the wire, which are immediately received during the write, and each -write call causes the signal edge detection to promote the previously sent and -received bits to the octet buffer. - -In a more generalized scenario, calculate the bits (use message size in octets -times 10) that may be asynchronously received to determine the value for -``isrBufCapacity`` in the constructor. Also use the number of received octets -that must be buffered for reading as the value of ``bufCapacity``. -The more frequently your code calls write or read functions, the greater the -chances are that you can reduce the ``isrBufCapacity`` footprint without losing data, -and each time you call read to fetch from the octet buffer, you reduce the -need for space there. - -## SoftwareSerialConfig and parity -The configuration of the data stream is done via a ``SoftwareSerialConfig`` -argument to ``begin()``. Word lengths can be set to between 5 and 8 bits, parity -can be N(one), O(dd) or E(ven) and 1 or 2 stop bits can be used. The default is -``SWSERIAL_8N1`` using 8 bits, no parity and 1 stop bit but any combination can -be used, e.g. ``SWSERIAL_7E2``. If using EVEN or ODD parity, any parity errors -can be detected with the ``peekParityError()`` function. Note that parity -checking must be done before ``read()``, as the parity information is removed -from the buffer when reading the corresponding byte. - -To allow flexible 9-bit and data/addressing protocols, the additional parity -modes MARK and SPACE are also available. Furthermore, the parity mode can be -individually set in each call to ``write()``. - -This allows a simple implementation of protocols where the parity bit is used to -distinguish between data and addresses/commands ("9-bit" protocols). First set -up SoftwareSerial with parity mode SPACE, e.g. ``SWSERIAL_8S1``. This will add a -parity bit to every byte sent, setting it to logical zero (SPACE parity). - -To detect incoming bytes with the parity bit set (MARK parity), use the -``peekParityError()`` function. To send a byte with the parity bit set, just add -``MARK`` as the second argument when writing, e.g. ``write(ch, MARK)``. - -## Using and updating EspSoftwareSerial in the esp8266com/esp8266 Arduino build environment - -EspSoftwareSerial is both part of the BSP download for ESP8266 in Arduino, -and it is set up as a Git submodule in the esp8266 source tree, -specifically in ``.../esp8266/libraries/SoftwareSerial`` when using a Github -repository clone in your Arduino sketchbook hardware directory. -This supersedes any version of EspSoftwareSerial installed for instance via -the Arduino library manager, it is not required to install EspSoftwareSerial -for the ESP8266 separately at all, but doing so has ill effect. - -The responsible maintainer of the esp8266 repository has kindly shared the -following command line instructions to use, if one wishes to manually -update EspSoftwareSerial to a newer release than pulled in via the ESP8266 Arduino BSP: - -To update esp8266/arduino SoftwareSerial submodule to lastest master: - -Clean it (optional): -```shell -$ rm -rf libraries/SoftwareSerial -$ git submodule update --init -``` -Now update it: -```shell -$ cd libraries/SoftwareSerial -$ git checkout master -$ git pull -``` From 2848011af3292aef43acc8f4634b0efe07fecd2e Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Wed, 5 Feb 2020 16:40:31 +0100 Subject: [PATCH 035/106] Delete keywords.txt --- EspSoftwareSerial/keywords.txt | 43 ---------------------------------- 1 file changed, 43 deletions(-) delete mode 100644 EspSoftwareSerial/keywords.txt diff --git a/EspSoftwareSerial/keywords.txt b/EspSoftwareSerial/keywords.txt deleted file mode 100644 index 52d48ab3..00000000 --- a/EspSoftwareSerial/keywords.txt +++ /dev/null @@ -1,43 +0,0 @@ -####################################### -# Syntax Coloring Map for SoftwareSerial -# (esp8266) -####################################### - -####################################### -# Datatypes (KEYWORD1) -####################################### - -SoftwareSerial KEYWORD1 - -####################################### -# Methods and Functions (KEYWORD2) -####################################### - -begin KEYWORD2 -baudRate KEYWORD2 -setTransmitEnablePin KEYWORD2 -enableIntTx KEYWORD2 -overflow KEYWORD2 -available KEYWORD2 -peek KEYWORD2 -read KEYWORD2 -flush KEYWORD2 -write KEYWORD2 -enableRx KEYWORD2 -enableTx KEYWORD2 -listen KEYWORD2 -end KEYWORD2 -isListening KEYWORD2 -stopListening KEYWORD2 -onReceive KEYWORD2 -perform_work KEYWORD2 - -####################################### -# Constants (LITERAL1) -####################################### - -SW_SERIAL_UNUSED_PIN LITERAL1 -SWSERIAL_5N1 LITERAL1 -SWSERIAL_6N1 LITERAL1 -SWSERIAL_7N1 LITERAL1 -SWSERIAL_8N1 LITERAL1 From 2c1851fa1914a1552f09a67ad128f5bd09890f16 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Wed, 5 Feb 2020 16:40:46 +0100 Subject: [PATCH 036/106] Delete library.json --- EspSoftwareSerial/library.json | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 EspSoftwareSerial/library.json diff --git a/EspSoftwareSerial/library.json b/EspSoftwareSerial/library.json deleted file mode 100644 index 3415b056..00000000 --- a/EspSoftwareSerial/library.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "EspSoftwareSerial", - "version": "6.6.1", - "keywords": [ - "serial", "io", "softwareserial" - ], - "description": "Implementation of the Arduino software serial for ESP8266/ESP32.", - "repository": - { - "type": "git", - "url": "https://github.com/plerup/espsoftwareserial" - }, - "frameworks": "arduino", - "platforms": "*" -} From 73823ec0705f4ea7784374e63f169773abdffae5 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Wed, 5 Feb 2020 16:40:56 +0100 Subject: [PATCH 037/106] Delete library.properties --- EspSoftwareSerial/library.properties | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 EspSoftwareSerial/library.properties diff --git a/EspSoftwareSerial/library.properties b/EspSoftwareSerial/library.properties deleted file mode 100644 index bd052153..00000000 --- a/EspSoftwareSerial/library.properties +++ /dev/null @@ -1,9 +0,0 @@ -name=EspSoftwareSerial -version=6.6.1 -author=Peter Lerup, Dirk Kaar -maintainer=Peter Lerup -sentence=Implementation of the Arduino software serial for ESP8266/ESP32. -paragraph= -category=Signal Input/Output -url=https://github.com/plerup/espsoftwareserial/ -architectures=esp8266,esp32 From 92da9d96850e7fb92e2361ae880505c4995d007a Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Wed, 5 Feb 2020 16:41:35 +0100 Subject: [PATCH 038/106] Delete loopback.ino --- .../examples/loopback/loopback.ino | 263 ------------------ 1 file changed, 263 deletions(-) delete mode 100644 EspSoftwareSerial/examples/loopback/loopback.ino diff --git a/EspSoftwareSerial/examples/loopback/loopback.ino b/EspSoftwareSerial/examples/loopback/loopback.ino deleted file mode 100644 index b612bdec..00000000 --- a/EspSoftwareSerial/examples/loopback/loopback.ino +++ /dev/null @@ -1,263 +0,0 @@ -#include - -// On ESP8266: -// Local SoftwareSerial loopback, connect D5 (rx) and D6 (tx). -// For local hardware loopback, connect D5 to D8 (tx), D6 to D7 (rx). -// For hardware send/sink, connect D7 (rx) and D8 (tx). -// Hint: The logger is run at 9600bps such that enableIntTx(true) can remain unchanged. Blocking -// interrupts severely impacts the ability of the SoftwareSerial devices to operate concurrently -// and/or in duplex mode. -// Operating in software serial full duplex mode, runs at 19200bps and few errors (~2.5%). -// Operating in software serial half duplex mode (both loopback and repeater), -// runs at 57600bps with nearly no errors. -// Operating loopback in full duplex, and repeater in half duplex, runs at 38400bps with nearly no errors. -// On ESP32: -// For SoftwareSerial or hardware send/sink, connect D5 (rx) and D6 (tx). -// Hardware Serial2 defaults to D4 (rx), D3 (tx). -// For local hardware loopback, connect D5 (rx) to D3 (tx), D6 (tx) to D4 (rx). - -#if defined(ESP8266) && !defined(D5) -#define D5 (14) -#define D6 (12) -#define D7 (13) -#define D8 (15) -#define TX (1) -#endif - -// Pick only one of HWLOOPBACK, HWSOURCESWSINK, or HWSOURCESINK -//#define HWLOOPBACK 1 -//#define HWSOURCESWSINK 1 -//#define HWSOURCESINK 1 -#define HALFDUPLEX 1 - -#ifdef ESP32 -constexpr int IUTBITRATE = 19200; -#else -constexpr int IUTBITRATE = 19200; -#endif - -#if defined(ESP8266) -constexpr SoftwareSerialConfig swSerialConfig = SWSERIAL_8E1; -constexpr SerialConfig hwSerialConfig = SERIAL_8E1; -#elif defined(ESP32) -constexpr SoftwareSerialConfig swSerialConfig = SWSERIAL_8E1; -constexpr uint32_t hwSerialConfig = SERIAL_8E1; -#else -constexpr unsigned swSerialConfig = 3; -#endif -constexpr bool invert = false; - -constexpr int BLOCKSIZE = 16; // use fractions of 256 - -unsigned long start; -String effTxTxt("eff. tx: "); -String effRxTxt("eff. rx: "); -int txCount; -int rxCount; -int expected; -int rxErrors; -int rxParityErrors; -constexpr int ReportInterval = IUTBITRATE / 8; - -#if defined(ESP8266) -#if defined(HWLOOPBACK) || defined(HWSOURCESWSINK) -HardwareSerial& hwSerial(Serial); -SoftwareSerial serialIUT; -SoftwareSerial logger; -#elif defined(HWSOURCESINK) -HardwareSerial& serialIUT(Serial); -SoftwareSerial logger; -#else -SoftwareSerial serialIUT; -HardwareSerial& logger(Serial); -#endif -#elif defined(ESP32) -#if defined(HWLOOPBACK) || defined (HWSOURCESWSINK) -HardwareSerial& hwSerial(Serial2); -SoftwareSerial serialIUT; -#elif defined(HWSOURCESINK) -HardwareSerial& serialIUT(Serial2); -#else -SoftwareSerial serialIUT; -#endif -HardwareSerial& logger(Serial); -#else -SoftwareSerial serialIUT(14, 12); -HardwareSerial& logger(Serial); -#endif - -void setup() { -#if defined(ESP8266) -#if defined(HWLOOPBACK) || defined(HWSOURCESINK) || defined(HWSOURCESWSINK) - Serial.begin(IUTBITRATE, hwSerialConfig, SERIAL_FULL, 1, invert); - Serial.swap(); - Serial.setRxBufferSize(2 * BLOCKSIZE); - logger.begin(9600, SWSERIAL_8N1, -1, TX); -#else - logger.begin(9600); -#endif -#if !defined(HWSOURCESINK) - serialIUT.begin(IUTBITRATE, swSerialConfig, D5, D6, invert, 2 * BLOCKSIZE); -#ifdef HALFDUPLEX - serialIUT.enableIntTx(false); -#endif -#endif -#elif defined(ESP32) -#if defined(HWLOOPBACK) || defined(HWSOURCESWSINK) - Serial2.begin(IUTBITRATE, hwSerialConfig, D4, D3, invert); - Serial2.setRxBufferSize(2 * BLOCKSIZE); -#elif defined(HWSOURCESINK) - serialIUT.begin(IUTBITRATE, hwSerialConfig, D5, D6, invert); - serialIUT.setRxBufferSize(2 * BLOCKSIZE); -#endif -#if !defined(HWSOURCESINK) - serialIUT.begin(IUTBITRATE, swSerialConfig, D5, D6, invert, 2 * BLOCKSIZE); -#ifdef HALFDUPLEX - serialIUT.enableIntTx(false); -#endif -#endif - logger.begin(9600); -#else -#if !defined(HWSOURCESINK) - serialIUT.begin(IUTBITRATE); -#endif - logger.begin(9600); -#endif - - logger.println("Loopback example for EspSoftwareSerial"); - - start = micros(); - txCount = 0; - rxCount = 0; - rxErrors = 0; - rxParityErrors = 0; - expected = -1; -} - -unsigned char c = 0; - -void loop() { -#ifdef HALFDUPLEX - char block[BLOCKSIZE]; -#endif - char inBuf[BLOCKSIZE]; - for (int i = 0; i < BLOCKSIZE; ++i) { -#ifndef HALFDUPLEX -#ifdef HWSOURCESWSINK - hwSerial.write(c); -#else - serialIUT.write(c); -#endif -#ifdef HWLOOPBACK - int avail = hwSerial.available(); - while ((0 == (i % 8)) && avail > 0) { - int inCnt = hwSerial.read(inBuf, min(avail, min(BLOCKSIZE, hwSerial.availableForWrite()))); - hwSerial.write(inBuf, inCnt); - avail -= inCnt; - } -#endif -#else - block[i] = c; -#endif - c = (c + 1) % 256; - ++txCount; - } -#ifdef HALFDUPLEX -#ifdef HWSOURCESWSINK - hwSerial.write(block, BLOCKSIZE); -#else - serialIUT.write(block, BLOCKSIZE); -#endif -#endif -#ifdef HWSOURCESINK -#if defined(ESP8266) - if (serialIUT.hasOverrun()) { logger.println("serialIUT.overrun"); } -#endif -#else - if (serialIUT.overflow()) { logger.println("serialIUT.overflow"); } -#endif - - int inCnt; - uint32_t deadlineStart; - -#ifdef HWLOOPBACK - // starting deadline for the first bytes to become readable - deadlineStart = ESP.getCycleCount(); - inCnt = 0; - while ((ESP.getCycleCount() - deadlineStart) < (1000000UL * 12 * BLOCKSIZE) / IUTBITRATE * 24 * ESP.getCpuFreqMHz()) { - int avail = hwSerial.available(); - inCnt += hwSerial.read(&inBuf[inCnt], min(avail, min(BLOCKSIZE - inCnt, hwSerial.availableForWrite()))); - if (inCnt >= BLOCKSIZE) { break; } - // wait for more outstanding bytes to trickle in - if (avail) deadlineStart = ESP.getCycleCount(); - } - hwSerial.write(inBuf, inCnt); -#endif - - // starting deadline for the first bytes to come in - deadlineStart = ESP.getCycleCount(); - inCnt = 0; - while ((ESP.getCycleCount() - deadlineStart) < (1000000UL * 12 * BLOCKSIZE) / IUTBITRATE * 8 * ESP.getCpuFreqMHz()) { - int avail; - if (0 != (swSerialConfig & 070)) - avail = serialIUT.available(); - else - avail = serialIUT.read(inBuf, BLOCKSIZE); - for (int i = 0; i < avail; ++i) - { - unsigned char r; - if (0 != (swSerialConfig & 070)) - r = serialIUT.read(); - else - r = inBuf[i]; - if (expected == -1) { expected = r; } - else { - expected = (expected + 1) % (1UL << (5 + swSerialConfig % 4)); - } - if (r != expected) { - ++rxErrors; - expected = -1; - } -#ifndef HWSOURCESINK - if (serialIUT.readParity() != (static_cast(swSerialConfig & 010) ? serialIUT.parityOdd(r) : serialIUT.parityEven(r))) - { - ++rxParityErrors; - } -#endif - ++rxCount; - ++inCnt; - } - - if (inCnt >= BLOCKSIZE) { break; } - // wait for more outstanding bytes to trickle in - if (avail) deadlineStart = ESP.getCycleCount(); - } - - const uint32_t interval = micros() - start; - if (txCount >= ReportInterval && interval) { - uint8_t wordBits = (5 + swSerialConfig % 4) + static_cast(swSerialConfig & 070) + 1 + ((swSerialConfig & 0300) ? 1 : 0); - logger.println(String("tx/rx: ") + txCount + "/" + rxCount); - const long txCps = txCount * (1000000.0 / interval); - const long rxCps = rxCount * (1000000.0 / interval); - logger.print(effTxTxt + wordBits * txCps + "bps, " - + effRxTxt + wordBits * rxCps + "bps, " - + rxErrors + " errors (" + 100.0 * rxErrors / (!rxErrors ? 1 : rxCount) + "%)"); - if (0 != (swSerialConfig & 070)) - { - logger.print(" ("); logger.print(rxParityErrors); logger.println(" parity errors)"); - } - else - { - logger.println(); - } - txCount = 0; - rxCount = 0; - rxErrors = 0; - rxParityErrors = 0; - expected = -1; - // resync - delay(1000UL * 12 * BLOCKSIZE / IUTBITRATE * 16); - serialIUT.flush(); - start = micros(); - } -} From 72bf6ad6c6efc11ba35737e47b7c6d8c8e58bdcd Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Wed, 5 Feb 2020 16:41:48 +0100 Subject: [PATCH 039/106] Delete onewiretest.ino --- .../examples/onewiretest/onewiretest.ino | 48 ------------------- 1 file changed, 48 deletions(-) delete mode 100644 EspSoftwareSerial/examples/onewiretest/onewiretest.ino diff --git a/EspSoftwareSerial/examples/onewiretest/onewiretest.ino b/EspSoftwareSerial/examples/onewiretest/onewiretest.ino deleted file mode 100644 index 3e96401b..00000000 --- a/EspSoftwareSerial/examples/onewiretest/onewiretest.ino +++ /dev/null @@ -1,48 +0,0 @@ -#include -#include "SoftwareSerial.h" - -SoftwareSerial swSer1; -SoftwareSerial swSer2; - -void setup() { - delay(2000); - Serial.begin(115200); - Serial.println("\nOne Wire Half Duplex Serial Tester"); - swSer1.begin(115200, SWSERIAL_8N1, 12, 12, false, 256); - swSer1.enableIntTx(true); - swSer2.begin(115200, SWSERIAL_8N1, 14, 14, false, 256); - swSer2.enableIntTx(true); -} - -void loop() { - Serial.println("\n\nTesting on swSer1"); - Serial.print("Enter something to send using swSer1."); - checkSwSerial(&swSer1); - - Serial.println("\n\nTesting on swSer2"); - Serial.print("Enter something to send using swSer2."); - checkSwSerial(&swSer2); - -} - -void checkSwSerial(SoftwareSerial* ss) { - byte ch; - while (!Serial.available()); - ss->enableTx(true); - while (Serial.available()) { - ch = Serial.read(); - ss->write(ch); - } - ss->enableTx(false); - // wait 1 second for the reply from SOftwareSerial if any - delay(1000); - if (ss->available()) { - Serial.print("\nResult:"); - while (ss->available()) { - ch = (byte)ss->read(); - Serial.print(ch < 0x01 ? " 0" : " "); - Serial.print(ch, HEX); - } - Serial.println(); - } -} From ab688075bc225a5817397cf51a460ec28f4e770c Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Wed, 5 Feb 2020 16:42:01 +0100 Subject: [PATCH 040/106] Delete repeater.ino --- .../examples/repeater/repeater.ino | 183 ------------------ 1 file changed, 183 deletions(-) delete mode 100644 EspSoftwareSerial/examples/repeater/repeater.ino diff --git a/EspSoftwareSerial/examples/repeater/repeater.ino b/EspSoftwareSerial/examples/repeater/repeater.ino deleted file mode 100644 index fa5566de..00000000 --- a/EspSoftwareSerial/examples/repeater/repeater.ino +++ /dev/null @@ -1,183 +0,0 @@ -#include - -// On ESP8266: -// SoftwareSerial loopback for remote source (loopback.ino), or hardware loopback. -// Connect source D5 (rx) to local D8 (tx), source D6 (tx) to local D7 (rx). -// Hint: The logger is run at 9600bps such that enableIntTx(true) can remain unchanged. Blocking -// interrupts severely impacts the ability of the SoftwareSerial devices to operate concurrently -// and/or in duplex mode. -// On ESP32: -// For software or hardware loopback, connect source rx to local D8 (tx), source tx to local D7 (rx). - -#if defined(ESP8266) && !defined(D5) -#define D5 (14) -#define D6 (12) -#define D7 (13) -#define D8 (15) -#define TX (1) -#endif - -#define HWLOOPBACK 1 -#define HALFDUPLEX 1 - -#ifdef ESP32 -constexpr int IUTBITRATE = 19200; -#else -constexpr int IUTBITRATE = 19200; -#endif - -#if defined(ESP8266) -constexpr SoftwareSerialConfig swSerialConfig = SWSERIAL_8E1; -constexpr SerialConfig hwSerialConfig = SERIAL_8E1; -#elif defined(ESP32) -constexpr SoftwareSerialConfig swSerialConfig = SWSERIAL_8E1; -constexpr uint32_t hwSerialConfig = SERIAL_8E1; -#else -constexpr unsigned swSerialConfig = 3; -#endif -constexpr bool invert = false; - -constexpr int BLOCKSIZE = 16; // use fractions of 256 - -unsigned long start; -String bitRateTxt("Effective data rate: "); -int rxCount; -int seqErrors; -int parityErrors; -int expected; -constexpr int ReportInterval = IUTBITRATE / 8; - -#if defined(ESP8266) -#if defined(HWLOOPBACK) -HardwareSerial& repeater(Serial); -SoftwareSerial logger; -#else -SoftwareSerial repeater; -HardwareSerial& logger(Serial); -#endif -#elif defined(ESP32) -#if defined(HWLOOPBACK) -HardwareSerial& repeater(Serial2); -#else -SoftwareSerial repeater; -#endif -HardwareSerial& logger(Serial); -#else -SoftwareSerial repeater(14, 12); -HardwareSerial& logger(Serial); -#endif - -void setup() { -#if defined(ESP8266) -#if defined(HWLOOPBACK) - repeater.begin(IUTBITRATE, hwSerialConfig, SERIAL_FULL, 1, invert); - repeater.swap(); - repeater.setRxBufferSize(2 * BLOCKSIZE); - logger.begin(9600, SWSERIAL_8N1, -1, TX); -#else - repeater.begin(IUTBITRATE, swSerialConfig, D7, D8, invert, 4 * BLOCKSIZE); -#ifdef HALFDUPLEX - repeater.enableIntTx(false); -#endif - logger.begin(9600); -#endif -#elif defined(ESP32) -#if defined(HWLOOPBACK) - repeater.begin(IUTBITRATE, hwSerialConfig, D7, D8, invert); - repeater.setRxBufferSize(2 * BLOCKSIZE); -#else - repeater.begin(IUTBITRATE, swSerialConfig, D7, D8, invert, 4 * BLOCKSIZE); -#ifdef HALFDUPLEX - repeater.enableIntTx(false); -#endif -#endif - logger.begin(9600); -#else - repeater.begin(IUTBITRATE); - logger.begin(9600); -#endif - - logger.println("Repeater example for EspSoftwareSerial"); - start = micros(); - rxCount = 0; - seqErrors = 0; - parityErrors = 0; - expected = -1; -} - -void loop() { -#ifdef HWLOOPBACK -#if defined(ESP8266) - if (repeater.hasOverrun()) { logger.println("repeater.overrun"); } -#endif -#else - if (repeater.overflow()) { logger.println("repeater.overflow"); } -#endif - -#ifdef HALFDUPLEX - char block[BLOCKSIZE]; -#endif - // starting deadline for the first bytes to come in - uint32_t deadlineStart = ESP.getCycleCount(); - int inCnt = 0; - while ((ESP.getCycleCount() - deadlineStart) < (1000000UL * 12 * BLOCKSIZE) / IUTBITRATE * 24 * ESP.getCpuFreqMHz()) { - int avail = repeater.available(); - for (int i = 0; i < avail; ++i) - { - int r = repeater.read(); - if (r == -1) { logger.println("read() == -1"); } - if (expected == -1) { expected = r; } - else { - expected = (expected + 1) % (1UL << (5 + swSerialConfig % 4)); - } - if (r != expected) { - ++seqErrors; - expected = -1; - } -#ifndef HWLOOPBACK - if (repeater.readParity() != (static_cast(swSerialConfig & 010) ? repeater.parityOdd(r) : repeater.parityEven(r))) - { - ++parityErrors; - } -#endif - ++rxCount; -#ifdef HALFDUPLEX - block[inCnt] = r; -#else - repeater.write(r); -#endif - if (++inCnt >= BLOCKSIZE) { break; } - } - if (inCnt >= BLOCKSIZE) { break; } - // wait for more outstanding bytes to trickle in - if (avail) deadlineStart = ESP.getCycleCount(); - } - -#ifdef HALFDUPLEX - repeater.write(block, inCnt); -#endif - - if (rxCount >= ReportInterval) { - auto end = micros(); - unsigned long interval = end - start; - long cps = rxCount * (1000000.0 / interval); - long seqErrorsps = seqErrors * (1000000.0 / interval); - logger.print(bitRateTxt + 10 * cps + "bps, " - + seqErrorsps + "cps seq. errors (" + 100.0 * seqErrors / rxCount + "%)"); -#ifndef HWLOOPBACK - if (0 != (swSerialConfig & 070)) - { - logger.print(" ("); logger.print(parityErrors); logger.print(" parity errors)"); - } - else -#endif - { - logger.println(); - } - start = end; - rxCount = 0; - seqErrors = 0; - parityErrors = 0; - expected = -1; - } -} From 1fca8adf5b65cecac58add34c4e564ab5bb37674 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Wed, 5 Feb 2020 16:42:14 +0100 Subject: [PATCH 041/106] Delete servoTester.ino --- .../examples/servoTester/servoTester.ino | 115 ------------------ 1 file changed, 115 deletions(-) delete mode 100644 EspSoftwareSerial/examples/servoTester/servoTester.ino diff --git a/EspSoftwareSerial/examples/servoTester/servoTester.ino b/EspSoftwareSerial/examples/servoTester/servoTester.ino deleted file mode 100644 index cbc784d8..00000000 --- a/EspSoftwareSerial/examples/servoTester/servoTester.ino +++ /dev/null @@ -1,115 +0,0 @@ -#include -#include - -SoftwareSerial swSer; - -byte buf[10] = { 0xFA, 0xAF,0x00,0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0xED }; -byte cmd[10] = { 0xFA, 0xAF,0x00,0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0xED }; -byte ver[10] = { 0xFC, 0xCF,0x00,0xAA,0x41, 0x16, 0x51, 0x01, 0x00, 0xED }; - - -void setup() { - delay(2000); - Serial.begin(115200); - Serial.println("\nAlpha 1S Servo Tester"); - swSer.begin(115200, SWSERIAL_8N1, 12, 12, false, 256); -} - -void loop() { - for (int i = 1; i <= 32; i++) { - GetVersion(i); - delay(100); - } - SetLED(1, 0); - GoPos(1, 0, 50); - delay(1000); - GoPos(1, 90, 50); - delay(1000); - GoPos(1, 100, 50); - delay(1000); - SetLED(1, 1); - delay(2000); -} - - - - -void GetVersion(byte id) { - memcpy(buf, cmd, 10); - buf[0] = 0xFC; - buf[1] = 0xCF; - buf[2] = id; - buf[3] = 0x01; - SendCommand(); -} - - -void GoPos(byte id, byte Pos, byte Time) { - memcpy(buf, cmd, 10); - buf[2] = id; - buf[3] = 0x01; - buf[4] = Pos; - buf[5] = Time; - buf[6] = 0x00; - buf[7] = Time; - SendCommand(); -} - -void GetPos(byte id) { - memcpy(buf, cmd, 10); - buf[2] = id; - buf[3] = 0x02; - SendCommand(); -} - - -void SetLED(byte id, byte mode) { - memcpy(buf, cmd, 10); - buf[2] = id; - buf[3] = 0x04; - buf[4] = mode; - SendCommand(); -} - -void SendCommand() { - SendCommand(true); -} - -void SendCommand(bool checkResult) { - byte sum = 0; - for (int i = 2; i < 8; i++) { - sum += buf[i]; - } - buf[8] = sum; - ShowCommand(); - swSer.flush(); - swSer.enableTx(true); - swSer.write(buf, 10); - swSer.enableTx(false); - if (checkResult) checkReturn(); -} - -void ShowCommand() { - Serial.print(millis()); - Serial.print(" OUT>>"); - for (int i = 0; i < 10; i++) { - Serial.print((buf[i] < 0x10 ? " 0" : " ")); - Serial.print(buf[i], HEX); - } - Serial.println(); -} - -void checkReturn() { - unsigned long startMs = millis(); - while (((millis() - startMs) < 500) && (!swSer.available())); - if (swSer.available()) { - Serial.print(millis()); - Serial.print(" IN>>>"); - while (swSer.available()) { - byte ch = (byte)swSer.read(); - Serial.print((ch < 0x10 ? " 0" : " ")); - Serial.print(ch, HEX); - } - Serial.println(); - } -} From 66db07f33da7b33a2e7f20e23f5a01c9f12ff9db Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Wed, 5 Feb 2020 16:42:26 +0100 Subject: [PATCH 042/106] Delete swsertest.ino --- .../examples/swsertest/swsertest.ino | 47 ------------------- 1 file changed, 47 deletions(-) delete mode 100644 EspSoftwareSerial/examples/swsertest/swsertest.ino diff --git a/EspSoftwareSerial/examples/swsertest/swsertest.ino b/EspSoftwareSerial/examples/swsertest/swsertest.ino deleted file mode 100644 index a047c1be..00000000 --- a/EspSoftwareSerial/examples/swsertest/swsertest.ino +++ /dev/null @@ -1,47 +0,0 @@ -// On ESP8266: -// At 80MHz runs up 57600ps, and at 160MHz CPU frequency up to 115200bps with only negligible errors. -// Connect pin 12 to 14. - -#include - -#if defined(ESP8266) && !defined(D5) -#define D5 (14) -#define D6 (12) -#define D7 (13) -#define D8 (15) -#endif - -#ifdef ESP32 -#define BAUD_RATE 57600 -#else -#define BAUD_RATE 57600 -#endif - -// Reminder: the buffer size optimizations here, in particular the isrBufSize that only accommodates -// a single 8N1 word, are on the basis that any char written to the loopback SoftwareSerial adapter gets read -// before another write is performed. Block writes with a size greater than 1 would usually fail. -SoftwareSerial swSer; - -void setup() { - Serial.begin(115200); - swSer.begin(BAUD_RATE, SWSERIAL_8N1, D5, D6, false, 95, 11); - - Serial.println("\nSoftware serial test started"); - - for (char ch = ' '; ch <= 'z'; ch++) { - swSer.write(ch); - } - swSer.println(""); -} - -void loop() { - while (swSer.available() > 0) { - Serial.write(swSer.read()); - yield(); - } - while (Serial.available() > 0) { - swSer.write(Serial.read()); - yield(); - } - -} From e6840766e21557ac7b4cbd3a62cdba2891df568a Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Wed, 5 Feb 2020 16:42:42 +0100 Subject: [PATCH 043/106] Delete Delegate.h --- .../src/circular_queue/Delegate.h | 1786 ----------------- 1 file changed, 1786 deletions(-) delete mode 100644 EspSoftwareSerial/src/circular_queue/Delegate.h diff --git a/EspSoftwareSerial/src/circular_queue/Delegate.h b/EspSoftwareSerial/src/circular_queue/Delegate.h deleted file mode 100644 index bd19c66e..00000000 --- a/EspSoftwareSerial/src/circular_queue/Delegate.h +++ /dev/null @@ -1,1786 +0,0 @@ -/* -Delegate.h - An efficient interchangeable C function ptr and C++ std::function delegate -Copyright (c) 2019 Dirk O. Kaar. All rights reserved. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#ifndef __Delegate_h -#define __Delegate_h - -#if defined(ESP8266) -#include -#elif defined(ESP32) -#include -#else -#define ICACHE_RAM_ATTR -#define IRAM_ATTR -#endif - -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) -#include -#include -#else -#include "circular_queue/ghostl.h" -#endif - -namespace detail -{ - template - static R IRAM_ATTR vPtrToFunPtrExec(void* fn, P... args) - { - using target_type = R(P...); - return reinterpret_cast(fn)(std::forward(args...)); - } - -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - template - class DelegatePImpl { - public: - using target_type = R(P...); - protected: - using FunPtr = target_type*; - using FunAPtr = R(*)(A, P...); - using FunVPPtr = R(*)(void*, P...); - using FunctionType = std::function; - public: - DelegatePImpl() - { - kind = FP; - fn = nullptr; - } - - DelegatePImpl(std::nullptr_t) - { - kind = FP; - fn = nullptr; - } - - ~DelegatePImpl() - { - if (FUNC == kind) - functional.~FunctionType(); - else if (FPA == kind) - obj.~A(); - } - - DelegatePImpl(const DelegatePImpl& del) - { - kind = del.kind; - if (FUNC == del.kind) - { - new (&functional) FunctionType(del.functional); - } - else if (FPA == del.kind) - { - fnA = del.fnA; - new (&obj) A(del.obj); - } - else - { - fn = del.fn; - } - } - - DelegatePImpl(DelegatePImpl&& del) - { - kind = del.kind; - if (FUNC == del.kind) - { - new (&functional) FunctionType(std::move(del.functional)); - } - else if (FPA == del.kind) - { - fnA = del.fnA; - new (&obj) A(std::move(del.obj)); - } - else - { - fn = del.fn; - } - } - - DelegatePImpl(FunAPtr fnA, const A& obj) - { - kind = FPA; - DelegatePImpl::fnA = fnA; - new (&this->obj) A(obj); - } - - DelegatePImpl(FunAPtr fnA, A&& obj) - { - kind = FPA; - DelegatePImpl::fnA = fnA; - new (&this->obj) A(std::move(obj)); - } - - DelegatePImpl(FunPtr fn) - { - kind = FP; - DelegatePImpl::fn = fn; - } - - template DelegatePImpl(F functional) - { - kind = FUNC; - new (&this->functional) FunctionType(std::forward(functional)); - } - - DelegatePImpl& operator=(const DelegatePImpl& del) - { - if (this == &del) return *this; - if (kind != del.kind) - { - if (FUNC == kind) - { - functional.~FunctionType(); - } - else if (FPA == kind) - { - obj.~A(); - } - if (FUNC == del.kind) - { - new (&this->functional) FunctionType(); - } - else if (FPA == del.kind) - { - new (&obj) A; - } - kind = del.kind; - } - if (FUNC == del.kind) - { - functional = del.functional; - } - else if (FPA == del.kind) - { - fnA = del.fnA; - obj = del.obj; - } - else - { - fn = del.fn; - } - return *this; - } - - DelegatePImpl& operator=(DelegatePImpl&& del) - { - if (this == &del) return *this; - if (kind != del.kind) - { - if (FUNC == kind) - { - functional.~FunctionType(); - } - else if (FPA == kind) - { - obj.~A(); - } - if (FUNC == del.kind) - { - new (&this->functional) FunctionType(); - } - else if (FPA == del.kind) - { - new (&obj) A; - } - kind = del.kind; - } - if (FUNC == del.kind) - { - functional = std::move(del.functional); - } - else if (FPA == del.kind) - { - fnA = del.fnA; - obj = std::move(del.obj); - } - else - { - fn = del.fn; - } - return *this; - } - - DelegatePImpl& operator=(FunPtr fn) - { - if (FUNC == kind) - { - functional.~FunctionType(); - } - else if (FPA == kind) - { - obj.~A(); - } - kind = FP; - this->fn = fn; - return *this; - } - - DelegatePImpl& IRAM_ATTR operator=(std::nullptr_t) - { - if (FUNC == kind) - { - functional.~FunctionType(); - } - else if (FPA == kind) - { - obj.~A(); - } - kind = FP; - fn = nullptr; - return *this; - } - - operator bool() const - { - if (FP == kind) - { - return fn; - } - else if (FPA == kind) - { - return fnA; - } - else - { - return functional ? true : false; - } - } - - static R IRAM_ATTR vPtrToFunAPtrExec(void* self, P... args) - { - return static_cast(self)->fnA( - static_cast(self)->obj, - std::forward(args...)); - }; - - operator FunVPPtr() const - { - if (FP == kind) - { - return vPtrToFunPtrExec; - } - else if (FPA == kind) - { - return vPtrToFunAPtrExec; - } - else - { - return [](void* self, P... args) -> R - { - return static_cast(self)->functional(std::forward(args...)); - }; - } - } - - void* arg() const - { - if (FP == kind) - { - return reinterpret_cast(fn); - } - else - { - return const_cast(this); - } - } - - operator FunctionType() const - { - if (FP == kind) - { - return fn; - } - else if (FPA == kind) - { - return [this](P... args) { return fnA(obj, std::forward(args...)); }; - } - else - { - return functional; - } - } - - R IRAM_ATTR operator()(P... args) const - { - if (FP == kind) - { - return fn(std::forward(args...)); - } - else if (FPA == kind) - { - return fnA(obj, std::forward(args...)); - } - else - { - return functional(std::forward(args...)); - } - } - - protected: - enum { FUNC, FP, FPA } kind; - union { - FunctionType functional; - FunPtr fn; - struct { - FunAPtr fnA; - A obj; - }; - }; - }; -#else - template - class DelegatePImpl { - public: - using target_type = R(P...); - protected: - using FunPtr = target_type*; - using FunAPtr = R(*)(A, P...); - using FunVPPtr = R(*)(void*, P...); - public: - DelegatePImpl() - { - kind = FP; - fn = nullptr; - } - - DelegatePImpl(std::nullptr_t) - { - kind = FP; - fn = nullptr; - } - - DelegatePImpl(const DelegatePImpl& del) - { - kind = del.kind; - if (FPA == del.kind) - { - fnA = del.fnA; - obj = del.obj; - } - else - { - fn = del.fn; - } - } - - DelegatePImpl(DelegatePImpl&& del) - { - kind = del.kind; - if (FPA == del.kind) - { - fnA = del.fnA; - obj = std::move(del.obj); - } - else - { - fn = del.fn; - } - } - - DelegatePImpl(FunAPtr fnA, const A& obj) - { - kind = FPA; - DelegatePImpl::fnA = fnA; - this->obj = obj; - } - - DelegatePImpl(FunAPtr fnA, A&& obj) - { - kind = FPA; - DelegatePImpl::fnA = fnA; - this->obj = std::move(obj); - } - - DelegatePImpl(FunPtr fn) - { - kind = FP; - DelegatePImpl::fn = fn; - } - - template DelegatePImpl(F fn) - { - kind = FP; - DelegatePImpl::fn = std::forward(fn); - } - - DelegatePImpl& operator=(const DelegatePImpl& del) - { - if (this == &del) return *this; - if (kind != del.kind) - { - if (FPA == kind) - { - obj = {}; - } - kind = del.kind; - } - if (FPA == del.kind) - { - fnA = del.fnA; - obj = del.obj; - } - else - { - fn = del.fn; - } - return *this; - } - - DelegatePImpl& operator=(DelegatePImpl&& del) - { - if (this == &del) return *this; - if (kind != del.kind) - { - if (FPA == kind) - { - obj = {}; - } - kind = del.kind; - } - if (FPA == del.kind) - { - fnA = del.fnA; - obj = std::move(del.obj); - } - else - { - fn = del.fn; - } - return *this; - } - - DelegatePImpl& operator=(FunPtr fn) - { - if (FPA == kind) - { - obj = {}; - } - kind = FP; - this->fn = fn; - return *this; - } - - DelegatePImpl& IRAM_ATTR operator=(std::nullptr_t) - { - if (FPA == kind) - { - obj = {}; - } - kind = FP; - fn = nullptr; - return *this; - } - - operator bool() const - { - if (FP == kind) - { - return fn; - } - else - { - return fnA; - } - } - - static R IRAM_ATTR vPtrToFunAPtrExec(void* self, P... args) - { - return static_cast(self)->fnA( - static_cast(self)->obj, - std::forward(args...)); - }; - - operator FunVPPtr() const - { - if (FP == kind) - { - return vPtrToFunPtrExec; - } - else - { - return vPtrToFunAPtrExec; - } - } - - void* arg() const - { - if (FP == kind) - { - return reinterpret_cast(fn); - } - else - { - return const_cast(this); - } - } - - R IRAM_ATTR operator()(P... args) const - { - if (FP == kind) - { - return fn(std::forward(args...)); - } - else - { - return fnA(obj, std::forward(args...)); - } - } - - protected: - enum { FP, FPA } kind; - union { - FunPtr fn; - FunAPtr fnA; - }; - A obj; - }; -#endif - -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - template - class DelegatePImpl { - public: - using target_type = R(P...); - protected: - using FunPtr = target_type*; - using FunctionType = std::function; - using FunVPPtr = R(*)(void*, P...); - public: - DelegatePImpl() - { - kind = FP; - fn = nullptr; - } - - DelegatePImpl(std::nullptr_t) - { - kind = FP; - fn = nullptr; - } - - ~DelegatePImpl() - { - if (FUNC == kind) - functional.~FunctionType(); - } - - DelegatePImpl(const DelegatePImpl& del) - { - kind = del.kind; - if (FUNC == del.kind) - { - new (&functional) FunctionType(del.functional); - } - else - { - fn = del.fn; - } - } - - DelegatePImpl(DelegatePImpl&& del) - { - kind = del.kind; - if (FUNC == del.kind) - { - new (&functional) FunctionType(std::move(del.functional)); - } - else - { - fn = del.fn; - } - } - - DelegatePImpl(FunPtr fn) - { - kind = FP; - DelegatePImpl::fn = fn; - } - - template DelegatePImpl(F functional) - { - kind = FUNC; - new (&this->functional) FunctionType(std::forward(functional)); - } - - DelegatePImpl& operator=(const DelegatePImpl& del) - { - if (this == &del) return *this; - if (FUNC == kind && FUNC != del.kind) - { - functional.~FunctionType(); - } - else if (FUNC != kind && FUNC == del.kind) - { - new (&this->functional) FunctionType(); - } - kind = del.kind; - if (FUNC == del.kind) - { - functional = del.functional; - } - else - { - fn = del.fn; - } - return *this; - } - - DelegatePImpl& operator=(DelegatePImpl&& del) - { - if (this == &del) return *this; - if (FUNC == kind && FUNC != del.kind) - { - functional.~FunctionType(); - } - else if (FUNC != kind && FUNC == del.kind) - { - new (&this->functional) FunctionType(); - } - kind = del.kind; - if (FUNC == del.kind) - { - functional = std::move(del.functional); - } - else - { - fn = del.fn; - } - return *this; - } - - DelegatePImpl& operator=(FunPtr fn) - { - if (FUNC == kind) - { - functional.~FunctionType(); - kind = FP; - } - DelegatePImpl::fn = fn; - return *this; - } - - DelegatePImpl& IRAM_ATTR operator=(std::nullptr_t) - { - if (FUNC == kind) - { - functional.~FunctionType(); - } - kind = FP; - fn = nullptr; - return *this; - } - - operator bool() const - { - if (FP == kind) - { - return fn; - } - else - { - return functional ? true : false; - } - } - - operator FunVPPtr() const - { - if (FP == kind) - { - return vPtrToFunPtrExec; - } - else - { - return [](void* self, P... args) -> R - { - return static_cast(self)->functional(std::forward(args...)); - }; - } - } - - void* arg() const - { - if (FP == kind) - { - return reinterpret_cast(fn); - } - else - { - return const_cast(this); - } - } - - operator FunctionType() const - { - if (FP == kind) - { - return fn; - } - else - { - return functional; - } - } - - R IRAM_ATTR operator()(P... args) const - { - if (FP == kind) - { - return fn(std::forward(args...)); - } - else - { - return functional(std::forward(args...)); - } - } - - protected: - enum { FUNC, FP } kind; - union { - FunctionType functional; - FunPtr fn; - }; - }; -#else - template - class DelegatePImpl { - public: - using target_type = R(P...); - protected: - using FunPtr = target_type*; - using FunVPPtr = R(*)(void*, P...); - public: - DelegatePImpl() - { - fn = nullptr; - } - - DelegatePImpl(std::nullptr_t) - { - fn = nullptr; - } - - DelegatePImpl(const DelegatePImpl& del) - { - fn = del.fn; - } - - DelegatePImpl(DelegatePImpl&& del) - { - fn = std::move(del.fn); - } - - DelegatePImpl(FunPtr fn) - { - DelegatePImpl::fn = fn; - } - - template DelegatePImpl(F fn) - { - DelegatePImpl::fn = std::forward(fn); - } - - DelegatePImpl& operator=(const DelegatePImpl& del) - { - if (this == &del) return *this; - fn = del.fn; - return *this; - } - - DelegatePImpl& operator=(DelegatePImpl&& del) - { - if (this == &del) return *this; - fn = std::move(del.fn); - return *this; - } - - DelegatePImpl& operator=(FunPtr fn) - { - DelegatePImpl::fn = fn; - return *this; - } - - DelegatePImpl& IRAM_ATTR operator=(std::nullptr_t) - { - fn = nullptr; - return *this; - } - - operator bool() const - { - return fn; - } - - operator FunVPPtr() const - { - return vPtrToFunPtrExec; - } - - void* arg() const - { - return reinterpret_cast(fn); - } - - R IRAM_ATTR operator()(P... args) const - { - return fn(std::forward(args...)); - } - - protected: - FunPtr fn; - }; -#endif - -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - template - class DelegateImpl { - public: - using target_type = R(); - protected: - using FunPtr = target_type*; - using FunAPtr = R(*)(A); - using FunctionType = std::function; - using FunVPPtr = R(*)(void*); - public: - DelegateImpl() - { - kind = FP; - fn = nullptr; - } - - DelegateImpl(std::nullptr_t) - { - kind = FP; - fn = nullptr; - } - - ~DelegateImpl() - { - if (FUNC == kind) - functional.~FunctionType(); - else if (FPA == kind) - obj.~A(); - } - - DelegateImpl(const DelegateImpl& del) - { - kind = del.kind; - if (FUNC == del.kind) - { - new (&functional) FunctionType(del.functional); - } - else if (FPA == del.kind) - { - fnA = del.fnA; - new (&obj) A(del.obj); - } - else - { - fn = del.fn; - } - } - - DelegateImpl(DelegateImpl&& del) - { - kind = del.kind; - if (FUNC == del.kind) - { - new (&functional) FunctionType(std::move(del.functional)); - } - else if (FPA == del.kind) - { - fnA = del.fnA; - new (&obj) A(std::move(del.obj)); - } - else - { - fn = del.fn; - } - } - - DelegateImpl(FunAPtr fnA, const A& obj) - { - kind = FPA; - DelegateImpl::fnA = fnA; - new (&this->obj) A(obj); - } - - DelegateImpl(FunAPtr fnA, A&& obj) - { - kind = FPA; - DelegateImpl::fnA = fnA; - new (&this->obj) A(std::move(obj)); - } - - DelegateImpl(FunPtr fn) - { - kind = FP; - DelegateImpl::fn = fn; - } - - template DelegateImpl(F functional) - { - kind = FUNC; - new (&this->functional) FunctionType(std::forward(functional)); - } - - DelegateImpl& operator=(const DelegateImpl& del) - { - if (this == &del) return *this; - if (kind != del.kind) - { - if (FUNC == kind) - { - functional.~FunctionType(); - } - else if (FPA == kind) - { - obj.~A(); - } - if (FUNC == del.kind) - { - new (&this->functional) FunctionType(); - } - else if (FPA == del.kind) - { - new (&obj) A; - } - kind = del.kind; - } - if (FUNC == del.kind) - { - functional = del.functional; - } - else if (FPA == del.kind) - { - fnA = del.fnA; - obj = del.obj; - } - else - { - fn = del.fn; - } - return *this; - } - - DelegateImpl& operator=(DelegateImpl&& del) - { - if (this == &del) return *this; - if (kind != del.kind) - { - if (FUNC == kind) - { - functional.~FunctionType(); - } - else if (FPA == kind) - { - obj.~A(); - } - if (FUNC == del.kind) - { - new (&this->functional) FunctionType(); - } - else if (FPA == del.kind) - { - new (&obj) A; - } - kind = del.kind; - } - if (FUNC == del.kind) - { - functional = std::move(del.functional); - } - else if (FPA == del.kind) - { - fnA = del.fnA; - obj = std::move(del.obj); - } - else - { - fn = del.fn; - } - return *this; - } - - DelegateImpl& operator=(FunPtr fn) - { - if (FUNC == kind) - { - functional.~FunctionType(); - } - else if (FPA == kind) - { - obj.~A(); - } - kind = FP; - this->fn = fn; - return *this; - } - - DelegateImpl& IRAM_ATTR operator=(std::nullptr_t) - { - if (FUNC == kind) - { - functional.~FunctionType(); - } - else if (FPA == kind) - { - obj.~A(); - } - kind = FP; - fn = nullptr; - return *this; - } - - operator bool() const - { - if (FP == kind) - { - return fn; - } - else if (FPA == kind) - { - return fnA; - } - else - { - return functional ? true : false; - } - } - - static R IRAM_ATTR vPtrToFunAPtrExec(void* self) - { - return static_cast(self)->fnA( - static_cast(self)->obj); - }; - - operator FunVPPtr() const - { - if (FP == kind) - { - return reinterpret_cast(fn); - } - else if (FPA == kind) - { - return vPtrToFunAPtrExec; - } - else - { - return [](void* self) -> R - { - return static_cast(self)->functional(); - }; - } - } - - void* arg() const - { - if (FP == kind) - { - return nullptr; - } - else - { - return const_cast(this); - } - } - - operator FunctionType() const - { - if (FP == kind) - { - return fn; - } - else if (FPA == kind) - { - return [this]() { return fnA(obj); }; - } - else - { - return functional; - } - } - - R IRAM_ATTR operator()() const - { - if (FP == kind) - { - return fn(); - } - else if (FPA == kind) - { - return fnA(obj); - } - else - { - return functional(); - } - } - - protected: - enum { FUNC, FP, FPA } kind; - union { - FunctionType functional; - FunPtr fn; - struct { - FunAPtr fnA; - A obj; - }; - }; - }; -#else - template - class DelegateImpl { - public: - using target_type = R(); - protected: - using FunPtr = target_type*; - using FunAPtr = R(*)(A); - using FunVPPtr = R(*)(void*); - public: - DelegateImpl() - { - kind = FP; - fn = nullptr; - } - - DelegateImpl(std::nullptr_t) - { - kind = FP; - fn = nullptr; - } - - DelegateImpl(const DelegateImpl& del) - { - kind = del.kind; - if (FPA == del.kind) - { - fnA = del.fnA; - obj = del.obj; - } - else - { - fn = del.fn; - } - } - - DelegateImpl(DelegateImpl&& del) - { - kind = del.kind; - if (FPA == del.kind) - { - fnA = del.fnA; - obj = std::move(del.obj); - } - else - { - fn = del.fn; - } - } - - DelegateImpl(FunAPtr fnA, const A& obj) - { - kind = FPA; - DelegateImpl::fnA = fnA; - this->obj = obj; - } - - DelegateImpl(FunAPtr fnA, A&& obj) - { - kind = FPA; - DelegateImpl::fnA = fnA; - this->obj = std::move(obj); - } - - DelegateImpl(FunPtr fn) - { - kind = FP; - DelegateImpl::fn = fn; - } - - template DelegateImpl(F fn) - { - kind = FP; - DelegateImpl::fn = std::forward(fn); - } - - DelegateImpl& operator=(const DelegateImpl& del) - { - if (this == &del) return *this; - if (kind != del.kind) - { - if (FPA == kind) - { - obj = {}; - } - kind = del.kind; - } - if (FPA == del.kind) - { - fnA = del.fnA; - obj = del.obj; - } - else - { - fn = del.fn; - } - return *this; - } - - DelegateImpl& operator=(DelegateImpl&& del) - { - if (this == &del) return *this; - if (kind != del.kind) - { - if (FPA == kind) - { - obj = {}; - } - kind = del.kind; - } - if (FPA == del.kind) - { - fnA = del.fnA; - obj = std::move(del.obj); - } - else - { - fn = del.fn; - } - return *this; - } - - DelegateImpl& operator=(FunPtr fn) - { - if (FPA == kind) - { - obj = {}; - } - kind = FP; - this->fn = fn; - return *this; - } - - DelegateImpl& IRAM_ATTR operator=(std::nullptr_t) - { - if (FPA == kind) - { - obj = {}; - } - kind = FP; - fn = nullptr; - return *this; - } - - operator bool() const - { - if (FP == kind) - { - return fn; - } - else - { - return fnA; - } - } - - static R IRAM_ATTR vPtrToFunAPtrExec(void* self) - { - return static_cast(self)->fnA( - static_cast(self)->obj); - }; - - operator FunVPPtr() const - { - if (FP == kind) - { - return reinterpret_cast(fn); - } - else - { - return vPtrToFunAPtrExec; - } - } - - void* arg() const - { - if (FP == kind) - { - return nullptr; - } - else - { - return const_cast(this); - } - } - - R IRAM_ATTR operator()() const - { - if (FP == kind) - { - return fn(); - } - else - { - return fnA(obj); - } - } - - protected: - enum { FP, FPA } kind; - union { - FunPtr fn; - FunAPtr fnA; - }; - A obj; - }; -#endif - -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - template - class DelegateImpl { - public: - using target_type = R(); - protected: - using FunPtr = target_type*; - using FunctionType = std::function; - using FunVPPtr = R(*)(void*); - public: - DelegateImpl() - { - kind = FP; - fn = nullptr; - } - - DelegateImpl(std::nullptr_t) - { - kind = FP; - fn = nullptr; - } - - ~DelegateImpl() - { - if (FUNC == kind) - functional.~FunctionType(); - } - - DelegateImpl(const DelegateImpl& del) - { - kind = del.kind; - if (FUNC == del.kind) - { - new (&functional) FunctionType(del.functional); - } - else - { - fn = del.fn; - } - } - - DelegateImpl(DelegateImpl&& del) - { - kind = del.kind; - if (FUNC == del.kind) - { - new (&functional) FunctionType(std::move(del.functional)); - } - else - { - fn = del.fn; - } - } - - DelegateImpl(FunPtr fn) - { - kind = FP; - DelegateImpl::fn = fn; - } - - template DelegateImpl(F functional) - { - kind = FUNC; - new (&this->functional) FunctionType(std::forward(functional)); - } - - DelegateImpl& operator=(const DelegateImpl& del) - { - if (this == &del) return *this; - if (FUNC == kind && FUNC != del.kind) - { - functional.~FunctionType(); - } - else if (FUNC != kind && FUNC == del.kind) - { - new (&this->functional) FunctionType(); - } - kind = del.kind; - if (FUNC == del.kind) - { - functional = del.functional; - } - else - { - fn = del.fn; - } - return *this; - } - - DelegateImpl& operator=(DelegateImpl&& del) - { - if (this == &del) return *this; - if (FUNC == kind && FUNC != del.kind) - { - functional.~FunctionType(); - } - else if (FUNC != kind && FUNC == del.kind) - { - new (&this->functional) FunctionType(); - } - kind = del.kind; - if (FUNC == del.kind) - { - functional = std::move(del.functional); - } - else - { - fn = del.fn; - } - return *this; - } - - DelegateImpl& operator=(FunPtr fn) - { - if (FUNC == kind) - { - functional.~FunctionType(); - kind = FP; - } - DelegateImpl::fn = fn; - return *this; - } - - DelegateImpl& IRAM_ATTR operator=(std::nullptr_t) - { - if (FUNC == kind) - { - functional.~FunctionType(); - } - kind = FP; - fn = nullptr; - return *this; - } - - operator bool() const - { - if (FP == kind) - { - return fn; - } - else - { - return functional ? true : false; - } - } - - operator FunVPPtr() const - { - if (FP == kind) - { - return reinterpret_cast(fn); - } - else - { - return [](void* self) -> R - { - return static_cast(self)->functional(); - }; - } - } - - void* arg() const - { - if (FP == kind) - { - return nullptr; - } - else - { - return const_cast(this); - } - } - - operator FunctionType() const - { - if (FP == kind) - { - return fn; - } - else - { - return functional; - } - } - - R IRAM_ATTR operator()() const - { - if (FP == kind) - { - return fn(); - } - else - { - return functional(); - } - } - - protected: - enum { FUNC, FP } kind; - union { - FunctionType functional; - FunPtr fn; - }; - }; -#else - template - class DelegateImpl { - public: - using target_type = R(); - protected: - using FunPtr = target_type*; - using FunVPPtr = R(*)(void*); - public: - DelegateImpl() - { - fn = nullptr; - } - - DelegateImpl(std::nullptr_t) - { - fn = nullptr; - } - - DelegateImpl(const DelegateImpl& del) - { - fn = del.fn; - } - - DelegateImpl(DelegateImpl&& del) - { - fn = std::move(del.fn); - } - - DelegateImpl(FunPtr fn) - { - DelegateImpl::fn = fn; - } - - template DelegateImpl(F fn) - { - DelegateImpl::fn = std::forward(fn); - } - - DelegateImpl& operator=(const DelegateImpl& del) - { - if (this == &del) return *this; - fn = del.fn; - return *this; - } - - DelegateImpl& operator=(DelegateImpl&& del) - { - if (this == &del) return *this; - fn = std::move(del.fn); - return *this; - } - - DelegateImpl& operator=(FunPtr fn) - { - DelegateImpl::fn = fn; - return *this; - } - - DelegateImpl& IRAM_ATTR operator=(std::nullptr_t) - { - fn = nullptr; - return *this; - } - - operator bool() const - { - return fn; - } - - operator FunVPPtr() const - { - return reinterpret_cast(fn); - } - - void* arg() const - { - return nullptr; - } - - R IRAM_ATTR operator()() const - { - return fn(); - } - - protected: - FunPtr fn; - }; -#endif - - template - class Delegate : private detail::DelegatePImpl - { - private: - using typename detail::DelegatePImpl::FunVPPtr; -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - using typename detail::DelegatePImpl::FunctionType; -#endif - public: - using detail::DelegatePImpl::target_type; - using detail::DelegatePImpl::DelegatePImpl; - using detail::DelegatePImpl::operator=; - using detail::DelegatePImpl::operator bool; - using detail::DelegatePImpl::operator FunVPPtr; - using detail::DelegatePImpl::arg; -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - using detail::DelegatePImpl::operator FunctionType; -#endif - using detail::DelegatePImpl::operator(); - }; - - template - class Delegate : private detail::DelegatePImpl - { - private: - using typename detail::DelegatePImpl::FunVPPtr; -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - using typename detail::DelegatePImpl::FunctionType; -#endif - public: - using detail::DelegatePImpl::target_type; - using detail::DelegatePImpl::DelegatePImpl; - using detail::DelegatePImpl::operator=; - using detail::DelegatePImpl::operator bool; -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - using detail::DelegatePImpl::operator FunctionType; -#endif - using detail::DelegatePImpl::operator(); - operator FunVPPtr() const - { - if (detail::DelegatePImpl::FPA == detail::DelegatePImpl::kind) - { - return reinterpret_cast(detail::DelegatePImpl::fnA); - } - else - { - return detail::DelegatePImpl::operator FunVPPtr(); - } - } - void* arg() const - { - if (detail::DelegatePImpl::FPA == detail::DelegatePImpl::kind) - { - return detail::DelegatePImpl::obj; - } - else - { - return detail::DelegatePImpl::arg(); - } - } - }; - - template - class Delegate : private detail::DelegateImpl - { - private: - using typename detail::DelegateImpl::FunVPPtr; -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - using typename detail::DelegateImpl::FunctionType; -#endif - public: - using detail::DelegateImpl::target_type; - using detail::DelegateImpl::DelegateImpl; - using detail::DelegateImpl::operator=; - using detail::DelegateImpl::operator bool; - using detail::DelegateImpl::operator FunVPPtr; - using detail::DelegateImpl::arg; -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - using detail::DelegateImpl::operator FunctionType; -#endif - using detail::DelegateImpl::operator(); - }; - - template - class Delegate : private detail::DelegateImpl - { - private: - using typename detail::DelegateImpl::FunVPPtr; -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - using typename detail::DelegateImpl::FunctionType; -#endif - public: - using detail::DelegateImpl::target_type; - using detail::DelegateImpl::DelegateImpl; - using detail::DelegateImpl::operator=; - using detail::DelegateImpl::operator bool; -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - using detail::DelegateImpl::operator FunctionType; -#endif - using detail::DelegateImpl::operator(); - operator FunVPPtr() const - { - if (detail::DelegateImpl::FPA == detail::DelegateImpl::kind) - { - return reinterpret_cast(detail::DelegateImpl::fnA); - } - else - { - return detail::DelegateImpl::operator FunVPPtr(); - } - } - void* arg() const - { - if (detail::DelegateImpl::FPA == detail::DelegateImpl::kind) - { - return detail::DelegateImpl::obj; - } - else - { - return detail::DelegateImpl::arg(); - } - } - }; - -} - -template class Delegate; -template class Delegate : public detail::Delegate -{ -public: - using detail::Delegate::Delegate; -}; -template class Delegate : public detail::Delegate -{ -public: - using detail::Delegate::Delegate; -}; - -#endif // __Delegate_h From 3b3a8b58fee66d8ef9c54566c8cf59ddf284f410 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Wed, 5 Feb 2020 16:42:50 +0100 Subject: [PATCH 044/106] Delete MultiDelegate.h --- .../src/circular_queue/MultiDelegate.h | 503 ------------------ 1 file changed, 503 deletions(-) delete mode 100644 EspSoftwareSerial/src/circular_queue/MultiDelegate.h diff --git a/EspSoftwareSerial/src/circular_queue/MultiDelegate.h b/EspSoftwareSerial/src/circular_queue/MultiDelegate.h deleted file mode 100644 index 1fd4188d..00000000 --- a/EspSoftwareSerial/src/circular_queue/MultiDelegate.h +++ /dev/null @@ -1,503 +0,0 @@ -/* -MultiDelegate.h - A queue or event multiplexer based on the efficient Delegate -class -Copyright (c) 2019 Dirk O. Kaar. All rights reserved. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#ifndef __MULTIDELEGATE_H -#define __MULTIDELEGATE_H - -#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) -#include -#else -#include "circular_queue/ghostl.h" -#endif - -#if defined(ESP8266) -#include -using esp8266::InterruptLock; -#elif defined(ARDUINO) -class InterruptLock { -public: - InterruptLock() { - noInterrupts(); - } - ~InterruptLock() { - interrupts(); - } -}; -#else -#include -#endif - -namespace detail -{ - namespace - { - template< typename Delegate, typename R, bool ISQUEUE = false, typename... P> - struct CallP - { - static R execute(Delegate& del, P... args) - { - return del(std::forward(args...)) ? !ISQUEUE : ISQUEUE; - } - }; - - template< typename Delegate, bool ISQUEUE, typename... P> - struct CallP - { - static bool execute(Delegate& del, P... args) - { - del(std::forward(args...)); - return !ISQUEUE; - } - }; - - template< typename Delegate, typename R, bool ISQUEUE = false> - struct Call - { - static R execute(Delegate& del) - { - return del() ? !ISQUEUE : ISQUEUE; - } - }; - - template< typename Delegate, bool ISQUEUE> - struct Call - { - static bool execute(Delegate& del) - { - del(); - return !ISQUEUE; - } - }; - }; - - template< typename Delegate, typename R = void, bool ISQUEUE = false, uint32_t QUEUE_CAPACITY = 32, typename... P> - class MultiDelegatePImpl - { - public: - MultiDelegatePImpl() = default; - ~MultiDelegatePImpl() - { - *this = nullptr; - } - - MultiDelegatePImpl(const MultiDelegatePImpl&) = delete; - MultiDelegatePImpl& operator=(const MultiDelegatePImpl&) = delete; - - MultiDelegatePImpl(MultiDelegatePImpl&& md) - { - first = md.first; - last = md.last; - unused = md.unused; - nodeCount = md.nodeCount; - md.first = nullptr; - md.last = nullptr; - md.unused = nullptr; - md.nodeCount = 0; - } - - MultiDelegatePImpl(const Delegate& del) - { - add(del); - } - - MultiDelegatePImpl(Delegate&& del) - { - add(std::move(del)); - } - - MultiDelegatePImpl& operator=(MultiDelegatePImpl&& md) - { - first = md.first; - last = md.last; - unused = md.unused; - nodeCount = md.nodeCount; - md.first = nullptr; - md.last = nullptr; - md.unused = nullptr; - md.nodeCount = 0; - return *this; - } - - MultiDelegatePImpl& operator=(std::nullptr_t) - { - if (last) - last->mNext = unused; - if (first) - unused = first; - while (unused) - { - auto to_delete = unused; - unused = unused->mNext; - delete(to_delete); - } - return *this; - } - - MultiDelegatePImpl& operator+=(const Delegate& del) - { - add(del); - return *this; - } - - MultiDelegatePImpl& operator+=(Delegate&& del) - { - add(std::move(del)); - return *this; - } - - protected: - struct Node_t - { - ~Node_t() - { - mDelegate = nullptr; // special overload in Delegate - } - Node_t* mNext = nullptr; - Delegate mDelegate; - }; - - Node_t* first = nullptr; - Node_t* last = nullptr; - Node_t* unused = nullptr; - uint32_t nodeCount = 0; - - // Returns a pointer to an unused Node_t, - // or if none are available allocates a new one, - // or nullptr if limit is reached - Node_t* IRAM_ATTR get_node_unsafe() - { - Node_t* result = nullptr; - // try to get an item from unused items list - if (unused) - { - result = unused; - unused = unused->mNext; - } - // if no unused items, and count not too high, allocate a new one - else if (nodeCount < QUEUE_CAPACITY) - { -#if defined(ESP8266) || defined(ESP32) - result = new (std::nothrow) Node_t; -#else - result = new Node_t; -#endif - if (result) - ++nodeCount; - } - return result; - } - - void recycle_node_unsafe(Node_t* node) - { - node->mDelegate = nullptr; // special overload in Delegate - node->mNext = unused; - unused = node; - } - -#ifndef ARDUINO - std::mutex mutex_unused; -#endif - public: - const Delegate* IRAM_ATTR add(const Delegate& del) - { - return add(Delegate(del)); - } - - const Delegate* IRAM_ATTR add(Delegate&& del) - { - if (!del) - return nullptr; - -#ifdef ARDUINO - InterruptLock lockAllInterruptsInThisScope; -#else - std::lock_guard lock(mutex_unused); -#endif - - Node_t* item = ISQUEUE ? get_node_unsafe() : -#if defined(ESP8266) || defined(ESP32) - new (std::nothrow) Node_t; -#else - new Node_t; -#endif - if (!item) - return nullptr; - - item->mDelegate = std::move(del); - item->mNext = nullptr; - - if (last) - last->mNext = item; - else - first = item; - last = item; - - return &item->mDelegate; - } - - bool remove(const Delegate* del) - { - auto current = first; - if (!current) - return false; - - Node_t* prev = nullptr; - do - { - if (del == ¤t->mDelegate) - { - // remove callback from stack -#ifdef ARDUINO - InterruptLock lockAllInterruptsInThisScope; -#else - std::lock_guard lock(mutex_unused); -#endif - - auto to_recycle = current; - - // removing rLast - if (last == current) - last = prev; - - current = current->mNext; - if (prev) - { - prev->mNext = current; - } - else - { - first = current; - } - - if (ISQUEUE) - recycle_node_unsafe(to_recycle); - else - delete to_recycle; - return true; - } - else - { - prev = current; - current = current->mNext; - } - } while (current); - return false; - } - - void operator()(P... args) - { - auto current = first; - if (!current) - return; - - static std::atomic fence(false); - // prevent recursive calls -#if defined(ARDUINO) && !defined(ESP32) - if (fence.load()) return; - fence.store(true); -#else - if (fence.exchange(true)) return; -#endif - - Node_t* prev = nullptr; - // prevent execution of new callbacks during this run - auto stop = last; - - bool done; - do - { - done = current == stop; - if (!CallP::execute(current->mDelegate, args...)) - { - // remove callback from stack -#ifdef ARDUINO - InterruptLock lockAllInterruptsInThisScope; -#else - std::lock_guard lock(mutex_unused); -#endif - - auto to_recycle = current; - - // removing rLast - if (last == current) - last = prev; - - current = current->mNext; - if (prev) - { - prev->mNext = current; - } - else - { - first = current; - } - - if (ISQUEUE) - recycle_node_unsafe(to_recycle); - else - delete to_recycle; - } - else - { - prev = current; - current = current->mNext; - } - -#if defined(ESP8266) || defined(ESP32) - // running callbacks might last too long for watchdog etc. - optimistic_yield(10000); -#endif - } while (current && !done); - - fence.store(false); - } - }; - - template< typename Delegate, typename R = void, bool ISQUEUE = false, uint32_t QUEUE_CAPACITY = 32> - class MultiDelegateImpl : public MultiDelegatePImpl - { - protected: - using typename MultiDelegatePImpl::Node_t; - using MultiDelegatePImpl::first; - using MultiDelegatePImpl::last; - using MultiDelegatePImpl::unused; - using MultiDelegatePImpl::nodeCount; - using MultiDelegatePImpl::recycle_node_unsafe; -#ifndef ARDUINO - using MultiDelegatePImpl::mutex_unused; -#endif - - public: - using MultiDelegatePImpl::MultiDelegatePImpl; - - void operator()() - { - auto current = first; - if (!current) - return; - - static std::atomic fence(false); - // prevent recursive calls -#if defined(ARDUINO) && !defined(ESP32) - if (fence.load()) return; - fence.store(true); -#else - if (fence.exchange(true)) return; -#endif - - Node_t* prev = nullptr; - // prevent execution of new callbacks during this run - auto stop = last; - - bool done; - do - { - done = current == stop; - if (!Call::execute(current->mDelegate)) - { - // remove callback from stack -#ifdef ARDUINO - InterruptLock lockAllInterruptsInThisScope; -#else - std::lock_guard lock(mutex_unused); -#endif - - auto to_recycle = current; - - // removing rLast - if (last == current) - last = prev; - - current = current->mNext; - if (prev) - { - prev->mNext = current; - } - else - { - first = current; - } - - if (ISQUEUE) - recycle_node_unsafe(to_recycle); - else - delete to_recycle; - } - else - { - prev = current; - current = current->mNext; - } - -#if defined(ESP8266) || defined(ESP32) - // running callbacks might last too long for watchdog etc. - optimistic_yield(10000); -#endif - } while (current && !done); - - fence.store(false); - } - }; - - template< typename Delegate, typename R, bool ISQUEUE, uint32_t QUEUE_CAPACITY, typename... P> class MultiDelegate; - - template< typename Delegate, typename R, bool ISQUEUE, uint32_t QUEUE_CAPACITY, typename... P> - class MultiDelegate : public MultiDelegatePImpl - { - public: - using MultiDelegatePImpl::MultiDelegatePImpl; - }; - - template< typename Delegate, typename R, bool ISQUEUE, uint32_t QUEUE_CAPACITY> - class MultiDelegate : public MultiDelegateImpl - { - public: - using MultiDelegateImpl::MultiDelegateImpl; - }; -}; - -/** -The MultiDelegate class template can be specialized to either a queue or an event multiplexer. -It is designed to be used with Delegate, the efficient runtime wrapper for C function ptr and C++ std::function. -@tparam Delegate specifies the concrete type that MultiDelegate bases the queue or event multiplexer on. -@tparam ISQUEUE modifies the generated MultiDelegate class in subtle ways. In queue mode (ISQUEUE == true), - the value of QUEUE_CAPACITY enforces the maximum number of simultaneous items the queue can contain. - This is exploited to minimize the use of new and delete by reusing already allocated items, thus - reducing heap fragmentation. In event multiplexer mode (ISQUEUE = false), new and delete are - used for allocation of the event handler items. - If the result type of the function call operator of Delegate is void, calling a MultiDelegate queue - removes each item after calling it; a Multidelegate event multiplexer keeps event handlers until - explicitly removed. - If the result type of the function call operator of Delegate is non-void, the type-conversion to bool - of that result determines if the item is immediately removed or kept after each call: a Multidelegate - queue removes an item only if true is returned, but a Multidelegate event multiplexer removes event - handlers that return false. -@tparam QUEUE_CAPACITY is only used if ISQUEUE == true. Then, it sets the maximum capacity that the queue dynamically - allocates from the heap. Unused items are not returned to the heap, but are managed by the MultiDelegate - instance during its own lifetime for efficiency. -*/ -template< typename Delegate, bool ISQUEUE = false, uint32_t QUEUE_CAPACITY = 32> -class MultiDelegate : public detail::MultiDelegate -{ -public: - using detail::MultiDelegate::MultiDelegate; -}; - -#endif // __MULTIDELEGATE_H From e4d645cf4119c2fdaa7f66b83fdc2cd99cead95a Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Wed, 5 Feb 2020 16:42:58 +0100 Subject: [PATCH 045/106] Delete circular_queue.h --- .../src/circular_queue/circular_queue.h | 399 ------------------ 1 file changed, 399 deletions(-) delete mode 100644 EspSoftwareSerial/src/circular_queue/circular_queue.h diff --git a/EspSoftwareSerial/src/circular_queue/circular_queue.h b/EspSoftwareSerial/src/circular_queue/circular_queue.h deleted file mode 100644 index 46e3f66e..00000000 --- a/EspSoftwareSerial/src/circular_queue/circular_queue.h +++ /dev/null @@ -1,399 +0,0 @@ -/* -circular_queue.h - Implementation of a lock-free circular queue for EspSoftwareSerial. -Copyright (c) 2019 Dirk O. Kaar. All rights reserved. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#ifndef __circular_queue_h -#define __circular_queue_h - -#ifdef ARDUINO -#include -#endif - -#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) -#include -#include -#include -#include "Delegate.h" -using std::min; -#else -#include "ghostl.h" -#endif - -#if !defined(ESP32) && !defined(ESP8266) -#define ICACHE_RAM_ATTR -#define IRAM_ATTR -#endif - -/*! - @brief Instance class for a single-producer, single-consumer circular queue / ring buffer (FIFO). - This implementation is lock-free between producer and consumer for the available(), peek(), - pop(), and push() type functions. -*/ -template< typename T, typename ForEachArg = void > -class circular_queue -{ -public: - /*! - @brief Constructs a valid, but zero-capacity dummy queue. - */ - circular_queue() : m_bufSize(1) - { - m_inPos.store(0); - m_outPos.store(0); - } - /*! - @brief Constructs a queue of the given maximum capacity. - */ - circular_queue(const size_t capacity) : m_bufSize(capacity + 1), m_buffer(new T[m_bufSize]) - { - m_inPos.store(0); - m_outPos.store(0); - } - circular_queue(circular_queue&& cq) : - m_bufSize(cq.m_bufSize), m_buffer(cq.m_buffer), m_inPos(cq.m_inPos.load()), m_outPos(cq.m_outPos.load()) - {} - ~circular_queue() - { - m_buffer.reset(); - } - circular_queue(const circular_queue&) = delete; - circular_queue& operator=(circular_queue&& cq) - { - m_bufSize = cq.m_bufSize; - m_buffer = cq.m_buffer; - m_inPos.store(cq.m_inPos.load()); - m_outPos.store(cq.m_outPos.load()); - } - circular_queue& operator=(const circular_queue&) = delete; - - /*! - @brief Get the numer of elements the queue can hold at most. - */ - size_t capacity() const - { - return m_bufSize - 1; - } - - /*! - @brief Resize the queue. The available elements in the queue are preserved. - This is not lock-free and concurrent producer or consumer access - will lead to corruption. - @return True if the new capacity could accommodate the present elements in - the queue, otherwise nothing is done and false is returned. - */ - bool capacity(const size_t cap); - - /*! - @brief Discard all data in the queue. - */ - void flush() - { - m_outPos.store(m_inPos.load()); - } - - /*! - @brief Get a snapshot number of elements that can be retrieved by pop. - */ - size_t available() const - { - int avail = static_cast(m_inPos.load() - m_outPos.load()); - if (avail < 0) avail += m_bufSize; - return avail; - } - - /*! - @brief Get the remaining free elementes for pushing. - */ - size_t available_for_push() const - { - int avail = static_cast(m_outPos.load() - m_inPos.load()) - 1; - if (avail < 0) avail += m_bufSize; - return avail; - } - - /*! - @brief Peek at the next element pop will return without removing it from the queue. - @return An rvalue copy of the next element that can be popped. If the queue is empty, - return an rvalue copy of the element that is pending the next push. - */ - T peek() const - { - const auto outPos = m_outPos.load(std::memory_order_relaxed); - std::atomic_thread_fence(std::memory_order_acquire); - return m_buffer[outPos]; - } - - /*! - @brief Peek at the next pending input value. - @return A reference to the next element that can be pushed. - */ - T& IRAM_ATTR pushpeek() - { - const auto inPos = m_inPos.load(std::memory_order_relaxed); - std::atomic_thread_fence(std::memory_order_acquire); - return m_buffer[inPos]; - } - - /*! - @brief Release the next pending input value, accessible by pushpeek(), into the queue. - @return true if the queue accepted the value, false if the queue - was full. - */ - bool IRAM_ATTR push(); - - /*! - @brief Move the rvalue parameter into the queue. - @return true if the queue accepted the value, false if the queue - was full. - */ - bool IRAM_ATTR push(T&& val); - - /*! - @brief Push a copy of the parameter into the queue. - @return true if the queue accepted the value, false if the queue - was full. - */ - bool IRAM_ATTR push(const T& val) - { - return push(T(val)); - } - -#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) - /*! - @brief Push copies of multiple elements from a buffer into the queue, - in order, beginning at buffer's head. - @return The number of elements actually copied into the queue, counted - from the buffer head. - */ - size_t push_n(const T* buffer, size_t size); -#endif - - /*! - @brief Pop the next available element from the queue. - @return An rvalue copy of the popped element, or a default - value of type T if the queue is empty. - */ - T pop(); - -#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) - /*! - @brief Pop multiple elements in ordered sequence from the queue to a buffer. - If buffer is nullptr, simply discards up to size elements from the queue. - @return The number of elements actually popped from the queue to - buffer. - */ - size_t pop_n(T* buffer, size_t size); -#endif - - /*! - @brief Iterate over and remove each available element from queue, - calling back fun with an rvalue reference of every single element. - */ -#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) - void for_each(const Delegate& fun); -#else - void for_each(Delegate fun); -#endif - - /*! - @brief In reverse order, iterate over, pop and optionally requeue each available element from the queue, - calling back fun with a reference of every single element. - Requeuing is dependent on the return boolean of the callback function. If it - returns true, the requeue occurs. - */ -#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) - bool for_each_rev_requeue(const Delegate& fun); -#else - bool for_each_rev_requeue(Delegate fun); -#endif - -protected: - const T defaultValue = {}; - unsigned m_bufSize; -#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) - std::unique_ptr m_buffer; -#else - std::unique_ptr m_buffer; -#endif - std::atomic m_inPos; - std::atomic m_outPos; -}; - -template< typename T, typename ForEachArg > -bool circular_queue::capacity(const size_t cap) -{ - if (cap + 1 == m_bufSize) return true; - else if (available() > cap) return false; - std::unique_ptr buffer(new T[cap + 1]); - const auto available = pop_n(buffer, cap); - m_buffer.reset(buffer); - m_bufSize = cap + 1; - std::atomic_thread_fence(std::memory_order_release); - m_inPos.store(available, std::memory_order_relaxed); - m_outPos.store(0, std::memory_order_release); - return true; -} - -template< typename T, typename ForEachArg > -bool IRAM_ATTR circular_queue::push() -{ - const auto inPos = m_inPos.load(std::memory_order_acquire); - const unsigned next = (inPos + 1) % m_bufSize; - if (next == m_outPos.load(std::memory_order_relaxed)) { - return false; - } - - std::atomic_thread_fence(std::memory_order_acquire); - - m_inPos.store(next, std::memory_order_release); - return true; -} - -template< typename T, typename ForEachArg > -bool IRAM_ATTR circular_queue::push(T&& val) -{ - const auto inPos = m_inPos.load(std::memory_order_acquire); - const unsigned next = (inPos + 1) % m_bufSize; - if (next == m_outPos.load(std::memory_order_relaxed)) { - return false; - } - - std::atomic_thread_fence(std::memory_order_acquire); - - m_buffer[inPos] = std::move(val); - - std::atomic_thread_fence(std::memory_order_release); - - m_inPos.store(next, std::memory_order_release); - return true; -} - -#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) -template< typename T, typename ForEachArg > -size_t circular_queue::push_n(const T* buffer, size_t size) -{ - const auto inPos = m_inPos.load(std::memory_order_acquire); - const auto outPos = m_outPos.load(std::memory_order_relaxed); - - size_t blockSize = (outPos > inPos) ? outPos - 1 - inPos : (outPos == 0) ? m_bufSize - 1 - inPos : m_bufSize - inPos; - blockSize = min(size, blockSize); - if (!blockSize) return 0; - int next = (inPos + blockSize) % m_bufSize; - - std::atomic_thread_fence(std::memory_order_acquire); - - auto dest = m_buffer.get() + inPos; - std::copy_n(std::make_move_iterator(buffer), blockSize, dest); - size = min(size - blockSize, outPos > 1 ? static_cast(outPos - next - 1) : 0); - next += size; - dest = m_buffer.get(); - std::copy_n(std::make_move_iterator(buffer + blockSize), size, dest); - - std::atomic_thread_fence(std::memory_order_release); - - m_inPos.store(next, std::memory_order_release); - return blockSize + size; -} -#endif - -template< typename T, typename ForEachArg > -T circular_queue::pop() -{ - const auto outPos = m_outPos.load(std::memory_order_acquire); - if (m_inPos.load(std::memory_order_relaxed) == outPos) return defaultValue; - - std::atomic_thread_fence(std::memory_order_acquire); - - auto val = std::move(m_buffer[outPos]); - - std::atomic_thread_fence(std::memory_order_release); - - m_outPos.store((outPos + 1) % m_bufSize, std::memory_order_release); - return val; -} - -#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) -template< typename T, typename ForEachArg > -size_t circular_queue::pop_n(T* buffer, size_t size) { - size_t avail = size = min(size, available()); - if (!avail) return 0; - const auto outPos = m_outPos.load(std::memory_order_acquire); - size_t n = min(avail, static_cast(m_bufSize - outPos)); - - std::atomic_thread_fence(std::memory_order_acquire); - - if (buffer) { - buffer = std::copy_n(std::make_move_iterator(m_buffer.get() + outPos), n, buffer); - avail -= n; - std::copy_n(std::make_move_iterator(m_buffer.get()), avail, buffer); - } - - std::atomic_thread_fence(std::memory_order_release); - - m_outPos.store((outPos + size) % m_bufSize, std::memory_order_release); - return size; -} -#endif - -template< typename T, typename ForEachArg > -#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) -void circular_queue::for_each(const Delegate& fun) -#else -void circular_queue::for_each(Delegate fun) -#endif -{ - auto outPos = m_outPos.load(std::memory_order_acquire); - const auto inPos = m_inPos.load(std::memory_order_relaxed); - std::atomic_thread_fence(std::memory_order_acquire); - while (outPos != inPos) - { - fun(std::move(m_buffer[outPos])); - std::atomic_thread_fence(std::memory_order_release); - outPos = (outPos + 1) % m_bufSize; - m_outPos.store(outPos, std::memory_order_release); - } -} - -template< typename T, typename ForEachArg > -#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) -bool circular_queue::for_each_rev_requeue(const Delegate& fun) -#else -bool circular_queue::for_each_rev_requeue(Delegate fun) -#endif -{ - auto inPos0 = circular_queue::m_inPos.load(std::memory_order_acquire); - auto outPos = circular_queue::m_outPos.load(std::memory_order_relaxed); - std::atomic_thread_fence(std::memory_order_acquire); - if (outPos == inPos0) return false; - auto pos = inPos0; - auto outPos1 = inPos0; - const auto posDecr = circular_queue::m_bufSize - 1; - do { - pos = (pos + posDecr) % circular_queue::m_bufSize; - T&& val = std::move(circular_queue::m_buffer[pos]); - if (fun(val)) - { - outPos1 = (outPos1 + posDecr) % circular_queue::m_bufSize; - if (outPos1 != pos) circular_queue::m_buffer[outPos1] = std::move(val); - } - } while (pos != outPos); - circular_queue::m_outPos.store(outPos1, std::memory_order_release); - return true; -} - -#endif // __circular_queue_h From 25b926510d30e58de7a9c3a7f673b869a966934e Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Wed, 5 Feb 2020 16:43:08 +0100 Subject: [PATCH 046/106] Delete circular_queue_mp.h --- .../src/circular_queue/circular_queue_mp.h | 200 ------------------ 1 file changed, 200 deletions(-) delete mode 100644 EspSoftwareSerial/src/circular_queue/circular_queue_mp.h diff --git a/EspSoftwareSerial/src/circular_queue/circular_queue_mp.h b/EspSoftwareSerial/src/circular_queue/circular_queue_mp.h deleted file mode 100644 index 7024247a..00000000 --- a/EspSoftwareSerial/src/circular_queue/circular_queue_mp.h +++ /dev/null @@ -1,200 +0,0 @@ -/* -circular_queue_mp.h - Implementation of a lock-free circular queue for EspSoftwareSerial. -Copyright (c) 2019 Dirk O. Kaar. All rights reserved. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#ifndef __circular_queue_mp_h -#define __circular_queue_mp_h - -#include "circular_queue.h" - -#ifdef ESP8266 -#include "interrupts.h" -#else -#include -#endif - -/*! - @brief Instance class for a multi-producer, single-consumer circular queue / ring buffer (FIFO). - This implementation is lock-free between producers and consumer for the available(), peek(), - pop(), and push() type functions, but is guarded to safely allow only a single producer - at any instant. -*/ -template< typename T, typename ForEachArg = void > -class circular_queue_mp : protected circular_queue -{ -public: - circular_queue_mp() = default; - circular_queue_mp(const size_t capacity) : circular_queue(capacity) - {} - circular_queue_mp(circular_queue&& cq) : circular_queue(std::move(cq)) - {} - using circular_queue::operator=; - using circular_queue::capacity; - using circular_queue::flush; - using circular_queue::available; - using circular_queue::available_for_push; - using circular_queue::peek; - using circular_queue::pop; - using circular_queue::pop_n; - using circular_queue::for_each; - using circular_queue::for_each_rev_requeue; - - /*! - @brief Resize the queue. The available elements in the queue are preserved. - This is not lock-free, but safe, concurrent producer or consumer access - is guarded. - @return True if the new capacity could accommodate the present elements in - the queue, otherwise nothing is done and false is returned. - */ - bool capacity(const size_t cap) - { -#ifdef ESP8266 - esp8266::InterruptLock lock; -#else - std::lock_guard lock(m_pushMtx); -#endif - return circular_queue::capacity(cap); - } - - bool IRAM_ATTR push() = delete; - - /*! - @brief Move the rvalue parameter into the queue, guarded - for multiple concurrent producers. - @return true if the queue accepted the value, false if the queue - was full. - */ - bool IRAM_ATTR push(T&& val) - { -#ifdef ESP8266 - esp8266::InterruptLock lock; -#else - std::lock_guard lock(m_pushMtx); -#endif - return circular_queue::push(std::move(val)); - } - - /*! - @brief Push a copy of the parameter into the queue, guarded - for multiple concurrent producers. - @return true if the queue accepted the value, false if the queue - was full. - */ - bool IRAM_ATTR push(const T& val) - { -#ifdef ESP8266 - esp8266::InterruptLock lock; -#else - std::lock_guard lock(m_pushMtx); -#endif - return circular_queue::push(val); - } - - /*! - @brief Push copies of multiple elements from a buffer into the queue, - in order, beginning at buffer's head. This is guarded for - multiple producers, push_n() is atomic. - @return The number of elements actually copied into the queue, counted - from the buffer head. - */ - size_t push_n(const T* buffer, size_t size) - { -#ifdef ESP8266 - esp8266::InterruptLock lock; -#else - std::lock_guard lock(m_pushMtx); -#endif - return circular_queue::push_n(buffer, size); - } - - /*! - @brief Pops the next available element from the queue, requeues - it immediately. - @return A reference to the just requeued element, or the default - value of type T if the queue is empty. - */ - T& pop_requeue(); - - /*! - @brief Iterate over, pop and optionally requeue each available element from the queue, - calling back fun with a reference of every single element. - Requeuing is dependent on the return boolean of the callback function. If it - returns true, the requeue occurs. - */ - bool for_each_requeue(const Delegate& fun); - -#ifndef ESP8266 -protected: - std::mutex m_pushMtx; -#endif -}; - -template< typename T, typename ForEachArg > -T& circular_queue_mp::pop_requeue() -{ -#ifdef ESP8266 - esp8266::InterruptLock lock; -#else - std::lock_guard lock(m_pushMtx); -#endif - const auto outPos = circular_queue::m_outPos.load(std::memory_order_acquire); - const auto inPos = circular_queue::m_inPos.load(std::memory_order_relaxed); - std::atomic_thread_fence(std::memory_order_acquire); - if (inPos == outPos) return circular_queue::defaultValue; - T& val = circular_queue::m_buffer[inPos] = std::move(circular_queue::m_buffer[outPos]); - const auto bufSize = circular_queue::m_bufSize; - std::atomic_thread_fence(std::memory_order_release); - circular_queue::m_outPos.store((outPos + 1) % bufSize, std::memory_order_relaxed); - circular_queue::m_inPos.store((inPos + 1) % bufSize, std::memory_order_release); - return val; -} - -template< typename T, typename ForEachArg > -bool circular_queue_mp::for_each_requeue(const Delegate& fun) -{ - auto inPos0 = circular_queue::m_inPos.load(std::memory_order_acquire); - auto outPos = circular_queue::m_outPos.load(std::memory_order_relaxed); - std::atomic_thread_fence(std::memory_order_acquire); - if (outPos == inPos0) return false; - do { - T&& val = std::move(circular_queue::m_buffer[outPos]); - if (fun(val)) - { -#ifdef ESP8266 - esp8266::InterruptLock lock; -#else - std::lock_guard lock(m_pushMtx); -#endif - std::atomic_thread_fence(std::memory_order_release); - auto inPos = circular_queue::m_inPos.load(std::memory_order_relaxed); - std::atomic_thread_fence(std::memory_order_acquire); - circular_queue::m_buffer[inPos] = std::move(val); - std::atomic_thread_fence(std::memory_order_release); - circular_queue::m_inPos.store((inPos + 1) % circular_queue::m_bufSize, std::memory_order_release); - } - else - { - std::atomic_thread_fence(std::memory_order_release); - } - outPos = (outPos + 1) % circular_queue::m_bufSize; - circular_queue::m_outPos.store(outPos, std::memory_order_release); - } while (outPos != inPos0); - return true; -} - -#endif // __circular_queue_mp_h From d7155fd25b9c87422379aaf0cb2de507f11f3916 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Wed, 5 Feb 2020 16:43:15 +0100 Subject: [PATCH 047/106] Delete ghostl.h --- EspSoftwareSerial/src/circular_queue/ghostl.h | 92 ------------------- 1 file changed, 92 deletions(-) delete mode 100644 EspSoftwareSerial/src/circular_queue/ghostl.h diff --git a/EspSoftwareSerial/src/circular_queue/ghostl.h b/EspSoftwareSerial/src/circular_queue/ghostl.h deleted file mode 100644 index 11683805..00000000 --- a/EspSoftwareSerial/src/circular_queue/ghostl.h +++ /dev/null @@ -1,92 +0,0 @@ -/* -ghostl.h - Implementation of a bare-bones, mostly no-op, C++ STL shell - that allows building some Arduino ESP8266/ESP32 - libraries on Aruduino AVR. -Copyright (c) 2019 Dirk O. Kaar. All rights reserved. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#ifndef __ghostl_h -#define __ghostl_h - -#if defined(ARDUINO_ARCH_SAMD) -#include -#endif - -namespace std -{ -#if !defined(ARDUINO_ARCH_SAMD) - typedef enum memory_order { - memory_order_relaxed, - memory_order_acquire, - memory_order_release, - memory_order_seq_cst - } memory_order; - template< typename T > class atomic { - private: - T value; - public: - atomic() {} - atomic(T desired) { value = desired; } - void store(T desired, std::memory_order = std::memory_order_seq_cst) volatile noexcept { value = desired; } - T load(std::memory_order = std::memory_order_seq_cst) const volatile noexcept { return value; } - }; - inline void atomic_thread_fence(std::memory_order order) noexcept {} - template< typename T > T&& move(T& t) noexcept { return static_cast(t); } -#endif - - template< typename T, unsigned long N > struct array - { - T _M_elems[N]; - decltype(sizeof(0)) size() const { return N; } - T& operator[](decltype(sizeof(0)) i) { return _M_elems[i]; } - const T& operator[](decltype(sizeof(0)) i) const { return _M_elems[i]; } - }; - - template< typename T > class unique_ptr - { - public: - using pointer = T*; - unique_ptr() noexcept : ptr(nullptr) {} - unique_ptr(pointer p) : ptr(p) {} - pointer operator->() const noexcept { return ptr; } - T& operator[](decltype(sizeof(0)) i) const { return ptr[i]; } - void reset(pointer p = pointer()) noexcept - { - delete ptr; - ptr = p; - } - T& operator*() const { return *ptr; } - private: - pointer ptr; - }; - - template< typename T > using function = T*; - using nullptr_t = decltype(nullptr); - - template - struct identity { - typedef T type; - }; - - template - inline T&& forward(typename identity::type& t) noexcept - { - return static_cast::type&&>(t); - } -} - -#endif // __ghostl_h From 8e1fee23bb7bfdbf5ee6f667ab54516979d41a6b Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Wed, 5 Feb 2020 16:43:27 +0100 Subject: [PATCH 048/106] Delete SoftwareSerial.cpp --- EspSoftwareSerial/src/SoftwareSerial.cpp | 542 ----------------------- 1 file changed, 542 deletions(-) delete mode 100644 EspSoftwareSerial/src/SoftwareSerial.cpp diff --git a/EspSoftwareSerial/src/SoftwareSerial.cpp b/EspSoftwareSerial/src/SoftwareSerial.cpp deleted file mode 100644 index 0e7b8e08..00000000 --- a/EspSoftwareSerial/src/SoftwareSerial.cpp +++ /dev/null @@ -1,542 +0,0 @@ -/* - -SoftwareSerial.cpp - Implementation of the Arduino software serial for ESP8266/ESP32. -Copyright (c) 2015-2016 Peter Lerup. All rights reserved. -Copyright (c) 2018-2019 Dirk O. Kaar. All rights reserved. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -*/ - -#include "SoftwareSerial.h" -#include - -#ifdef ESP32 -#define xt_rsil(a) (a) -#define xt_wsr_ps(a) -#endif - -constexpr uint8_t BYTE_ALL_BITS_SET = ~static_cast(0); - -SoftwareSerial::SoftwareSerial() { - m_isrOverflow = false; -} - -SoftwareSerial::SoftwareSerial(int8_t rxPin, int8_t txPin, bool invert) -{ - m_isrOverflow = false; - m_rxPin = rxPin; - m_txPin = txPin; - m_invert = invert; -} - -SoftwareSerial::~SoftwareSerial() { - end(); -} - -bool SoftwareSerial::isValidGPIOpin(int8_t pin) { -#if defined(ESP8266) - return (pin >= 0 && pin <= 5) || (pin >= 12 && pin <= 15); -#elif defined(ESP32) - return pin == 0 || pin == 2 || (pin >= 4 && pin <= 5) || (pin >= 12 && pin <= 19) || - (pin >= 21 && pin <= 23) || (pin >= 25 && pin <= 27) || (pin >= 32 && pin <= 35); -#else - return true; -#endif -} - -void SoftwareSerial::begin(uint32_t baud, SoftwareSerialConfig config, - int8_t rxPin, int8_t txPin, - bool invert, int bufCapacity, int isrBufCapacity) { - if (-1 != rxPin) m_rxPin = rxPin; - if (-1 != txPin) m_txPin = txPin; - m_oneWire = (m_rxPin == m_txPin); - m_invert = invert; - m_dataBits = 5 + (config & 07); - m_parityMode = static_cast(config & 070); - m_stopBits = 1 + ((config & 0300) ? 1 : 0); - m_pduBits = m_dataBits + static_cast(m_parityMode) + m_stopBits; - m_bitCycles = (ESP.getCpuFreqMHz() * 1000000UL + baud / 2) / baud; - m_intTxEnabled = true; - if (isValidGPIOpin(m_rxPin)) { - std::unique_ptr > buffer(new circular_queue((bufCapacity > 0) ? bufCapacity : 64)); - m_buffer = move(buffer); - if (m_parityMode) - { - std::unique_ptr > parityBuffer(new circular_queue((bufCapacity > 0) ? (bufCapacity + 7) / 8 : 8)); - m_parityBuffer = move(parityBuffer); - m_parityInPos = m_parityOutPos = 1; - } - std::unique_ptr > isrBuffer(new circular_queue((isrBufCapacity > 0) ? isrBufCapacity : (sizeof(uint8_t) * 8 + 2) * bufCapacity)); - m_isrBuffer = move(isrBuffer); - if (m_buffer && (!m_parityMode || m_parityBuffer) && m_isrBuffer) { - m_rxValid = true; - pinMode(m_rxPin, INPUT_PULLUP); - } - } - if (isValidGPIOpin(m_txPin) -#ifdef ESP8266 - || ((m_txPin == 16) && !m_oneWire)) { -#else - ) { -#endif - m_txValid = true; - if (!m_oneWire) { - pinMode(m_txPin, OUTPUT); - digitalWrite(m_txPin, !m_invert); - } - } - if (!m_rxEnabled) { enableRx(true); } -} - -void SoftwareSerial::end() -{ - enableRx(false); - m_txValid = false; - if (m_buffer) { - m_buffer.reset(); - } - m_parityBuffer.reset(); - if (m_isrBuffer) { - m_isrBuffer.reset(); - } -} - -uint32_t SoftwareSerial::baudRate() { - return ESP.getCpuFreqMHz() * 1000000UL / m_bitCycles; -} - -void SoftwareSerial::setTransmitEnablePin(int8_t txEnablePin) { - if (isValidGPIOpin(txEnablePin)) { - m_txEnableValid = true; - m_txEnablePin = txEnablePin; - pinMode(m_txEnablePin, OUTPUT); - digitalWrite(m_txEnablePin, LOW); - } - else { - m_txEnableValid = false; - } -} - -void SoftwareSerial::enableIntTx(bool on) { - m_intTxEnabled = on; -} - -void SoftwareSerial::enableTx(bool on) { - if (m_txValid && m_oneWire) { - if (on) { - enableRx(false); - pinMode(m_txPin, OUTPUT); - digitalWrite(m_txPin, !m_invert); - } - else { - pinMode(m_rxPin, INPUT_PULLUP); - enableRx(true); - } - } -} - -void SoftwareSerial::enableRx(bool on) { - if (m_rxValid) { - if (on) { - m_rxCurBit = m_pduBits - 1; - // Init to stop bit level and current cycle - m_isrLastCycle = (ESP.getCycleCount() | 1) ^ m_invert; - if (m_bitCycles >= (ESP.getCpuFreqMHz() * 1000000UL) / 74880UL) - attachInterruptArg(digitalPinToInterrupt(m_rxPin), reinterpret_cast(rxBitISR), this, CHANGE); - else - attachInterruptArg(digitalPinToInterrupt(m_rxPin), reinterpret_cast(rxBitSyncISR), this, m_invert ? RISING : FALLING); - } - else { - detachInterrupt(digitalPinToInterrupt(m_rxPin)); - } - m_rxEnabled = on; - } -} - -int SoftwareSerial::read() { - if (!m_rxValid) { return -1; } - if (!m_buffer->available()) { - rxBits(); - if (!m_buffer->available()) { return -1; } - } - auto val = m_buffer->pop(); - if (m_parityBuffer) - { - m_lastReadParity = m_parityBuffer->peek() & m_parityOutPos; - m_parityOutPos <<= 1; - if (!m_parityOutPos) - { - m_parityOutPos = 1; - m_parityBuffer->pop(); - } - } - return val; -} - -size_t SoftwareSerial::read(uint8_t * buffer, size_t size) { - if (!m_rxValid) { return 0; } - size_t avail; - if (0 == (avail = m_buffer->pop_n(buffer, size))) { - rxBits(); - avail = m_buffer->pop_n(buffer, size); - } - if (!avail) return 0; - if (m_parityBuffer) { - uint32_t parityBits = avail; - while (m_parityOutPos >>= 1) ++parityBits; - m_parityOutPos = (1 << (parityBits % 8)); - m_parityBuffer->pop_n(nullptr, parityBits / 8); - } - return avail; -} - -size_t SoftwareSerial::readBytes(uint8_t * buffer, size_t size) { - if (!m_rxValid || !size) { return 0; } - size_t count = 0; - const auto start = millis(); - do { - count += read(&buffer[count], size - count); - if (count >= size) break; - yield(); - } while (millis() - start < _timeout); - return count; -} - -int SoftwareSerial::available() { - if (!m_rxValid) { return 0; } - rxBits(); - int avail = m_buffer->available(); - if (!avail) { - optimistic_yield(10000UL); - } - return avail; -} - -void ICACHE_RAM_ATTR SoftwareSerial::preciseDelay(bool sync) { - if (!sync) - { - // Reenable interrupts while delaying to avoid other tasks piling up - if (!m_intTxEnabled) { xt_wsr_ps(m_savedPS); } - auto expired = ESP.getCycleCount() - m_periodStart; - if (expired < m_periodDuration) - { - auto ms = (m_periodDuration - expired) / ESP.getCpuFreqMHz() / 1000UL; - if (ms) delay(ms); - } - while ((ESP.getCycleCount() - m_periodStart) < m_periodDuration) { optimistic_yield(10000); } - // Disable interrupts again - if (!m_intTxEnabled) { m_savedPS = xt_rsil(15); } - } - else - { - while ((ESP.getCycleCount() - m_periodStart) < m_periodDuration) {} - } - m_periodDuration = 0; - m_periodStart = ESP.getCycleCount(); -} - -void ICACHE_RAM_ATTR SoftwareSerial::writePeriod( - uint32_t dutyCycle, uint32_t offCycle, bool withStopBit) { - preciseDelay(true); - if (dutyCycle) - { - digitalWrite(m_txPin, HIGH); - m_periodDuration += dutyCycle; - if (offCycle || (withStopBit && !m_invert)) preciseDelay(!withStopBit || m_invert); - } - if (offCycle) - { - digitalWrite(m_txPin, LOW); - m_periodDuration += offCycle; - if (withStopBit && m_invert) preciseDelay(false); - } -} - -size_t SoftwareSerial::write(uint8_t byte) { - return write(&byte, 1); -} - -size_t SoftwareSerial::write(uint8_t byte, SoftwareSerialParity parity) { - return write(&byte, 1, parity); -} - -size_t SoftwareSerial::write(const uint8_t * buffer, size_t size) { - return write(buffer, size, m_parityMode); -} - -size_t ICACHE_RAM_ATTR SoftwareSerial::write(const uint8_t * buffer, size_t size, SoftwareSerialParity parity) { - if (m_rxValid) { rxBits(); } - if (!m_txValid) { return -1; } - - if (m_txEnableValid) { - digitalWrite(m_txEnablePin, HIGH); - } - // Stop bit: if inverted, LOW, otherwise HIGH - bool b = !m_invert; - uint32_t dutyCycle = 0; - uint32_t offCycle = 0; - if (!m_intTxEnabled) { - // Disable interrupts in order to get a clean transmit timing - m_savedPS = xt_rsil(15); - } - const uint32_t dataMask = ((1UL << m_dataBits) - 1); - bool withStopBit = true; - m_periodDuration = 0; - m_periodStart = ESP.getCycleCount(); - for (size_t cnt = 0; cnt < size; ++cnt) { - uint8_t byte = ~buffer[cnt] & dataMask; - // push LSB start-data-parity-stop bit pattern into uint32_t - // Stop bits: HIGH - uint32_t word = ~0UL; - // parity bit, if any - if (parity && m_parityMode) - { - uint32_t parityBit; - switch (parity) - { - case SWSERIAL_PARITY_EVEN: - // from inverted, so use odd parity - parityBit = byte; - parityBit ^= parityBit >> 4; - parityBit &= 0xf; - parityBit = (0x9669 >> parityBit) & 1; - break; - case SWSERIAL_PARITY_ODD: - // from inverted, so use even parity - parityBit = byte; - parityBit ^= parityBit >> 4; - parityBit &= 0xf; - parityBit = (0x6996 >> parityBit) & 1; - break; - case SWSERIAL_PARITY_MARK: - parityBit = false; - break; - case SWSERIAL_PARITY_SPACE: - // suppresses warning parityBit uninitialized - default: - parityBit = true; - break; - } - word ^= parityBit << m_dataBits; - } - word ^= byte; - // Stop bit: LOW - word <<= 1; - if (m_invert) word = ~word; - for (int i = 0; i <= m_pduBits; ++i) { - bool pb = b; - b = word & (1UL << i); - if (!pb && b) { - writePeriod(dutyCycle, offCycle, withStopBit); - withStopBit = false; - dutyCycle = offCycle = 0; - } - if (b) { - dutyCycle += m_bitCycles; - } - else { - offCycle += m_bitCycles; - } - } - withStopBit = true; - } - writePeriod(dutyCycle, offCycle, true); - if (!m_intTxEnabled) { - // restore the interrupt state - xt_wsr_ps(m_savedPS); - } - if (m_txEnableValid) { - digitalWrite(m_txEnablePin, LOW); - } - return size; -} - -void SoftwareSerial::flush() { - if (!m_rxValid) { return; } - m_buffer->flush(); - if (m_parityBuffer) - { - m_parityInPos = m_parityOutPos = 1; - m_parityBuffer->flush(); - } -} - -bool SoftwareSerial::overflow() { - bool res = m_overflow; - m_overflow = false; - return res; -} - -int SoftwareSerial::peek() { - if (!m_rxValid) { return -1; } - if (!m_buffer->available()) { - rxBits(); - if (!m_buffer->available()) return -1; - } - auto val = m_buffer->peek(); - if (m_parityBuffer) m_lastReadParity = m_parityBuffer->peek() & m_parityOutPos; - return val; -} - -void SoftwareSerial::rxBits() { - int isrAvail = m_isrBuffer->available(); -#ifdef ESP8266 - if (m_isrOverflow.load()) { - m_overflow = true; - m_isrOverflow.store(false); - } -#else - if (m_isrOverflow.exchange(false)) { - m_overflow = true; - } -#endif - - // stop bit can go undetected if leading data bits are at same level - // and there was also no next start bit yet, so one byte may be pending. - // low-cost check first - if (!isrAvail && m_rxCurBit >= -1 && m_rxCurBit < m_pduBits - m_stopBits) { - uint32_t detectionCycles = (m_pduBits - m_stopBits - m_rxCurBit) * m_bitCycles; - if (ESP.getCycleCount() - m_isrLastCycle > detectionCycles) { - // Produce faux stop bit level, prevents start bit maldetection - // cycle's LSB is repurposed for the level bit - rxBits(((m_isrLastCycle + detectionCycles) | 1) ^ m_invert); - } - } - - m_isrBuffer->for_each([this](const uint32_t& isrCycle) { rxBits(isrCycle); }); -} - -void SoftwareSerial::rxBits(const uint32_t & isrCycle) { - bool level = (m_isrLastCycle & 1) ^ m_invert; - - // error introduced by edge value in LSB of isrCycle is negligible - int32_t cycles = isrCycle - m_isrLastCycle; - m_isrLastCycle = isrCycle; - - uint8_t bits = cycles / m_bitCycles; - if (cycles % m_bitCycles > (m_bitCycles >> 1)) ++bits; - while (bits > 0) { - // start bit detection - if (m_rxCurBit >= (m_pduBits - 1)) { - // leading edge of start bit - if (level) break; - m_rxCurBit = -1; - --bits; - continue; - } - // data bits - if (m_rxCurBit >= -1 && m_rxCurBit < (m_dataBits - 1)) { - int8_t dataBits = min(bits, static_cast(m_dataBits - 1 - m_rxCurBit)); - m_rxCurBit += dataBits; - bits -= dataBits; - m_rxCurByte >>= dataBits; - if (level) { m_rxCurByte |= (BYTE_ALL_BITS_SET << (8 - dataBits)); } - continue; - } - // parity bit - if (m_parityMode && m_rxCurBit == (m_dataBits - 1)) { - ++m_rxCurBit; - --bits; - m_rxCurParity = level; - continue; - } - // stop bits - if (m_rxCurBit < (m_pduBits - m_stopBits - 1)) { - ++m_rxCurBit; - --bits; - continue; - } - if (m_rxCurBit == (m_pduBits - m_stopBits - 1)) { - // Store the received value in the buffer unless we have an overflow - // if not high stop bit level, discard word - if (level) - { - m_rxCurByte >>= (sizeof(uint8_t) * 8 - m_dataBits); - if (!m_buffer->push(m_rxCurByte)) { - m_overflow = true; - } - else { - if (m_parityBuffer) - { - if (m_rxCurParity) { - m_parityBuffer->pushpeek() |= m_parityInPos; - } - else { - m_parityBuffer->pushpeek() &= ~m_parityInPos; - } - m_parityInPos <<= 1; - if (!m_parityInPos) - { - m_parityBuffer->push(); - m_parityInPos = 1; - } - } - } - } - m_rxCurBit = m_pduBits; - // reset to 0 is important for masked bit logic - m_rxCurByte = 0; - m_rxCurParity = false; - break; - } - break; - } -} - -void ICACHE_RAM_ATTR SoftwareSerial::rxBitISR(SoftwareSerial * self) { - uint32_t curCycle = ESP.getCycleCount(); - bool level = digitalRead(self->m_rxPin); - - // Store level and cycle in the buffer unless we have an overflow - // cycle's LSB is repurposed for the level bit - if (!self->m_isrBuffer->push((curCycle | 1U) ^ !level)) self->m_isrOverflow.store(true); -} - -void ICACHE_RAM_ATTR SoftwareSerial::rxBitSyncISR(SoftwareSerial * self) { - uint32_t start = ESP.getCycleCount(); - uint32_t wait = self->m_bitCycles - 172U; - - bool level = self->m_invert; - // Store level and cycle in the buffer unless we have an overflow - // cycle's LSB is repurposed for the level bit - if (!self->m_isrBuffer->push(((start + wait) | 1U) ^ !level)) self->m_isrOverflow.store(true); - - for (uint32_t i = 0; i < self->m_pduBits; ++i) { - while (ESP.getCycleCount() - start < wait) {}; - wait += self->m_bitCycles; - - // Store level and cycle in the buffer unless we have an overflow - // cycle's LSB is repurposed for the level bit - if (digitalRead(self->m_rxPin) != level) - { - if (!self->m_isrBuffer->push(((start + wait) | 1U) ^ level)) self->m_isrOverflow.store(true); - level = !level; - } - } -} - -void SoftwareSerial::onReceive(Delegate handler) { - receiveHandler = handler; -} - -void SoftwareSerial::perform_work() { - if (!m_rxValid) { return; } - rxBits(); - if (receiveHandler) { - int avail = m_buffer->available(); - if (avail) { receiveHandler(avail); } - } -} From 1294528ef6d5ba5007bb2f496787ce9b48997492 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Wed, 5 Feb 2020 16:43:36 +0100 Subject: [PATCH 049/106] Delete SoftwareSerial.h --- EspSoftwareSerial/src/SoftwareSerial.h | 255 ------------------------- 1 file changed, 255 deletions(-) delete mode 100644 EspSoftwareSerial/src/SoftwareSerial.h diff --git a/EspSoftwareSerial/src/SoftwareSerial.h b/EspSoftwareSerial/src/SoftwareSerial.h deleted file mode 100644 index 371e3731..00000000 --- a/EspSoftwareSerial/src/SoftwareSerial.h +++ /dev/null @@ -1,255 +0,0 @@ -/* -SoftwareSerial.h - -SoftwareSerial.cpp - Implementation of the Arduino software serial for ESP8266/ESP32. -Copyright (c) 2015-2016 Peter Lerup. All rights reserved. -Copyright (c) 2018-2019 Dirk O. Kaar. All rights reserved. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -*/ - -#ifndef __SoftwareSerial_h -#define __SoftwareSerial_h - -#include "circular_queue/circular_queue.h" -#include - -enum SoftwareSerialParity : uint8_t { - SWSERIAL_PARITY_NONE = 000, - SWSERIAL_PARITY_EVEN = 020, - SWSERIAL_PARITY_ODD = 030, - SWSERIAL_PARITY_MARK = 040, - SWSERIAL_PARITY_SPACE = 070, -}; - -enum SoftwareSerialConfig { - SWSERIAL_5N1 = SWSERIAL_PARITY_NONE, - SWSERIAL_6N1, - SWSERIAL_7N1, - SWSERIAL_8N1, - SWSERIAL_5E1 = SWSERIAL_PARITY_EVEN, - SWSERIAL_6E1, - SWSERIAL_7E1, - SWSERIAL_8E1, - SWSERIAL_5O1 = SWSERIAL_PARITY_ODD, - SWSERIAL_6O1, - SWSERIAL_7O1, - SWSERIAL_8O1, - SWSERIAL_5M1 = SWSERIAL_PARITY_MARK, - SWSERIAL_6M1, - SWSERIAL_7M1, - SWSERIAL_8M1, - SWSERIAL_5S1 = SWSERIAL_PARITY_SPACE, - SWSERIAL_6S1, - SWSERIAL_7S1, - SWSERIAL_8S1, - SWSERIAL_5N2 = 0200 | SWSERIAL_PARITY_NONE, - SWSERIAL_6N2, - SWSERIAL_7N2, - SWSERIAL_8N2, - SWSERIAL_5E2 = 0200 | SWSERIAL_PARITY_EVEN, - SWSERIAL_6E2, - SWSERIAL_7E2, - SWSERIAL_8E2, - SWSERIAL_5O2 = 0200 | SWSERIAL_PARITY_ODD, - SWSERIAL_6O2, - SWSERIAL_7O2, - SWSERIAL_8O2, - SWSERIAL_5M2 = 0200 | SWSERIAL_PARITY_MARK, - SWSERIAL_6M2, - SWSERIAL_7M2, - SWSERIAL_8M2, - SWSERIAL_5S2 = 0200 | SWSERIAL_PARITY_SPACE, - SWSERIAL_6S2, - SWSERIAL_7S2, - SWSERIAL_8S2, -}; - -/// This class is compatible with the corresponding AVR one, however, -/// the constructor takes no arguments, for compatibility with the -/// HardwareSerial class. -/// Instead, the begin() function handles pin assignments and logic inversion. -/// It also has optional input buffer capacity arguments for byte buffer and ISR bit buffer. -/// Bitrates up to at least 115200 can be used. -class SoftwareSerial : public Stream { -public: - SoftwareSerial(); - /// Ctor to set defaults for pins. - /// @param rxPin the GPIO pin used for RX - /// @param txPin -1 for onewire protocol, GPIO pin used for twowire TX - SoftwareSerial(int8_t rxPin, int8_t txPin = -1, bool invert = false); - SoftwareSerial(const SoftwareSerial&) = delete; - SoftwareSerial& operator= (const SoftwareSerial&) = delete; - virtual ~SoftwareSerial(); - /// Configure the SoftwareSerial object for use. - /// @param baud the TX/RX bitrate - /// @param config sets databits, parity, and stop bit count - /// @param rxPin -1 or default: either no RX pin, or keeps the rxPin set in the ctor - /// @param txPin -1 or default: either no TX pin (onewire), or keeps the txPin set in the ctor - /// @param invert true: uses invert line level logic - /// @param bufCapacity the capacity for the received bytes buffer - /// @param isrBufCapacity 0: derived from bufCapacity. The capacity of the internal asynchronous - /// bit receive buffer, a suggested size is bufCapacity times the sum of - /// start, data, parity and stop bit count. - void begin(uint32_t baud, SoftwareSerialConfig config, - int8_t rxPin, int8_t txPin, bool invert, - int bufCapacity = 64, int isrBufCapacity = 0); - void begin(uint32_t baud, SoftwareSerialConfig config, - int8_t rxPin, int8_t txPin) { - begin(baud, config, rxPin, txPin, m_invert); - } - void begin(uint32_t baud, SoftwareSerialConfig config, - int8_t rxPin) { - begin(baud, config, rxPin, m_txPin, m_invert); - } - void begin(uint32_t baud, SoftwareSerialConfig config = SWSERIAL_8N1) { - begin(baud, config, m_rxPin, m_txPin, m_invert); - } - - uint32_t baudRate(); - /// Transmit control pin. - void setTransmitEnablePin(int8_t txEnablePin); - /// Enable or disable interrupts during tx. - void enableIntTx(bool on); - - bool overflow(); - - int available() override; - int availableForWrite() { - if (!m_txValid) return 0; - return 1; - } - int peek() override; - int read() override; - /// @returns The verbatim parity bit associated with the last read() or peek() call - bool readParity() - { - return m_lastReadParity; - } - /// @returns The calculated bit for even parity of the parameter byte - static bool parityEven(uint8_t byte) { - byte ^= byte >> 4; - byte &= 0xf; - return (0x6996 >> byte) & 1; - } - /// @returns The calculated bit for odd parity of the parameter byte - static bool parityOdd(uint8_t byte) { - byte ^= byte >> 4; - byte &= 0xf; - return (0x9669 >> byte) & 1; - } - /// The read(buffer, size) functions are non-blocking, the same as readBytes but without timeout - size_t read(uint8_t* buffer, size_t size); - /// The read(buffer, size) functions are non-blocking, the same as readBytes but without timeout - size_t read(char* buffer, size_t size) { - return read(reinterpret_cast(buffer), size); - } - /// @returns The number of bytes read into buffer, up to size. Times out if the limit set through - /// Stream::setTimeout() is reached. - size_t readBytes(uint8_t* buffer, size_t size) override; - /// @returns The number of bytes read into buffer, up to size. Times out if the limit set through - /// Stream::setTimeout() is reached. - size_t readBytes(char* buffer, size_t size) override { - return readBytes(reinterpret_cast(buffer), size); - } - void flush() override; - size_t write(uint8_t byte) override; - size_t write(uint8_t byte, SoftwareSerialParity parity); - size_t write(const uint8_t* buffer, size_t size) override; - size_t write(const char* buffer, size_t size) { - return write(reinterpret_cast(buffer), size); - } - size_t write(const uint8_t* buffer, size_t size, SoftwareSerialParity parity); - size_t write(const char* buffer, size_t size, SoftwareSerialParity parity) { - return write(reinterpret_cast(buffer), size, parity); - } - operator bool() const { return m_rxValid || m_txValid; } - - /// Disable or enable interrupts on the rx pin. - void enableRx(bool on); - /// One wire control. - void enableTx(bool on); - - // AVR compatibility methods. - bool listen() { enableRx(true); return true; } - void end(); - bool isListening() { return m_rxEnabled; } - bool stopListening() { enableRx(false); return true; } - - /// Set an event handler for received data. - void onReceive(Delegate handler); - - /// Run the internal processing and event engine. Can be iteratively called - /// from loop, or otherwise scheduled. - void perform_work(); - - using Print::write; - -private: - // If sync is false, it's legal to exceed the deadline, for instance, - // by enabling interrupts. - void preciseDelay(bool sync); - // If withStopBit is set, either cycle contains a stop bit. - // If dutyCycle == 0, the level is not forced to HIGH. - // If offCycle == 0, the level remains unchanged from dutyCycle. - void writePeriod( - uint32_t dutyCycle, uint32_t offCycle, bool withStopBit); - bool isValidGPIOpin(int8_t pin); - /* check m_rxValid that calling is safe */ - void rxBits(); - void rxBits(const uint32_t& isrCycle); - - static void rxBitISR(SoftwareSerial* self); - static void rxBitSyncISR(SoftwareSerial* self); - - // Member variables - int8_t m_rxPin = -1; - int8_t m_txPin = -1; - int8_t m_txEnablePin = -1; - uint8_t m_dataBits; - bool m_oneWire; - bool m_rxValid = false; - bool m_rxEnabled = false; - bool m_txValid = false; - bool m_txEnableValid = false; - bool m_invert; - /// PDU bits include data, parity and stop bits; the start bit is not counted. - uint8_t m_pduBits; - bool m_intTxEnabled; - SoftwareSerialParity m_parityMode; - uint8_t m_stopBits; - bool m_lastReadParity; - bool m_overflow = false; - uint32_t m_bitCycles; - uint8_t m_parityInPos; - uint8_t m_parityOutPos; - int8_t m_rxCurBit; // 0 thru (m_pduBits - m_stopBits - 1): data/parity bits. -1: start bit. (m_pduBits - 1): stop bit. - uint8_t m_rxCurByte = 0; - std::unique_ptr > m_buffer; - std::unique_ptr > m_parityBuffer; - uint32_t m_periodStart; - uint32_t m_periodDuration; - uint32_t m_savedPS = 0; - // the ISR stores the relative bit times in the buffer. The inversion corrected level is used as sign bit (2's complement): - // 1 = positive including 0, 0 = negative. - std::unique_ptr > m_isrBuffer; - std::atomic m_isrOverflow; - uint32_t m_isrLastCycle; - bool m_rxCurParity = false; - Delegate receiveHandler; -}; - -#endif // __SoftwareSerial_h From 24c78da6fe3d211aaca341619cb8e729dc8f0902 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Wed, 5 Feb 2020 16:44:21 +0100 Subject: [PATCH 050/106] Add files via upload --- .../examples/loopback/loopback.ino | 263 ++++++++++++++++++ .../examples/onewiretest/onewiretest.ino | 48 ++++ .../examples/repeater/repeater.ino | 183 ++++++++++++ .../examples/servoTester/servoTester.ino | 115 ++++++++ .../examples/swsertest/swsertest.ino | 47 ++++ 5 files changed, 656 insertions(+) create mode 100644 lib/EspSoftwareSerial/examples/loopback/loopback.ino create mode 100644 lib/EspSoftwareSerial/examples/onewiretest/onewiretest.ino create mode 100644 lib/EspSoftwareSerial/examples/repeater/repeater.ino create mode 100644 lib/EspSoftwareSerial/examples/servoTester/servoTester.ino create mode 100644 lib/EspSoftwareSerial/examples/swsertest/swsertest.ino diff --git a/lib/EspSoftwareSerial/examples/loopback/loopback.ino b/lib/EspSoftwareSerial/examples/loopback/loopback.ino new file mode 100644 index 00000000..b612bdec --- /dev/null +++ b/lib/EspSoftwareSerial/examples/loopback/loopback.ino @@ -0,0 +1,263 @@ +#include + +// On ESP8266: +// Local SoftwareSerial loopback, connect D5 (rx) and D6 (tx). +// For local hardware loopback, connect D5 to D8 (tx), D6 to D7 (rx). +// For hardware send/sink, connect D7 (rx) and D8 (tx). +// Hint: The logger is run at 9600bps such that enableIntTx(true) can remain unchanged. Blocking +// interrupts severely impacts the ability of the SoftwareSerial devices to operate concurrently +// and/or in duplex mode. +// Operating in software serial full duplex mode, runs at 19200bps and few errors (~2.5%). +// Operating in software serial half duplex mode (both loopback and repeater), +// runs at 57600bps with nearly no errors. +// Operating loopback in full duplex, and repeater in half duplex, runs at 38400bps with nearly no errors. +// On ESP32: +// For SoftwareSerial or hardware send/sink, connect D5 (rx) and D6 (tx). +// Hardware Serial2 defaults to D4 (rx), D3 (tx). +// For local hardware loopback, connect D5 (rx) to D3 (tx), D6 (tx) to D4 (rx). + +#if defined(ESP8266) && !defined(D5) +#define D5 (14) +#define D6 (12) +#define D7 (13) +#define D8 (15) +#define TX (1) +#endif + +// Pick only one of HWLOOPBACK, HWSOURCESWSINK, or HWSOURCESINK +//#define HWLOOPBACK 1 +//#define HWSOURCESWSINK 1 +//#define HWSOURCESINK 1 +#define HALFDUPLEX 1 + +#ifdef ESP32 +constexpr int IUTBITRATE = 19200; +#else +constexpr int IUTBITRATE = 19200; +#endif + +#if defined(ESP8266) +constexpr SoftwareSerialConfig swSerialConfig = SWSERIAL_8E1; +constexpr SerialConfig hwSerialConfig = SERIAL_8E1; +#elif defined(ESP32) +constexpr SoftwareSerialConfig swSerialConfig = SWSERIAL_8E1; +constexpr uint32_t hwSerialConfig = SERIAL_8E1; +#else +constexpr unsigned swSerialConfig = 3; +#endif +constexpr bool invert = false; + +constexpr int BLOCKSIZE = 16; // use fractions of 256 + +unsigned long start; +String effTxTxt("eff. tx: "); +String effRxTxt("eff. rx: "); +int txCount; +int rxCount; +int expected; +int rxErrors; +int rxParityErrors; +constexpr int ReportInterval = IUTBITRATE / 8; + +#if defined(ESP8266) +#if defined(HWLOOPBACK) || defined(HWSOURCESWSINK) +HardwareSerial& hwSerial(Serial); +SoftwareSerial serialIUT; +SoftwareSerial logger; +#elif defined(HWSOURCESINK) +HardwareSerial& serialIUT(Serial); +SoftwareSerial logger; +#else +SoftwareSerial serialIUT; +HardwareSerial& logger(Serial); +#endif +#elif defined(ESP32) +#if defined(HWLOOPBACK) || defined (HWSOURCESWSINK) +HardwareSerial& hwSerial(Serial2); +SoftwareSerial serialIUT; +#elif defined(HWSOURCESINK) +HardwareSerial& serialIUT(Serial2); +#else +SoftwareSerial serialIUT; +#endif +HardwareSerial& logger(Serial); +#else +SoftwareSerial serialIUT(14, 12); +HardwareSerial& logger(Serial); +#endif + +void setup() { +#if defined(ESP8266) +#if defined(HWLOOPBACK) || defined(HWSOURCESINK) || defined(HWSOURCESWSINK) + Serial.begin(IUTBITRATE, hwSerialConfig, SERIAL_FULL, 1, invert); + Serial.swap(); + Serial.setRxBufferSize(2 * BLOCKSIZE); + logger.begin(9600, SWSERIAL_8N1, -1, TX); +#else + logger.begin(9600); +#endif +#if !defined(HWSOURCESINK) + serialIUT.begin(IUTBITRATE, swSerialConfig, D5, D6, invert, 2 * BLOCKSIZE); +#ifdef HALFDUPLEX + serialIUT.enableIntTx(false); +#endif +#endif +#elif defined(ESP32) +#if defined(HWLOOPBACK) || defined(HWSOURCESWSINK) + Serial2.begin(IUTBITRATE, hwSerialConfig, D4, D3, invert); + Serial2.setRxBufferSize(2 * BLOCKSIZE); +#elif defined(HWSOURCESINK) + serialIUT.begin(IUTBITRATE, hwSerialConfig, D5, D6, invert); + serialIUT.setRxBufferSize(2 * BLOCKSIZE); +#endif +#if !defined(HWSOURCESINK) + serialIUT.begin(IUTBITRATE, swSerialConfig, D5, D6, invert, 2 * BLOCKSIZE); +#ifdef HALFDUPLEX + serialIUT.enableIntTx(false); +#endif +#endif + logger.begin(9600); +#else +#if !defined(HWSOURCESINK) + serialIUT.begin(IUTBITRATE); +#endif + logger.begin(9600); +#endif + + logger.println("Loopback example for EspSoftwareSerial"); + + start = micros(); + txCount = 0; + rxCount = 0; + rxErrors = 0; + rxParityErrors = 0; + expected = -1; +} + +unsigned char c = 0; + +void loop() { +#ifdef HALFDUPLEX + char block[BLOCKSIZE]; +#endif + char inBuf[BLOCKSIZE]; + for (int i = 0; i < BLOCKSIZE; ++i) { +#ifndef HALFDUPLEX +#ifdef HWSOURCESWSINK + hwSerial.write(c); +#else + serialIUT.write(c); +#endif +#ifdef HWLOOPBACK + int avail = hwSerial.available(); + while ((0 == (i % 8)) && avail > 0) { + int inCnt = hwSerial.read(inBuf, min(avail, min(BLOCKSIZE, hwSerial.availableForWrite()))); + hwSerial.write(inBuf, inCnt); + avail -= inCnt; + } +#endif +#else + block[i] = c; +#endif + c = (c + 1) % 256; + ++txCount; + } +#ifdef HALFDUPLEX +#ifdef HWSOURCESWSINK + hwSerial.write(block, BLOCKSIZE); +#else + serialIUT.write(block, BLOCKSIZE); +#endif +#endif +#ifdef HWSOURCESINK +#if defined(ESP8266) + if (serialIUT.hasOverrun()) { logger.println("serialIUT.overrun"); } +#endif +#else + if (serialIUT.overflow()) { logger.println("serialIUT.overflow"); } +#endif + + int inCnt; + uint32_t deadlineStart; + +#ifdef HWLOOPBACK + // starting deadline for the first bytes to become readable + deadlineStart = ESP.getCycleCount(); + inCnt = 0; + while ((ESP.getCycleCount() - deadlineStart) < (1000000UL * 12 * BLOCKSIZE) / IUTBITRATE * 24 * ESP.getCpuFreqMHz()) { + int avail = hwSerial.available(); + inCnt += hwSerial.read(&inBuf[inCnt], min(avail, min(BLOCKSIZE - inCnt, hwSerial.availableForWrite()))); + if (inCnt >= BLOCKSIZE) { break; } + // wait for more outstanding bytes to trickle in + if (avail) deadlineStart = ESP.getCycleCount(); + } + hwSerial.write(inBuf, inCnt); +#endif + + // starting deadline for the first bytes to come in + deadlineStart = ESP.getCycleCount(); + inCnt = 0; + while ((ESP.getCycleCount() - deadlineStart) < (1000000UL * 12 * BLOCKSIZE) / IUTBITRATE * 8 * ESP.getCpuFreqMHz()) { + int avail; + if (0 != (swSerialConfig & 070)) + avail = serialIUT.available(); + else + avail = serialIUT.read(inBuf, BLOCKSIZE); + for (int i = 0; i < avail; ++i) + { + unsigned char r; + if (0 != (swSerialConfig & 070)) + r = serialIUT.read(); + else + r = inBuf[i]; + if (expected == -1) { expected = r; } + else { + expected = (expected + 1) % (1UL << (5 + swSerialConfig % 4)); + } + if (r != expected) { + ++rxErrors; + expected = -1; + } +#ifndef HWSOURCESINK + if (serialIUT.readParity() != (static_cast(swSerialConfig & 010) ? serialIUT.parityOdd(r) : serialIUT.parityEven(r))) + { + ++rxParityErrors; + } +#endif + ++rxCount; + ++inCnt; + } + + if (inCnt >= BLOCKSIZE) { break; } + // wait for more outstanding bytes to trickle in + if (avail) deadlineStart = ESP.getCycleCount(); + } + + const uint32_t interval = micros() - start; + if (txCount >= ReportInterval && interval) { + uint8_t wordBits = (5 + swSerialConfig % 4) + static_cast(swSerialConfig & 070) + 1 + ((swSerialConfig & 0300) ? 1 : 0); + logger.println(String("tx/rx: ") + txCount + "/" + rxCount); + const long txCps = txCount * (1000000.0 / interval); + const long rxCps = rxCount * (1000000.0 / interval); + logger.print(effTxTxt + wordBits * txCps + "bps, " + + effRxTxt + wordBits * rxCps + "bps, " + + rxErrors + " errors (" + 100.0 * rxErrors / (!rxErrors ? 1 : rxCount) + "%)"); + if (0 != (swSerialConfig & 070)) + { + logger.print(" ("); logger.print(rxParityErrors); logger.println(" parity errors)"); + } + else + { + logger.println(); + } + txCount = 0; + rxCount = 0; + rxErrors = 0; + rxParityErrors = 0; + expected = -1; + // resync + delay(1000UL * 12 * BLOCKSIZE / IUTBITRATE * 16); + serialIUT.flush(); + start = micros(); + } +} diff --git a/lib/EspSoftwareSerial/examples/onewiretest/onewiretest.ino b/lib/EspSoftwareSerial/examples/onewiretest/onewiretest.ino new file mode 100644 index 00000000..3e96401b --- /dev/null +++ b/lib/EspSoftwareSerial/examples/onewiretest/onewiretest.ino @@ -0,0 +1,48 @@ +#include +#include "SoftwareSerial.h" + +SoftwareSerial swSer1; +SoftwareSerial swSer2; + +void setup() { + delay(2000); + Serial.begin(115200); + Serial.println("\nOne Wire Half Duplex Serial Tester"); + swSer1.begin(115200, SWSERIAL_8N1, 12, 12, false, 256); + swSer1.enableIntTx(true); + swSer2.begin(115200, SWSERIAL_8N1, 14, 14, false, 256); + swSer2.enableIntTx(true); +} + +void loop() { + Serial.println("\n\nTesting on swSer1"); + Serial.print("Enter something to send using swSer1."); + checkSwSerial(&swSer1); + + Serial.println("\n\nTesting on swSer2"); + Serial.print("Enter something to send using swSer2."); + checkSwSerial(&swSer2); + +} + +void checkSwSerial(SoftwareSerial* ss) { + byte ch; + while (!Serial.available()); + ss->enableTx(true); + while (Serial.available()) { + ch = Serial.read(); + ss->write(ch); + } + ss->enableTx(false); + // wait 1 second for the reply from SOftwareSerial if any + delay(1000); + if (ss->available()) { + Serial.print("\nResult:"); + while (ss->available()) { + ch = (byte)ss->read(); + Serial.print(ch < 0x01 ? " 0" : " "); + Serial.print(ch, HEX); + } + Serial.println(); + } +} diff --git a/lib/EspSoftwareSerial/examples/repeater/repeater.ino b/lib/EspSoftwareSerial/examples/repeater/repeater.ino new file mode 100644 index 00000000..fa5566de --- /dev/null +++ b/lib/EspSoftwareSerial/examples/repeater/repeater.ino @@ -0,0 +1,183 @@ +#include + +// On ESP8266: +// SoftwareSerial loopback for remote source (loopback.ino), or hardware loopback. +// Connect source D5 (rx) to local D8 (tx), source D6 (tx) to local D7 (rx). +// Hint: The logger is run at 9600bps such that enableIntTx(true) can remain unchanged. Blocking +// interrupts severely impacts the ability of the SoftwareSerial devices to operate concurrently +// and/or in duplex mode. +// On ESP32: +// For software or hardware loopback, connect source rx to local D8 (tx), source tx to local D7 (rx). + +#if defined(ESP8266) && !defined(D5) +#define D5 (14) +#define D6 (12) +#define D7 (13) +#define D8 (15) +#define TX (1) +#endif + +#define HWLOOPBACK 1 +#define HALFDUPLEX 1 + +#ifdef ESP32 +constexpr int IUTBITRATE = 19200; +#else +constexpr int IUTBITRATE = 19200; +#endif + +#if defined(ESP8266) +constexpr SoftwareSerialConfig swSerialConfig = SWSERIAL_8E1; +constexpr SerialConfig hwSerialConfig = SERIAL_8E1; +#elif defined(ESP32) +constexpr SoftwareSerialConfig swSerialConfig = SWSERIAL_8E1; +constexpr uint32_t hwSerialConfig = SERIAL_8E1; +#else +constexpr unsigned swSerialConfig = 3; +#endif +constexpr bool invert = false; + +constexpr int BLOCKSIZE = 16; // use fractions of 256 + +unsigned long start; +String bitRateTxt("Effective data rate: "); +int rxCount; +int seqErrors; +int parityErrors; +int expected; +constexpr int ReportInterval = IUTBITRATE / 8; + +#if defined(ESP8266) +#if defined(HWLOOPBACK) +HardwareSerial& repeater(Serial); +SoftwareSerial logger; +#else +SoftwareSerial repeater; +HardwareSerial& logger(Serial); +#endif +#elif defined(ESP32) +#if defined(HWLOOPBACK) +HardwareSerial& repeater(Serial2); +#else +SoftwareSerial repeater; +#endif +HardwareSerial& logger(Serial); +#else +SoftwareSerial repeater(14, 12); +HardwareSerial& logger(Serial); +#endif + +void setup() { +#if defined(ESP8266) +#if defined(HWLOOPBACK) + repeater.begin(IUTBITRATE, hwSerialConfig, SERIAL_FULL, 1, invert); + repeater.swap(); + repeater.setRxBufferSize(2 * BLOCKSIZE); + logger.begin(9600, SWSERIAL_8N1, -1, TX); +#else + repeater.begin(IUTBITRATE, swSerialConfig, D7, D8, invert, 4 * BLOCKSIZE); +#ifdef HALFDUPLEX + repeater.enableIntTx(false); +#endif + logger.begin(9600); +#endif +#elif defined(ESP32) +#if defined(HWLOOPBACK) + repeater.begin(IUTBITRATE, hwSerialConfig, D7, D8, invert); + repeater.setRxBufferSize(2 * BLOCKSIZE); +#else + repeater.begin(IUTBITRATE, swSerialConfig, D7, D8, invert, 4 * BLOCKSIZE); +#ifdef HALFDUPLEX + repeater.enableIntTx(false); +#endif +#endif + logger.begin(9600); +#else + repeater.begin(IUTBITRATE); + logger.begin(9600); +#endif + + logger.println("Repeater example for EspSoftwareSerial"); + start = micros(); + rxCount = 0; + seqErrors = 0; + parityErrors = 0; + expected = -1; +} + +void loop() { +#ifdef HWLOOPBACK +#if defined(ESP8266) + if (repeater.hasOverrun()) { logger.println("repeater.overrun"); } +#endif +#else + if (repeater.overflow()) { logger.println("repeater.overflow"); } +#endif + +#ifdef HALFDUPLEX + char block[BLOCKSIZE]; +#endif + // starting deadline for the first bytes to come in + uint32_t deadlineStart = ESP.getCycleCount(); + int inCnt = 0; + while ((ESP.getCycleCount() - deadlineStart) < (1000000UL * 12 * BLOCKSIZE) / IUTBITRATE * 24 * ESP.getCpuFreqMHz()) { + int avail = repeater.available(); + for (int i = 0; i < avail; ++i) + { + int r = repeater.read(); + if (r == -1) { logger.println("read() == -1"); } + if (expected == -1) { expected = r; } + else { + expected = (expected + 1) % (1UL << (5 + swSerialConfig % 4)); + } + if (r != expected) { + ++seqErrors; + expected = -1; + } +#ifndef HWLOOPBACK + if (repeater.readParity() != (static_cast(swSerialConfig & 010) ? repeater.parityOdd(r) : repeater.parityEven(r))) + { + ++parityErrors; + } +#endif + ++rxCount; +#ifdef HALFDUPLEX + block[inCnt] = r; +#else + repeater.write(r); +#endif + if (++inCnt >= BLOCKSIZE) { break; } + } + if (inCnt >= BLOCKSIZE) { break; } + // wait for more outstanding bytes to trickle in + if (avail) deadlineStart = ESP.getCycleCount(); + } + +#ifdef HALFDUPLEX + repeater.write(block, inCnt); +#endif + + if (rxCount >= ReportInterval) { + auto end = micros(); + unsigned long interval = end - start; + long cps = rxCount * (1000000.0 / interval); + long seqErrorsps = seqErrors * (1000000.0 / interval); + logger.print(bitRateTxt + 10 * cps + "bps, " + + seqErrorsps + "cps seq. errors (" + 100.0 * seqErrors / rxCount + "%)"); +#ifndef HWLOOPBACK + if (0 != (swSerialConfig & 070)) + { + logger.print(" ("); logger.print(parityErrors); logger.print(" parity errors)"); + } + else +#endif + { + logger.println(); + } + start = end; + rxCount = 0; + seqErrors = 0; + parityErrors = 0; + expected = -1; + } +} diff --git a/lib/EspSoftwareSerial/examples/servoTester/servoTester.ino b/lib/EspSoftwareSerial/examples/servoTester/servoTester.ino new file mode 100644 index 00000000..cbc784d8 --- /dev/null +++ b/lib/EspSoftwareSerial/examples/servoTester/servoTester.ino @@ -0,0 +1,115 @@ +#include +#include + +SoftwareSerial swSer; + +byte buf[10] = { 0xFA, 0xAF,0x00,0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0xED }; +byte cmd[10] = { 0xFA, 0xAF,0x00,0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0xED }; +byte ver[10] = { 0xFC, 0xCF,0x00,0xAA,0x41, 0x16, 0x51, 0x01, 0x00, 0xED }; + + +void setup() { + delay(2000); + Serial.begin(115200); + Serial.println("\nAlpha 1S Servo Tester"); + swSer.begin(115200, SWSERIAL_8N1, 12, 12, false, 256); +} + +void loop() { + for (int i = 1; i <= 32; i++) { + GetVersion(i); + delay(100); + } + SetLED(1, 0); + GoPos(1, 0, 50); + delay(1000); + GoPos(1, 90, 50); + delay(1000); + GoPos(1, 100, 50); + delay(1000); + SetLED(1, 1); + delay(2000); +} + + + + +void GetVersion(byte id) { + memcpy(buf, cmd, 10); + buf[0] = 0xFC; + buf[1] = 0xCF; + buf[2] = id; + buf[3] = 0x01; + SendCommand(); +} + + +void GoPos(byte id, byte Pos, byte Time) { + memcpy(buf, cmd, 10); + buf[2] = id; + buf[3] = 0x01; + buf[4] = Pos; + buf[5] = Time; + buf[6] = 0x00; + buf[7] = Time; + SendCommand(); +} + +void GetPos(byte id) { + memcpy(buf, cmd, 10); + buf[2] = id; + buf[3] = 0x02; + SendCommand(); +} + + +void SetLED(byte id, byte mode) { + memcpy(buf, cmd, 10); + buf[2] = id; + buf[3] = 0x04; + buf[4] = mode; + SendCommand(); +} + +void SendCommand() { + SendCommand(true); +} + +void SendCommand(bool checkResult) { + byte sum = 0; + for (int i = 2; i < 8; i++) { + sum += buf[i]; + } + buf[8] = sum; + ShowCommand(); + swSer.flush(); + swSer.enableTx(true); + swSer.write(buf, 10); + swSer.enableTx(false); + if (checkResult) checkReturn(); +} + +void ShowCommand() { + Serial.print(millis()); + Serial.print(" OUT>>"); + for (int i = 0; i < 10; i++) { + Serial.print((buf[i] < 0x10 ? " 0" : " ")); + Serial.print(buf[i], HEX); + } + Serial.println(); +} + +void checkReturn() { + unsigned long startMs = millis(); + while (((millis() - startMs) < 500) && (!swSer.available())); + if (swSer.available()) { + Serial.print(millis()); + Serial.print(" IN>>>"); + while (swSer.available()) { + byte ch = (byte)swSer.read(); + Serial.print((ch < 0x10 ? " 0" : " ")); + Serial.print(ch, HEX); + } + Serial.println(); + } +} diff --git a/lib/EspSoftwareSerial/examples/swsertest/swsertest.ino b/lib/EspSoftwareSerial/examples/swsertest/swsertest.ino new file mode 100644 index 00000000..a047c1be --- /dev/null +++ b/lib/EspSoftwareSerial/examples/swsertest/swsertest.ino @@ -0,0 +1,47 @@ +// On ESP8266: +// At 80MHz runs up 57600ps, and at 160MHz CPU frequency up to 115200bps with only negligible errors. +// Connect pin 12 to 14. + +#include + +#if defined(ESP8266) && !defined(D5) +#define D5 (14) +#define D6 (12) +#define D7 (13) +#define D8 (15) +#endif + +#ifdef ESP32 +#define BAUD_RATE 57600 +#else +#define BAUD_RATE 57600 +#endif + +// Reminder: the buffer size optimizations here, in particular the isrBufSize that only accommodates +// a single 8N1 word, are on the basis that any char written to the loopback SoftwareSerial adapter gets read +// before another write is performed. Block writes with a size greater than 1 would usually fail. +SoftwareSerial swSer; + +void setup() { + Serial.begin(115200); + swSer.begin(BAUD_RATE, SWSERIAL_8N1, D5, D6, false, 95, 11); + + Serial.println("\nSoftware serial test started"); + + for (char ch = ' '; ch <= 'z'; ch++) { + swSer.write(ch); + } + swSer.println(""); +} + +void loop() { + while (swSer.available() > 0) { + Serial.write(swSer.read()); + yield(); + } + while (Serial.available() > 0) { + swSer.write(Serial.read()); + yield(); + } + +} From 701461805673d29dba4a6981f9d7552f2d6d0000 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Wed, 5 Feb 2020 16:45:12 +0100 Subject: [PATCH 051/106] Add files via upload --- lib/EspSoftwareSerial/src/SoftwareSerial.cpp | 542 +++++ lib/EspSoftwareSerial/src/SoftwareSerial.h | 255 +++ .../src/circular_queue/Delegate.h | 1786 +++++++++++++++++ .../src/circular_queue/MultiDelegate.h | 503 +++++ .../src/circular_queue/circular_queue.h | 399 ++++ .../src/circular_queue/circular_queue_mp.h | 200 ++ .../src/circular_queue/ghostl.h | 92 + 7 files changed, 3777 insertions(+) create mode 100644 lib/EspSoftwareSerial/src/SoftwareSerial.cpp create mode 100644 lib/EspSoftwareSerial/src/SoftwareSerial.h create mode 100644 lib/EspSoftwareSerial/src/circular_queue/Delegate.h create mode 100644 lib/EspSoftwareSerial/src/circular_queue/MultiDelegate.h create mode 100644 lib/EspSoftwareSerial/src/circular_queue/circular_queue.h create mode 100644 lib/EspSoftwareSerial/src/circular_queue/circular_queue_mp.h create mode 100644 lib/EspSoftwareSerial/src/circular_queue/ghostl.h diff --git a/lib/EspSoftwareSerial/src/SoftwareSerial.cpp b/lib/EspSoftwareSerial/src/SoftwareSerial.cpp new file mode 100644 index 00000000..0e7b8e08 --- /dev/null +++ b/lib/EspSoftwareSerial/src/SoftwareSerial.cpp @@ -0,0 +1,542 @@ +/* + +SoftwareSerial.cpp - Implementation of the Arduino software serial for ESP8266/ESP32. +Copyright (c) 2015-2016 Peter Lerup. All rights reserved. +Copyright (c) 2018-2019 Dirk O. Kaar. All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "SoftwareSerial.h" +#include + +#ifdef ESP32 +#define xt_rsil(a) (a) +#define xt_wsr_ps(a) +#endif + +constexpr uint8_t BYTE_ALL_BITS_SET = ~static_cast(0); + +SoftwareSerial::SoftwareSerial() { + m_isrOverflow = false; +} + +SoftwareSerial::SoftwareSerial(int8_t rxPin, int8_t txPin, bool invert) +{ + m_isrOverflow = false; + m_rxPin = rxPin; + m_txPin = txPin; + m_invert = invert; +} + +SoftwareSerial::~SoftwareSerial() { + end(); +} + +bool SoftwareSerial::isValidGPIOpin(int8_t pin) { +#if defined(ESP8266) + return (pin >= 0 && pin <= 5) || (pin >= 12 && pin <= 15); +#elif defined(ESP32) + return pin == 0 || pin == 2 || (pin >= 4 && pin <= 5) || (pin >= 12 && pin <= 19) || + (pin >= 21 && pin <= 23) || (pin >= 25 && pin <= 27) || (pin >= 32 && pin <= 35); +#else + return true; +#endif +} + +void SoftwareSerial::begin(uint32_t baud, SoftwareSerialConfig config, + int8_t rxPin, int8_t txPin, + bool invert, int bufCapacity, int isrBufCapacity) { + if (-1 != rxPin) m_rxPin = rxPin; + if (-1 != txPin) m_txPin = txPin; + m_oneWire = (m_rxPin == m_txPin); + m_invert = invert; + m_dataBits = 5 + (config & 07); + m_parityMode = static_cast(config & 070); + m_stopBits = 1 + ((config & 0300) ? 1 : 0); + m_pduBits = m_dataBits + static_cast(m_parityMode) + m_stopBits; + m_bitCycles = (ESP.getCpuFreqMHz() * 1000000UL + baud / 2) / baud; + m_intTxEnabled = true; + if (isValidGPIOpin(m_rxPin)) { + std::unique_ptr > buffer(new circular_queue((bufCapacity > 0) ? bufCapacity : 64)); + m_buffer = move(buffer); + if (m_parityMode) + { + std::unique_ptr > parityBuffer(new circular_queue((bufCapacity > 0) ? (bufCapacity + 7) / 8 : 8)); + m_parityBuffer = move(parityBuffer); + m_parityInPos = m_parityOutPos = 1; + } + std::unique_ptr > isrBuffer(new circular_queue((isrBufCapacity > 0) ? isrBufCapacity : (sizeof(uint8_t) * 8 + 2) * bufCapacity)); + m_isrBuffer = move(isrBuffer); + if (m_buffer && (!m_parityMode || m_parityBuffer) && m_isrBuffer) { + m_rxValid = true; + pinMode(m_rxPin, INPUT_PULLUP); + } + } + if (isValidGPIOpin(m_txPin) +#ifdef ESP8266 + || ((m_txPin == 16) && !m_oneWire)) { +#else + ) { +#endif + m_txValid = true; + if (!m_oneWire) { + pinMode(m_txPin, OUTPUT); + digitalWrite(m_txPin, !m_invert); + } + } + if (!m_rxEnabled) { enableRx(true); } +} + +void SoftwareSerial::end() +{ + enableRx(false); + m_txValid = false; + if (m_buffer) { + m_buffer.reset(); + } + m_parityBuffer.reset(); + if (m_isrBuffer) { + m_isrBuffer.reset(); + } +} + +uint32_t SoftwareSerial::baudRate() { + return ESP.getCpuFreqMHz() * 1000000UL / m_bitCycles; +} + +void SoftwareSerial::setTransmitEnablePin(int8_t txEnablePin) { + if (isValidGPIOpin(txEnablePin)) { + m_txEnableValid = true; + m_txEnablePin = txEnablePin; + pinMode(m_txEnablePin, OUTPUT); + digitalWrite(m_txEnablePin, LOW); + } + else { + m_txEnableValid = false; + } +} + +void SoftwareSerial::enableIntTx(bool on) { + m_intTxEnabled = on; +} + +void SoftwareSerial::enableTx(bool on) { + if (m_txValid && m_oneWire) { + if (on) { + enableRx(false); + pinMode(m_txPin, OUTPUT); + digitalWrite(m_txPin, !m_invert); + } + else { + pinMode(m_rxPin, INPUT_PULLUP); + enableRx(true); + } + } +} + +void SoftwareSerial::enableRx(bool on) { + if (m_rxValid) { + if (on) { + m_rxCurBit = m_pduBits - 1; + // Init to stop bit level and current cycle + m_isrLastCycle = (ESP.getCycleCount() | 1) ^ m_invert; + if (m_bitCycles >= (ESP.getCpuFreqMHz() * 1000000UL) / 74880UL) + attachInterruptArg(digitalPinToInterrupt(m_rxPin), reinterpret_cast(rxBitISR), this, CHANGE); + else + attachInterruptArg(digitalPinToInterrupt(m_rxPin), reinterpret_cast(rxBitSyncISR), this, m_invert ? RISING : FALLING); + } + else { + detachInterrupt(digitalPinToInterrupt(m_rxPin)); + } + m_rxEnabled = on; + } +} + +int SoftwareSerial::read() { + if (!m_rxValid) { return -1; } + if (!m_buffer->available()) { + rxBits(); + if (!m_buffer->available()) { return -1; } + } + auto val = m_buffer->pop(); + if (m_parityBuffer) + { + m_lastReadParity = m_parityBuffer->peek() & m_parityOutPos; + m_parityOutPos <<= 1; + if (!m_parityOutPos) + { + m_parityOutPos = 1; + m_parityBuffer->pop(); + } + } + return val; +} + +size_t SoftwareSerial::read(uint8_t * buffer, size_t size) { + if (!m_rxValid) { return 0; } + size_t avail; + if (0 == (avail = m_buffer->pop_n(buffer, size))) { + rxBits(); + avail = m_buffer->pop_n(buffer, size); + } + if (!avail) return 0; + if (m_parityBuffer) { + uint32_t parityBits = avail; + while (m_parityOutPos >>= 1) ++parityBits; + m_parityOutPos = (1 << (parityBits % 8)); + m_parityBuffer->pop_n(nullptr, parityBits / 8); + } + return avail; +} + +size_t SoftwareSerial::readBytes(uint8_t * buffer, size_t size) { + if (!m_rxValid || !size) { return 0; } + size_t count = 0; + const auto start = millis(); + do { + count += read(&buffer[count], size - count); + if (count >= size) break; + yield(); + } while (millis() - start < _timeout); + return count; +} + +int SoftwareSerial::available() { + if (!m_rxValid) { return 0; } + rxBits(); + int avail = m_buffer->available(); + if (!avail) { + optimistic_yield(10000UL); + } + return avail; +} + +void ICACHE_RAM_ATTR SoftwareSerial::preciseDelay(bool sync) { + if (!sync) + { + // Reenable interrupts while delaying to avoid other tasks piling up + if (!m_intTxEnabled) { xt_wsr_ps(m_savedPS); } + auto expired = ESP.getCycleCount() - m_periodStart; + if (expired < m_periodDuration) + { + auto ms = (m_periodDuration - expired) / ESP.getCpuFreqMHz() / 1000UL; + if (ms) delay(ms); + } + while ((ESP.getCycleCount() - m_periodStart) < m_periodDuration) { optimistic_yield(10000); } + // Disable interrupts again + if (!m_intTxEnabled) { m_savedPS = xt_rsil(15); } + } + else + { + while ((ESP.getCycleCount() - m_periodStart) < m_periodDuration) {} + } + m_periodDuration = 0; + m_periodStart = ESP.getCycleCount(); +} + +void ICACHE_RAM_ATTR SoftwareSerial::writePeriod( + uint32_t dutyCycle, uint32_t offCycle, bool withStopBit) { + preciseDelay(true); + if (dutyCycle) + { + digitalWrite(m_txPin, HIGH); + m_periodDuration += dutyCycle; + if (offCycle || (withStopBit && !m_invert)) preciseDelay(!withStopBit || m_invert); + } + if (offCycle) + { + digitalWrite(m_txPin, LOW); + m_periodDuration += offCycle; + if (withStopBit && m_invert) preciseDelay(false); + } +} + +size_t SoftwareSerial::write(uint8_t byte) { + return write(&byte, 1); +} + +size_t SoftwareSerial::write(uint8_t byte, SoftwareSerialParity parity) { + return write(&byte, 1, parity); +} + +size_t SoftwareSerial::write(const uint8_t * buffer, size_t size) { + return write(buffer, size, m_parityMode); +} + +size_t ICACHE_RAM_ATTR SoftwareSerial::write(const uint8_t * buffer, size_t size, SoftwareSerialParity parity) { + if (m_rxValid) { rxBits(); } + if (!m_txValid) { return -1; } + + if (m_txEnableValid) { + digitalWrite(m_txEnablePin, HIGH); + } + // Stop bit: if inverted, LOW, otherwise HIGH + bool b = !m_invert; + uint32_t dutyCycle = 0; + uint32_t offCycle = 0; + if (!m_intTxEnabled) { + // Disable interrupts in order to get a clean transmit timing + m_savedPS = xt_rsil(15); + } + const uint32_t dataMask = ((1UL << m_dataBits) - 1); + bool withStopBit = true; + m_periodDuration = 0; + m_periodStart = ESP.getCycleCount(); + for (size_t cnt = 0; cnt < size; ++cnt) { + uint8_t byte = ~buffer[cnt] & dataMask; + // push LSB start-data-parity-stop bit pattern into uint32_t + // Stop bits: HIGH + uint32_t word = ~0UL; + // parity bit, if any + if (parity && m_parityMode) + { + uint32_t parityBit; + switch (parity) + { + case SWSERIAL_PARITY_EVEN: + // from inverted, so use odd parity + parityBit = byte; + parityBit ^= parityBit >> 4; + parityBit &= 0xf; + parityBit = (0x9669 >> parityBit) & 1; + break; + case SWSERIAL_PARITY_ODD: + // from inverted, so use even parity + parityBit = byte; + parityBit ^= parityBit >> 4; + parityBit &= 0xf; + parityBit = (0x6996 >> parityBit) & 1; + break; + case SWSERIAL_PARITY_MARK: + parityBit = false; + break; + case SWSERIAL_PARITY_SPACE: + // suppresses warning parityBit uninitialized + default: + parityBit = true; + break; + } + word ^= parityBit << m_dataBits; + } + word ^= byte; + // Stop bit: LOW + word <<= 1; + if (m_invert) word = ~word; + for (int i = 0; i <= m_pduBits; ++i) { + bool pb = b; + b = word & (1UL << i); + if (!pb && b) { + writePeriod(dutyCycle, offCycle, withStopBit); + withStopBit = false; + dutyCycle = offCycle = 0; + } + if (b) { + dutyCycle += m_bitCycles; + } + else { + offCycle += m_bitCycles; + } + } + withStopBit = true; + } + writePeriod(dutyCycle, offCycle, true); + if (!m_intTxEnabled) { + // restore the interrupt state + xt_wsr_ps(m_savedPS); + } + if (m_txEnableValid) { + digitalWrite(m_txEnablePin, LOW); + } + return size; +} + +void SoftwareSerial::flush() { + if (!m_rxValid) { return; } + m_buffer->flush(); + if (m_parityBuffer) + { + m_parityInPos = m_parityOutPos = 1; + m_parityBuffer->flush(); + } +} + +bool SoftwareSerial::overflow() { + bool res = m_overflow; + m_overflow = false; + return res; +} + +int SoftwareSerial::peek() { + if (!m_rxValid) { return -1; } + if (!m_buffer->available()) { + rxBits(); + if (!m_buffer->available()) return -1; + } + auto val = m_buffer->peek(); + if (m_parityBuffer) m_lastReadParity = m_parityBuffer->peek() & m_parityOutPos; + return val; +} + +void SoftwareSerial::rxBits() { + int isrAvail = m_isrBuffer->available(); +#ifdef ESP8266 + if (m_isrOverflow.load()) { + m_overflow = true; + m_isrOverflow.store(false); + } +#else + if (m_isrOverflow.exchange(false)) { + m_overflow = true; + } +#endif + + // stop bit can go undetected if leading data bits are at same level + // and there was also no next start bit yet, so one byte may be pending. + // low-cost check first + if (!isrAvail && m_rxCurBit >= -1 && m_rxCurBit < m_pduBits - m_stopBits) { + uint32_t detectionCycles = (m_pduBits - m_stopBits - m_rxCurBit) * m_bitCycles; + if (ESP.getCycleCount() - m_isrLastCycle > detectionCycles) { + // Produce faux stop bit level, prevents start bit maldetection + // cycle's LSB is repurposed for the level bit + rxBits(((m_isrLastCycle + detectionCycles) | 1) ^ m_invert); + } + } + + m_isrBuffer->for_each([this](const uint32_t& isrCycle) { rxBits(isrCycle); }); +} + +void SoftwareSerial::rxBits(const uint32_t & isrCycle) { + bool level = (m_isrLastCycle & 1) ^ m_invert; + + // error introduced by edge value in LSB of isrCycle is negligible + int32_t cycles = isrCycle - m_isrLastCycle; + m_isrLastCycle = isrCycle; + + uint8_t bits = cycles / m_bitCycles; + if (cycles % m_bitCycles > (m_bitCycles >> 1)) ++bits; + while (bits > 0) { + // start bit detection + if (m_rxCurBit >= (m_pduBits - 1)) { + // leading edge of start bit + if (level) break; + m_rxCurBit = -1; + --bits; + continue; + } + // data bits + if (m_rxCurBit >= -1 && m_rxCurBit < (m_dataBits - 1)) { + int8_t dataBits = min(bits, static_cast(m_dataBits - 1 - m_rxCurBit)); + m_rxCurBit += dataBits; + bits -= dataBits; + m_rxCurByte >>= dataBits; + if (level) { m_rxCurByte |= (BYTE_ALL_BITS_SET << (8 - dataBits)); } + continue; + } + // parity bit + if (m_parityMode && m_rxCurBit == (m_dataBits - 1)) { + ++m_rxCurBit; + --bits; + m_rxCurParity = level; + continue; + } + // stop bits + if (m_rxCurBit < (m_pduBits - m_stopBits - 1)) { + ++m_rxCurBit; + --bits; + continue; + } + if (m_rxCurBit == (m_pduBits - m_stopBits - 1)) { + // Store the received value in the buffer unless we have an overflow + // if not high stop bit level, discard word + if (level) + { + m_rxCurByte >>= (sizeof(uint8_t) * 8 - m_dataBits); + if (!m_buffer->push(m_rxCurByte)) { + m_overflow = true; + } + else { + if (m_parityBuffer) + { + if (m_rxCurParity) { + m_parityBuffer->pushpeek() |= m_parityInPos; + } + else { + m_parityBuffer->pushpeek() &= ~m_parityInPos; + } + m_parityInPos <<= 1; + if (!m_parityInPos) + { + m_parityBuffer->push(); + m_parityInPos = 1; + } + } + } + } + m_rxCurBit = m_pduBits; + // reset to 0 is important for masked bit logic + m_rxCurByte = 0; + m_rxCurParity = false; + break; + } + break; + } +} + +void ICACHE_RAM_ATTR SoftwareSerial::rxBitISR(SoftwareSerial * self) { + uint32_t curCycle = ESP.getCycleCount(); + bool level = digitalRead(self->m_rxPin); + + // Store level and cycle in the buffer unless we have an overflow + // cycle's LSB is repurposed for the level bit + if (!self->m_isrBuffer->push((curCycle | 1U) ^ !level)) self->m_isrOverflow.store(true); +} + +void ICACHE_RAM_ATTR SoftwareSerial::rxBitSyncISR(SoftwareSerial * self) { + uint32_t start = ESP.getCycleCount(); + uint32_t wait = self->m_bitCycles - 172U; + + bool level = self->m_invert; + // Store level and cycle in the buffer unless we have an overflow + // cycle's LSB is repurposed for the level bit + if (!self->m_isrBuffer->push(((start + wait) | 1U) ^ !level)) self->m_isrOverflow.store(true); + + for (uint32_t i = 0; i < self->m_pduBits; ++i) { + while (ESP.getCycleCount() - start < wait) {}; + wait += self->m_bitCycles; + + // Store level and cycle in the buffer unless we have an overflow + // cycle's LSB is repurposed for the level bit + if (digitalRead(self->m_rxPin) != level) + { + if (!self->m_isrBuffer->push(((start + wait) | 1U) ^ level)) self->m_isrOverflow.store(true); + level = !level; + } + } +} + +void SoftwareSerial::onReceive(Delegate handler) { + receiveHandler = handler; +} + +void SoftwareSerial::perform_work() { + if (!m_rxValid) { return; } + rxBits(); + if (receiveHandler) { + int avail = m_buffer->available(); + if (avail) { receiveHandler(avail); } + } +} diff --git a/lib/EspSoftwareSerial/src/SoftwareSerial.h b/lib/EspSoftwareSerial/src/SoftwareSerial.h new file mode 100644 index 00000000..371e3731 --- /dev/null +++ b/lib/EspSoftwareSerial/src/SoftwareSerial.h @@ -0,0 +1,255 @@ +/* +SoftwareSerial.h + +SoftwareSerial.cpp - Implementation of the Arduino software serial for ESP8266/ESP32. +Copyright (c) 2015-2016 Peter Lerup. All rights reserved. +Copyright (c) 2018-2019 Dirk O. Kaar. All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef __SoftwareSerial_h +#define __SoftwareSerial_h + +#include "circular_queue/circular_queue.h" +#include + +enum SoftwareSerialParity : uint8_t { + SWSERIAL_PARITY_NONE = 000, + SWSERIAL_PARITY_EVEN = 020, + SWSERIAL_PARITY_ODD = 030, + SWSERIAL_PARITY_MARK = 040, + SWSERIAL_PARITY_SPACE = 070, +}; + +enum SoftwareSerialConfig { + SWSERIAL_5N1 = SWSERIAL_PARITY_NONE, + SWSERIAL_6N1, + SWSERIAL_7N1, + SWSERIAL_8N1, + SWSERIAL_5E1 = SWSERIAL_PARITY_EVEN, + SWSERIAL_6E1, + SWSERIAL_7E1, + SWSERIAL_8E1, + SWSERIAL_5O1 = SWSERIAL_PARITY_ODD, + SWSERIAL_6O1, + SWSERIAL_7O1, + SWSERIAL_8O1, + SWSERIAL_5M1 = SWSERIAL_PARITY_MARK, + SWSERIAL_6M1, + SWSERIAL_7M1, + SWSERIAL_8M1, + SWSERIAL_5S1 = SWSERIAL_PARITY_SPACE, + SWSERIAL_6S1, + SWSERIAL_7S1, + SWSERIAL_8S1, + SWSERIAL_5N2 = 0200 | SWSERIAL_PARITY_NONE, + SWSERIAL_6N2, + SWSERIAL_7N2, + SWSERIAL_8N2, + SWSERIAL_5E2 = 0200 | SWSERIAL_PARITY_EVEN, + SWSERIAL_6E2, + SWSERIAL_7E2, + SWSERIAL_8E2, + SWSERIAL_5O2 = 0200 | SWSERIAL_PARITY_ODD, + SWSERIAL_6O2, + SWSERIAL_7O2, + SWSERIAL_8O2, + SWSERIAL_5M2 = 0200 | SWSERIAL_PARITY_MARK, + SWSERIAL_6M2, + SWSERIAL_7M2, + SWSERIAL_8M2, + SWSERIAL_5S2 = 0200 | SWSERIAL_PARITY_SPACE, + SWSERIAL_6S2, + SWSERIAL_7S2, + SWSERIAL_8S2, +}; + +/// This class is compatible with the corresponding AVR one, however, +/// the constructor takes no arguments, for compatibility with the +/// HardwareSerial class. +/// Instead, the begin() function handles pin assignments and logic inversion. +/// It also has optional input buffer capacity arguments for byte buffer and ISR bit buffer. +/// Bitrates up to at least 115200 can be used. +class SoftwareSerial : public Stream { +public: + SoftwareSerial(); + /// Ctor to set defaults for pins. + /// @param rxPin the GPIO pin used for RX + /// @param txPin -1 for onewire protocol, GPIO pin used for twowire TX + SoftwareSerial(int8_t rxPin, int8_t txPin = -1, bool invert = false); + SoftwareSerial(const SoftwareSerial&) = delete; + SoftwareSerial& operator= (const SoftwareSerial&) = delete; + virtual ~SoftwareSerial(); + /// Configure the SoftwareSerial object for use. + /// @param baud the TX/RX bitrate + /// @param config sets databits, parity, and stop bit count + /// @param rxPin -1 or default: either no RX pin, or keeps the rxPin set in the ctor + /// @param txPin -1 or default: either no TX pin (onewire), or keeps the txPin set in the ctor + /// @param invert true: uses invert line level logic + /// @param bufCapacity the capacity for the received bytes buffer + /// @param isrBufCapacity 0: derived from bufCapacity. The capacity of the internal asynchronous + /// bit receive buffer, a suggested size is bufCapacity times the sum of + /// start, data, parity and stop bit count. + void begin(uint32_t baud, SoftwareSerialConfig config, + int8_t rxPin, int8_t txPin, bool invert, + int bufCapacity = 64, int isrBufCapacity = 0); + void begin(uint32_t baud, SoftwareSerialConfig config, + int8_t rxPin, int8_t txPin) { + begin(baud, config, rxPin, txPin, m_invert); + } + void begin(uint32_t baud, SoftwareSerialConfig config, + int8_t rxPin) { + begin(baud, config, rxPin, m_txPin, m_invert); + } + void begin(uint32_t baud, SoftwareSerialConfig config = SWSERIAL_8N1) { + begin(baud, config, m_rxPin, m_txPin, m_invert); + } + + uint32_t baudRate(); + /// Transmit control pin. + void setTransmitEnablePin(int8_t txEnablePin); + /// Enable or disable interrupts during tx. + void enableIntTx(bool on); + + bool overflow(); + + int available() override; + int availableForWrite() { + if (!m_txValid) return 0; + return 1; + } + int peek() override; + int read() override; + /// @returns The verbatim parity bit associated with the last read() or peek() call + bool readParity() + { + return m_lastReadParity; + } + /// @returns The calculated bit for even parity of the parameter byte + static bool parityEven(uint8_t byte) { + byte ^= byte >> 4; + byte &= 0xf; + return (0x6996 >> byte) & 1; + } + /// @returns The calculated bit for odd parity of the parameter byte + static bool parityOdd(uint8_t byte) { + byte ^= byte >> 4; + byte &= 0xf; + return (0x9669 >> byte) & 1; + } + /// The read(buffer, size) functions are non-blocking, the same as readBytes but without timeout + size_t read(uint8_t* buffer, size_t size); + /// The read(buffer, size) functions are non-blocking, the same as readBytes but without timeout + size_t read(char* buffer, size_t size) { + return read(reinterpret_cast(buffer), size); + } + /// @returns The number of bytes read into buffer, up to size. Times out if the limit set through + /// Stream::setTimeout() is reached. + size_t readBytes(uint8_t* buffer, size_t size) override; + /// @returns The number of bytes read into buffer, up to size. Times out if the limit set through + /// Stream::setTimeout() is reached. + size_t readBytes(char* buffer, size_t size) override { + return readBytes(reinterpret_cast(buffer), size); + } + void flush() override; + size_t write(uint8_t byte) override; + size_t write(uint8_t byte, SoftwareSerialParity parity); + size_t write(const uint8_t* buffer, size_t size) override; + size_t write(const char* buffer, size_t size) { + return write(reinterpret_cast(buffer), size); + } + size_t write(const uint8_t* buffer, size_t size, SoftwareSerialParity parity); + size_t write(const char* buffer, size_t size, SoftwareSerialParity parity) { + return write(reinterpret_cast(buffer), size, parity); + } + operator bool() const { return m_rxValid || m_txValid; } + + /// Disable or enable interrupts on the rx pin. + void enableRx(bool on); + /// One wire control. + void enableTx(bool on); + + // AVR compatibility methods. + bool listen() { enableRx(true); return true; } + void end(); + bool isListening() { return m_rxEnabled; } + bool stopListening() { enableRx(false); return true; } + + /// Set an event handler for received data. + void onReceive(Delegate handler); + + /// Run the internal processing and event engine. Can be iteratively called + /// from loop, or otherwise scheduled. + void perform_work(); + + using Print::write; + +private: + // If sync is false, it's legal to exceed the deadline, for instance, + // by enabling interrupts. + void preciseDelay(bool sync); + // If withStopBit is set, either cycle contains a stop bit. + // If dutyCycle == 0, the level is not forced to HIGH. + // If offCycle == 0, the level remains unchanged from dutyCycle. + void writePeriod( + uint32_t dutyCycle, uint32_t offCycle, bool withStopBit); + bool isValidGPIOpin(int8_t pin); + /* check m_rxValid that calling is safe */ + void rxBits(); + void rxBits(const uint32_t& isrCycle); + + static void rxBitISR(SoftwareSerial* self); + static void rxBitSyncISR(SoftwareSerial* self); + + // Member variables + int8_t m_rxPin = -1; + int8_t m_txPin = -1; + int8_t m_txEnablePin = -1; + uint8_t m_dataBits; + bool m_oneWire; + bool m_rxValid = false; + bool m_rxEnabled = false; + bool m_txValid = false; + bool m_txEnableValid = false; + bool m_invert; + /// PDU bits include data, parity and stop bits; the start bit is not counted. + uint8_t m_pduBits; + bool m_intTxEnabled; + SoftwareSerialParity m_parityMode; + uint8_t m_stopBits; + bool m_lastReadParity; + bool m_overflow = false; + uint32_t m_bitCycles; + uint8_t m_parityInPos; + uint8_t m_parityOutPos; + int8_t m_rxCurBit; // 0 thru (m_pduBits - m_stopBits - 1): data/parity bits. -1: start bit. (m_pduBits - 1): stop bit. + uint8_t m_rxCurByte = 0; + std::unique_ptr > m_buffer; + std::unique_ptr > m_parityBuffer; + uint32_t m_periodStart; + uint32_t m_periodDuration; + uint32_t m_savedPS = 0; + // the ISR stores the relative bit times in the buffer. The inversion corrected level is used as sign bit (2's complement): + // 1 = positive including 0, 0 = negative. + std::unique_ptr > m_isrBuffer; + std::atomic m_isrOverflow; + uint32_t m_isrLastCycle; + bool m_rxCurParity = false; + Delegate receiveHandler; +}; + +#endif // __SoftwareSerial_h diff --git a/lib/EspSoftwareSerial/src/circular_queue/Delegate.h b/lib/EspSoftwareSerial/src/circular_queue/Delegate.h new file mode 100644 index 00000000..bd19c66e --- /dev/null +++ b/lib/EspSoftwareSerial/src/circular_queue/Delegate.h @@ -0,0 +1,1786 @@ +/* +Delegate.h - An efficient interchangeable C function ptr and C++ std::function delegate +Copyright (c) 2019 Dirk O. Kaar. All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __Delegate_h +#define __Delegate_h + +#if defined(ESP8266) +#include +#elif defined(ESP32) +#include +#else +#define ICACHE_RAM_ATTR +#define IRAM_ATTR +#endif + +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) +#include +#include +#else +#include "circular_queue/ghostl.h" +#endif + +namespace detail +{ + template + static R IRAM_ATTR vPtrToFunPtrExec(void* fn, P... args) + { + using target_type = R(P...); + return reinterpret_cast(fn)(std::forward(args...)); + } + +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + template + class DelegatePImpl { + public: + using target_type = R(P...); + protected: + using FunPtr = target_type*; + using FunAPtr = R(*)(A, P...); + using FunVPPtr = R(*)(void*, P...); + using FunctionType = std::function; + public: + DelegatePImpl() + { + kind = FP; + fn = nullptr; + } + + DelegatePImpl(std::nullptr_t) + { + kind = FP; + fn = nullptr; + } + + ~DelegatePImpl() + { + if (FUNC == kind) + functional.~FunctionType(); + else if (FPA == kind) + obj.~A(); + } + + DelegatePImpl(const DelegatePImpl& del) + { + kind = del.kind; + if (FUNC == del.kind) + { + new (&functional) FunctionType(del.functional); + } + else if (FPA == del.kind) + { + fnA = del.fnA; + new (&obj) A(del.obj); + } + else + { + fn = del.fn; + } + } + + DelegatePImpl(DelegatePImpl&& del) + { + kind = del.kind; + if (FUNC == del.kind) + { + new (&functional) FunctionType(std::move(del.functional)); + } + else if (FPA == del.kind) + { + fnA = del.fnA; + new (&obj) A(std::move(del.obj)); + } + else + { + fn = del.fn; + } + } + + DelegatePImpl(FunAPtr fnA, const A& obj) + { + kind = FPA; + DelegatePImpl::fnA = fnA; + new (&this->obj) A(obj); + } + + DelegatePImpl(FunAPtr fnA, A&& obj) + { + kind = FPA; + DelegatePImpl::fnA = fnA; + new (&this->obj) A(std::move(obj)); + } + + DelegatePImpl(FunPtr fn) + { + kind = FP; + DelegatePImpl::fn = fn; + } + + template DelegatePImpl(F functional) + { + kind = FUNC; + new (&this->functional) FunctionType(std::forward(functional)); + } + + DelegatePImpl& operator=(const DelegatePImpl& del) + { + if (this == &del) return *this; + if (kind != del.kind) + { + if (FUNC == kind) + { + functional.~FunctionType(); + } + else if (FPA == kind) + { + obj.~A(); + } + if (FUNC == del.kind) + { + new (&this->functional) FunctionType(); + } + else if (FPA == del.kind) + { + new (&obj) A; + } + kind = del.kind; + } + if (FUNC == del.kind) + { + functional = del.functional; + } + else if (FPA == del.kind) + { + fnA = del.fnA; + obj = del.obj; + } + else + { + fn = del.fn; + } + return *this; + } + + DelegatePImpl& operator=(DelegatePImpl&& del) + { + if (this == &del) return *this; + if (kind != del.kind) + { + if (FUNC == kind) + { + functional.~FunctionType(); + } + else if (FPA == kind) + { + obj.~A(); + } + if (FUNC == del.kind) + { + new (&this->functional) FunctionType(); + } + else if (FPA == del.kind) + { + new (&obj) A; + } + kind = del.kind; + } + if (FUNC == del.kind) + { + functional = std::move(del.functional); + } + else if (FPA == del.kind) + { + fnA = del.fnA; + obj = std::move(del.obj); + } + else + { + fn = del.fn; + } + return *this; + } + + DelegatePImpl& operator=(FunPtr fn) + { + if (FUNC == kind) + { + functional.~FunctionType(); + } + else if (FPA == kind) + { + obj.~A(); + } + kind = FP; + this->fn = fn; + return *this; + } + + DelegatePImpl& IRAM_ATTR operator=(std::nullptr_t) + { + if (FUNC == kind) + { + functional.~FunctionType(); + } + else if (FPA == kind) + { + obj.~A(); + } + kind = FP; + fn = nullptr; + return *this; + } + + operator bool() const + { + if (FP == kind) + { + return fn; + } + else if (FPA == kind) + { + return fnA; + } + else + { + return functional ? true : false; + } + } + + static R IRAM_ATTR vPtrToFunAPtrExec(void* self, P... args) + { + return static_cast(self)->fnA( + static_cast(self)->obj, + std::forward(args...)); + }; + + operator FunVPPtr() const + { + if (FP == kind) + { + return vPtrToFunPtrExec; + } + else if (FPA == kind) + { + return vPtrToFunAPtrExec; + } + else + { + return [](void* self, P... args) -> R + { + return static_cast(self)->functional(std::forward(args...)); + }; + } + } + + void* arg() const + { + if (FP == kind) + { + return reinterpret_cast(fn); + } + else + { + return const_cast(this); + } + } + + operator FunctionType() const + { + if (FP == kind) + { + return fn; + } + else if (FPA == kind) + { + return [this](P... args) { return fnA(obj, std::forward(args...)); }; + } + else + { + return functional; + } + } + + R IRAM_ATTR operator()(P... args) const + { + if (FP == kind) + { + return fn(std::forward(args...)); + } + else if (FPA == kind) + { + return fnA(obj, std::forward(args...)); + } + else + { + return functional(std::forward(args...)); + } + } + + protected: + enum { FUNC, FP, FPA } kind; + union { + FunctionType functional; + FunPtr fn; + struct { + FunAPtr fnA; + A obj; + }; + }; + }; +#else + template + class DelegatePImpl { + public: + using target_type = R(P...); + protected: + using FunPtr = target_type*; + using FunAPtr = R(*)(A, P...); + using FunVPPtr = R(*)(void*, P...); + public: + DelegatePImpl() + { + kind = FP; + fn = nullptr; + } + + DelegatePImpl(std::nullptr_t) + { + kind = FP; + fn = nullptr; + } + + DelegatePImpl(const DelegatePImpl& del) + { + kind = del.kind; + if (FPA == del.kind) + { + fnA = del.fnA; + obj = del.obj; + } + else + { + fn = del.fn; + } + } + + DelegatePImpl(DelegatePImpl&& del) + { + kind = del.kind; + if (FPA == del.kind) + { + fnA = del.fnA; + obj = std::move(del.obj); + } + else + { + fn = del.fn; + } + } + + DelegatePImpl(FunAPtr fnA, const A& obj) + { + kind = FPA; + DelegatePImpl::fnA = fnA; + this->obj = obj; + } + + DelegatePImpl(FunAPtr fnA, A&& obj) + { + kind = FPA; + DelegatePImpl::fnA = fnA; + this->obj = std::move(obj); + } + + DelegatePImpl(FunPtr fn) + { + kind = FP; + DelegatePImpl::fn = fn; + } + + template DelegatePImpl(F fn) + { + kind = FP; + DelegatePImpl::fn = std::forward(fn); + } + + DelegatePImpl& operator=(const DelegatePImpl& del) + { + if (this == &del) return *this; + if (kind != del.kind) + { + if (FPA == kind) + { + obj = {}; + } + kind = del.kind; + } + if (FPA == del.kind) + { + fnA = del.fnA; + obj = del.obj; + } + else + { + fn = del.fn; + } + return *this; + } + + DelegatePImpl& operator=(DelegatePImpl&& del) + { + if (this == &del) return *this; + if (kind != del.kind) + { + if (FPA == kind) + { + obj = {}; + } + kind = del.kind; + } + if (FPA == del.kind) + { + fnA = del.fnA; + obj = std::move(del.obj); + } + else + { + fn = del.fn; + } + return *this; + } + + DelegatePImpl& operator=(FunPtr fn) + { + if (FPA == kind) + { + obj = {}; + } + kind = FP; + this->fn = fn; + return *this; + } + + DelegatePImpl& IRAM_ATTR operator=(std::nullptr_t) + { + if (FPA == kind) + { + obj = {}; + } + kind = FP; + fn = nullptr; + return *this; + } + + operator bool() const + { + if (FP == kind) + { + return fn; + } + else + { + return fnA; + } + } + + static R IRAM_ATTR vPtrToFunAPtrExec(void* self, P... args) + { + return static_cast(self)->fnA( + static_cast(self)->obj, + std::forward(args...)); + }; + + operator FunVPPtr() const + { + if (FP == kind) + { + return vPtrToFunPtrExec; + } + else + { + return vPtrToFunAPtrExec; + } + } + + void* arg() const + { + if (FP == kind) + { + return reinterpret_cast(fn); + } + else + { + return const_cast(this); + } + } + + R IRAM_ATTR operator()(P... args) const + { + if (FP == kind) + { + return fn(std::forward(args...)); + } + else + { + return fnA(obj, std::forward(args...)); + } + } + + protected: + enum { FP, FPA } kind; + union { + FunPtr fn; + FunAPtr fnA; + }; + A obj; + }; +#endif + +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + template + class DelegatePImpl { + public: + using target_type = R(P...); + protected: + using FunPtr = target_type*; + using FunctionType = std::function; + using FunVPPtr = R(*)(void*, P...); + public: + DelegatePImpl() + { + kind = FP; + fn = nullptr; + } + + DelegatePImpl(std::nullptr_t) + { + kind = FP; + fn = nullptr; + } + + ~DelegatePImpl() + { + if (FUNC == kind) + functional.~FunctionType(); + } + + DelegatePImpl(const DelegatePImpl& del) + { + kind = del.kind; + if (FUNC == del.kind) + { + new (&functional) FunctionType(del.functional); + } + else + { + fn = del.fn; + } + } + + DelegatePImpl(DelegatePImpl&& del) + { + kind = del.kind; + if (FUNC == del.kind) + { + new (&functional) FunctionType(std::move(del.functional)); + } + else + { + fn = del.fn; + } + } + + DelegatePImpl(FunPtr fn) + { + kind = FP; + DelegatePImpl::fn = fn; + } + + template DelegatePImpl(F functional) + { + kind = FUNC; + new (&this->functional) FunctionType(std::forward(functional)); + } + + DelegatePImpl& operator=(const DelegatePImpl& del) + { + if (this == &del) return *this; + if (FUNC == kind && FUNC != del.kind) + { + functional.~FunctionType(); + } + else if (FUNC != kind && FUNC == del.kind) + { + new (&this->functional) FunctionType(); + } + kind = del.kind; + if (FUNC == del.kind) + { + functional = del.functional; + } + else + { + fn = del.fn; + } + return *this; + } + + DelegatePImpl& operator=(DelegatePImpl&& del) + { + if (this == &del) return *this; + if (FUNC == kind && FUNC != del.kind) + { + functional.~FunctionType(); + } + else if (FUNC != kind && FUNC == del.kind) + { + new (&this->functional) FunctionType(); + } + kind = del.kind; + if (FUNC == del.kind) + { + functional = std::move(del.functional); + } + else + { + fn = del.fn; + } + return *this; + } + + DelegatePImpl& operator=(FunPtr fn) + { + if (FUNC == kind) + { + functional.~FunctionType(); + kind = FP; + } + DelegatePImpl::fn = fn; + return *this; + } + + DelegatePImpl& IRAM_ATTR operator=(std::nullptr_t) + { + if (FUNC == kind) + { + functional.~FunctionType(); + } + kind = FP; + fn = nullptr; + return *this; + } + + operator bool() const + { + if (FP == kind) + { + return fn; + } + else + { + return functional ? true : false; + } + } + + operator FunVPPtr() const + { + if (FP == kind) + { + return vPtrToFunPtrExec; + } + else + { + return [](void* self, P... args) -> R + { + return static_cast(self)->functional(std::forward(args...)); + }; + } + } + + void* arg() const + { + if (FP == kind) + { + return reinterpret_cast(fn); + } + else + { + return const_cast(this); + } + } + + operator FunctionType() const + { + if (FP == kind) + { + return fn; + } + else + { + return functional; + } + } + + R IRAM_ATTR operator()(P... args) const + { + if (FP == kind) + { + return fn(std::forward(args...)); + } + else + { + return functional(std::forward(args...)); + } + } + + protected: + enum { FUNC, FP } kind; + union { + FunctionType functional; + FunPtr fn; + }; + }; +#else + template + class DelegatePImpl { + public: + using target_type = R(P...); + protected: + using FunPtr = target_type*; + using FunVPPtr = R(*)(void*, P...); + public: + DelegatePImpl() + { + fn = nullptr; + } + + DelegatePImpl(std::nullptr_t) + { + fn = nullptr; + } + + DelegatePImpl(const DelegatePImpl& del) + { + fn = del.fn; + } + + DelegatePImpl(DelegatePImpl&& del) + { + fn = std::move(del.fn); + } + + DelegatePImpl(FunPtr fn) + { + DelegatePImpl::fn = fn; + } + + template DelegatePImpl(F fn) + { + DelegatePImpl::fn = std::forward(fn); + } + + DelegatePImpl& operator=(const DelegatePImpl& del) + { + if (this == &del) return *this; + fn = del.fn; + return *this; + } + + DelegatePImpl& operator=(DelegatePImpl&& del) + { + if (this == &del) return *this; + fn = std::move(del.fn); + return *this; + } + + DelegatePImpl& operator=(FunPtr fn) + { + DelegatePImpl::fn = fn; + return *this; + } + + DelegatePImpl& IRAM_ATTR operator=(std::nullptr_t) + { + fn = nullptr; + return *this; + } + + operator bool() const + { + return fn; + } + + operator FunVPPtr() const + { + return vPtrToFunPtrExec; + } + + void* arg() const + { + return reinterpret_cast(fn); + } + + R IRAM_ATTR operator()(P... args) const + { + return fn(std::forward(args...)); + } + + protected: + FunPtr fn; + }; +#endif + +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + template + class DelegateImpl { + public: + using target_type = R(); + protected: + using FunPtr = target_type*; + using FunAPtr = R(*)(A); + using FunctionType = std::function; + using FunVPPtr = R(*)(void*); + public: + DelegateImpl() + { + kind = FP; + fn = nullptr; + } + + DelegateImpl(std::nullptr_t) + { + kind = FP; + fn = nullptr; + } + + ~DelegateImpl() + { + if (FUNC == kind) + functional.~FunctionType(); + else if (FPA == kind) + obj.~A(); + } + + DelegateImpl(const DelegateImpl& del) + { + kind = del.kind; + if (FUNC == del.kind) + { + new (&functional) FunctionType(del.functional); + } + else if (FPA == del.kind) + { + fnA = del.fnA; + new (&obj) A(del.obj); + } + else + { + fn = del.fn; + } + } + + DelegateImpl(DelegateImpl&& del) + { + kind = del.kind; + if (FUNC == del.kind) + { + new (&functional) FunctionType(std::move(del.functional)); + } + else if (FPA == del.kind) + { + fnA = del.fnA; + new (&obj) A(std::move(del.obj)); + } + else + { + fn = del.fn; + } + } + + DelegateImpl(FunAPtr fnA, const A& obj) + { + kind = FPA; + DelegateImpl::fnA = fnA; + new (&this->obj) A(obj); + } + + DelegateImpl(FunAPtr fnA, A&& obj) + { + kind = FPA; + DelegateImpl::fnA = fnA; + new (&this->obj) A(std::move(obj)); + } + + DelegateImpl(FunPtr fn) + { + kind = FP; + DelegateImpl::fn = fn; + } + + template DelegateImpl(F functional) + { + kind = FUNC; + new (&this->functional) FunctionType(std::forward(functional)); + } + + DelegateImpl& operator=(const DelegateImpl& del) + { + if (this == &del) return *this; + if (kind != del.kind) + { + if (FUNC == kind) + { + functional.~FunctionType(); + } + else if (FPA == kind) + { + obj.~A(); + } + if (FUNC == del.kind) + { + new (&this->functional) FunctionType(); + } + else if (FPA == del.kind) + { + new (&obj) A; + } + kind = del.kind; + } + if (FUNC == del.kind) + { + functional = del.functional; + } + else if (FPA == del.kind) + { + fnA = del.fnA; + obj = del.obj; + } + else + { + fn = del.fn; + } + return *this; + } + + DelegateImpl& operator=(DelegateImpl&& del) + { + if (this == &del) return *this; + if (kind != del.kind) + { + if (FUNC == kind) + { + functional.~FunctionType(); + } + else if (FPA == kind) + { + obj.~A(); + } + if (FUNC == del.kind) + { + new (&this->functional) FunctionType(); + } + else if (FPA == del.kind) + { + new (&obj) A; + } + kind = del.kind; + } + if (FUNC == del.kind) + { + functional = std::move(del.functional); + } + else if (FPA == del.kind) + { + fnA = del.fnA; + obj = std::move(del.obj); + } + else + { + fn = del.fn; + } + return *this; + } + + DelegateImpl& operator=(FunPtr fn) + { + if (FUNC == kind) + { + functional.~FunctionType(); + } + else if (FPA == kind) + { + obj.~A(); + } + kind = FP; + this->fn = fn; + return *this; + } + + DelegateImpl& IRAM_ATTR operator=(std::nullptr_t) + { + if (FUNC == kind) + { + functional.~FunctionType(); + } + else if (FPA == kind) + { + obj.~A(); + } + kind = FP; + fn = nullptr; + return *this; + } + + operator bool() const + { + if (FP == kind) + { + return fn; + } + else if (FPA == kind) + { + return fnA; + } + else + { + return functional ? true : false; + } + } + + static R IRAM_ATTR vPtrToFunAPtrExec(void* self) + { + return static_cast(self)->fnA( + static_cast(self)->obj); + }; + + operator FunVPPtr() const + { + if (FP == kind) + { + return reinterpret_cast(fn); + } + else if (FPA == kind) + { + return vPtrToFunAPtrExec; + } + else + { + return [](void* self) -> R + { + return static_cast(self)->functional(); + }; + } + } + + void* arg() const + { + if (FP == kind) + { + return nullptr; + } + else + { + return const_cast(this); + } + } + + operator FunctionType() const + { + if (FP == kind) + { + return fn; + } + else if (FPA == kind) + { + return [this]() { return fnA(obj); }; + } + else + { + return functional; + } + } + + R IRAM_ATTR operator()() const + { + if (FP == kind) + { + return fn(); + } + else if (FPA == kind) + { + return fnA(obj); + } + else + { + return functional(); + } + } + + protected: + enum { FUNC, FP, FPA } kind; + union { + FunctionType functional; + FunPtr fn; + struct { + FunAPtr fnA; + A obj; + }; + }; + }; +#else + template + class DelegateImpl { + public: + using target_type = R(); + protected: + using FunPtr = target_type*; + using FunAPtr = R(*)(A); + using FunVPPtr = R(*)(void*); + public: + DelegateImpl() + { + kind = FP; + fn = nullptr; + } + + DelegateImpl(std::nullptr_t) + { + kind = FP; + fn = nullptr; + } + + DelegateImpl(const DelegateImpl& del) + { + kind = del.kind; + if (FPA == del.kind) + { + fnA = del.fnA; + obj = del.obj; + } + else + { + fn = del.fn; + } + } + + DelegateImpl(DelegateImpl&& del) + { + kind = del.kind; + if (FPA == del.kind) + { + fnA = del.fnA; + obj = std::move(del.obj); + } + else + { + fn = del.fn; + } + } + + DelegateImpl(FunAPtr fnA, const A& obj) + { + kind = FPA; + DelegateImpl::fnA = fnA; + this->obj = obj; + } + + DelegateImpl(FunAPtr fnA, A&& obj) + { + kind = FPA; + DelegateImpl::fnA = fnA; + this->obj = std::move(obj); + } + + DelegateImpl(FunPtr fn) + { + kind = FP; + DelegateImpl::fn = fn; + } + + template DelegateImpl(F fn) + { + kind = FP; + DelegateImpl::fn = std::forward(fn); + } + + DelegateImpl& operator=(const DelegateImpl& del) + { + if (this == &del) return *this; + if (kind != del.kind) + { + if (FPA == kind) + { + obj = {}; + } + kind = del.kind; + } + if (FPA == del.kind) + { + fnA = del.fnA; + obj = del.obj; + } + else + { + fn = del.fn; + } + return *this; + } + + DelegateImpl& operator=(DelegateImpl&& del) + { + if (this == &del) return *this; + if (kind != del.kind) + { + if (FPA == kind) + { + obj = {}; + } + kind = del.kind; + } + if (FPA == del.kind) + { + fnA = del.fnA; + obj = std::move(del.obj); + } + else + { + fn = del.fn; + } + return *this; + } + + DelegateImpl& operator=(FunPtr fn) + { + if (FPA == kind) + { + obj = {}; + } + kind = FP; + this->fn = fn; + return *this; + } + + DelegateImpl& IRAM_ATTR operator=(std::nullptr_t) + { + if (FPA == kind) + { + obj = {}; + } + kind = FP; + fn = nullptr; + return *this; + } + + operator bool() const + { + if (FP == kind) + { + return fn; + } + else + { + return fnA; + } + } + + static R IRAM_ATTR vPtrToFunAPtrExec(void* self) + { + return static_cast(self)->fnA( + static_cast(self)->obj); + }; + + operator FunVPPtr() const + { + if (FP == kind) + { + return reinterpret_cast(fn); + } + else + { + return vPtrToFunAPtrExec; + } + } + + void* arg() const + { + if (FP == kind) + { + return nullptr; + } + else + { + return const_cast(this); + } + } + + R IRAM_ATTR operator()() const + { + if (FP == kind) + { + return fn(); + } + else + { + return fnA(obj); + } + } + + protected: + enum { FP, FPA } kind; + union { + FunPtr fn; + FunAPtr fnA; + }; + A obj; + }; +#endif + +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + template + class DelegateImpl { + public: + using target_type = R(); + protected: + using FunPtr = target_type*; + using FunctionType = std::function; + using FunVPPtr = R(*)(void*); + public: + DelegateImpl() + { + kind = FP; + fn = nullptr; + } + + DelegateImpl(std::nullptr_t) + { + kind = FP; + fn = nullptr; + } + + ~DelegateImpl() + { + if (FUNC == kind) + functional.~FunctionType(); + } + + DelegateImpl(const DelegateImpl& del) + { + kind = del.kind; + if (FUNC == del.kind) + { + new (&functional) FunctionType(del.functional); + } + else + { + fn = del.fn; + } + } + + DelegateImpl(DelegateImpl&& del) + { + kind = del.kind; + if (FUNC == del.kind) + { + new (&functional) FunctionType(std::move(del.functional)); + } + else + { + fn = del.fn; + } + } + + DelegateImpl(FunPtr fn) + { + kind = FP; + DelegateImpl::fn = fn; + } + + template DelegateImpl(F functional) + { + kind = FUNC; + new (&this->functional) FunctionType(std::forward(functional)); + } + + DelegateImpl& operator=(const DelegateImpl& del) + { + if (this == &del) return *this; + if (FUNC == kind && FUNC != del.kind) + { + functional.~FunctionType(); + } + else if (FUNC != kind && FUNC == del.kind) + { + new (&this->functional) FunctionType(); + } + kind = del.kind; + if (FUNC == del.kind) + { + functional = del.functional; + } + else + { + fn = del.fn; + } + return *this; + } + + DelegateImpl& operator=(DelegateImpl&& del) + { + if (this == &del) return *this; + if (FUNC == kind && FUNC != del.kind) + { + functional.~FunctionType(); + } + else if (FUNC != kind && FUNC == del.kind) + { + new (&this->functional) FunctionType(); + } + kind = del.kind; + if (FUNC == del.kind) + { + functional = std::move(del.functional); + } + else + { + fn = del.fn; + } + return *this; + } + + DelegateImpl& operator=(FunPtr fn) + { + if (FUNC == kind) + { + functional.~FunctionType(); + kind = FP; + } + DelegateImpl::fn = fn; + return *this; + } + + DelegateImpl& IRAM_ATTR operator=(std::nullptr_t) + { + if (FUNC == kind) + { + functional.~FunctionType(); + } + kind = FP; + fn = nullptr; + return *this; + } + + operator bool() const + { + if (FP == kind) + { + return fn; + } + else + { + return functional ? true : false; + } + } + + operator FunVPPtr() const + { + if (FP == kind) + { + return reinterpret_cast(fn); + } + else + { + return [](void* self) -> R + { + return static_cast(self)->functional(); + }; + } + } + + void* arg() const + { + if (FP == kind) + { + return nullptr; + } + else + { + return const_cast(this); + } + } + + operator FunctionType() const + { + if (FP == kind) + { + return fn; + } + else + { + return functional; + } + } + + R IRAM_ATTR operator()() const + { + if (FP == kind) + { + return fn(); + } + else + { + return functional(); + } + } + + protected: + enum { FUNC, FP } kind; + union { + FunctionType functional; + FunPtr fn; + }; + }; +#else + template + class DelegateImpl { + public: + using target_type = R(); + protected: + using FunPtr = target_type*; + using FunVPPtr = R(*)(void*); + public: + DelegateImpl() + { + fn = nullptr; + } + + DelegateImpl(std::nullptr_t) + { + fn = nullptr; + } + + DelegateImpl(const DelegateImpl& del) + { + fn = del.fn; + } + + DelegateImpl(DelegateImpl&& del) + { + fn = std::move(del.fn); + } + + DelegateImpl(FunPtr fn) + { + DelegateImpl::fn = fn; + } + + template DelegateImpl(F fn) + { + DelegateImpl::fn = std::forward(fn); + } + + DelegateImpl& operator=(const DelegateImpl& del) + { + if (this == &del) return *this; + fn = del.fn; + return *this; + } + + DelegateImpl& operator=(DelegateImpl&& del) + { + if (this == &del) return *this; + fn = std::move(del.fn); + return *this; + } + + DelegateImpl& operator=(FunPtr fn) + { + DelegateImpl::fn = fn; + return *this; + } + + DelegateImpl& IRAM_ATTR operator=(std::nullptr_t) + { + fn = nullptr; + return *this; + } + + operator bool() const + { + return fn; + } + + operator FunVPPtr() const + { + return reinterpret_cast(fn); + } + + void* arg() const + { + return nullptr; + } + + R IRAM_ATTR operator()() const + { + return fn(); + } + + protected: + FunPtr fn; + }; +#endif + + template + class Delegate : private detail::DelegatePImpl + { + private: + using typename detail::DelegatePImpl::FunVPPtr; +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + using typename detail::DelegatePImpl::FunctionType; +#endif + public: + using detail::DelegatePImpl::target_type; + using detail::DelegatePImpl::DelegatePImpl; + using detail::DelegatePImpl::operator=; + using detail::DelegatePImpl::operator bool; + using detail::DelegatePImpl::operator FunVPPtr; + using detail::DelegatePImpl::arg; +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + using detail::DelegatePImpl::operator FunctionType; +#endif + using detail::DelegatePImpl::operator(); + }; + + template + class Delegate : private detail::DelegatePImpl + { + private: + using typename detail::DelegatePImpl::FunVPPtr; +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + using typename detail::DelegatePImpl::FunctionType; +#endif + public: + using detail::DelegatePImpl::target_type; + using detail::DelegatePImpl::DelegatePImpl; + using detail::DelegatePImpl::operator=; + using detail::DelegatePImpl::operator bool; +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + using detail::DelegatePImpl::operator FunctionType; +#endif + using detail::DelegatePImpl::operator(); + operator FunVPPtr() const + { + if (detail::DelegatePImpl::FPA == detail::DelegatePImpl::kind) + { + return reinterpret_cast(detail::DelegatePImpl::fnA); + } + else + { + return detail::DelegatePImpl::operator FunVPPtr(); + } + } + void* arg() const + { + if (detail::DelegatePImpl::FPA == detail::DelegatePImpl::kind) + { + return detail::DelegatePImpl::obj; + } + else + { + return detail::DelegatePImpl::arg(); + } + } + }; + + template + class Delegate : private detail::DelegateImpl + { + private: + using typename detail::DelegateImpl::FunVPPtr; +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + using typename detail::DelegateImpl::FunctionType; +#endif + public: + using detail::DelegateImpl::target_type; + using detail::DelegateImpl::DelegateImpl; + using detail::DelegateImpl::operator=; + using detail::DelegateImpl::operator bool; + using detail::DelegateImpl::operator FunVPPtr; + using detail::DelegateImpl::arg; +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + using detail::DelegateImpl::operator FunctionType; +#endif + using detail::DelegateImpl::operator(); + }; + + template + class Delegate : private detail::DelegateImpl + { + private: + using typename detail::DelegateImpl::FunVPPtr; +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + using typename detail::DelegateImpl::FunctionType; +#endif + public: + using detail::DelegateImpl::target_type; + using detail::DelegateImpl::DelegateImpl; + using detail::DelegateImpl::operator=; + using detail::DelegateImpl::operator bool; +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + using detail::DelegateImpl::operator FunctionType; +#endif + using detail::DelegateImpl::operator(); + operator FunVPPtr() const + { + if (detail::DelegateImpl::FPA == detail::DelegateImpl::kind) + { + return reinterpret_cast(detail::DelegateImpl::fnA); + } + else + { + return detail::DelegateImpl::operator FunVPPtr(); + } + } + void* arg() const + { + if (detail::DelegateImpl::FPA == detail::DelegateImpl::kind) + { + return detail::DelegateImpl::obj; + } + else + { + return detail::DelegateImpl::arg(); + } + } + }; + +} + +template class Delegate; +template class Delegate : public detail::Delegate +{ +public: + using detail::Delegate::Delegate; +}; +template class Delegate : public detail::Delegate +{ +public: + using detail::Delegate::Delegate; +}; + +#endif // __Delegate_h diff --git a/lib/EspSoftwareSerial/src/circular_queue/MultiDelegate.h b/lib/EspSoftwareSerial/src/circular_queue/MultiDelegate.h new file mode 100644 index 00000000..1fd4188d --- /dev/null +++ b/lib/EspSoftwareSerial/src/circular_queue/MultiDelegate.h @@ -0,0 +1,503 @@ +/* +MultiDelegate.h - A queue or event multiplexer based on the efficient Delegate +class +Copyright (c) 2019 Dirk O. Kaar. All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __MULTIDELEGATE_H +#define __MULTIDELEGATE_H + +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) +#include +#else +#include "circular_queue/ghostl.h" +#endif + +#if defined(ESP8266) +#include +using esp8266::InterruptLock; +#elif defined(ARDUINO) +class InterruptLock { +public: + InterruptLock() { + noInterrupts(); + } + ~InterruptLock() { + interrupts(); + } +}; +#else +#include +#endif + +namespace detail +{ + namespace + { + template< typename Delegate, typename R, bool ISQUEUE = false, typename... P> + struct CallP + { + static R execute(Delegate& del, P... args) + { + return del(std::forward(args...)) ? !ISQUEUE : ISQUEUE; + } + }; + + template< typename Delegate, bool ISQUEUE, typename... P> + struct CallP + { + static bool execute(Delegate& del, P... args) + { + del(std::forward(args...)); + return !ISQUEUE; + } + }; + + template< typename Delegate, typename R, bool ISQUEUE = false> + struct Call + { + static R execute(Delegate& del) + { + return del() ? !ISQUEUE : ISQUEUE; + } + }; + + template< typename Delegate, bool ISQUEUE> + struct Call + { + static bool execute(Delegate& del) + { + del(); + return !ISQUEUE; + } + }; + }; + + template< typename Delegate, typename R = void, bool ISQUEUE = false, uint32_t QUEUE_CAPACITY = 32, typename... P> + class MultiDelegatePImpl + { + public: + MultiDelegatePImpl() = default; + ~MultiDelegatePImpl() + { + *this = nullptr; + } + + MultiDelegatePImpl(const MultiDelegatePImpl&) = delete; + MultiDelegatePImpl& operator=(const MultiDelegatePImpl&) = delete; + + MultiDelegatePImpl(MultiDelegatePImpl&& md) + { + first = md.first; + last = md.last; + unused = md.unused; + nodeCount = md.nodeCount; + md.first = nullptr; + md.last = nullptr; + md.unused = nullptr; + md.nodeCount = 0; + } + + MultiDelegatePImpl(const Delegate& del) + { + add(del); + } + + MultiDelegatePImpl(Delegate&& del) + { + add(std::move(del)); + } + + MultiDelegatePImpl& operator=(MultiDelegatePImpl&& md) + { + first = md.first; + last = md.last; + unused = md.unused; + nodeCount = md.nodeCount; + md.first = nullptr; + md.last = nullptr; + md.unused = nullptr; + md.nodeCount = 0; + return *this; + } + + MultiDelegatePImpl& operator=(std::nullptr_t) + { + if (last) + last->mNext = unused; + if (first) + unused = first; + while (unused) + { + auto to_delete = unused; + unused = unused->mNext; + delete(to_delete); + } + return *this; + } + + MultiDelegatePImpl& operator+=(const Delegate& del) + { + add(del); + return *this; + } + + MultiDelegatePImpl& operator+=(Delegate&& del) + { + add(std::move(del)); + return *this; + } + + protected: + struct Node_t + { + ~Node_t() + { + mDelegate = nullptr; // special overload in Delegate + } + Node_t* mNext = nullptr; + Delegate mDelegate; + }; + + Node_t* first = nullptr; + Node_t* last = nullptr; + Node_t* unused = nullptr; + uint32_t nodeCount = 0; + + // Returns a pointer to an unused Node_t, + // or if none are available allocates a new one, + // or nullptr if limit is reached + Node_t* IRAM_ATTR get_node_unsafe() + { + Node_t* result = nullptr; + // try to get an item from unused items list + if (unused) + { + result = unused; + unused = unused->mNext; + } + // if no unused items, and count not too high, allocate a new one + else if (nodeCount < QUEUE_CAPACITY) + { +#if defined(ESP8266) || defined(ESP32) + result = new (std::nothrow) Node_t; +#else + result = new Node_t; +#endif + if (result) + ++nodeCount; + } + return result; + } + + void recycle_node_unsafe(Node_t* node) + { + node->mDelegate = nullptr; // special overload in Delegate + node->mNext = unused; + unused = node; + } + +#ifndef ARDUINO + std::mutex mutex_unused; +#endif + public: + const Delegate* IRAM_ATTR add(const Delegate& del) + { + return add(Delegate(del)); + } + + const Delegate* IRAM_ATTR add(Delegate&& del) + { + if (!del) + return nullptr; + +#ifdef ARDUINO + InterruptLock lockAllInterruptsInThisScope; +#else + std::lock_guard lock(mutex_unused); +#endif + + Node_t* item = ISQUEUE ? get_node_unsafe() : +#if defined(ESP8266) || defined(ESP32) + new (std::nothrow) Node_t; +#else + new Node_t; +#endif + if (!item) + return nullptr; + + item->mDelegate = std::move(del); + item->mNext = nullptr; + + if (last) + last->mNext = item; + else + first = item; + last = item; + + return &item->mDelegate; + } + + bool remove(const Delegate* del) + { + auto current = first; + if (!current) + return false; + + Node_t* prev = nullptr; + do + { + if (del == ¤t->mDelegate) + { + // remove callback from stack +#ifdef ARDUINO + InterruptLock lockAllInterruptsInThisScope; +#else + std::lock_guard lock(mutex_unused); +#endif + + auto to_recycle = current; + + // removing rLast + if (last == current) + last = prev; + + current = current->mNext; + if (prev) + { + prev->mNext = current; + } + else + { + first = current; + } + + if (ISQUEUE) + recycle_node_unsafe(to_recycle); + else + delete to_recycle; + return true; + } + else + { + prev = current; + current = current->mNext; + } + } while (current); + return false; + } + + void operator()(P... args) + { + auto current = first; + if (!current) + return; + + static std::atomic fence(false); + // prevent recursive calls +#if defined(ARDUINO) && !defined(ESP32) + if (fence.load()) return; + fence.store(true); +#else + if (fence.exchange(true)) return; +#endif + + Node_t* prev = nullptr; + // prevent execution of new callbacks during this run + auto stop = last; + + bool done; + do + { + done = current == stop; + if (!CallP::execute(current->mDelegate, args...)) + { + // remove callback from stack +#ifdef ARDUINO + InterruptLock lockAllInterruptsInThisScope; +#else + std::lock_guard lock(mutex_unused); +#endif + + auto to_recycle = current; + + // removing rLast + if (last == current) + last = prev; + + current = current->mNext; + if (prev) + { + prev->mNext = current; + } + else + { + first = current; + } + + if (ISQUEUE) + recycle_node_unsafe(to_recycle); + else + delete to_recycle; + } + else + { + prev = current; + current = current->mNext; + } + +#if defined(ESP8266) || defined(ESP32) + // running callbacks might last too long for watchdog etc. + optimistic_yield(10000); +#endif + } while (current && !done); + + fence.store(false); + } + }; + + template< typename Delegate, typename R = void, bool ISQUEUE = false, uint32_t QUEUE_CAPACITY = 32> + class MultiDelegateImpl : public MultiDelegatePImpl + { + protected: + using typename MultiDelegatePImpl::Node_t; + using MultiDelegatePImpl::first; + using MultiDelegatePImpl::last; + using MultiDelegatePImpl::unused; + using MultiDelegatePImpl::nodeCount; + using MultiDelegatePImpl::recycle_node_unsafe; +#ifndef ARDUINO + using MultiDelegatePImpl::mutex_unused; +#endif + + public: + using MultiDelegatePImpl::MultiDelegatePImpl; + + void operator()() + { + auto current = first; + if (!current) + return; + + static std::atomic fence(false); + // prevent recursive calls +#if defined(ARDUINO) && !defined(ESP32) + if (fence.load()) return; + fence.store(true); +#else + if (fence.exchange(true)) return; +#endif + + Node_t* prev = nullptr; + // prevent execution of new callbacks during this run + auto stop = last; + + bool done; + do + { + done = current == stop; + if (!Call::execute(current->mDelegate)) + { + // remove callback from stack +#ifdef ARDUINO + InterruptLock lockAllInterruptsInThisScope; +#else + std::lock_guard lock(mutex_unused); +#endif + + auto to_recycle = current; + + // removing rLast + if (last == current) + last = prev; + + current = current->mNext; + if (prev) + { + prev->mNext = current; + } + else + { + first = current; + } + + if (ISQUEUE) + recycle_node_unsafe(to_recycle); + else + delete to_recycle; + } + else + { + prev = current; + current = current->mNext; + } + +#if defined(ESP8266) || defined(ESP32) + // running callbacks might last too long for watchdog etc. + optimistic_yield(10000); +#endif + } while (current && !done); + + fence.store(false); + } + }; + + template< typename Delegate, typename R, bool ISQUEUE, uint32_t QUEUE_CAPACITY, typename... P> class MultiDelegate; + + template< typename Delegate, typename R, bool ISQUEUE, uint32_t QUEUE_CAPACITY, typename... P> + class MultiDelegate : public MultiDelegatePImpl + { + public: + using MultiDelegatePImpl::MultiDelegatePImpl; + }; + + template< typename Delegate, typename R, bool ISQUEUE, uint32_t QUEUE_CAPACITY> + class MultiDelegate : public MultiDelegateImpl + { + public: + using MultiDelegateImpl::MultiDelegateImpl; + }; +}; + +/** +The MultiDelegate class template can be specialized to either a queue or an event multiplexer. +It is designed to be used with Delegate, the efficient runtime wrapper for C function ptr and C++ std::function. +@tparam Delegate specifies the concrete type that MultiDelegate bases the queue or event multiplexer on. +@tparam ISQUEUE modifies the generated MultiDelegate class in subtle ways. In queue mode (ISQUEUE == true), + the value of QUEUE_CAPACITY enforces the maximum number of simultaneous items the queue can contain. + This is exploited to minimize the use of new and delete by reusing already allocated items, thus + reducing heap fragmentation. In event multiplexer mode (ISQUEUE = false), new and delete are + used for allocation of the event handler items. + If the result type of the function call operator of Delegate is void, calling a MultiDelegate queue + removes each item after calling it; a Multidelegate event multiplexer keeps event handlers until + explicitly removed. + If the result type of the function call operator of Delegate is non-void, the type-conversion to bool + of that result determines if the item is immediately removed or kept after each call: a Multidelegate + queue removes an item only if true is returned, but a Multidelegate event multiplexer removes event + handlers that return false. +@tparam QUEUE_CAPACITY is only used if ISQUEUE == true. Then, it sets the maximum capacity that the queue dynamically + allocates from the heap. Unused items are not returned to the heap, but are managed by the MultiDelegate + instance during its own lifetime for efficiency. +*/ +template< typename Delegate, bool ISQUEUE = false, uint32_t QUEUE_CAPACITY = 32> +class MultiDelegate : public detail::MultiDelegate +{ +public: + using detail::MultiDelegate::MultiDelegate; +}; + +#endif // __MULTIDELEGATE_H diff --git a/lib/EspSoftwareSerial/src/circular_queue/circular_queue.h b/lib/EspSoftwareSerial/src/circular_queue/circular_queue.h new file mode 100644 index 00000000..46e3f66e --- /dev/null +++ b/lib/EspSoftwareSerial/src/circular_queue/circular_queue.h @@ -0,0 +1,399 @@ +/* +circular_queue.h - Implementation of a lock-free circular queue for EspSoftwareSerial. +Copyright (c) 2019 Dirk O. Kaar. All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __circular_queue_h +#define __circular_queue_h + +#ifdef ARDUINO +#include +#endif + +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) +#include +#include +#include +#include "Delegate.h" +using std::min; +#else +#include "ghostl.h" +#endif + +#if !defined(ESP32) && !defined(ESP8266) +#define ICACHE_RAM_ATTR +#define IRAM_ATTR +#endif + +/*! + @brief Instance class for a single-producer, single-consumer circular queue / ring buffer (FIFO). + This implementation is lock-free between producer and consumer for the available(), peek(), + pop(), and push() type functions. +*/ +template< typename T, typename ForEachArg = void > +class circular_queue +{ +public: + /*! + @brief Constructs a valid, but zero-capacity dummy queue. + */ + circular_queue() : m_bufSize(1) + { + m_inPos.store(0); + m_outPos.store(0); + } + /*! + @brief Constructs a queue of the given maximum capacity. + */ + circular_queue(const size_t capacity) : m_bufSize(capacity + 1), m_buffer(new T[m_bufSize]) + { + m_inPos.store(0); + m_outPos.store(0); + } + circular_queue(circular_queue&& cq) : + m_bufSize(cq.m_bufSize), m_buffer(cq.m_buffer), m_inPos(cq.m_inPos.load()), m_outPos(cq.m_outPos.load()) + {} + ~circular_queue() + { + m_buffer.reset(); + } + circular_queue(const circular_queue&) = delete; + circular_queue& operator=(circular_queue&& cq) + { + m_bufSize = cq.m_bufSize; + m_buffer = cq.m_buffer; + m_inPos.store(cq.m_inPos.load()); + m_outPos.store(cq.m_outPos.load()); + } + circular_queue& operator=(const circular_queue&) = delete; + + /*! + @brief Get the numer of elements the queue can hold at most. + */ + size_t capacity() const + { + return m_bufSize - 1; + } + + /*! + @brief Resize the queue. The available elements in the queue are preserved. + This is not lock-free and concurrent producer or consumer access + will lead to corruption. + @return True if the new capacity could accommodate the present elements in + the queue, otherwise nothing is done and false is returned. + */ + bool capacity(const size_t cap); + + /*! + @brief Discard all data in the queue. + */ + void flush() + { + m_outPos.store(m_inPos.load()); + } + + /*! + @brief Get a snapshot number of elements that can be retrieved by pop. + */ + size_t available() const + { + int avail = static_cast(m_inPos.load() - m_outPos.load()); + if (avail < 0) avail += m_bufSize; + return avail; + } + + /*! + @brief Get the remaining free elementes for pushing. + */ + size_t available_for_push() const + { + int avail = static_cast(m_outPos.load() - m_inPos.load()) - 1; + if (avail < 0) avail += m_bufSize; + return avail; + } + + /*! + @brief Peek at the next element pop will return without removing it from the queue. + @return An rvalue copy of the next element that can be popped. If the queue is empty, + return an rvalue copy of the element that is pending the next push. + */ + T peek() const + { + const auto outPos = m_outPos.load(std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_acquire); + return m_buffer[outPos]; + } + + /*! + @brief Peek at the next pending input value. + @return A reference to the next element that can be pushed. + */ + T& IRAM_ATTR pushpeek() + { + const auto inPos = m_inPos.load(std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_acquire); + return m_buffer[inPos]; + } + + /*! + @brief Release the next pending input value, accessible by pushpeek(), into the queue. + @return true if the queue accepted the value, false if the queue + was full. + */ + bool IRAM_ATTR push(); + + /*! + @brief Move the rvalue parameter into the queue. + @return true if the queue accepted the value, false if the queue + was full. + */ + bool IRAM_ATTR push(T&& val); + + /*! + @brief Push a copy of the parameter into the queue. + @return true if the queue accepted the value, false if the queue + was full. + */ + bool IRAM_ATTR push(const T& val) + { + return push(T(val)); + } + +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) + /*! + @brief Push copies of multiple elements from a buffer into the queue, + in order, beginning at buffer's head. + @return The number of elements actually copied into the queue, counted + from the buffer head. + */ + size_t push_n(const T* buffer, size_t size); +#endif + + /*! + @brief Pop the next available element from the queue. + @return An rvalue copy of the popped element, or a default + value of type T if the queue is empty. + */ + T pop(); + +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) + /*! + @brief Pop multiple elements in ordered sequence from the queue to a buffer. + If buffer is nullptr, simply discards up to size elements from the queue. + @return The number of elements actually popped from the queue to + buffer. + */ + size_t pop_n(T* buffer, size_t size); +#endif + + /*! + @brief Iterate over and remove each available element from queue, + calling back fun with an rvalue reference of every single element. + */ +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) + void for_each(const Delegate& fun); +#else + void for_each(Delegate fun); +#endif + + /*! + @brief In reverse order, iterate over, pop and optionally requeue each available element from the queue, + calling back fun with a reference of every single element. + Requeuing is dependent on the return boolean of the callback function. If it + returns true, the requeue occurs. + */ +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) + bool for_each_rev_requeue(const Delegate& fun); +#else + bool for_each_rev_requeue(Delegate fun); +#endif + +protected: + const T defaultValue = {}; + unsigned m_bufSize; +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) + std::unique_ptr m_buffer; +#else + std::unique_ptr m_buffer; +#endif + std::atomic m_inPos; + std::atomic m_outPos; +}; + +template< typename T, typename ForEachArg > +bool circular_queue::capacity(const size_t cap) +{ + if (cap + 1 == m_bufSize) return true; + else if (available() > cap) return false; + std::unique_ptr buffer(new T[cap + 1]); + const auto available = pop_n(buffer, cap); + m_buffer.reset(buffer); + m_bufSize = cap + 1; + std::atomic_thread_fence(std::memory_order_release); + m_inPos.store(available, std::memory_order_relaxed); + m_outPos.store(0, std::memory_order_release); + return true; +} + +template< typename T, typename ForEachArg > +bool IRAM_ATTR circular_queue::push() +{ + const auto inPos = m_inPos.load(std::memory_order_acquire); + const unsigned next = (inPos + 1) % m_bufSize; + if (next == m_outPos.load(std::memory_order_relaxed)) { + return false; + } + + std::atomic_thread_fence(std::memory_order_acquire); + + m_inPos.store(next, std::memory_order_release); + return true; +} + +template< typename T, typename ForEachArg > +bool IRAM_ATTR circular_queue::push(T&& val) +{ + const auto inPos = m_inPos.load(std::memory_order_acquire); + const unsigned next = (inPos + 1) % m_bufSize; + if (next == m_outPos.load(std::memory_order_relaxed)) { + return false; + } + + std::atomic_thread_fence(std::memory_order_acquire); + + m_buffer[inPos] = std::move(val); + + std::atomic_thread_fence(std::memory_order_release); + + m_inPos.store(next, std::memory_order_release); + return true; +} + +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) +template< typename T, typename ForEachArg > +size_t circular_queue::push_n(const T* buffer, size_t size) +{ + const auto inPos = m_inPos.load(std::memory_order_acquire); + const auto outPos = m_outPos.load(std::memory_order_relaxed); + + size_t blockSize = (outPos > inPos) ? outPos - 1 - inPos : (outPos == 0) ? m_bufSize - 1 - inPos : m_bufSize - inPos; + blockSize = min(size, blockSize); + if (!blockSize) return 0; + int next = (inPos + blockSize) % m_bufSize; + + std::atomic_thread_fence(std::memory_order_acquire); + + auto dest = m_buffer.get() + inPos; + std::copy_n(std::make_move_iterator(buffer), blockSize, dest); + size = min(size - blockSize, outPos > 1 ? static_cast(outPos - next - 1) : 0); + next += size; + dest = m_buffer.get(); + std::copy_n(std::make_move_iterator(buffer + blockSize), size, dest); + + std::atomic_thread_fence(std::memory_order_release); + + m_inPos.store(next, std::memory_order_release); + return blockSize + size; +} +#endif + +template< typename T, typename ForEachArg > +T circular_queue::pop() +{ + const auto outPos = m_outPos.load(std::memory_order_acquire); + if (m_inPos.load(std::memory_order_relaxed) == outPos) return defaultValue; + + std::atomic_thread_fence(std::memory_order_acquire); + + auto val = std::move(m_buffer[outPos]); + + std::atomic_thread_fence(std::memory_order_release); + + m_outPos.store((outPos + 1) % m_bufSize, std::memory_order_release); + return val; +} + +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) +template< typename T, typename ForEachArg > +size_t circular_queue::pop_n(T* buffer, size_t size) { + size_t avail = size = min(size, available()); + if (!avail) return 0; + const auto outPos = m_outPos.load(std::memory_order_acquire); + size_t n = min(avail, static_cast(m_bufSize - outPos)); + + std::atomic_thread_fence(std::memory_order_acquire); + + if (buffer) { + buffer = std::copy_n(std::make_move_iterator(m_buffer.get() + outPos), n, buffer); + avail -= n; + std::copy_n(std::make_move_iterator(m_buffer.get()), avail, buffer); + } + + std::atomic_thread_fence(std::memory_order_release); + + m_outPos.store((outPos + size) % m_bufSize, std::memory_order_release); + return size; +} +#endif + +template< typename T, typename ForEachArg > +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) +void circular_queue::for_each(const Delegate& fun) +#else +void circular_queue::for_each(Delegate fun) +#endif +{ + auto outPos = m_outPos.load(std::memory_order_acquire); + const auto inPos = m_inPos.load(std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_acquire); + while (outPos != inPos) + { + fun(std::move(m_buffer[outPos])); + std::atomic_thread_fence(std::memory_order_release); + outPos = (outPos + 1) % m_bufSize; + m_outPos.store(outPos, std::memory_order_release); + } +} + +template< typename T, typename ForEachArg > +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) +bool circular_queue::for_each_rev_requeue(const Delegate& fun) +#else +bool circular_queue::for_each_rev_requeue(Delegate fun) +#endif +{ + auto inPos0 = circular_queue::m_inPos.load(std::memory_order_acquire); + auto outPos = circular_queue::m_outPos.load(std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_acquire); + if (outPos == inPos0) return false; + auto pos = inPos0; + auto outPos1 = inPos0; + const auto posDecr = circular_queue::m_bufSize - 1; + do { + pos = (pos + posDecr) % circular_queue::m_bufSize; + T&& val = std::move(circular_queue::m_buffer[pos]); + if (fun(val)) + { + outPos1 = (outPos1 + posDecr) % circular_queue::m_bufSize; + if (outPos1 != pos) circular_queue::m_buffer[outPos1] = std::move(val); + } + } while (pos != outPos); + circular_queue::m_outPos.store(outPos1, std::memory_order_release); + return true; +} + +#endif // __circular_queue_h diff --git a/lib/EspSoftwareSerial/src/circular_queue/circular_queue_mp.h b/lib/EspSoftwareSerial/src/circular_queue/circular_queue_mp.h new file mode 100644 index 00000000..7024247a --- /dev/null +++ b/lib/EspSoftwareSerial/src/circular_queue/circular_queue_mp.h @@ -0,0 +1,200 @@ +/* +circular_queue_mp.h - Implementation of a lock-free circular queue for EspSoftwareSerial. +Copyright (c) 2019 Dirk O. Kaar. All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __circular_queue_mp_h +#define __circular_queue_mp_h + +#include "circular_queue.h" + +#ifdef ESP8266 +#include "interrupts.h" +#else +#include +#endif + +/*! + @brief Instance class for a multi-producer, single-consumer circular queue / ring buffer (FIFO). + This implementation is lock-free between producers and consumer for the available(), peek(), + pop(), and push() type functions, but is guarded to safely allow only a single producer + at any instant. +*/ +template< typename T, typename ForEachArg = void > +class circular_queue_mp : protected circular_queue +{ +public: + circular_queue_mp() = default; + circular_queue_mp(const size_t capacity) : circular_queue(capacity) + {} + circular_queue_mp(circular_queue&& cq) : circular_queue(std::move(cq)) + {} + using circular_queue::operator=; + using circular_queue::capacity; + using circular_queue::flush; + using circular_queue::available; + using circular_queue::available_for_push; + using circular_queue::peek; + using circular_queue::pop; + using circular_queue::pop_n; + using circular_queue::for_each; + using circular_queue::for_each_rev_requeue; + + /*! + @brief Resize the queue. The available elements in the queue are preserved. + This is not lock-free, but safe, concurrent producer or consumer access + is guarded. + @return True if the new capacity could accommodate the present elements in + the queue, otherwise nothing is done and false is returned. + */ + bool capacity(const size_t cap) + { +#ifdef ESP8266 + esp8266::InterruptLock lock; +#else + std::lock_guard lock(m_pushMtx); +#endif + return circular_queue::capacity(cap); + } + + bool IRAM_ATTR push() = delete; + + /*! + @brief Move the rvalue parameter into the queue, guarded + for multiple concurrent producers. + @return true if the queue accepted the value, false if the queue + was full. + */ + bool IRAM_ATTR push(T&& val) + { +#ifdef ESP8266 + esp8266::InterruptLock lock; +#else + std::lock_guard lock(m_pushMtx); +#endif + return circular_queue::push(std::move(val)); + } + + /*! + @brief Push a copy of the parameter into the queue, guarded + for multiple concurrent producers. + @return true if the queue accepted the value, false if the queue + was full. + */ + bool IRAM_ATTR push(const T& val) + { +#ifdef ESP8266 + esp8266::InterruptLock lock; +#else + std::lock_guard lock(m_pushMtx); +#endif + return circular_queue::push(val); + } + + /*! + @brief Push copies of multiple elements from a buffer into the queue, + in order, beginning at buffer's head. This is guarded for + multiple producers, push_n() is atomic. + @return The number of elements actually copied into the queue, counted + from the buffer head. + */ + size_t push_n(const T* buffer, size_t size) + { +#ifdef ESP8266 + esp8266::InterruptLock lock; +#else + std::lock_guard lock(m_pushMtx); +#endif + return circular_queue::push_n(buffer, size); + } + + /*! + @brief Pops the next available element from the queue, requeues + it immediately. + @return A reference to the just requeued element, or the default + value of type T if the queue is empty. + */ + T& pop_requeue(); + + /*! + @brief Iterate over, pop and optionally requeue each available element from the queue, + calling back fun with a reference of every single element. + Requeuing is dependent on the return boolean of the callback function. If it + returns true, the requeue occurs. + */ + bool for_each_requeue(const Delegate& fun); + +#ifndef ESP8266 +protected: + std::mutex m_pushMtx; +#endif +}; + +template< typename T, typename ForEachArg > +T& circular_queue_mp::pop_requeue() +{ +#ifdef ESP8266 + esp8266::InterruptLock lock; +#else + std::lock_guard lock(m_pushMtx); +#endif + const auto outPos = circular_queue::m_outPos.load(std::memory_order_acquire); + const auto inPos = circular_queue::m_inPos.load(std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_acquire); + if (inPos == outPos) return circular_queue::defaultValue; + T& val = circular_queue::m_buffer[inPos] = std::move(circular_queue::m_buffer[outPos]); + const auto bufSize = circular_queue::m_bufSize; + std::atomic_thread_fence(std::memory_order_release); + circular_queue::m_outPos.store((outPos + 1) % bufSize, std::memory_order_relaxed); + circular_queue::m_inPos.store((inPos + 1) % bufSize, std::memory_order_release); + return val; +} + +template< typename T, typename ForEachArg > +bool circular_queue_mp::for_each_requeue(const Delegate& fun) +{ + auto inPos0 = circular_queue::m_inPos.load(std::memory_order_acquire); + auto outPos = circular_queue::m_outPos.load(std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_acquire); + if (outPos == inPos0) return false; + do { + T&& val = std::move(circular_queue::m_buffer[outPos]); + if (fun(val)) + { +#ifdef ESP8266 + esp8266::InterruptLock lock; +#else + std::lock_guard lock(m_pushMtx); +#endif + std::atomic_thread_fence(std::memory_order_release); + auto inPos = circular_queue::m_inPos.load(std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_acquire); + circular_queue::m_buffer[inPos] = std::move(val); + std::atomic_thread_fence(std::memory_order_release); + circular_queue::m_inPos.store((inPos + 1) % circular_queue::m_bufSize, std::memory_order_release); + } + else + { + std::atomic_thread_fence(std::memory_order_release); + } + outPos = (outPos + 1) % circular_queue::m_bufSize; + circular_queue::m_outPos.store(outPos, std::memory_order_release); + } while (outPos != inPos0); + return true; +} + +#endif // __circular_queue_mp_h diff --git a/lib/EspSoftwareSerial/src/circular_queue/ghostl.h b/lib/EspSoftwareSerial/src/circular_queue/ghostl.h new file mode 100644 index 00000000..11683805 --- /dev/null +++ b/lib/EspSoftwareSerial/src/circular_queue/ghostl.h @@ -0,0 +1,92 @@ +/* +ghostl.h - Implementation of a bare-bones, mostly no-op, C++ STL shell + that allows building some Arduino ESP8266/ESP32 + libraries on Aruduino AVR. +Copyright (c) 2019 Dirk O. Kaar. All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __ghostl_h +#define __ghostl_h + +#if defined(ARDUINO_ARCH_SAMD) +#include +#endif + +namespace std +{ +#if !defined(ARDUINO_ARCH_SAMD) + typedef enum memory_order { + memory_order_relaxed, + memory_order_acquire, + memory_order_release, + memory_order_seq_cst + } memory_order; + template< typename T > class atomic { + private: + T value; + public: + atomic() {} + atomic(T desired) { value = desired; } + void store(T desired, std::memory_order = std::memory_order_seq_cst) volatile noexcept { value = desired; } + T load(std::memory_order = std::memory_order_seq_cst) const volatile noexcept { return value; } + }; + inline void atomic_thread_fence(std::memory_order order) noexcept {} + template< typename T > T&& move(T& t) noexcept { return static_cast(t); } +#endif + + template< typename T, unsigned long N > struct array + { + T _M_elems[N]; + decltype(sizeof(0)) size() const { return N; } + T& operator[](decltype(sizeof(0)) i) { return _M_elems[i]; } + const T& operator[](decltype(sizeof(0)) i) const { return _M_elems[i]; } + }; + + template< typename T > class unique_ptr + { + public: + using pointer = T*; + unique_ptr() noexcept : ptr(nullptr) {} + unique_ptr(pointer p) : ptr(p) {} + pointer operator->() const noexcept { return ptr; } + T& operator[](decltype(sizeof(0)) i) const { return ptr[i]; } + void reset(pointer p = pointer()) noexcept + { + delete ptr; + ptr = p; + } + T& operator*() const { return *ptr; } + private: + pointer ptr; + }; + + template< typename T > using function = T*; + using nullptr_t = decltype(nullptr); + + template + struct identity { + typedef T type; + }; + + template + inline T&& forward(typename identity::type& t) noexcept + { + return static_cast::type&&>(t); + } +} + +#endif // __ghostl_h From 867c5477df5f311d330561ed1a3cd75c16d1fec5 Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Wed, 5 Feb 2020 22:13:30 +0100 Subject: [PATCH 052/106] sds011 code sanitizations --- include/sds011read.h | 4 - lib/EspSoftwareSerial/LICENSE | 502 ----- lib/EspSoftwareSerial/README.md | 124 -- .../examples/loopback/loopback.ino | 263 --- .../examples/onewiretest/onewiretest.ino | 48 - .../examples/repeater/repeater.ino | 183 -- .../examples/servoTester/servoTester.ino | 115 -- .../examples/swsertest/swsertest.ino | 47 - lib/EspSoftwareSerial/keywords.txt | 43 - lib/EspSoftwareSerial/library.json | 15 - lib/EspSoftwareSerial/library.properties | 9 - lib/EspSoftwareSerial/src/SoftwareSerial.cpp | 542 ----- lib/EspSoftwareSerial/src/SoftwareSerial.h | 255 --- .../src/circular_queue/Delegate.h | 1786 ----------------- .../src/circular_queue/MultiDelegate.h | 503 ----- .../src/circular_queue/circular_queue.h | 399 ---- .../src/circular_queue/circular_queue_mp.h | 200 -- .../src/circular_queue/ghostl.h | 92 - lib/SDS011/src/SDS011.cpp | 191 -- lib/SDS011/src/SDS011.h | 40 - platformio.ini | 1 + src/hal/generic.h | 4 + src/sds011read.cpp | 68 +- src/senddata.cpp | 4 - 24 files changed, 38 insertions(+), 5400 deletions(-) delete mode 100644 lib/EspSoftwareSerial/LICENSE delete mode 100644 lib/EspSoftwareSerial/README.md delete mode 100644 lib/EspSoftwareSerial/examples/loopback/loopback.ino delete mode 100644 lib/EspSoftwareSerial/examples/onewiretest/onewiretest.ino delete mode 100644 lib/EspSoftwareSerial/examples/repeater/repeater.ino delete mode 100644 lib/EspSoftwareSerial/examples/servoTester/servoTester.ino delete mode 100644 lib/EspSoftwareSerial/examples/swsertest/swsertest.ino delete mode 100644 lib/EspSoftwareSerial/keywords.txt delete mode 100644 lib/EspSoftwareSerial/library.json delete mode 100644 lib/EspSoftwareSerial/library.properties delete mode 100644 lib/EspSoftwareSerial/src/SoftwareSerial.cpp delete mode 100644 lib/EspSoftwareSerial/src/SoftwareSerial.h delete mode 100644 lib/EspSoftwareSerial/src/circular_queue/Delegate.h delete mode 100644 lib/EspSoftwareSerial/src/circular_queue/MultiDelegate.h delete mode 100644 lib/EspSoftwareSerial/src/circular_queue/circular_queue.h delete mode 100644 lib/EspSoftwareSerial/src/circular_queue/circular_queue_mp.h delete mode 100644 lib/EspSoftwareSerial/src/circular_queue/ghostl.h delete mode 100644 lib/SDS011/src/SDS011.cpp delete mode 100644 lib/SDS011/src/SDS011.h diff --git a/include/sds011read.h b/include/sds011read.h index d278a5fd..8c573c0d 100644 --- a/include/sds011read.h +++ b/include/sds011read.h @@ -3,10 +3,6 @@ #include -// used pins on the ESP-side: -#define ESP_PIN_TX 19 // connect to RX on the SDS011 -#define ESP_PIN_RX 23 // connect to TX on the SDS011 - #define SDCARD_FILE_HEADER_SDS011 ", PM10,PM25" bool sds011_init(); diff --git a/lib/EspSoftwareSerial/LICENSE b/lib/EspSoftwareSerial/LICENSE deleted file mode 100644 index f166cc57..00000000 --- a/lib/EspSoftwareSerial/LICENSE +++ /dev/null @@ -1,502 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! \ No newline at end of file diff --git a/lib/EspSoftwareSerial/README.md b/lib/EspSoftwareSerial/README.md deleted file mode 100644 index 2d14c9e6..00000000 --- a/lib/EspSoftwareSerial/README.md +++ /dev/null @@ -1,124 +0,0 @@ -# EspSoftwareSerial - -## Implementation of the Arduino software serial library for the ESP8266 / ESP32 - -This fork implements interrupt service routine best practice. -In the receive interrupt, instead of blocking for whole bytes -at a time - voiding any near-realtime behavior of the CPU - only level -change and timestamp are recorded. The more time consuming phase -detection and byte assembly are done in the main code. - -Except at high bitrates, depending on other ongoing activity, -interrupts in particular, this software serial adapter -supports full duplex receive and send. At high bitrates (115200bps) -send bit timing can be improved at the expense of blocking concurrent -full duplex receives, with the ``SoftwareSerial::enableIntTx(false)`` function call. - -The same functionality is given as the corresponding AVR library but -several instances can be active at the same time. Speed up to 115200 baud -is supported. Besides a constructor compatible to the AVR SoftwareSerial class, -and updated constructor that takes no arguments exists, instead the ``begin()`` -function can handle the pin assignments and logic inversion. -It also has optional input buffer capacity arguments for byte buffer and ISR bit buffer. -This way, it is a better drop-in replacement for the hardware serial APIs on the ESP MCUs. - -Please note that due to the fact that the ESPs always have other activities -ongoing, there will be some inexactness in interrupt timings. This may -lead to inevitable, but few, bit errors when having heavy data traffic -at high baud rates. - -## Resource optimization - -The memory footprint can be optimized to just fit the amount of expected -incoming asynchronous data. -For this, the ``SoftwareSerial`` constructor provides two arguments. First, the -octet buffer capacity for assembled received octets can be set. Read calls are -satisfied from this buffer, freeing it in return. -Second, the signal edge detection buffer of 32bit fields can be resized. -One octet may require up to to 10 fields, but fewer may be needed, -depending on the bit pattern. Any read or write calls check this buffer -to assemble received octets, thus promoting completed octets to the octet -buffer, freeing fields in the edge detection buffer. - -Look at the swsertest.ino example. There, on reset, ASCII characters ' ' to 'z' -are sent. This happens not as a block write, but in a single write call per -character. As the example uses a local loopback wire, every outgoing bit is -immediately received back. Therefore, any single write call causes up to -10 fields - depending on the exact bit pattern - to be occupied in the signal -edge detection buffer. In turn, as explained before, each single write call -also causes received bit assembly to be performed, promoting these bits from -the signal edge detection buffer to the octet buffer as soon as possible. -Explaining by way of contrast, if during a a single write call, perhaps because -of using block writing, more than a single octet is received, there will be a -need for more than 10 fields in the signal edge detection buffer. -The necessary capacity of the octet buffer only depends on the amount of incoming -data until the next read call. - -For the swsertest.ino example, this results in the following optimized -constructor arguments to spend only the minimum RAM on buffers required: - -The octet buffer capacity (``bufCapacity``) is 93 (91 characters net plus two tolerance). -The signal edge detection buffer capacity (``isrBufCapacity``) is 10, as each octet has -10 bits on the wire, which are immediately received during the write, and each -write call causes the signal edge detection to promote the previously sent and -received bits to the octet buffer. - -In a more generalized scenario, calculate the bits (use message size in octets -times 10) that may be asynchronously received to determine the value for -``isrBufCapacity`` in the constructor. Also use the number of received octets -that must be buffered for reading as the value of ``bufCapacity``. -The more frequently your code calls write or read functions, the greater the -chances are that you can reduce the ``isrBufCapacity`` footprint without losing data, -and each time you call read to fetch from the octet buffer, you reduce the -need for space there. - -## SoftwareSerialConfig and parity -The configuration of the data stream is done via a ``SoftwareSerialConfig`` -argument to ``begin()``. Word lengths can be set to between 5 and 8 bits, parity -can be N(one), O(dd) or E(ven) and 1 or 2 stop bits can be used. The default is -``SWSERIAL_8N1`` using 8 bits, no parity and 1 stop bit but any combination can -be used, e.g. ``SWSERIAL_7E2``. If using EVEN or ODD parity, any parity errors -can be detected with the ``peekParityError()`` function. Note that parity -checking must be done before ``read()``, as the parity information is removed -from the buffer when reading the corresponding byte. - -To allow flexible 9-bit and data/addressing protocols, the additional parity -modes MARK and SPACE are also available. Furthermore, the parity mode can be -individually set in each call to ``write()``. - -This allows a simple implementation of protocols where the parity bit is used to -distinguish between data and addresses/commands ("9-bit" protocols). First set -up SoftwareSerial with parity mode SPACE, e.g. ``SWSERIAL_8S1``. This will add a -parity bit to every byte sent, setting it to logical zero (SPACE parity). - -To detect incoming bytes with the parity bit set (MARK parity), use the -``peekParityError()`` function. To send a byte with the parity bit set, just add -``MARK`` as the second argument when writing, e.g. ``write(ch, MARK)``. - -## Using and updating EspSoftwareSerial in the esp8266com/esp8266 Arduino build environment - -EspSoftwareSerial is both part of the BSP download for ESP8266 in Arduino, -and it is set up as a Git submodule in the esp8266 source tree, -specifically in ``.../esp8266/libraries/SoftwareSerial`` when using a Github -repository clone in your Arduino sketchbook hardware directory. -This supersedes any version of EspSoftwareSerial installed for instance via -the Arduino library manager, it is not required to install EspSoftwareSerial -for the ESP8266 separately at all, but doing so has ill effect. - -The responsible maintainer of the esp8266 repository has kindly shared the -following command line instructions to use, if one wishes to manually -update EspSoftwareSerial to a newer release than pulled in via the ESP8266 Arduino BSP: - -To update esp8266/arduino SoftwareSerial submodule to lastest master: - -Clean it (optional): -```shell -$ rm -rf libraries/SoftwareSerial -$ git submodule update --init -``` -Now update it: -```shell -$ cd libraries/SoftwareSerial -$ git checkout master -$ git pull -``` diff --git a/lib/EspSoftwareSerial/examples/loopback/loopback.ino b/lib/EspSoftwareSerial/examples/loopback/loopback.ino deleted file mode 100644 index b612bdec..00000000 --- a/lib/EspSoftwareSerial/examples/loopback/loopback.ino +++ /dev/null @@ -1,263 +0,0 @@ -#include - -// On ESP8266: -// Local SoftwareSerial loopback, connect D5 (rx) and D6 (tx). -// For local hardware loopback, connect D5 to D8 (tx), D6 to D7 (rx). -// For hardware send/sink, connect D7 (rx) and D8 (tx). -// Hint: The logger is run at 9600bps such that enableIntTx(true) can remain unchanged. Blocking -// interrupts severely impacts the ability of the SoftwareSerial devices to operate concurrently -// and/or in duplex mode. -// Operating in software serial full duplex mode, runs at 19200bps and few errors (~2.5%). -// Operating in software serial half duplex mode (both loopback and repeater), -// runs at 57600bps with nearly no errors. -// Operating loopback in full duplex, and repeater in half duplex, runs at 38400bps with nearly no errors. -// On ESP32: -// For SoftwareSerial or hardware send/sink, connect D5 (rx) and D6 (tx). -// Hardware Serial2 defaults to D4 (rx), D3 (tx). -// For local hardware loopback, connect D5 (rx) to D3 (tx), D6 (tx) to D4 (rx). - -#if defined(ESP8266) && !defined(D5) -#define D5 (14) -#define D6 (12) -#define D7 (13) -#define D8 (15) -#define TX (1) -#endif - -// Pick only one of HWLOOPBACK, HWSOURCESWSINK, or HWSOURCESINK -//#define HWLOOPBACK 1 -//#define HWSOURCESWSINK 1 -//#define HWSOURCESINK 1 -#define HALFDUPLEX 1 - -#ifdef ESP32 -constexpr int IUTBITRATE = 19200; -#else -constexpr int IUTBITRATE = 19200; -#endif - -#if defined(ESP8266) -constexpr SoftwareSerialConfig swSerialConfig = SWSERIAL_8E1; -constexpr SerialConfig hwSerialConfig = SERIAL_8E1; -#elif defined(ESP32) -constexpr SoftwareSerialConfig swSerialConfig = SWSERIAL_8E1; -constexpr uint32_t hwSerialConfig = SERIAL_8E1; -#else -constexpr unsigned swSerialConfig = 3; -#endif -constexpr bool invert = false; - -constexpr int BLOCKSIZE = 16; // use fractions of 256 - -unsigned long start; -String effTxTxt("eff. tx: "); -String effRxTxt("eff. rx: "); -int txCount; -int rxCount; -int expected; -int rxErrors; -int rxParityErrors; -constexpr int ReportInterval = IUTBITRATE / 8; - -#if defined(ESP8266) -#if defined(HWLOOPBACK) || defined(HWSOURCESWSINK) -HardwareSerial& hwSerial(Serial); -SoftwareSerial serialIUT; -SoftwareSerial logger; -#elif defined(HWSOURCESINK) -HardwareSerial& serialIUT(Serial); -SoftwareSerial logger; -#else -SoftwareSerial serialIUT; -HardwareSerial& logger(Serial); -#endif -#elif defined(ESP32) -#if defined(HWLOOPBACK) || defined (HWSOURCESWSINK) -HardwareSerial& hwSerial(Serial2); -SoftwareSerial serialIUT; -#elif defined(HWSOURCESINK) -HardwareSerial& serialIUT(Serial2); -#else -SoftwareSerial serialIUT; -#endif -HardwareSerial& logger(Serial); -#else -SoftwareSerial serialIUT(14, 12); -HardwareSerial& logger(Serial); -#endif - -void setup() { -#if defined(ESP8266) -#if defined(HWLOOPBACK) || defined(HWSOURCESINK) || defined(HWSOURCESWSINK) - Serial.begin(IUTBITRATE, hwSerialConfig, SERIAL_FULL, 1, invert); - Serial.swap(); - Serial.setRxBufferSize(2 * BLOCKSIZE); - logger.begin(9600, SWSERIAL_8N1, -1, TX); -#else - logger.begin(9600); -#endif -#if !defined(HWSOURCESINK) - serialIUT.begin(IUTBITRATE, swSerialConfig, D5, D6, invert, 2 * BLOCKSIZE); -#ifdef HALFDUPLEX - serialIUT.enableIntTx(false); -#endif -#endif -#elif defined(ESP32) -#if defined(HWLOOPBACK) || defined(HWSOURCESWSINK) - Serial2.begin(IUTBITRATE, hwSerialConfig, D4, D3, invert); - Serial2.setRxBufferSize(2 * BLOCKSIZE); -#elif defined(HWSOURCESINK) - serialIUT.begin(IUTBITRATE, hwSerialConfig, D5, D6, invert); - serialIUT.setRxBufferSize(2 * BLOCKSIZE); -#endif -#if !defined(HWSOURCESINK) - serialIUT.begin(IUTBITRATE, swSerialConfig, D5, D6, invert, 2 * BLOCKSIZE); -#ifdef HALFDUPLEX - serialIUT.enableIntTx(false); -#endif -#endif - logger.begin(9600); -#else -#if !defined(HWSOURCESINK) - serialIUT.begin(IUTBITRATE); -#endif - logger.begin(9600); -#endif - - logger.println("Loopback example for EspSoftwareSerial"); - - start = micros(); - txCount = 0; - rxCount = 0; - rxErrors = 0; - rxParityErrors = 0; - expected = -1; -} - -unsigned char c = 0; - -void loop() { -#ifdef HALFDUPLEX - char block[BLOCKSIZE]; -#endif - char inBuf[BLOCKSIZE]; - for (int i = 0; i < BLOCKSIZE; ++i) { -#ifndef HALFDUPLEX -#ifdef HWSOURCESWSINK - hwSerial.write(c); -#else - serialIUT.write(c); -#endif -#ifdef HWLOOPBACK - int avail = hwSerial.available(); - while ((0 == (i % 8)) && avail > 0) { - int inCnt = hwSerial.read(inBuf, min(avail, min(BLOCKSIZE, hwSerial.availableForWrite()))); - hwSerial.write(inBuf, inCnt); - avail -= inCnt; - } -#endif -#else - block[i] = c; -#endif - c = (c + 1) % 256; - ++txCount; - } -#ifdef HALFDUPLEX -#ifdef HWSOURCESWSINK - hwSerial.write(block, BLOCKSIZE); -#else - serialIUT.write(block, BLOCKSIZE); -#endif -#endif -#ifdef HWSOURCESINK -#if defined(ESP8266) - if (serialIUT.hasOverrun()) { logger.println("serialIUT.overrun"); } -#endif -#else - if (serialIUT.overflow()) { logger.println("serialIUT.overflow"); } -#endif - - int inCnt; - uint32_t deadlineStart; - -#ifdef HWLOOPBACK - // starting deadline for the first bytes to become readable - deadlineStart = ESP.getCycleCount(); - inCnt = 0; - while ((ESP.getCycleCount() - deadlineStart) < (1000000UL * 12 * BLOCKSIZE) / IUTBITRATE * 24 * ESP.getCpuFreqMHz()) { - int avail = hwSerial.available(); - inCnt += hwSerial.read(&inBuf[inCnt], min(avail, min(BLOCKSIZE - inCnt, hwSerial.availableForWrite()))); - if (inCnt >= BLOCKSIZE) { break; } - // wait for more outstanding bytes to trickle in - if (avail) deadlineStart = ESP.getCycleCount(); - } - hwSerial.write(inBuf, inCnt); -#endif - - // starting deadline for the first bytes to come in - deadlineStart = ESP.getCycleCount(); - inCnt = 0; - while ((ESP.getCycleCount() - deadlineStart) < (1000000UL * 12 * BLOCKSIZE) / IUTBITRATE * 8 * ESP.getCpuFreqMHz()) { - int avail; - if (0 != (swSerialConfig & 070)) - avail = serialIUT.available(); - else - avail = serialIUT.read(inBuf, BLOCKSIZE); - for (int i = 0; i < avail; ++i) - { - unsigned char r; - if (0 != (swSerialConfig & 070)) - r = serialIUT.read(); - else - r = inBuf[i]; - if (expected == -1) { expected = r; } - else { - expected = (expected + 1) % (1UL << (5 + swSerialConfig % 4)); - } - if (r != expected) { - ++rxErrors; - expected = -1; - } -#ifndef HWSOURCESINK - if (serialIUT.readParity() != (static_cast(swSerialConfig & 010) ? serialIUT.parityOdd(r) : serialIUT.parityEven(r))) - { - ++rxParityErrors; - } -#endif - ++rxCount; - ++inCnt; - } - - if (inCnt >= BLOCKSIZE) { break; } - // wait for more outstanding bytes to trickle in - if (avail) deadlineStart = ESP.getCycleCount(); - } - - const uint32_t interval = micros() - start; - if (txCount >= ReportInterval && interval) { - uint8_t wordBits = (5 + swSerialConfig % 4) + static_cast(swSerialConfig & 070) + 1 + ((swSerialConfig & 0300) ? 1 : 0); - logger.println(String("tx/rx: ") + txCount + "/" + rxCount); - const long txCps = txCount * (1000000.0 / interval); - const long rxCps = rxCount * (1000000.0 / interval); - logger.print(effTxTxt + wordBits * txCps + "bps, " - + effRxTxt + wordBits * rxCps + "bps, " - + rxErrors + " errors (" + 100.0 * rxErrors / (!rxErrors ? 1 : rxCount) + "%)"); - if (0 != (swSerialConfig & 070)) - { - logger.print(" ("); logger.print(rxParityErrors); logger.println(" parity errors)"); - } - else - { - logger.println(); - } - txCount = 0; - rxCount = 0; - rxErrors = 0; - rxParityErrors = 0; - expected = -1; - // resync - delay(1000UL * 12 * BLOCKSIZE / IUTBITRATE * 16); - serialIUT.flush(); - start = micros(); - } -} diff --git a/lib/EspSoftwareSerial/examples/onewiretest/onewiretest.ino b/lib/EspSoftwareSerial/examples/onewiretest/onewiretest.ino deleted file mode 100644 index 3e96401b..00000000 --- a/lib/EspSoftwareSerial/examples/onewiretest/onewiretest.ino +++ /dev/null @@ -1,48 +0,0 @@ -#include -#include "SoftwareSerial.h" - -SoftwareSerial swSer1; -SoftwareSerial swSer2; - -void setup() { - delay(2000); - Serial.begin(115200); - Serial.println("\nOne Wire Half Duplex Serial Tester"); - swSer1.begin(115200, SWSERIAL_8N1, 12, 12, false, 256); - swSer1.enableIntTx(true); - swSer2.begin(115200, SWSERIAL_8N1, 14, 14, false, 256); - swSer2.enableIntTx(true); -} - -void loop() { - Serial.println("\n\nTesting on swSer1"); - Serial.print("Enter something to send using swSer1."); - checkSwSerial(&swSer1); - - Serial.println("\n\nTesting on swSer2"); - Serial.print("Enter something to send using swSer2."); - checkSwSerial(&swSer2); - -} - -void checkSwSerial(SoftwareSerial* ss) { - byte ch; - while (!Serial.available()); - ss->enableTx(true); - while (Serial.available()) { - ch = Serial.read(); - ss->write(ch); - } - ss->enableTx(false); - // wait 1 second for the reply from SOftwareSerial if any - delay(1000); - if (ss->available()) { - Serial.print("\nResult:"); - while (ss->available()) { - ch = (byte)ss->read(); - Serial.print(ch < 0x01 ? " 0" : " "); - Serial.print(ch, HEX); - } - Serial.println(); - } -} diff --git a/lib/EspSoftwareSerial/examples/repeater/repeater.ino b/lib/EspSoftwareSerial/examples/repeater/repeater.ino deleted file mode 100644 index fa5566de..00000000 --- a/lib/EspSoftwareSerial/examples/repeater/repeater.ino +++ /dev/null @@ -1,183 +0,0 @@ -#include - -// On ESP8266: -// SoftwareSerial loopback for remote source (loopback.ino), or hardware loopback. -// Connect source D5 (rx) to local D8 (tx), source D6 (tx) to local D7 (rx). -// Hint: The logger is run at 9600bps such that enableIntTx(true) can remain unchanged. Blocking -// interrupts severely impacts the ability of the SoftwareSerial devices to operate concurrently -// and/or in duplex mode. -// On ESP32: -// For software or hardware loopback, connect source rx to local D8 (tx), source tx to local D7 (rx). - -#if defined(ESP8266) && !defined(D5) -#define D5 (14) -#define D6 (12) -#define D7 (13) -#define D8 (15) -#define TX (1) -#endif - -#define HWLOOPBACK 1 -#define HALFDUPLEX 1 - -#ifdef ESP32 -constexpr int IUTBITRATE = 19200; -#else -constexpr int IUTBITRATE = 19200; -#endif - -#if defined(ESP8266) -constexpr SoftwareSerialConfig swSerialConfig = SWSERIAL_8E1; -constexpr SerialConfig hwSerialConfig = SERIAL_8E1; -#elif defined(ESP32) -constexpr SoftwareSerialConfig swSerialConfig = SWSERIAL_8E1; -constexpr uint32_t hwSerialConfig = SERIAL_8E1; -#else -constexpr unsigned swSerialConfig = 3; -#endif -constexpr bool invert = false; - -constexpr int BLOCKSIZE = 16; // use fractions of 256 - -unsigned long start; -String bitRateTxt("Effective data rate: "); -int rxCount; -int seqErrors; -int parityErrors; -int expected; -constexpr int ReportInterval = IUTBITRATE / 8; - -#if defined(ESP8266) -#if defined(HWLOOPBACK) -HardwareSerial& repeater(Serial); -SoftwareSerial logger; -#else -SoftwareSerial repeater; -HardwareSerial& logger(Serial); -#endif -#elif defined(ESP32) -#if defined(HWLOOPBACK) -HardwareSerial& repeater(Serial2); -#else -SoftwareSerial repeater; -#endif -HardwareSerial& logger(Serial); -#else -SoftwareSerial repeater(14, 12); -HardwareSerial& logger(Serial); -#endif - -void setup() { -#if defined(ESP8266) -#if defined(HWLOOPBACK) - repeater.begin(IUTBITRATE, hwSerialConfig, SERIAL_FULL, 1, invert); - repeater.swap(); - repeater.setRxBufferSize(2 * BLOCKSIZE); - logger.begin(9600, SWSERIAL_8N1, -1, TX); -#else - repeater.begin(IUTBITRATE, swSerialConfig, D7, D8, invert, 4 * BLOCKSIZE); -#ifdef HALFDUPLEX - repeater.enableIntTx(false); -#endif - logger.begin(9600); -#endif -#elif defined(ESP32) -#if defined(HWLOOPBACK) - repeater.begin(IUTBITRATE, hwSerialConfig, D7, D8, invert); - repeater.setRxBufferSize(2 * BLOCKSIZE); -#else - repeater.begin(IUTBITRATE, swSerialConfig, D7, D8, invert, 4 * BLOCKSIZE); -#ifdef HALFDUPLEX - repeater.enableIntTx(false); -#endif -#endif - logger.begin(9600); -#else - repeater.begin(IUTBITRATE); - logger.begin(9600); -#endif - - logger.println("Repeater example for EspSoftwareSerial"); - start = micros(); - rxCount = 0; - seqErrors = 0; - parityErrors = 0; - expected = -1; -} - -void loop() { -#ifdef HWLOOPBACK -#if defined(ESP8266) - if (repeater.hasOverrun()) { logger.println("repeater.overrun"); } -#endif -#else - if (repeater.overflow()) { logger.println("repeater.overflow"); } -#endif - -#ifdef HALFDUPLEX - char block[BLOCKSIZE]; -#endif - // starting deadline for the first bytes to come in - uint32_t deadlineStart = ESP.getCycleCount(); - int inCnt = 0; - while ((ESP.getCycleCount() - deadlineStart) < (1000000UL * 12 * BLOCKSIZE) / IUTBITRATE * 24 * ESP.getCpuFreqMHz()) { - int avail = repeater.available(); - for (int i = 0; i < avail; ++i) - { - int r = repeater.read(); - if (r == -1) { logger.println("read() == -1"); } - if (expected == -1) { expected = r; } - else { - expected = (expected + 1) % (1UL << (5 + swSerialConfig % 4)); - } - if (r != expected) { - ++seqErrors; - expected = -1; - } -#ifndef HWLOOPBACK - if (repeater.readParity() != (static_cast(swSerialConfig & 010) ? repeater.parityOdd(r) : repeater.parityEven(r))) - { - ++parityErrors; - } -#endif - ++rxCount; -#ifdef HALFDUPLEX - block[inCnt] = r; -#else - repeater.write(r); -#endif - if (++inCnt >= BLOCKSIZE) { break; } - } - if (inCnt >= BLOCKSIZE) { break; } - // wait for more outstanding bytes to trickle in - if (avail) deadlineStart = ESP.getCycleCount(); - } - -#ifdef HALFDUPLEX - repeater.write(block, inCnt); -#endif - - if (rxCount >= ReportInterval) { - auto end = micros(); - unsigned long interval = end - start; - long cps = rxCount * (1000000.0 / interval); - long seqErrorsps = seqErrors * (1000000.0 / interval); - logger.print(bitRateTxt + 10 * cps + "bps, " - + seqErrorsps + "cps seq. errors (" + 100.0 * seqErrors / rxCount + "%)"); -#ifndef HWLOOPBACK - if (0 != (swSerialConfig & 070)) - { - logger.print(" ("); logger.print(parityErrors); logger.print(" parity errors)"); - } - else -#endif - { - logger.println(); - } - start = end; - rxCount = 0; - seqErrors = 0; - parityErrors = 0; - expected = -1; - } -} diff --git a/lib/EspSoftwareSerial/examples/servoTester/servoTester.ino b/lib/EspSoftwareSerial/examples/servoTester/servoTester.ino deleted file mode 100644 index cbc784d8..00000000 --- a/lib/EspSoftwareSerial/examples/servoTester/servoTester.ino +++ /dev/null @@ -1,115 +0,0 @@ -#include -#include - -SoftwareSerial swSer; - -byte buf[10] = { 0xFA, 0xAF,0x00,0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0xED }; -byte cmd[10] = { 0xFA, 0xAF,0x00,0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0xED }; -byte ver[10] = { 0xFC, 0xCF,0x00,0xAA,0x41, 0x16, 0x51, 0x01, 0x00, 0xED }; - - -void setup() { - delay(2000); - Serial.begin(115200); - Serial.println("\nAlpha 1S Servo Tester"); - swSer.begin(115200, SWSERIAL_8N1, 12, 12, false, 256); -} - -void loop() { - for (int i = 1; i <= 32; i++) { - GetVersion(i); - delay(100); - } - SetLED(1, 0); - GoPos(1, 0, 50); - delay(1000); - GoPos(1, 90, 50); - delay(1000); - GoPos(1, 100, 50); - delay(1000); - SetLED(1, 1); - delay(2000); -} - - - - -void GetVersion(byte id) { - memcpy(buf, cmd, 10); - buf[0] = 0xFC; - buf[1] = 0xCF; - buf[2] = id; - buf[3] = 0x01; - SendCommand(); -} - - -void GoPos(byte id, byte Pos, byte Time) { - memcpy(buf, cmd, 10); - buf[2] = id; - buf[3] = 0x01; - buf[4] = Pos; - buf[5] = Time; - buf[6] = 0x00; - buf[7] = Time; - SendCommand(); -} - -void GetPos(byte id) { - memcpy(buf, cmd, 10); - buf[2] = id; - buf[3] = 0x02; - SendCommand(); -} - - -void SetLED(byte id, byte mode) { - memcpy(buf, cmd, 10); - buf[2] = id; - buf[3] = 0x04; - buf[4] = mode; - SendCommand(); -} - -void SendCommand() { - SendCommand(true); -} - -void SendCommand(bool checkResult) { - byte sum = 0; - for (int i = 2; i < 8; i++) { - sum += buf[i]; - } - buf[8] = sum; - ShowCommand(); - swSer.flush(); - swSer.enableTx(true); - swSer.write(buf, 10); - swSer.enableTx(false); - if (checkResult) checkReturn(); -} - -void ShowCommand() { - Serial.print(millis()); - Serial.print(" OUT>>"); - for (int i = 0; i < 10; i++) { - Serial.print((buf[i] < 0x10 ? " 0" : " ")); - Serial.print(buf[i], HEX); - } - Serial.println(); -} - -void checkReturn() { - unsigned long startMs = millis(); - while (((millis() - startMs) < 500) && (!swSer.available())); - if (swSer.available()) { - Serial.print(millis()); - Serial.print(" IN>>>"); - while (swSer.available()) { - byte ch = (byte)swSer.read(); - Serial.print((ch < 0x10 ? " 0" : " ")); - Serial.print(ch, HEX); - } - Serial.println(); - } -} diff --git a/lib/EspSoftwareSerial/examples/swsertest/swsertest.ino b/lib/EspSoftwareSerial/examples/swsertest/swsertest.ino deleted file mode 100644 index a047c1be..00000000 --- a/lib/EspSoftwareSerial/examples/swsertest/swsertest.ino +++ /dev/null @@ -1,47 +0,0 @@ -// On ESP8266: -// At 80MHz runs up 57600ps, and at 160MHz CPU frequency up to 115200bps with only negligible errors. -// Connect pin 12 to 14. - -#include - -#if defined(ESP8266) && !defined(D5) -#define D5 (14) -#define D6 (12) -#define D7 (13) -#define D8 (15) -#endif - -#ifdef ESP32 -#define BAUD_RATE 57600 -#else -#define BAUD_RATE 57600 -#endif - -// Reminder: the buffer size optimizations here, in particular the isrBufSize that only accommodates -// a single 8N1 word, are on the basis that any char written to the loopback SoftwareSerial adapter gets read -// before another write is performed. Block writes with a size greater than 1 would usually fail. -SoftwareSerial swSer; - -void setup() { - Serial.begin(115200); - swSer.begin(BAUD_RATE, SWSERIAL_8N1, D5, D6, false, 95, 11); - - Serial.println("\nSoftware serial test started"); - - for (char ch = ' '; ch <= 'z'; ch++) { - swSer.write(ch); - } - swSer.println(""); -} - -void loop() { - while (swSer.available() > 0) { - Serial.write(swSer.read()); - yield(); - } - while (Serial.available() > 0) { - swSer.write(Serial.read()); - yield(); - } - -} diff --git a/lib/EspSoftwareSerial/keywords.txt b/lib/EspSoftwareSerial/keywords.txt deleted file mode 100644 index 52d48ab3..00000000 --- a/lib/EspSoftwareSerial/keywords.txt +++ /dev/null @@ -1,43 +0,0 @@ -####################################### -# Syntax Coloring Map for SoftwareSerial -# (esp8266) -####################################### - -####################################### -# Datatypes (KEYWORD1) -####################################### - -SoftwareSerial KEYWORD1 - -####################################### -# Methods and Functions (KEYWORD2) -####################################### - -begin KEYWORD2 -baudRate KEYWORD2 -setTransmitEnablePin KEYWORD2 -enableIntTx KEYWORD2 -overflow KEYWORD2 -available KEYWORD2 -peek KEYWORD2 -read KEYWORD2 -flush KEYWORD2 -write KEYWORD2 -enableRx KEYWORD2 -enableTx KEYWORD2 -listen KEYWORD2 -end KEYWORD2 -isListening KEYWORD2 -stopListening KEYWORD2 -onReceive KEYWORD2 -perform_work KEYWORD2 - -####################################### -# Constants (LITERAL1) -####################################### - -SW_SERIAL_UNUSED_PIN LITERAL1 -SWSERIAL_5N1 LITERAL1 -SWSERIAL_6N1 LITERAL1 -SWSERIAL_7N1 LITERAL1 -SWSERIAL_8N1 LITERAL1 diff --git a/lib/EspSoftwareSerial/library.json b/lib/EspSoftwareSerial/library.json deleted file mode 100644 index 3415b056..00000000 --- a/lib/EspSoftwareSerial/library.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "EspSoftwareSerial", - "version": "6.6.1", - "keywords": [ - "serial", "io", "softwareserial" - ], - "description": "Implementation of the Arduino software serial for ESP8266/ESP32.", - "repository": - { - "type": "git", - "url": "https://github.com/plerup/espsoftwareserial" - }, - "frameworks": "arduino", - "platforms": "*" -} diff --git a/lib/EspSoftwareSerial/library.properties b/lib/EspSoftwareSerial/library.properties deleted file mode 100644 index bd052153..00000000 --- a/lib/EspSoftwareSerial/library.properties +++ /dev/null @@ -1,9 +0,0 @@ -name=EspSoftwareSerial -version=6.6.1 -author=Peter Lerup, Dirk Kaar -maintainer=Peter Lerup -sentence=Implementation of the Arduino software serial for ESP8266/ESP32. -paragraph= -category=Signal Input/Output -url=https://github.com/plerup/espsoftwareserial/ -architectures=esp8266,esp32 diff --git a/lib/EspSoftwareSerial/src/SoftwareSerial.cpp b/lib/EspSoftwareSerial/src/SoftwareSerial.cpp deleted file mode 100644 index 0e7b8e08..00000000 --- a/lib/EspSoftwareSerial/src/SoftwareSerial.cpp +++ /dev/null @@ -1,542 +0,0 @@ -/* - -SoftwareSerial.cpp - Implementation of the Arduino software serial for ESP8266/ESP32. -Copyright (c) 2015-2016 Peter Lerup. All rights reserved. -Copyright (c) 2018-2019 Dirk O. Kaar. All rights reserved. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -*/ - -#include "SoftwareSerial.h" -#include - -#ifdef ESP32 -#define xt_rsil(a) (a) -#define xt_wsr_ps(a) -#endif - -constexpr uint8_t BYTE_ALL_BITS_SET = ~static_cast(0); - -SoftwareSerial::SoftwareSerial() { - m_isrOverflow = false; -} - -SoftwareSerial::SoftwareSerial(int8_t rxPin, int8_t txPin, bool invert) -{ - m_isrOverflow = false; - m_rxPin = rxPin; - m_txPin = txPin; - m_invert = invert; -} - -SoftwareSerial::~SoftwareSerial() { - end(); -} - -bool SoftwareSerial::isValidGPIOpin(int8_t pin) { -#if defined(ESP8266) - return (pin >= 0 && pin <= 5) || (pin >= 12 && pin <= 15); -#elif defined(ESP32) - return pin == 0 || pin == 2 || (pin >= 4 && pin <= 5) || (pin >= 12 && pin <= 19) || - (pin >= 21 && pin <= 23) || (pin >= 25 && pin <= 27) || (pin >= 32 && pin <= 35); -#else - return true; -#endif -} - -void SoftwareSerial::begin(uint32_t baud, SoftwareSerialConfig config, - int8_t rxPin, int8_t txPin, - bool invert, int bufCapacity, int isrBufCapacity) { - if (-1 != rxPin) m_rxPin = rxPin; - if (-1 != txPin) m_txPin = txPin; - m_oneWire = (m_rxPin == m_txPin); - m_invert = invert; - m_dataBits = 5 + (config & 07); - m_parityMode = static_cast(config & 070); - m_stopBits = 1 + ((config & 0300) ? 1 : 0); - m_pduBits = m_dataBits + static_cast(m_parityMode) + m_stopBits; - m_bitCycles = (ESP.getCpuFreqMHz() * 1000000UL + baud / 2) / baud; - m_intTxEnabled = true; - if (isValidGPIOpin(m_rxPin)) { - std::unique_ptr > buffer(new circular_queue((bufCapacity > 0) ? bufCapacity : 64)); - m_buffer = move(buffer); - if (m_parityMode) - { - std::unique_ptr > parityBuffer(new circular_queue((bufCapacity > 0) ? (bufCapacity + 7) / 8 : 8)); - m_parityBuffer = move(parityBuffer); - m_parityInPos = m_parityOutPos = 1; - } - std::unique_ptr > isrBuffer(new circular_queue((isrBufCapacity > 0) ? isrBufCapacity : (sizeof(uint8_t) * 8 + 2) * bufCapacity)); - m_isrBuffer = move(isrBuffer); - if (m_buffer && (!m_parityMode || m_parityBuffer) && m_isrBuffer) { - m_rxValid = true; - pinMode(m_rxPin, INPUT_PULLUP); - } - } - if (isValidGPIOpin(m_txPin) -#ifdef ESP8266 - || ((m_txPin == 16) && !m_oneWire)) { -#else - ) { -#endif - m_txValid = true; - if (!m_oneWire) { - pinMode(m_txPin, OUTPUT); - digitalWrite(m_txPin, !m_invert); - } - } - if (!m_rxEnabled) { enableRx(true); } -} - -void SoftwareSerial::end() -{ - enableRx(false); - m_txValid = false; - if (m_buffer) { - m_buffer.reset(); - } - m_parityBuffer.reset(); - if (m_isrBuffer) { - m_isrBuffer.reset(); - } -} - -uint32_t SoftwareSerial::baudRate() { - return ESP.getCpuFreqMHz() * 1000000UL / m_bitCycles; -} - -void SoftwareSerial::setTransmitEnablePin(int8_t txEnablePin) { - if (isValidGPIOpin(txEnablePin)) { - m_txEnableValid = true; - m_txEnablePin = txEnablePin; - pinMode(m_txEnablePin, OUTPUT); - digitalWrite(m_txEnablePin, LOW); - } - else { - m_txEnableValid = false; - } -} - -void SoftwareSerial::enableIntTx(bool on) { - m_intTxEnabled = on; -} - -void SoftwareSerial::enableTx(bool on) { - if (m_txValid && m_oneWire) { - if (on) { - enableRx(false); - pinMode(m_txPin, OUTPUT); - digitalWrite(m_txPin, !m_invert); - } - else { - pinMode(m_rxPin, INPUT_PULLUP); - enableRx(true); - } - } -} - -void SoftwareSerial::enableRx(bool on) { - if (m_rxValid) { - if (on) { - m_rxCurBit = m_pduBits - 1; - // Init to stop bit level and current cycle - m_isrLastCycle = (ESP.getCycleCount() | 1) ^ m_invert; - if (m_bitCycles >= (ESP.getCpuFreqMHz() * 1000000UL) / 74880UL) - attachInterruptArg(digitalPinToInterrupt(m_rxPin), reinterpret_cast(rxBitISR), this, CHANGE); - else - attachInterruptArg(digitalPinToInterrupt(m_rxPin), reinterpret_cast(rxBitSyncISR), this, m_invert ? RISING : FALLING); - } - else { - detachInterrupt(digitalPinToInterrupt(m_rxPin)); - } - m_rxEnabled = on; - } -} - -int SoftwareSerial::read() { - if (!m_rxValid) { return -1; } - if (!m_buffer->available()) { - rxBits(); - if (!m_buffer->available()) { return -1; } - } - auto val = m_buffer->pop(); - if (m_parityBuffer) - { - m_lastReadParity = m_parityBuffer->peek() & m_parityOutPos; - m_parityOutPos <<= 1; - if (!m_parityOutPos) - { - m_parityOutPos = 1; - m_parityBuffer->pop(); - } - } - return val; -} - -size_t SoftwareSerial::read(uint8_t * buffer, size_t size) { - if (!m_rxValid) { return 0; } - size_t avail; - if (0 == (avail = m_buffer->pop_n(buffer, size))) { - rxBits(); - avail = m_buffer->pop_n(buffer, size); - } - if (!avail) return 0; - if (m_parityBuffer) { - uint32_t parityBits = avail; - while (m_parityOutPos >>= 1) ++parityBits; - m_parityOutPos = (1 << (parityBits % 8)); - m_parityBuffer->pop_n(nullptr, parityBits / 8); - } - return avail; -} - -size_t SoftwareSerial::readBytes(uint8_t * buffer, size_t size) { - if (!m_rxValid || !size) { return 0; } - size_t count = 0; - const auto start = millis(); - do { - count += read(&buffer[count], size - count); - if (count >= size) break; - yield(); - } while (millis() - start < _timeout); - return count; -} - -int SoftwareSerial::available() { - if (!m_rxValid) { return 0; } - rxBits(); - int avail = m_buffer->available(); - if (!avail) { - optimistic_yield(10000UL); - } - return avail; -} - -void ICACHE_RAM_ATTR SoftwareSerial::preciseDelay(bool sync) { - if (!sync) - { - // Reenable interrupts while delaying to avoid other tasks piling up - if (!m_intTxEnabled) { xt_wsr_ps(m_savedPS); } - auto expired = ESP.getCycleCount() - m_periodStart; - if (expired < m_periodDuration) - { - auto ms = (m_periodDuration - expired) / ESP.getCpuFreqMHz() / 1000UL; - if (ms) delay(ms); - } - while ((ESP.getCycleCount() - m_periodStart) < m_periodDuration) { optimistic_yield(10000); } - // Disable interrupts again - if (!m_intTxEnabled) { m_savedPS = xt_rsil(15); } - } - else - { - while ((ESP.getCycleCount() - m_periodStart) < m_periodDuration) {} - } - m_periodDuration = 0; - m_periodStart = ESP.getCycleCount(); -} - -void ICACHE_RAM_ATTR SoftwareSerial::writePeriod( - uint32_t dutyCycle, uint32_t offCycle, bool withStopBit) { - preciseDelay(true); - if (dutyCycle) - { - digitalWrite(m_txPin, HIGH); - m_periodDuration += dutyCycle; - if (offCycle || (withStopBit && !m_invert)) preciseDelay(!withStopBit || m_invert); - } - if (offCycle) - { - digitalWrite(m_txPin, LOW); - m_periodDuration += offCycle; - if (withStopBit && m_invert) preciseDelay(false); - } -} - -size_t SoftwareSerial::write(uint8_t byte) { - return write(&byte, 1); -} - -size_t SoftwareSerial::write(uint8_t byte, SoftwareSerialParity parity) { - return write(&byte, 1, parity); -} - -size_t SoftwareSerial::write(const uint8_t * buffer, size_t size) { - return write(buffer, size, m_parityMode); -} - -size_t ICACHE_RAM_ATTR SoftwareSerial::write(const uint8_t * buffer, size_t size, SoftwareSerialParity parity) { - if (m_rxValid) { rxBits(); } - if (!m_txValid) { return -1; } - - if (m_txEnableValid) { - digitalWrite(m_txEnablePin, HIGH); - } - // Stop bit: if inverted, LOW, otherwise HIGH - bool b = !m_invert; - uint32_t dutyCycle = 0; - uint32_t offCycle = 0; - if (!m_intTxEnabled) { - // Disable interrupts in order to get a clean transmit timing - m_savedPS = xt_rsil(15); - } - const uint32_t dataMask = ((1UL << m_dataBits) - 1); - bool withStopBit = true; - m_periodDuration = 0; - m_periodStart = ESP.getCycleCount(); - for (size_t cnt = 0; cnt < size; ++cnt) { - uint8_t byte = ~buffer[cnt] & dataMask; - // push LSB start-data-parity-stop bit pattern into uint32_t - // Stop bits: HIGH - uint32_t word = ~0UL; - // parity bit, if any - if (parity && m_parityMode) - { - uint32_t parityBit; - switch (parity) - { - case SWSERIAL_PARITY_EVEN: - // from inverted, so use odd parity - parityBit = byte; - parityBit ^= parityBit >> 4; - parityBit &= 0xf; - parityBit = (0x9669 >> parityBit) & 1; - break; - case SWSERIAL_PARITY_ODD: - // from inverted, so use even parity - parityBit = byte; - parityBit ^= parityBit >> 4; - parityBit &= 0xf; - parityBit = (0x6996 >> parityBit) & 1; - break; - case SWSERIAL_PARITY_MARK: - parityBit = false; - break; - case SWSERIAL_PARITY_SPACE: - // suppresses warning parityBit uninitialized - default: - parityBit = true; - break; - } - word ^= parityBit << m_dataBits; - } - word ^= byte; - // Stop bit: LOW - word <<= 1; - if (m_invert) word = ~word; - for (int i = 0; i <= m_pduBits; ++i) { - bool pb = b; - b = word & (1UL << i); - if (!pb && b) { - writePeriod(dutyCycle, offCycle, withStopBit); - withStopBit = false; - dutyCycle = offCycle = 0; - } - if (b) { - dutyCycle += m_bitCycles; - } - else { - offCycle += m_bitCycles; - } - } - withStopBit = true; - } - writePeriod(dutyCycle, offCycle, true); - if (!m_intTxEnabled) { - // restore the interrupt state - xt_wsr_ps(m_savedPS); - } - if (m_txEnableValid) { - digitalWrite(m_txEnablePin, LOW); - } - return size; -} - -void SoftwareSerial::flush() { - if (!m_rxValid) { return; } - m_buffer->flush(); - if (m_parityBuffer) - { - m_parityInPos = m_parityOutPos = 1; - m_parityBuffer->flush(); - } -} - -bool SoftwareSerial::overflow() { - bool res = m_overflow; - m_overflow = false; - return res; -} - -int SoftwareSerial::peek() { - if (!m_rxValid) { return -1; } - if (!m_buffer->available()) { - rxBits(); - if (!m_buffer->available()) return -1; - } - auto val = m_buffer->peek(); - if (m_parityBuffer) m_lastReadParity = m_parityBuffer->peek() & m_parityOutPos; - return val; -} - -void SoftwareSerial::rxBits() { - int isrAvail = m_isrBuffer->available(); -#ifdef ESP8266 - if (m_isrOverflow.load()) { - m_overflow = true; - m_isrOverflow.store(false); - } -#else - if (m_isrOverflow.exchange(false)) { - m_overflow = true; - } -#endif - - // stop bit can go undetected if leading data bits are at same level - // and there was also no next start bit yet, so one byte may be pending. - // low-cost check first - if (!isrAvail && m_rxCurBit >= -1 && m_rxCurBit < m_pduBits - m_stopBits) { - uint32_t detectionCycles = (m_pduBits - m_stopBits - m_rxCurBit) * m_bitCycles; - if (ESP.getCycleCount() - m_isrLastCycle > detectionCycles) { - // Produce faux stop bit level, prevents start bit maldetection - // cycle's LSB is repurposed for the level bit - rxBits(((m_isrLastCycle + detectionCycles) | 1) ^ m_invert); - } - } - - m_isrBuffer->for_each([this](const uint32_t& isrCycle) { rxBits(isrCycle); }); -} - -void SoftwareSerial::rxBits(const uint32_t & isrCycle) { - bool level = (m_isrLastCycle & 1) ^ m_invert; - - // error introduced by edge value in LSB of isrCycle is negligible - int32_t cycles = isrCycle - m_isrLastCycle; - m_isrLastCycle = isrCycle; - - uint8_t bits = cycles / m_bitCycles; - if (cycles % m_bitCycles > (m_bitCycles >> 1)) ++bits; - while (bits > 0) { - // start bit detection - if (m_rxCurBit >= (m_pduBits - 1)) { - // leading edge of start bit - if (level) break; - m_rxCurBit = -1; - --bits; - continue; - } - // data bits - if (m_rxCurBit >= -1 && m_rxCurBit < (m_dataBits - 1)) { - int8_t dataBits = min(bits, static_cast(m_dataBits - 1 - m_rxCurBit)); - m_rxCurBit += dataBits; - bits -= dataBits; - m_rxCurByte >>= dataBits; - if (level) { m_rxCurByte |= (BYTE_ALL_BITS_SET << (8 - dataBits)); } - continue; - } - // parity bit - if (m_parityMode && m_rxCurBit == (m_dataBits - 1)) { - ++m_rxCurBit; - --bits; - m_rxCurParity = level; - continue; - } - // stop bits - if (m_rxCurBit < (m_pduBits - m_stopBits - 1)) { - ++m_rxCurBit; - --bits; - continue; - } - if (m_rxCurBit == (m_pduBits - m_stopBits - 1)) { - // Store the received value in the buffer unless we have an overflow - // if not high stop bit level, discard word - if (level) - { - m_rxCurByte >>= (sizeof(uint8_t) * 8 - m_dataBits); - if (!m_buffer->push(m_rxCurByte)) { - m_overflow = true; - } - else { - if (m_parityBuffer) - { - if (m_rxCurParity) { - m_parityBuffer->pushpeek() |= m_parityInPos; - } - else { - m_parityBuffer->pushpeek() &= ~m_parityInPos; - } - m_parityInPos <<= 1; - if (!m_parityInPos) - { - m_parityBuffer->push(); - m_parityInPos = 1; - } - } - } - } - m_rxCurBit = m_pduBits; - // reset to 0 is important for masked bit logic - m_rxCurByte = 0; - m_rxCurParity = false; - break; - } - break; - } -} - -void ICACHE_RAM_ATTR SoftwareSerial::rxBitISR(SoftwareSerial * self) { - uint32_t curCycle = ESP.getCycleCount(); - bool level = digitalRead(self->m_rxPin); - - // Store level and cycle in the buffer unless we have an overflow - // cycle's LSB is repurposed for the level bit - if (!self->m_isrBuffer->push((curCycle | 1U) ^ !level)) self->m_isrOverflow.store(true); -} - -void ICACHE_RAM_ATTR SoftwareSerial::rxBitSyncISR(SoftwareSerial * self) { - uint32_t start = ESP.getCycleCount(); - uint32_t wait = self->m_bitCycles - 172U; - - bool level = self->m_invert; - // Store level and cycle in the buffer unless we have an overflow - // cycle's LSB is repurposed for the level bit - if (!self->m_isrBuffer->push(((start + wait) | 1U) ^ !level)) self->m_isrOverflow.store(true); - - for (uint32_t i = 0; i < self->m_pduBits; ++i) { - while (ESP.getCycleCount() - start < wait) {}; - wait += self->m_bitCycles; - - // Store level and cycle in the buffer unless we have an overflow - // cycle's LSB is repurposed for the level bit - if (digitalRead(self->m_rxPin) != level) - { - if (!self->m_isrBuffer->push(((start + wait) | 1U) ^ level)) self->m_isrOverflow.store(true); - level = !level; - } - } -} - -void SoftwareSerial::onReceive(Delegate handler) { - receiveHandler = handler; -} - -void SoftwareSerial::perform_work() { - if (!m_rxValid) { return; } - rxBits(); - if (receiveHandler) { - int avail = m_buffer->available(); - if (avail) { receiveHandler(avail); } - } -} diff --git a/lib/EspSoftwareSerial/src/SoftwareSerial.h b/lib/EspSoftwareSerial/src/SoftwareSerial.h deleted file mode 100644 index 371e3731..00000000 --- a/lib/EspSoftwareSerial/src/SoftwareSerial.h +++ /dev/null @@ -1,255 +0,0 @@ -/* -SoftwareSerial.h - -SoftwareSerial.cpp - Implementation of the Arduino software serial for ESP8266/ESP32. -Copyright (c) 2015-2016 Peter Lerup. All rights reserved. -Copyright (c) 2018-2019 Dirk O. Kaar. All rights reserved. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -*/ - -#ifndef __SoftwareSerial_h -#define __SoftwareSerial_h - -#include "circular_queue/circular_queue.h" -#include - -enum SoftwareSerialParity : uint8_t { - SWSERIAL_PARITY_NONE = 000, - SWSERIAL_PARITY_EVEN = 020, - SWSERIAL_PARITY_ODD = 030, - SWSERIAL_PARITY_MARK = 040, - SWSERIAL_PARITY_SPACE = 070, -}; - -enum SoftwareSerialConfig { - SWSERIAL_5N1 = SWSERIAL_PARITY_NONE, - SWSERIAL_6N1, - SWSERIAL_7N1, - SWSERIAL_8N1, - SWSERIAL_5E1 = SWSERIAL_PARITY_EVEN, - SWSERIAL_6E1, - SWSERIAL_7E1, - SWSERIAL_8E1, - SWSERIAL_5O1 = SWSERIAL_PARITY_ODD, - SWSERIAL_6O1, - SWSERIAL_7O1, - SWSERIAL_8O1, - SWSERIAL_5M1 = SWSERIAL_PARITY_MARK, - SWSERIAL_6M1, - SWSERIAL_7M1, - SWSERIAL_8M1, - SWSERIAL_5S1 = SWSERIAL_PARITY_SPACE, - SWSERIAL_6S1, - SWSERIAL_7S1, - SWSERIAL_8S1, - SWSERIAL_5N2 = 0200 | SWSERIAL_PARITY_NONE, - SWSERIAL_6N2, - SWSERIAL_7N2, - SWSERIAL_8N2, - SWSERIAL_5E2 = 0200 | SWSERIAL_PARITY_EVEN, - SWSERIAL_6E2, - SWSERIAL_7E2, - SWSERIAL_8E2, - SWSERIAL_5O2 = 0200 | SWSERIAL_PARITY_ODD, - SWSERIAL_6O2, - SWSERIAL_7O2, - SWSERIAL_8O2, - SWSERIAL_5M2 = 0200 | SWSERIAL_PARITY_MARK, - SWSERIAL_6M2, - SWSERIAL_7M2, - SWSERIAL_8M2, - SWSERIAL_5S2 = 0200 | SWSERIAL_PARITY_SPACE, - SWSERIAL_6S2, - SWSERIAL_7S2, - SWSERIAL_8S2, -}; - -/// This class is compatible with the corresponding AVR one, however, -/// the constructor takes no arguments, for compatibility with the -/// HardwareSerial class. -/// Instead, the begin() function handles pin assignments and logic inversion. -/// It also has optional input buffer capacity arguments for byte buffer and ISR bit buffer. -/// Bitrates up to at least 115200 can be used. -class SoftwareSerial : public Stream { -public: - SoftwareSerial(); - /// Ctor to set defaults for pins. - /// @param rxPin the GPIO pin used for RX - /// @param txPin -1 for onewire protocol, GPIO pin used for twowire TX - SoftwareSerial(int8_t rxPin, int8_t txPin = -1, bool invert = false); - SoftwareSerial(const SoftwareSerial&) = delete; - SoftwareSerial& operator= (const SoftwareSerial&) = delete; - virtual ~SoftwareSerial(); - /// Configure the SoftwareSerial object for use. - /// @param baud the TX/RX bitrate - /// @param config sets databits, parity, and stop bit count - /// @param rxPin -1 or default: either no RX pin, or keeps the rxPin set in the ctor - /// @param txPin -1 or default: either no TX pin (onewire), or keeps the txPin set in the ctor - /// @param invert true: uses invert line level logic - /// @param bufCapacity the capacity for the received bytes buffer - /// @param isrBufCapacity 0: derived from bufCapacity. The capacity of the internal asynchronous - /// bit receive buffer, a suggested size is bufCapacity times the sum of - /// start, data, parity and stop bit count. - void begin(uint32_t baud, SoftwareSerialConfig config, - int8_t rxPin, int8_t txPin, bool invert, - int bufCapacity = 64, int isrBufCapacity = 0); - void begin(uint32_t baud, SoftwareSerialConfig config, - int8_t rxPin, int8_t txPin) { - begin(baud, config, rxPin, txPin, m_invert); - } - void begin(uint32_t baud, SoftwareSerialConfig config, - int8_t rxPin) { - begin(baud, config, rxPin, m_txPin, m_invert); - } - void begin(uint32_t baud, SoftwareSerialConfig config = SWSERIAL_8N1) { - begin(baud, config, m_rxPin, m_txPin, m_invert); - } - - uint32_t baudRate(); - /// Transmit control pin. - void setTransmitEnablePin(int8_t txEnablePin); - /// Enable or disable interrupts during tx. - void enableIntTx(bool on); - - bool overflow(); - - int available() override; - int availableForWrite() { - if (!m_txValid) return 0; - return 1; - } - int peek() override; - int read() override; - /// @returns The verbatim parity bit associated with the last read() or peek() call - bool readParity() - { - return m_lastReadParity; - } - /// @returns The calculated bit for even parity of the parameter byte - static bool parityEven(uint8_t byte) { - byte ^= byte >> 4; - byte &= 0xf; - return (0x6996 >> byte) & 1; - } - /// @returns The calculated bit for odd parity of the parameter byte - static bool parityOdd(uint8_t byte) { - byte ^= byte >> 4; - byte &= 0xf; - return (0x9669 >> byte) & 1; - } - /// The read(buffer, size) functions are non-blocking, the same as readBytes but without timeout - size_t read(uint8_t* buffer, size_t size); - /// The read(buffer, size) functions are non-blocking, the same as readBytes but without timeout - size_t read(char* buffer, size_t size) { - return read(reinterpret_cast(buffer), size); - } - /// @returns The number of bytes read into buffer, up to size. Times out if the limit set through - /// Stream::setTimeout() is reached. - size_t readBytes(uint8_t* buffer, size_t size) override; - /// @returns The number of bytes read into buffer, up to size. Times out if the limit set through - /// Stream::setTimeout() is reached. - size_t readBytes(char* buffer, size_t size) override { - return readBytes(reinterpret_cast(buffer), size); - } - void flush() override; - size_t write(uint8_t byte) override; - size_t write(uint8_t byte, SoftwareSerialParity parity); - size_t write(const uint8_t* buffer, size_t size) override; - size_t write(const char* buffer, size_t size) { - return write(reinterpret_cast(buffer), size); - } - size_t write(const uint8_t* buffer, size_t size, SoftwareSerialParity parity); - size_t write(const char* buffer, size_t size, SoftwareSerialParity parity) { - return write(reinterpret_cast(buffer), size, parity); - } - operator bool() const { return m_rxValid || m_txValid; } - - /// Disable or enable interrupts on the rx pin. - void enableRx(bool on); - /// One wire control. - void enableTx(bool on); - - // AVR compatibility methods. - bool listen() { enableRx(true); return true; } - void end(); - bool isListening() { return m_rxEnabled; } - bool stopListening() { enableRx(false); return true; } - - /// Set an event handler for received data. - void onReceive(Delegate handler); - - /// Run the internal processing and event engine. Can be iteratively called - /// from loop, or otherwise scheduled. - void perform_work(); - - using Print::write; - -private: - // If sync is false, it's legal to exceed the deadline, for instance, - // by enabling interrupts. - void preciseDelay(bool sync); - // If withStopBit is set, either cycle contains a stop bit. - // If dutyCycle == 0, the level is not forced to HIGH. - // If offCycle == 0, the level remains unchanged from dutyCycle. - void writePeriod( - uint32_t dutyCycle, uint32_t offCycle, bool withStopBit); - bool isValidGPIOpin(int8_t pin); - /* check m_rxValid that calling is safe */ - void rxBits(); - void rxBits(const uint32_t& isrCycle); - - static void rxBitISR(SoftwareSerial* self); - static void rxBitSyncISR(SoftwareSerial* self); - - // Member variables - int8_t m_rxPin = -1; - int8_t m_txPin = -1; - int8_t m_txEnablePin = -1; - uint8_t m_dataBits; - bool m_oneWire; - bool m_rxValid = false; - bool m_rxEnabled = false; - bool m_txValid = false; - bool m_txEnableValid = false; - bool m_invert; - /// PDU bits include data, parity and stop bits; the start bit is not counted. - uint8_t m_pduBits; - bool m_intTxEnabled; - SoftwareSerialParity m_parityMode; - uint8_t m_stopBits; - bool m_lastReadParity; - bool m_overflow = false; - uint32_t m_bitCycles; - uint8_t m_parityInPos; - uint8_t m_parityOutPos; - int8_t m_rxCurBit; // 0 thru (m_pduBits - m_stopBits - 1): data/parity bits. -1: start bit. (m_pduBits - 1): stop bit. - uint8_t m_rxCurByte = 0; - std::unique_ptr > m_buffer; - std::unique_ptr > m_parityBuffer; - uint32_t m_periodStart; - uint32_t m_periodDuration; - uint32_t m_savedPS = 0; - // the ISR stores the relative bit times in the buffer. The inversion corrected level is used as sign bit (2's complement): - // 1 = positive including 0, 0 = negative. - std::unique_ptr > m_isrBuffer; - std::atomic m_isrOverflow; - uint32_t m_isrLastCycle; - bool m_rxCurParity = false; - Delegate receiveHandler; -}; - -#endif // __SoftwareSerial_h diff --git a/lib/EspSoftwareSerial/src/circular_queue/Delegate.h b/lib/EspSoftwareSerial/src/circular_queue/Delegate.h deleted file mode 100644 index bd19c66e..00000000 --- a/lib/EspSoftwareSerial/src/circular_queue/Delegate.h +++ /dev/null @@ -1,1786 +0,0 @@ -/* -Delegate.h - An efficient interchangeable C function ptr and C++ std::function delegate -Copyright (c) 2019 Dirk O. Kaar. All rights reserved. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#ifndef __Delegate_h -#define __Delegate_h - -#if defined(ESP8266) -#include -#elif defined(ESP32) -#include -#else -#define ICACHE_RAM_ATTR -#define IRAM_ATTR -#endif - -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) -#include -#include -#else -#include "circular_queue/ghostl.h" -#endif - -namespace detail -{ - template - static R IRAM_ATTR vPtrToFunPtrExec(void* fn, P... args) - { - using target_type = R(P...); - return reinterpret_cast(fn)(std::forward(args...)); - } - -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - template - class DelegatePImpl { - public: - using target_type = R(P...); - protected: - using FunPtr = target_type*; - using FunAPtr = R(*)(A, P...); - using FunVPPtr = R(*)(void*, P...); - using FunctionType = std::function; - public: - DelegatePImpl() - { - kind = FP; - fn = nullptr; - } - - DelegatePImpl(std::nullptr_t) - { - kind = FP; - fn = nullptr; - } - - ~DelegatePImpl() - { - if (FUNC == kind) - functional.~FunctionType(); - else if (FPA == kind) - obj.~A(); - } - - DelegatePImpl(const DelegatePImpl& del) - { - kind = del.kind; - if (FUNC == del.kind) - { - new (&functional) FunctionType(del.functional); - } - else if (FPA == del.kind) - { - fnA = del.fnA; - new (&obj) A(del.obj); - } - else - { - fn = del.fn; - } - } - - DelegatePImpl(DelegatePImpl&& del) - { - kind = del.kind; - if (FUNC == del.kind) - { - new (&functional) FunctionType(std::move(del.functional)); - } - else if (FPA == del.kind) - { - fnA = del.fnA; - new (&obj) A(std::move(del.obj)); - } - else - { - fn = del.fn; - } - } - - DelegatePImpl(FunAPtr fnA, const A& obj) - { - kind = FPA; - DelegatePImpl::fnA = fnA; - new (&this->obj) A(obj); - } - - DelegatePImpl(FunAPtr fnA, A&& obj) - { - kind = FPA; - DelegatePImpl::fnA = fnA; - new (&this->obj) A(std::move(obj)); - } - - DelegatePImpl(FunPtr fn) - { - kind = FP; - DelegatePImpl::fn = fn; - } - - template DelegatePImpl(F functional) - { - kind = FUNC; - new (&this->functional) FunctionType(std::forward(functional)); - } - - DelegatePImpl& operator=(const DelegatePImpl& del) - { - if (this == &del) return *this; - if (kind != del.kind) - { - if (FUNC == kind) - { - functional.~FunctionType(); - } - else if (FPA == kind) - { - obj.~A(); - } - if (FUNC == del.kind) - { - new (&this->functional) FunctionType(); - } - else if (FPA == del.kind) - { - new (&obj) A; - } - kind = del.kind; - } - if (FUNC == del.kind) - { - functional = del.functional; - } - else if (FPA == del.kind) - { - fnA = del.fnA; - obj = del.obj; - } - else - { - fn = del.fn; - } - return *this; - } - - DelegatePImpl& operator=(DelegatePImpl&& del) - { - if (this == &del) return *this; - if (kind != del.kind) - { - if (FUNC == kind) - { - functional.~FunctionType(); - } - else if (FPA == kind) - { - obj.~A(); - } - if (FUNC == del.kind) - { - new (&this->functional) FunctionType(); - } - else if (FPA == del.kind) - { - new (&obj) A; - } - kind = del.kind; - } - if (FUNC == del.kind) - { - functional = std::move(del.functional); - } - else if (FPA == del.kind) - { - fnA = del.fnA; - obj = std::move(del.obj); - } - else - { - fn = del.fn; - } - return *this; - } - - DelegatePImpl& operator=(FunPtr fn) - { - if (FUNC == kind) - { - functional.~FunctionType(); - } - else if (FPA == kind) - { - obj.~A(); - } - kind = FP; - this->fn = fn; - return *this; - } - - DelegatePImpl& IRAM_ATTR operator=(std::nullptr_t) - { - if (FUNC == kind) - { - functional.~FunctionType(); - } - else if (FPA == kind) - { - obj.~A(); - } - kind = FP; - fn = nullptr; - return *this; - } - - operator bool() const - { - if (FP == kind) - { - return fn; - } - else if (FPA == kind) - { - return fnA; - } - else - { - return functional ? true : false; - } - } - - static R IRAM_ATTR vPtrToFunAPtrExec(void* self, P... args) - { - return static_cast(self)->fnA( - static_cast(self)->obj, - std::forward(args...)); - }; - - operator FunVPPtr() const - { - if (FP == kind) - { - return vPtrToFunPtrExec; - } - else if (FPA == kind) - { - return vPtrToFunAPtrExec; - } - else - { - return [](void* self, P... args) -> R - { - return static_cast(self)->functional(std::forward(args...)); - }; - } - } - - void* arg() const - { - if (FP == kind) - { - return reinterpret_cast(fn); - } - else - { - return const_cast(this); - } - } - - operator FunctionType() const - { - if (FP == kind) - { - return fn; - } - else if (FPA == kind) - { - return [this](P... args) { return fnA(obj, std::forward(args...)); }; - } - else - { - return functional; - } - } - - R IRAM_ATTR operator()(P... args) const - { - if (FP == kind) - { - return fn(std::forward(args...)); - } - else if (FPA == kind) - { - return fnA(obj, std::forward(args...)); - } - else - { - return functional(std::forward(args...)); - } - } - - protected: - enum { FUNC, FP, FPA } kind; - union { - FunctionType functional; - FunPtr fn; - struct { - FunAPtr fnA; - A obj; - }; - }; - }; -#else - template - class DelegatePImpl { - public: - using target_type = R(P...); - protected: - using FunPtr = target_type*; - using FunAPtr = R(*)(A, P...); - using FunVPPtr = R(*)(void*, P...); - public: - DelegatePImpl() - { - kind = FP; - fn = nullptr; - } - - DelegatePImpl(std::nullptr_t) - { - kind = FP; - fn = nullptr; - } - - DelegatePImpl(const DelegatePImpl& del) - { - kind = del.kind; - if (FPA == del.kind) - { - fnA = del.fnA; - obj = del.obj; - } - else - { - fn = del.fn; - } - } - - DelegatePImpl(DelegatePImpl&& del) - { - kind = del.kind; - if (FPA == del.kind) - { - fnA = del.fnA; - obj = std::move(del.obj); - } - else - { - fn = del.fn; - } - } - - DelegatePImpl(FunAPtr fnA, const A& obj) - { - kind = FPA; - DelegatePImpl::fnA = fnA; - this->obj = obj; - } - - DelegatePImpl(FunAPtr fnA, A&& obj) - { - kind = FPA; - DelegatePImpl::fnA = fnA; - this->obj = std::move(obj); - } - - DelegatePImpl(FunPtr fn) - { - kind = FP; - DelegatePImpl::fn = fn; - } - - template DelegatePImpl(F fn) - { - kind = FP; - DelegatePImpl::fn = std::forward(fn); - } - - DelegatePImpl& operator=(const DelegatePImpl& del) - { - if (this == &del) return *this; - if (kind != del.kind) - { - if (FPA == kind) - { - obj = {}; - } - kind = del.kind; - } - if (FPA == del.kind) - { - fnA = del.fnA; - obj = del.obj; - } - else - { - fn = del.fn; - } - return *this; - } - - DelegatePImpl& operator=(DelegatePImpl&& del) - { - if (this == &del) return *this; - if (kind != del.kind) - { - if (FPA == kind) - { - obj = {}; - } - kind = del.kind; - } - if (FPA == del.kind) - { - fnA = del.fnA; - obj = std::move(del.obj); - } - else - { - fn = del.fn; - } - return *this; - } - - DelegatePImpl& operator=(FunPtr fn) - { - if (FPA == kind) - { - obj = {}; - } - kind = FP; - this->fn = fn; - return *this; - } - - DelegatePImpl& IRAM_ATTR operator=(std::nullptr_t) - { - if (FPA == kind) - { - obj = {}; - } - kind = FP; - fn = nullptr; - return *this; - } - - operator bool() const - { - if (FP == kind) - { - return fn; - } - else - { - return fnA; - } - } - - static R IRAM_ATTR vPtrToFunAPtrExec(void* self, P... args) - { - return static_cast(self)->fnA( - static_cast(self)->obj, - std::forward(args...)); - }; - - operator FunVPPtr() const - { - if (FP == kind) - { - return vPtrToFunPtrExec; - } - else - { - return vPtrToFunAPtrExec; - } - } - - void* arg() const - { - if (FP == kind) - { - return reinterpret_cast(fn); - } - else - { - return const_cast(this); - } - } - - R IRAM_ATTR operator()(P... args) const - { - if (FP == kind) - { - return fn(std::forward(args...)); - } - else - { - return fnA(obj, std::forward(args...)); - } - } - - protected: - enum { FP, FPA } kind; - union { - FunPtr fn; - FunAPtr fnA; - }; - A obj; - }; -#endif - -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - template - class DelegatePImpl { - public: - using target_type = R(P...); - protected: - using FunPtr = target_type*; - using FunctionType = std::function; - using FunVPPtr = R(*)(void*, P...); - public: - DelegatePImpl() - { - kind = FP; - fn = nullptr; - } - - DelegatePImpl(std::nullptr_t) - { - kind = FP; - fn = nullptr; - } - - ~DelegatePImpl() - { - if (FUNC == kind) - functional.~FunctionType(); - } - - DelegatePImpl(const DelegatePImpl& del) - { - kind = del.kind; - if (FUNC == del.kind) - { - new (&functional) FunctionType(del.functional); - } - else - { - fn = del.fn; - } - } - - DelegatePImpl(DelegatePImpl&& del) - { - kind = del.kind; - if (FUNC == del.kind) - { - new (&functional) FunctionType(std::move(del.functional)); - } - else - { - fn = del.fn; - } - } - - DelegatePImpl(FunPtr fn) - { - kind = FP; - DelegatePImpl::fn = fn; - } - - template DelegatePImpl(F functional) - { - kind = FUNC; - new (&this->functional) FunctionType(std::forward(functional)); - } - - DelegatePImpl& operator=(const DelegatePImpl& del) - { - if (this == &del) return *this; - if (FUNC == kind && FUNC != del.kind) - { - functional.~FunctionType(); - } - else if (FUNC != kind && FUNC == del.kind) - { - new (&this->functional) FunctionType(); - } - kind = del.kind; - if (FUNC == del.kind) - { - functional = del.functional; - } - else - { - fn = del.fn; - } - return *this; - } - - DelegatePImpl& operator=(DelegatePImpl&& del) - { - if (this == &del) return *this; - if (FUNC == kind && FUNC != del.kind) - { - functional.~FunctionType(); - } - else if (FUNC != kind && FUNC == del.kind) - { - new (&this->functional) FunctionType(); - } - kind = del.kind; - if (FUNC == del.kind) - { - functional = std::move(del.functional); - } - else - { - fn = del.fn; - } - return *this; - } - - DelegatePImpl& operator=(FunPtr fn) - { - if (FUNC == kind) - { - functional.~FunctionType(); - kind = FP; - } - DelegatePImpl::fn = fn; - return *this; - } - - DelegatePImpl& IRAM_ATTR operator=(std::nullptr_t) - { - if (FUNC == kind) - { - functional.~FunctionType(); - } - kind = FP; - fn = nullptr; - return *this; - } - - operator bool() const - { - if (FP == kind) - { - return fn; - } - else - { - return functional ? true : false; - } - } - - operator FunVPPtr() const - { - if (FP == kind) - { - return vPtrToFunPtrExec; - } - else - { - return [](void* self, P... args) -> R - { - return static_cast(self)->functional(std::forward(args...)); - }; - } - } - - void* arg() const - { - if (FP == kind) - { - return reinterpret_cast(fn); - } - else - { - return const_cast(this); - } - } - - operator FunctionType() const - { - if (FP == kind) - { - return fn; - } - else - { - return functional; - } - } - - R IRAM_ATTR operator()(P... args) const - { - if (FP == kind) - { - return fn(std::forward(args...)); - } - else - { - return functional(std::forward(args...)); - } - } - - protected: - enum { FUNC, FP } kind; - union { - FunctionType functional; - FunPtr fn; - }; - }; -#else - template - class DelegatePImpl { - public: - using target_type = R(P...); - protected: - using FunPtr = target_type*; - using FunVPPtr = R(*)(void*, P...); - public: - DelegatePImpl() - { - fn = nullptr; - } - - DelegatePImpl(std::nullptr_t) - { - fn = nullptr; - } - - DelegatePImpl(const DelegatePImpl& del) - { - fn = del.fn; - } - - DelegatePImpl(DelegatePImpl&& del) - { - fn = std::move(del.fn); - } - - DelegatePImpl(FunPtr fn) - { - DelegatePImpl::fn = fn; - } - - template DelegatePImpl(F fn) - { - DelegatePImpl::fn = std::forward(fn); - } - - DelegatePImpl& operator=(const DelegatePImpl& del) - { - if (this == &del) return *this; - fn = del.fn; - return *this; - } - - DelegatePImpl& operator=(DelegatePImpl&& del) - { - if (this == &del) return *this; - fn = std::move(del.fn); - return *this; - } - - DelegatePImpl& operator=(FunPtr fn) - { - DelegatePImpl::fn = fn; - return *this; - } - - DelegatePImpl& IRAM_ATTR operator=(std::nullptr_t) - { - fn = nullptr; - return *this; - } - - operator bool() const - { - return fn; - } - - operator FunVPPtr() const - { - return vPtrToFunPtrExec; - } - - void* arg() const - { - return reinterpret_cast(fn); - } - - R IRAM_ATTR operator()(P... args) const - { - return fn(std::forward(args...)); - } - - protected: - FunPtr fn; - }; -#endif - -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - template - class DelegateImpl { - public: - using target_type = R(); - protected: - using FunPtr = target_type*; - using FunAPtr = R(*)(A); - using FunctionType = std::function; - using FunVPPtr = R(*)(void*); - public: - DelegateImpl() - { - kind = FP; - fn = nullptr; - } - - DelegateImpl(std::nullptr_t) - { - kind = FP; - fn = nullptr; - } - - ~DelegateImpl() - { - if (FUNC == kind) - functional.~FunctionType(); - else if (FPA == kind) - obj.~A(); - } - - DelegateImpl(const DelegateImpl& del) - { - kind = del.kind; - if (FUNC == del.kind) - { - new (&functional) FunctionType(del.functional); - } - else if (FPA == del.kind) - { - fnA = del.fnA; - new (&obj) A(del.obj); - } - else - { - fn = del.fn; - } - } - - DelegateImpl(DelegateImpl&& del) - { - kind = del.kind; - if (FUNC == del.kind) - { - new (&functional) FunctionType(std::move(del.functional)); - } - else if (FPA == del.kind) - { - fnA = del.fnA; - new (&obj) A(std::move(del.obj)); - } - else - { - fn = del.fn; - } - } - - DelegateImpl(FunAPtr fnA, const A& obj) - { - kind = FPA; - DelegateImpl::fnA = fnA; - new (&this->obj) A(obj); - } - - DelegateImpl(FunAPtr fnA, A&& obj) - { - kind = FPA; - DelegateImpl::fnA = fnA; - new (&this->obj) A(std::move(obj)); - } - - DelegateImpl(FunPtr fn) - { - kind = FP; - DelegateImpl::fn = fn; - } - - template DelegateImpl(F functional) - { - kind = FUNC; - new (&this->functional) FunctionType(std::forward(functional)); - } - - DelegateImpl& operator=(const DelegateImpl& del) - { - if (this == &del) return *this; - if (kind != del.kind) - { - if (FUNC == kind) - { - functional.~FunctionType(); - } - else if (FPA == kind) - { - obj.~A(); - } - if (FUNC == del.kind) - { - new (&this->functional) FunctionType(); - } - else if (FPA == del.kind) - { - new (&obj) A; - } - kind = del.kind; - } - if (FUNC == del.kind) - { - functional = del.functional; - } - else if (FPA == del.kind) - { - fnA = del.fnA; - obj = del.obj; - } - else - { - fn = del.fn; - } - return *this; - } - - DelegateImpl& operator=(DelegateImpl&& del) - { - if (this == &del) return *this; - if (kind != del.kind) - { - if (FUNC == kind) - { - functional.~FunctionType(); - } - else if (FPA == kind) - { - obj.~A(); - } - if (FUNC == del.kind) - { - new (&this->functional) FunctionType(); - } - else if (FPA == del.kind) - { - new (&obj) A; - } - kind = del.kind; - } - if (FUNC == del.kind) - { - functional = std::move(del.functional); - } - else if (FPA == del.kind) - { - fnA = del.fnA; - obj = std::move(del.obj); - } - else - { - fn = del.fn; - } - return *this; - } - - DelegateImpl& operator=(FunPtr fn) - { - if (FUNC == kind) - { - functional.~FunctionType(); - } - else if (FPA == kind) - { - obj.~A(); - } - kind = FP; - this->fn = fn; - return *this; - } - - DelegateImpl& IRAM_ATTR operator=(std::nullptr_t) - { - if (FUNC == kind) - { - functional.~FunctionType(); - } - else if (FPA == kind) - { - obj.~A(); - } - kind = FP; - fn = nullptr; - return *this; - } - - operator bool() const - { - if (FP == kind) - { - return fn; - } - else if (FPA == kind) - { - return fnA; - } - else - { - return functional ? true : false; - } - } - - static R IRAM_ATTR vPtrToFunAPtrExec(void* self) - { - return static_cast(self)->fnA( - static_cast(self)->obj); - }; - - operator FunVPPtr() const - { - if (FP == kind) - { - return reinterpret_cast(fn); - } - else if (FPA == kind) - { - return vPtrToFunAPtrExec; - } - else - { - return [](void* self) -> R - { - return static_cast(self)->functional(); - }; - } - } - - void* arg() const - { - if (FP == kind) - { - return nullptr; - } - else - { - return const_cast(this); - } - } - - operator FunctionType() const - { - if (FP == kind) - { - return fn; - } - else if (FPA == kind) - { - return [this]() { return fnA(obj); }; - } - else - { - return functional; - } - } - - R IRAM_ATTR operator()() const - { - if (FP == kind) - { - return fn(); - } - else if (FPA == kind) - { - return fnA(obj); - } - else - { - return functional(); - } - } - - protected: - enum { FUNC, FP, FPA } kind; - union { - FunctionType functional; - FunPtr fn; - struct { - FunAPtr fnA; - A obj; - }; - }; - }; -#else - template - class DelegateImpl { - public: - using target_type = R(); - protected: - using FunPtr = target_type*; - using FunAPtr = R(*)(A); - using FunVPPtr = R(*)(void*); - public: - DelegateImpl() - { - kind = FP; - fn = nullptr; - } - - DelegateImpl(std::nullptr_t) - { - kind = FP; - fn = nullptr; - } - - DelegateImpl(const DelegateImpl& del) - { - kind = del.kind; - if (FPA == del.kind) - { - fnA = del.fnA; - obj = del.obj; - } - else - { - fn = del.fn; - } - } - - DelegateImpl(DelegateImpl&& del) - { - kind = del.kind; - if (FPA == del.kind) - { - fnA = del.fnA; - obj = std::move(del.obj); - } - else - { - fn = del.fn; - } - } - - DelegateImpl(FunAPtr fnA, const A& obj) - { - kind = FPA; - DelegateImpl::fnA = fnA; - this->obj = obj; - } - - DelegateImpl(FunAPtr fnA, A&& obj) - { - kind = FPA; - DelegateImpl::fnA = fnA; - this->obj = std::move(obj); - } - - DelegateImpl(FunPtr fn) - { - kind = FP; - DelegateImpl::fn = fn; - } - - template DelegateImpl(F fn) - { - kind = FP; - DelegateImpl::fn = std::forward(fn); - } - - DelegateImpl& operator=(const DelegateImpl& del) - { - if (this == &del) return *this; - if (kind != del.kind) - { - if (FPA == kind) - { - obj = {}; - } - kind = del.kind; - } - if (FPA == del.kind) - { - fnA = del.fnA; - obj = del.obj; - } - else - { - fn = del.fn; - } - return *this; - } - - DelegateImpl& operator=(DelegateImpl&& del) - { - if (this == &del) return *this; - if (kind != del.kind) - { - if (FPA == kind) - { - obj = {}; - } - kind = del.kind; - } - if (FPA == del.kind) - { - fnA = del.fnA; - obj = std::move(del.obj); - } - else - { - fn = del.fn; - } - return *this; - } - - DelegateImpl& operator=(FunPtr fn) - { - if (FPA == kind) - { - obj = {}; - } - kind = FP; - this->fn = fn; - return *this; - } - - DelegateImpl& IRAM_ATTR operator=(std::nullptr_t) - { - if (FPA == kind) - { - obj = {}; - } - kind = FP; - fn = nullptr; - return *this; - } - - operator bool() const - { - if (FP == kind) - { - return fn; - } - else - { - return fnA; - } - } - - static R IRAM_ATTR vPtrToFunAPtrExec(void* self) - { - return static_cast(self)->fnA( - static_cast(self)->obj); - }; - - operator FunVPPtr() const - { - if (FP == kind) - { - return reinterpret_cast(fn); - } - else - { - return vPtrToFunAPtrExec; - } - } - - void* arg() const - { - if (FP == kind) - { - return nullptr; - } - else - { - return const_cast(this); - } - } - - R IRAM_ATTR operator()() const - { - if (FP == kind) - { - return fn(); - } - else - { - return fnA(obj); - } - } - - protected: - enum { FP, FPA } kind; - union { - FunPtr fn; - FunAPtr fnA; - }; - A obj; - }; -#endif - -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - template - class DelegateImpl { - public: - using target_type = R(); - protected: - using FunPtr = target_type*; - using FunctionType = std::function; - using FunVPPtr = R(*)(void*); - public: - DelegateImpl() - { - kind = FP; - fn = nullptr; - } - - DelegateImpl(std::nullptr_t) - { - kind = FP; - fn = nullptr; - } - - ~DelegateImpl() - { - if (FUNC == kind) - functional.~FunctionType(); - } - - DelegateImpl(const DelegateImpl& del) - { - kind = del.kind; - if (FUNC == del.kind) - { - new (&functional) FunctionType(del.functional); - } - else - { - fn = del.fn; - } - } - - DelegateImpl(DelegateImpl&& del) - { - kind = del.kind; - if (FUNC == del.kind) - { - new (&functional) FunctionType(std::move(del.functional)); - } - else - { - fn = del.fn; - } - } - - DelegateImpl(FunPtr fn) - { - kind = FP; - DelegateImpl::fn = fn; - } - - template DelegateImpl(F functional) - { - kind = FUNC; - new (&this->functional) FunctionType(std::forward(functional)); - } - - DelegateImpl& operator=(const DelegateImpl& del) - { - if (this == &del) return *this; - if (FUNC == kind && FUNC != del.kind) - { - functional.~FunctionType(); - } - else if (FUNC != kind && FUNC == del.kind) - { - new (&this->functional) FunctionType(); - } - kind = del.kind; - if (FUNC == del.kind) - { - functional = del.functional; - } - else - { - fn = del.fn; - } - return *this; - } - - DelegateImpl& operator=(DelegateImpl&& del) - { - if (this == &del) return *this; - if (FUNC == kind && FUNC != del.kind) - { - functional.~FunctionType(); - } - else if (FUNC != kind && FUNC == del.kind) - { - new (&this->functional) FunctionType(); - } - kind = del.kind; - if (FUNC == del.kind) - { - functional = std::move(del.functional); - } - else - { - fn = del.fn; - } - return *this; - } - - DelegateImpl& operator=(FunPtr fn) - { - if (FUNC == kind) - { - functional.~FunctionType(); - kind = FP; - } - DelegateImpl::fn = fn; - return *this; - } - - DelegateImpl& IRAM_ATTR operator=(std::nullptr_t) - { - if (FUNC == kind) - { - functional.~FunctionType(); - } - kind = FP; - fn = nullptr; - return *this; - } - - operator bool() const - { - if (FP == kind) - { - return fn; - } - else - { - return functional ? true : false; - } - } - - operator FunVPPtr() const - { - if (FP == kind) - { - return reinterpret_cast(fn); - } - else - { - return [](void* self) -> R - { - return static_cast(self)->functional(); - }; - } - } - - void* arg() const - { - if (FP == kind) - { - return nullptr; - } - else - { - return const_cast(this); - } - } - - operator FunctionType() const - { - if (FP == kind) - { - return fn; - } - else - { - return functional; - } - } - - R IRAM_ATTR operator()() const - { - if (FP == kind) - { - return fn(); - } - else - { - return functional(); - } - } - - protected: - enum { FUNC, FP } kind; - union { - FunctionType functional; - FunPtr fn; - }; - }; -#else - template - class DelegateImpl { - public: - using target_type = R(); - protected: - using FunPtr = target_type*; - using FunVPPtr = R(*)(void*); - public: - DelegateImpl() - { - fn = nullptr; - } - - DelegateImpl(std::nullptr_t) - { - fn = nullptr; - } - - DelegateImpl(const DelegateImpl& del) - { - fn = del.fn; - } - - DelegateImpl(DelegateImpl&& del) - { - fn = std::move(del.fn); - } - - DelegateImpl(FunPtr fn) - { - DelegateImpl::fn = fn; - } - - template DelegateImpl(F fn) - { - DelegateImpl::fn = std::forward(fn); - } - - DelegateImpl& operator=(const DelegateImpl& del) - { - if (this == &del) return *this; - fn = del.fn; - return *this; - } - - DelegateImpl& operator=(DelegateImpl&& del) - { - if (this == &del) return *this; - fn = std::move(del.fn); - return *this; - } - - DelegateImpl& operator=(FunPtr fn) - { - DelegateImpl::fn = fn; - return *this; - } - - DelegateImpl& IRAM_ATTR operator=(std::nullptr_t) - { - fn = nullptr; - return *this; - } - - operator bool() const - { - return fn; - } - - operator FunVPPtr() const - { - return reinterpret_cast(fn); - } - - void* arg() const - { - return nullptr; - } - - R IRAM_ATTR operator()() const - { - return fn(); - } - - protected: - FunPtr fn; - }; -#endif - - template - class Delegate : private detail::DelegatePImpl - { - private: - using typename detail::DelegatePImpl::FunVPPtr; -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - using typename detail::DelegatePImpl::FunctionType; -#endif - public: - using detail::DelegatePImpl::target_type; - using detail::DelegatePImpl::DelegatePImpl; - using detail::DelegatePImpl::operator=; - using detail::DelegatePImpl::operator bool; - using detail::DelegatePImpl::operator FunVPPtr; - using detail::DelegatePImpl::arg; -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - using detail::DelegatePImpl::operator FunctionType; -#endif - using detail::DelegatePImpl::operator(); - }; - - template - class Delegate : private detail::DelegatePImpl - { - private: - using typename detail::DelegatePImpl::FunVPPtr; -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - using typename detail::DelegatePImpl::FunctionType; -#endif - public: - using detail::DelegatePImpl::target_type; - using detail::DelegatePImpl::DelegatePImpl; - using detail::DelegatePImpl::operator=; - using detail::DelegatePImpl::operator bool; -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - using detail::DelegatePImpl::operator FunctionType; -#endif - using detail::DelegatePImpl::operator(); - operator FunVPPtr() const - { - if (detail::DelegatePImpl::FPA == detail::DelegatePImpl::kind) - { - return reinterpret_cast(detail::DelegatePImpl::fnA); - } - else - { - return detail::DelegatePImpl::operator FunVPPtr(); - } - } - void* arg() const - { - if (detail::DelegatePImpl::FPA == detail::DelegatePImpl::kind) - { - return detail::DelegatePImpl::obj; - } - else - { - return detail::DelegatePImpl::arg(); - } - } - }; - - template - class Delegate : private detail::DelegateImpl - { - private: - using typename detail::DelegateImpl::FunVPPtr; -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - using typename detail::DelegateImpl::FunctionType; -#endif - public: - using detail::DelegateImpl::target_type; - using detail::DelegateImpl::DelegateImpl; - using detail::DelegateImpl::operator=; - using detail::DelegateImpl::operator bool; - using detail::DelegateImpl::operator FunVPPtr; - using detail::DelegateImpl::arg; -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - using detail::DelegateImpl::operator FunctionType; -#endif - using detail::DelegateImpl::operator(); - }; - - template - class Delegate : private detail::DelegateImpl - { - private: - using typename detail::DelegateImpl::FunVPPtr; -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - using typename detail::DelegateImpl::FunctionType; -#endif - public: - using detail::DelegateImpl::target_type; - using detail::DelegateImpl::DelegateImpl; - using detail::DelegateImpl::operator=; - using detail::DelegateImpl::operator bool; -#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) - using detail::DelegateImpl::operator FunctionType; -#endif - using detail::DelegateImpl::operator(); - operator FunVPPtr() const - { - if (detail::DelegateImpl::FPA == detail::DelegateImpl::kind) - { - return reinterpret_cast(detail::DelegateImpl::fnA); - } - else - { - return detail::DelegateImpl::operator FunVPPtr(); - } - } - void* arg() const - { - if (detail::DelegateImpl::FPA == detail::DelegateImpl::kind) - { - return detail::DelegateImpl::obj; - } - else - { - return detail::DelegateImpl::arg(); - } - } - }; - -} - -template class Delegate; -template class Delegate : public detail::Delegate -{ -public: - using detail::Delegate::Delegate; -}; -template class Delegate : public detail::Delegate -{ -public: - using detail::Delegate::Delegate; -}; - -#endif // __Delegate_h diff --git a/lib/EspSoftwareSerial/src/circular_queue/MultiDelegate.h b/lib/EspSoftwareSerial/src/circular_queue/MultiDelegate.h deleted file mode 100644 index 1fd4188d..00000000 --- a/lib/EspSoftwareSerial/src/circular_queue/MultiDelegate.h +++ /dev/null @@ -1,503 +0,0 @@ -/* -MultiDelegate.h - A queue or event multiplexer based on the efficient Delegate -class -Copyright (c) 2019 Dirk O. Kaar. All rights reserved. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#ifndef __MULTIDELEGATE_H -#define __MULTIDELEGATE_H - -#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) -#include -#else -#include "circular_queue/ghostl.h" -#endif - -#if defined(ESP8266) -#include -using esp8266::InterruptLock; -#elif defined(ARDUINO) -class InterruptLock { -public: - InterruptLock() { - noInterrupts(); - } - ~InterruptLock() { - interrupts(); - } -}; -#else -#include -#endif - -namespace detail -{ - namespace - { - template< typename Delegate, typename R, bool ISQUEUE = false, typename... P> - struct CallP - { - static R execute(Delegate& del, P... args) - { - return del(std::forward(args...)) ? !ISQUEUE : ISQUEUE; - } - }; - - template< typename Delegate, bool ISQUEUE, typename... P> - struct CallP - { - static bool execute(Delegate& del, P... args) - { - del(std::forward(args...)); - return !ISQUEUE; - } - }; - - template< typename Delegate, typename R, bool ISQUEUE = false> - struct Call - { - static R execute(Delegate& del) - { - return del() ? !ISQUEUE : ISQUEUE; - } - }; - - template< typename Delegate, bool ISQUEUE> - struct Call - { - static bool execute(Delegate& del) - { - del(); - return !ISQUEUE; - } - }; - }; - - template< typename Delegate, typename R = void, bool ISQUEUE = false, uint32_t QUEUE_CAPACITY = 32, typename... P> - class MultiDelegatePImpl - { - public: - MultiDelegatePImpl() = default; - ~MultiDelegatePImpl() - { - *this = nullptr; - } - - MultiDelegatePImpl(const MultiDelegatePImpl&) = delete; - MultiDelegatePImpl& operator=(const MultiDelegatePImpl&) = delete; - - MultiDelegatePImpl(MultiDelegatePImpl&& md) - { - first = md.first; - last = md.last; - unused = md.unused; - nodeCount = md.nodeCount; - md.first = nullptr; - md.last = nullptr; - md.unused = nullptr; - md.nodeCount = 0; - } - - MultiDelegatePImpl(const Delegate& del) - { - add(del); - } - - MultiDelegatePImpl(Delegate&& del) - { - add(std::move(del)); - } - - MultiDelegatePImpl& operator=(MultiDelegatePImpl&& md) - { - first = md.first; - last = md.last; - unused = md.unused; - nodeCount = md.nodeCount; - md.first = nullptr; - md.last = nullptr; - md.unused = nullptr; - md.nodeCount = 0; - return *this; - } - - MultiDelegatePImpl& operator=(std::nullptr_t) - { - if (last) - last->mNext = unused; - if (first) - unused = first; - while (unused) - { - auto to_delete = unused; - unused = unused->mNext; - delete(to_delete); - } - return *this; - } - - MultiDelegatePImpl& operator+=(const Delegate& del) - { - add(del); - return *this; - } - - MultiDelegatePImpl& operator+=(Delegate&& del) - { - add(std::move(del)); - return *this; - } - - protected: - struct Node_t - { - ~Node_t() - { - mDelegate = nullptr; // special overload in Delegate - } - Node_t* mNext = nullptr; - Delegate mDelegate; - }; - - Node_t* first = nullptr; - Node_t* last = nullptr; - Node_t* unused = nullptr; - uint32_t nodeCount = 0; - - // Returns a pointer to an unused Node_t, - // or if none are available allocates a new one, - // or nullptr if limit is reached - Node_t* IRAM_ATTR get_node_unsafe() - { - Node_t* result = nullptr; - // try to get an item from unused items list - if (unused) - { - result = unused; - unused = unused->mNext; - } - // if no unused items, and count not too high, allocate a new one - else if (nodeCount < QUEUE_CAPACITY) - { -#if defined(ESP8266) || defined(ESP32) - result = new (std::nothrow) Node_t; -#else - result = new Node_t; -#endif - if (result) - ++nodeCount; - } - return result; - } - - void recycle_node_unsafe(Node_t* node) - { - node->mDelegate = nullptr; // special overload in Delegate - node->mNext = unused; - unused = node; - } - -#ifndef ARDUINO - std::mutex mutex_unused; -#endif - public: - const Delegate* IRAM_ATTR add(const Delegate& del) - { - return add(Delegate(del)); - } - - const Delegate* IRAM_ATTR add(Delegate&& del) - { - if (!del) - return nullptr; - -#ifdef ARDUINO - InterruptLock lockAllInterruptsInThisScope; -#else - std::lock_guard lock(mutex_unused); -#endif - - Node_t* item = ISQUEUE ? get_node_unsafe() : -#if defined(ESP8266) || defined(ESP32) - new (std::nothrow) Node_t; -#else - new Node_t; -#endif - if (!item) - return nullptr; - - item->mDelegate = std::move(del); - item->mNext = nullptr; - - if (last) - last->mNext = item; - else - first = item; - last = item; - - return &item->mDelegate; - } - - bool remove(const Delegate* del) - { - auto current = first; - if (!current) - return false; - - Node_t* prev = nullptr; - do - { - if (del == ¤t->mDelegate) - { - // remove callback from stack -#ifdef ARDUINO - InterruptLock lockAllInterruptsInThisScope; -#else - std::lock_guard lock(mutex_unused); -#endif - - auto to_recycle = current; - - // removing rLast - if (last == current) - last = prev; - - current = current->mNext; - if (prev) - { - prev->mNext = current; - } - else - { - first = current; - } - - if (ISQUEUE) - recycle_node_unsafe(to_recycle); - else - delete to_recycle; - return true; - } - else - { - prev = current; - current = current->mNext; - } - } while (current); - return false; - } - - void operator()(P... args) - { - auto current = first; - if (!current) - return; - - static std::atomic fence(false); - // prevent recursive calls -#if defined(ARDUINO) && !defined(ESP32) - if (fence.load()) return; - fence.store(true); -#else - if (fence.exchange(true)) return; -#endif - - Node_t* prev = nullptr; - // prevent execution of new callbacks during this run - auto stop = last; - - bool done; - do - { - done = current == stop; - if (!CallP::execute(current->mDelegate, args...)) - { - // remove callback from stack -#ifdef ARDUINO - InterruptLock lockAllInterruptsInThisScope; -#else - std::lock_guard lock(mutex_unused); -#endif - - auto to_recycle = current; - - // removing rLast - if (last == current) - last = prev; - - current = current->mNext; - if (prev) - { - prev->mNext = current; - } - else - { - first = current; - } - - if (ISQUEUE) - recycle_node_unsafe(to_recycle); - else - delete to_recycle; - } - else - { - prev = current; - current = current->mNext; - } - -#if defined(ESP8266) || defined(ESP32) - // running callbacks might last too long for watchdog etc. - optimistic_yield(10000); -#endif - } while (current && !done); - - fence.store(false); - } - }; - - template< typename Delegate, typename R = void, bool ISQUEUE = false, uint32_t QUEUE_CAPACITY = 32> - class MultiDelegateImpl : public MultiDelegatePImpl - { - protected: - using typename MultiDelegatePImpl::Node_t; - using MultiDelegatePImpl::first; - using MultiDelegatePImpl::last; - using MultiDelegatePImpl::unused; - using MultiDelegatePImpl::nodeCount; - using MultiDelegatePImpl::recycle_node_unsafe; -#ifndef ARDUINO - using MultiDelegatePImpl::mutex_unused; -#endif - - public: - using MultiDelegatePImpl::MultiDelegatePImpl; - - void operator()() - { - auto current = first; - if (!current) - return; - - static std::atomic fence(false); - // prevent recursive calls -#if defined(ARDUINO) && !defined(ESP32) - if (fence.load()) return; - fence.store(true); -#else - if (fence.exchange(true)) return; -#endif - - Node_t* prev = nullptr; - // prevent execution of new callbacks during this run - auto stop = last; - - bool done; - do - { - done = current == stop; - if (!Call::execute(current->mDelegate)) - { - // remove callback from stack -#ifdef ARDUINO - InterruptLock lockAllInterruptsInThisScope; -#else - std::lock_guard lock(mutex_unused); -#endif - - auto to_recycle = current; - - // removing rLast - if (last == current) - last = prev; - - current = current->mNext; - if (prev) - { - prev->mNext = current; - } - else - { - first = current; - } - - if (ISQUEUE) - recycle_node_unsafe(to_recycle); - else - delete to_recycle; - } - else - { - prev = current; - current = current->mNext; - } - -#if defined(ESP8266) || defined(ESP32) - // running callbacks might last too long for watchdog etc. - optimistic_yield(10000); -#endif - } while (current && !done); - - fence.store(false); - } - }; - - template< typename Delegate, typename R, bool ISQUEUE, uint32_t QUEUE_CAPACITY, typename... P> class MultiDelegate; - - template< typename Delegate, typename R, bool ISQUEUE, uint32_t QUEUE_CAPACITY, typename... P> - class MultiDelegate : public MultiDelegatePImpl - { - public: - using MultiDelegatePImpl::MultiDelegatePImpl; - }; - - template< typename Delegate, typename R, bool ISQUEUE, uint32_t QUEUE_CAPACITY> - class MultiDelegate : public MultiDelegateImpl - { - public: - using MultiDelegateImpl::MultiDelegateImpl; - }; -}; - -/** -The MultiDelegate class template can be specialized to either a queue or an event multiplexer. -It is designed to be used with Delegate, the efficient runtime wrapper for C function ptr and C++ std::function. -@tparam Delegate specifies the concrete type that MultiDelegate bases the queue or event multiplexer on. -@tparam ISQUEUE modifies the generated MultiDelegate class in subtle ways. In queue mode (ISQUEUE == true), - the value of QUEUE_CAPACITY enforces the maximum number of simultaneous items the queue can contain. - This is exploited to minimize the use of new and delete by reusing already allocated items, thus - reducing heap fragmentation. In event multiplexer mode (ISQUEUE = false), new and delete are - used for allocation of the event handler items. - If the result type of the function call operator of Delegate is void, calling a MultiDelegate queue - removes each item after calling it; a Multidelegate event multiplexer keeps event handlers until - explicitly removed. - If the result type of the function call operator of Delegate is non-void, the type-conversion to bool - of that result determines if the item is immediately removed or kept after each call: a Multidelegate - queue removes an item only if true is returned, but a Multidelegate event multiplexer removes event - handlers that return false. -@tparam QUEUE_CAPACITY is only used if ISQUEUE == true. Then, it sets the maximum capacity that the queue dynamically - allocates from the heap. Unused items are not returned to the heap, but are managed by the MultiDelegate - instance during its own lifetime for efficiency. -*/ -template< typename Delegate, bool ISQUEUE = false, uint32_t QUEUE_CAPACITY = 32> -class MultiDelegate : public detail::MultiDelegate -{ -public: - using detail::MultiDelegate::MultiDelegate; -}; - -#endif // __MULTIDELEGATE_H diff --git a/lib/EspSoftwareSerial/src/circular_queue/circular_queue.h b/lib/EspSoftwareSerial/src/circular_queue/circular_queue.h deleted file mode 100644 index 46e3f66e..00000000 --- a/lib/EspSoftwareSerial/src/circular_queue/circular_queue.h +++ /dev/null @@ -1,399 +0,0 @@ -/* -circular_queue.h - Implementation of a lock-free circular queue for EspSoftwareSerial. -Copyright (c) 2019 Dirk O. Kaar. All rights reserved. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#ifndef __circular_queue_h -#define __circular_queue_h - -#ifdef ARDUINO -#include -#endif - -#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) -#include -#include -#include -#include "Delegate.h" -using std::min; -#else -#include "ghostl.h" -#endif - -#if !defined(ESP32) && !defined(ESP8266) -#define ICACHE_RAM_ATTR -#define IRAM_ATTR -#endif - -/*! - @brief Instance class for a single-producer, single-consumer circular queue / ring buffer (FIFO). - This implementation is lock-free between producer and consumer for the available(), peek(), - pop(), and push() type functions. -*/ -template< typename T, typename ForEachArg = void > -class circular_queue -{ -public: - /*! - @brief Constructs a valid, but zero-capacity dummy queue. - */ - circular_queue() : m_bufSize(1) - { - m_inPos.store(0); - m_outPos.store(0); - } - /*! - @brief Constructs a queue of the given maximum capacity. - */ - circular_queue(const size_t capacity) : m_bufSize(capacity + 1), m_buffer(new T[m_bufSize]) - { - m_inPos.store(0); - m_outPos.store(0); - } - circular_queue(circular_queue&& cq) : - m_bufSize(cq.m_bufSize), m_buffer(cq.m_buffer), m_inPos(cq.m_inPos.load()), m_outPos(cq.m_outPos.load()) - {} - ~circular_queue() - { - m_buffer.reset(); - } - circular_queue(const circular_queue&) = delete; - circular_queue& operator=(circular_queue&& cq) - { - m_bufSize = cq.m_bufSize; - m_buffer = cq.m_buffer; - m_inPos.store(cq.m_inPos.load()); - m_outPos.store(cq.m_outPos.load()); - } - circular_queue& operator=(const circular_queue&) = delete; - - /*! - @brief Get the numer of elements the queue can hold at most. - */ - size_t capacity() const - { - return m_bufSize - 1; - } - - /*! - @brief Resize the queue. The available elements in the queue are preserved. - This is not lock-free and concurrent producer or consumer access - will lead to corruption. - @return True if the new capacity could accommodate the present elements in - the queue, otherwise nothing is done and false is returned. - */ - bool capacity(const size_t cap); - - /*! - @brief Discard all data in the queue. - */ - void flush() - { - m_outPos.store(m_inPos.load()); - } - - /*! - @brief Get a snapshot number of elements that can be retrieved by pop. - */ - size_t available() const - { - int avail = static_cast(m_inPos.load() - m_outPos.load()); - if (avail < 0) avail += m_bufSize; - return avail; - } - - /*! - @brief Get the remaining free elementes for pushing. - */ - size_t available_for_push() const - { - int avail = static_cast(m_outPos.load() - m_inPos.load()) - 1; - if (avail < 0) avail += m_bufSize; - return avail; - } - - /*! - @brief Peek at the next element pop will return without removing it from the queue. - @return An rvalue copy of the next element that can be popped. If the queue is empty, - return an rvalue copy of the element that is pending the next push. - */ - T peek() const - { - const auto outPos = m_outPos.load(std::memory_order_relaxed); - std::atomic_thread_fence(std::memory_order_acquire); - return m_buffer[outPos]; - } - - /*! - @brief Peek at the next pending input value. - @return A reference to the next element that can be pushed. - */ - T& IRAM_ATTR pushpeek() - { - const auto inPos = m_inPos.load(std::memory_order_relaxed); - std::atomic_thread_fence(std::memory_order_acquire); - return m_buffer[inPos]; - } - - /*! - @brief Release the next pending input value, accessible by pushpeek(), into the queue. - @return true if the queue accepted the value, false if the queue - was full. - */ - bool IRAM_ATTR push(); - - /*! - @brief Move the rvalue parameter into the queue. - @return true if the queue accepted the value, false if the queue - was full. - */ - bool IRAM_ATTR push(T&& val); - - /*! - @brief Push a copy of the parameter into the queue. - @return true if the queue accepted the value, false if the queue - was full. - */ - bool IRAM_ATTR push(const T& val) - { - return push(T(val)); - } - -#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) - /*! - @brief Push copies of multiple elements from a buffer into the queue, - in order, beginning at buffer's head. - @return The number of elements actually copied into the queue, counted - from the buffer head. - */ - size_t push_n(const T* buffer, size_t size); -#endif - - /*! - @brief Pop the next available element from the queue. - @return An rvalue copy of the popped element, or a default - value of type T if the queue is empty. - */ - T pop(); - -#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) - /*! - @brief Pop multiple elements in ordered sequence from the queue to a buffer. - If buffer is nullptr, simply discards up to size elements from the queue. - @return The number of elements actually popped from the queue to - buffer. - */ - size_t pop_n(T* buffer, size_t size); -#endif - - /*! - @brief Iterate over and remove each available element from queue, - calling back fun with an rvalue reference of every single element. - */ -#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) - void for_each(const Delegate& fun); -#else - void for_each(Delegate fun); -#endif - - /*! - @brief In reverse order, iterate over, pop and optionally requeue each available element from the queue, - calling back fun with a reference of every single element. - Requeuing is dependent on the return boolean of the callback function. If it - returns true, the requeue occurs. - */ -#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) - bool for_each_rev_requeue(const Delegate& fun); -#else - bool for_each_rev_requeue(Delegate fun); -#endif - -protected: - const T defaultValue = {}; - unsigned m_bufSize; -#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) - std::unique_ptr m_buffer; -#else - std::unique_ptr m_buffer; -#endif - std::atomic m_inPos; - std::atomic m_outPos; -}; - -template< typename T, typename ForEachArg > -bool circular_queue::capacity(const size_t cap) -{ - if (cap + 1 == m_bufSize) return true; - else if (available() > cap) return false; - std::unique_ptr buffer(new T[cap + 1]); - const auto available = pop_n(buffer, cap); - m_buffer.reset(buffer); - m_bufSize = cap + 1; - std::atomic_thread_fence(std::memory_order_release); - m_inPos.store(available, std::memory_order_relaxed); - m_outPos.store(0, std::memory_order_release); - return true; -} - -template< typename T, typename ForEachArg > -bool IRAM_ATTR circular_queue::push() -{ - const auto inPos = m_inPos.load(std::memory_order_acquire); - const unsigned next = (inPos + 1) % m_bufSize; - if (next == m_outPos.load(std::memory_order_relaxed)) { - return false; - } - - std::atomic_thread_fence(std::memory_order_acquire); - - m_inPos.store(next, std::memory_order_release); - return true; -} - -template< typename T, typename ForEachArg > -bool IRAM_ATTR circular_queue::push(T&& val) -{ - const auto inPos = m_inPos.load(std::memory_order_acquire); - const unsigned next = (inPos + 1) % m_bufSize; - if (next == m_outPos.load(std::memory_order_relaxed)) { - return false; - } - - std::atomic_thread_fence(std::memory_order_acquire); - - m_buffer[inPos] = std::move(val); - - std::atomic_thread_fence(std::memory_order_release); - - m_inPos.store(next, std::memory_order_release); - return true; -} - -#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) -template< typename T, typename ForEachArg > -size_t circular_queue::push_n(const T* buffer, size_t size) -{ - const auto inPos = m_inPos.load(std::memory_order_acquire); - const auto outPos = m_outPos.load(std::memory_order_relaxed); - - size_t blockSize = (outPos > inPos) ? outPos - 1 - inPos : (outPos == 0) ? m_bufSize - 1 - inPos : m_bufSize - inPos; - blockSize = min(size, blockSize); - if (!blockSize) return 0; - int next = (inPos + blockSize) % m_bufSize; - - std::atomic_thread_fence(std::memory_order_acquire); - - auto dest = m_buffer.get() + inPos; - std::copy_n(std::make_move_iterator(buffer), blockSize, dest); - size = min(size - blockSize, outPos > 1 ? static_cast(outPos - next - 1) : 0); - next += size; - dest = m_buffer.get(); - std::copy_n(std::make_move_iterator(buffer + blockSize), size, dest); - - std::atomic_thread_fence(std::memory_order_release); - - m_inPos.store(next, std::memory_order_release); - return blockSize + size; -} -#endif - -template< typename T, typename ForEachArg > -T circular_queue::pop() -{ - const auto outPos = m_outPos.load(std::memory_order_acquire); - if (m_inPos.load(std::memory_order_relaxed) == outPos) return defaultValue; - - std::atomic_thread_fence(std::memory_order_acquire); - - auto val = std::move(m_buffer[outPos]); - - std::atomic_thread_fence(std::memory_order_release); - - m_outPos.store((outPos + 1) % m_bufSize, std::memory_order_release); - return val; -} - -#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) -template< typename T, typename ForEachArg > -size_t circular_queue::pop_n(T* buffer, size_t size) { - size_t avail = size = min(size, available()); - if (!avail) return 0; - const auto outPos = m_outPos.load(std::memory_order_acquire); - size_t n = min(avail, static_cast(m_bufSize - outPos)); - - std::atomic_thread_fence(std::memory_order_acquire); - - if (buffer) { - buffer = std::copy_n(std::make_move_iterator(m_buffer.get() + outPos), n, buffer); - avail -= n; - std::copy_n(std::make_move_iterator(m_buffer.get()), avail, buffer); - } - - std::atomic_thread_fence(std::memory_order_release); - - m_outPos.store((outPos + size) % m_bufSize, std::memory_order_release); - return size; -} -#endif - -template< typename T, typename ForEachArg > -#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) -void circular_queue::for_each(const Delegate& fun) -#else -void circular_queue::for_each(Delegate fun) -#endif -{ - auto outPos = m_outPos.load(std::memory_order_acquire); - const auto inPos = m_inPos.load(std::memory_order_relaxed); - std::atomic_thread_fence(std::memory_order_acquire); - while (outPos != inPos) - { - fun(std::move(m_buffer[outPos])); - std::atomic_thread_fence(std::memory_order_release); - outPos = (outPos + 1) % m_bufSize; - m_outPos.store(outPos, std::memory_order_release); - } -} - -template< typename T, typename ForEachArg > -#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) -bool circular_queue::for_each_rev_requeue(const Delegate& fun) -#else -bool circular_queue::for_each_rev_requeue(Delegate fun) -#endif -{ - auto inPos0 = circular_queue::m_inPos.load(std::memory_order_acquire); - auto outPos = circular_queue::m_outPos.load(std::memory_order_relaxed); - std::atomic_thread_fence(std::memory_order_acquire); - if (outPos == inPos0) return false; - auto pos = inPos0; - auto outPos1 = inPos0; - const auto posDecr = circular_queue::m_bufSize - 1; - do { - pos = (pos + posDecr) % circular_queue::m_bufSize; - T&& val = std::move(circular_queue::m_buffer[pos]); - if (fun(val)) - { - outPos1 = (outPos1 + posDecr) % circular_queue::m_bufSize; - if (outPos1 != pos) circular_queue::m_buffer[outPos1] = std::move(val); - } - } while (pos != outPos); - circular_queue::m_outPos.store(outPos1, std::memory_order_release); - return true; -} - -#endif // __circular_queue_h diff --git a/lib/EspSoftwareSerial/src/circular_queue/circular_queue_mp.h b/lib/EspSoftwareSerial/src/circular_queue/circular_queue_mp.h deleted file mode 100644 index 7024247a..00000000 --- a/lib/EspSoftwareSerial/src/circular_queue/circular_queue_mp.h +++ /dev/null @@ -1,200 +0,0 @@ -/* -circular_queue_mp.h - Implementation of a lock-free circular queue for EspSoftwareSerial. -Copyright (c) 2019 Dirk O. Kaar. All rights reserved. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#ifndef __circular_queue_mp_h -#define __circular_queue_mp_h - -#include "circular_queue.h" - -#ifdef ESP8266 -#include "interrupts.h" -#else -#include -#endif - -/*! - @brief Instance class for a multi-producer, single-consumer circular queue / ring buffer (FIFO). - This implementation is lock-free between producers and consumer for the available(), peek(), - pop(), and push() type functions, but is guarded to safely allow only a single producer - at any instant. -*/ -template< typename T, typename ForEachArg = void > -class circular_queue_mp : protected circular_queue -{ -public: - circular_queue_mp() = default; - circular_queue_mp(const size_t capacity) : circular_queue(capacity) - {} - circular_queue_mp(circular_queue&& cq) : circular_queue(std::move(cq)) - {} - using circular_queue::operator=; - using circular_queue::capacity; - using circular_queue::flush; - using circular_queue::available; - using circular_queue::available_for_push; - using circular_queue::peek; - using circular_queue::pop; - using circular_queue::pop_n; - using circular_queue::for_each; - using circular_queue::for_each_rev_requeue; - - /*! - @brief Resize the queue. The available elements in the queue are preserved. - This is not lock-free, but safe, concurrent producer or consumer access - is guarded. - @return True if the new capacity could accommodate the present elements in - the queue, otherwise nothing is done and false is returned. - */ - bool capacity(const size_t cap) - { -#ifdef ESP8266 - esp8266::InterruptLock lock; -#else - std::lock_guard lock(m_pushMtx); -#endif - return circular_queue::capacity(cap); - } - - bool IRAM_ATTR push() = delete; - - /*! - @brief Move the rvalue parameter into the queue, guarded - for multiple concurrent producers. - @return true if the queue accepted the value, false if the queue - was full. - */ - bool IRAM_ATTR push(T&& val) - { -#ifdef ESP8266 - esp8266::InterruptLock lock; -#else - std::lock_guard lock(m_pushMtx); -#endif - return circular_queue::push(std::move(val)); - } - - /*! - @brief Push a copy of the parameter into the queue, guarded - for multiple concurrent producers. - @return true if the queue accepted the value, false if the queue - was full. - */ - bool IRAM_ATTR push(const T& val) - { -#ifdef ESP8266 - esp8266::InterruptLock lock; -#else - std::lock_guard lock(m_pushMtx); -#endif - return circular_queue::push(val); - } - - /*! - @brief Push copies of multiple elements from a buffer into the queue, - in order, beginning at buffer's head. This is guarded for - multiple producers, push_n() is atomic. - @return The number of elements actually copied into the queue, counted - from the buffer head. - */ - size_t push_n(const T* buffer, size_t size) - { -#ifdef ESP8266 - esp8266::InterruptLock lock; -#else - std::lock_guard lock(m_pushMtx); -#endif - return circular_queue::push_n(buffer, size); - } - - /*! - @brief Pops the next available element from the queue, requeues - it immediately. - @return A reference to the just requeued element, or the default - value of type T if the queue is empty. - */ - T& pop_requeue(); - - /*! - @brief Iterate over, pop and optionally requeue each available element from the queue, - calling back fun with a reference of every single element. - Requeuing is dependent on the return boolean of the callback function. If it - returns true, the requeue occurs. - */ - bool for_each_requeue(const Delegate& fun); - -#ifndef ESP8266 -protected: - std::mutex m_pushMtx; -#endif -}; - -template< typename T, typename ForEachArg > -T& circular_queue_mp::pop_requeue() -{ -#ifdef ESP8266 - esp8266::InterruptLock lock; -#else - std::lock_guard lock(m_pushMtx); -#endif - const auto outPos = circular_queue::m_outPos.load(std::memory_order_acquire); - const auto inPos = circular_queue::m_inPos.load(std::memory_order_relaxed); - std::atomic_thread_fence(std::memory_order_acquire); - if (inPos == outPos) return circular_queue::defaultValue; - T& val = circular_queue::m_buffer[inPos] = std::move(circular_queue::m_buffer[outPos]); - const auto bufSize = circular_queue::m_bufSize; - std::atomic_thread_fence(std::memory_order_release); - circular_queue::m_outPos.store((outPos + 1) % bufSize, std::memory_order_relaxed); - circular_queue::m_inPos.store((inPos + 1) % bufSize, std::memory_order_release); - return val; -} - -template< typename T, typename ForEachArg > -bool circular_queue_mp::for_each_requeue(const Delegate& fun) -{ - auto inPos0 = circular_queue::m_inPos.load(std::memory_order_acquire); - auto outPos = circular_queue::m_outPos.load(std::memory_order_relaxed); - std::atomic_thread_fence(std::memory_order_acquire); - if (outPos == inPos0) return false; - do { - T&& val = std::move(circular_queue::m_buffer[outPos]); - if (fun(val)) - { -#ifdef ESP8266 - esp8266::InterruptLock lock; -#else - std::lock_guard lock(m_pushMtx); -#endif - std::atomic_thread_fence(std::memory_order_release); - auto inPos = circular_queue::m_inPos.load(std::memory_order_relaxed); - std::atomic_thread_fence(std::memory_order_acquire); - circular_queue::m_buffer[inPos] = std::move(val); - std::atomic_thread_fence(std::memory_order_release); - circular_queue::m_inPos.store((inPos + 1) % circular_queue::m_bufSize, std::memory_order_release); - } - else - { - std::atomic_thread_fence(std::memory_order_release); - } - outPos = (outPos + 1) % circular_queue::m_bufSize; - circular_queue::m_outPos.store(outPos, std::memory_order_release); - } while (outPos != inPos0); - return true; -} - -#endif // __circular_queue_mp_h diff --git a/lib/EspSoftwareSerial/src/circular_queue/ghostl.h b/lib/EspSoftwareSerial/src/circular_queue/ghostl.h deleted file mode 100644 index 11683805..00000000 --- a/lib/EspSoftwareSerial/src/circular_queue/ghostl.h +++ /dev/null @@ -1,92 +0,0 @@ -/* -ghostl.h - Implementation of a bare-bones, mostly no-op, C++ STL shell - that allows building some Arduino ESP8266/ESP32 - libraries on Aruduino AVR. -Copyright (c) 2019 Dirk O. Kaar. All rights reserved. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#ifndef __ghostl_h -#define __ghostl_h - -#if defined(ARDUINO_ARCH_SAMD) -#include -#endif - -namespace std -{ -#if !defined(ARDUINO_ARCH_SAMD) - typedef enum memory_order { - memory_order_relaxed, - memory_order_acquire, - memory_order_release, - memory_order_seq_cst - } memory_order; - template< typename T > class atomic { - private: - T value; - public: - atomic() {} - atomic(T desired) { value = desired; } - void store(T desired, std::memory_order = std::memory_order_seq_cst) volatile noexcept { value = desired; } - T load(std::memory_order = std::memory_order_seq_cst) const volatile noexcept { return value; } - }; - inline void atomic_thread_fence(std::memory_order order) noexcept {} - template< typename T > T&& move(T& t) noexcept { return static_cast(t); } -#endif - - template< typename T, unsigned long N > struct array - { - T _M_elems[N]; - decltype(sizeof(0)) size() const { return N; } - T& operator[](decltype(sizeof(0)) i) { return _M_elems[i]; } - const T& operator[](decltype(sizeof(0)) i) const { return _M_elems[i]; } - }; - - template< typename T > class unique_ptr - { - public: - using pointer = T*; - unique_ptr() noexcept : ptr(nullptr) {} - unique_ptr(pointer p) : ptr(p) {} - pointer operator->() const noexcept { return ptr; } - T& operator[](decltype(sizeof(0)) i) const { return ptr[i]; } - void reset(pointer p = pointer()) noexcept - { - delete ptr; - ptr = p; - } - T& operator*() const { return *ptr; } - private: - pointer ptr; - }; - - template< typename T > using function = T*; - using nullptr_t = decltype(nullptr); - - template - struct identity { - typedef T type; - }; - - template - inline T&& forward(typename identity::type& t) noexcept - { - return static_cast::type&&>(t); - } -} - -#endif // __ghostl_h diff --git a/lib/SDS011/src/SDS011.cpp b/lib/SDS011/src/SDS011.cpp deleted file mode 100644 index 31efdea4..00000000 --- a/lib/SDS011/src/SDS011.cpp +++ /dev/null @@ -1,191 +0,0 @@ -// SDS011 dust sensor PM2.5 and PM10 -// --------------------- -// -// By R. Zschiegner (rz@madavi.de) -// April 2016 -// -// Documentation: -// - The iNovaFitness SDS011 datasheet -// -// modified by AQ - 2018-11-18 -// - -#include "SDS011.h" - -static const byte SDS_SLEEP[] = { - 0xAA, // head - 0xB4, // command id - 0x06, // data byte 1 - 0x01, // data byte 2 (set mode) - 0x00, // data byte 3 (sleep) - 0x00, // data byte 4 - 0x00, // data byte 5 - 0x00, // data byte 6 - 0x00, // data byte 7 - 0x00, // data byte 8 - 0x00, // data byte 9 - 0x00, // data byte 10 - 0x00, // data byte 11 - 0x00, // data byte 12 - 0x00, // data byte 13 - 0xFF, // data byte 14 (device id byte 1) - 0xFF, // data byte 15 (device id byte 2) - 0x05, // checksum - 0xAB // tail -}; - -static const byte SDS_START[] = { - 0xAA, 0xB4, 0x06, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x06, 0xAB}; - -static const byte SDS_CONT_MODE[] = { - 0xAA, 0xB4, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x07, 0xAB}; - -static const byte SDS_VERSION[] = { - 0xAA, 0xB4, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x05, 0xAB}; - -const uint8_t SDS_cmd_len = 19; - -SDS011::SDS011(void) { - -} - -// -------------------------------------------------------- -// SDS011:read -// -------------------------------------------------------- -int SDS011::read(float *p25, float *p10) { - byte buffer; - int value; - int len = 0; - int pm10_serial = 0; - int pm25_serial = 0; - int checksum_is; - int checksum_ok = 0; - int error = 1; - - while ((sds_data->available() > 0) && (sds_data->available() >= (10-len))) { - buffer = sds_data->read(); - value = int(buffer); - switch (len) { - case (0): if (value != 170) { len = -1; }; break; - case (1): if (value != 192) { len = -1; }; break; - case (2): pm25_serial = value; checksum_is = value; break; - case (3): pm25_serial += (value << 8); checksum_is += value; break; - case (4): pm10_serial = value; checksum_is += value; break; - case (5): pm10_serial += (value << 8); checksum_is += value; break; - case (6): checksum_is += value; break; - case (7): checksum_is += value; break; - case (8): if (value == (checksum_is % 256)) { checksum_ok = 1; } else { len = -1; }; break; - case (9): if (value != 171) { len = -1; }; break; - } - len++; - if (len == 10 && checksum_ok == 1) { - *p10 = (float)pm10_serial/10.0; - *p25 = (float)pm25_serial/10.0; - len = 0; checksum_ok = 0; pm10_serial = 0.0; pm25_serial = 0.0; checksum_is = 0; - error = 0; - } - yield(); - } - return error; -} - -// -------------------------------------------------------- -// SDS011:sleep -// -------------------------------------------------------- -void SDS011::sleep() { - SDS_cmd(SDS_STOP_CMD); -} - -// -------------------------------------------------------- -// SDS011:wakeup -// -------------------------------------------------------- -void SDS011::wakeup() { - SDS_cmd(SDS_START_CMD); -} - -// -------------------------------------------------------- -// SDS011:continous mode -// -------------------------------------------------------- -void SDS011::contmode(int noOfMinutes) -{ - byte buffer[SDS_cmd_len]; - memcpy(buffer, SDS_CONT_MODE, SDS_cmd_len); - buffer[4] = (byte) noOfMinutes; - buffer[17] = calcChecksum( buffer ); - for (uint8_t i = 0; i < SDS_cmd_len; i++) { - sds_data->write(buffer[i]); - } - sds_data->flush(); - while (sds_data->available() > 0) { - sds_data->read(); - } -// SDS_cmd(SDS_CONTINUOUS_MODE_CMD); -} - -/***************************************************************** - * send SDS011 command (start, stop, continuous mode, version * - *****************************************************************/ -void SDS011::SDS_cmd(const uint8_t cmd) -{ - byte buf[SDS_cmd_len]; - switch (cmd) { - case SDS_START_CMD: - memcpy(buf, SDS_START, SDS_cmd_len); - break; - case SDS_STOP_CMD: - memcpy(buf, SDS_SLEEP, SDS_cmd_len); - break; - case SDS_CONTINUOUS_MODE_CMD: - memcpy(buf, SDS_CONT_MODE, SDS_cmd_len); - break; - case SDS_VERSION_DATE_CMD: - memcpy(buf, SDS_VERSION, SDS_cmd_len); - break; - default: - return; - } - for (uint8_t i = 0; i < SDS_cmd_len; i++) { - sds_data->write(buf[i]); - } - sds_data->flush(); - while (sds_data->available() > 0) { - sds_data->read(); - } - -} - -// -------------------------------------------------------- -// SDS011: calculate checksum -// -------------------------------------------------------- -uint8_t SDS011::calcChecksum( byte *buffer ) -{ - uint8_t value = 0; - - for (uint8_t i = 2; i < 17; i++ ) - { - value += buffer[i]; - value &= 0xff; - } - return value; -} - -void SDS011::begin(uint8_t pin_rx, uint8_t pin_tx) { - _pin_rx = pin_rx; - _pin_tx = pin_tx; - - SoftwareSerial *softSerial = new SoftwareSerial(_pin_rx, _pin_tx); - softSerial->begin(9600); - - sds_data = softSerial; -} - -void SDS011::begin(HardwareSerial* serial) { - Serial.println("SDS011::begin"); -// serial->begin(9600); // why do I have to remove this line? - sds_data = serial; -} - -void SDS011::begin(SoftwareSerial* serial) { - serial->begin(9600); - sds_data = serial; -} diff --git a/lib/SDS011/src/SDS011.h b/lib/SDS011/src/SDS011.h deleted file mode 100644 index f8a013d5..00000000 --- a/lib/SDS011/src/SDS011.h +++ /dev/null @@ -1,40 +0,0 @@ -// SDS011 dust sensor PM2.5 and PM10 -// --------------------------------- -// -// By R. Zschiegner (rz@madavi.de) -// April 2016 -// -// Documentation: -// - The iNovaFitness SDS011 datasheet -// - -#if ARDUINO >= 100 - #include "Arduino.h" -#else - #include "WProgram.h" -#endif - -#include - -// Definition SDS011 sensor 'commands' -#define SDS_START_CMD 1 -#define SDS_STOP_CMD 2 -#define SDS_CONTINUOUS_MODE_CMD 3 -#define SDS_VERSION_DATE_CMD 4 - -class SDS011 { - public: - SDS011(void); - void begin(uint8_t pin_rx, uint8_t pin_tx); - void begin(HardwareSerial* serial); - void begin(SoftwareSerial* serial); - int read(float *p25, float *p10); - void sleep(); - void wakeup(); - void contmode( int ); - private: - void SDS_cmd(const uint8_t); - uint8_t calcChecksum( byte *); - uint8_t _pin_rx, _pin_tx; - Stream *sds_data; -}; diff --git a/platformio.ini b/platformio.ini index 9cba5684..5f8f8570 100644 --- a/platformio.ini +++ b/platformio.ini @@ -73,6 +73,7 @@ lib_deps_sensors = Adafruit BME280 Library@>=2.0.0 Adafruit BMP085 Library@>=1.0.1 BSEC Software Library@1.5.1474 + SDS011 sensor Library lib_deps_basic = ArduinoJson@^5.13.1 76@>=1.2.4 ; #76 Timezone by Jack Christensen diff --git a/src/hal/generic.h b/src/hal/generic.h index 5d56e591..14c83ff5 100644 --- a/src/hal/generic.h +++ b/src/hal/generic.h @@ -10,6 +10,10 @@ // Hardware related definitions for generic ESP32 boards // generic.h is kitchensink with all available options +// SDS011 dust sensor settings +#define HAS_SDS011 1 // use SDS011 +#define SDS011_SERIAL 9600, SERIAL_8N1, GPIO_NUM_19, GPIO_NUM_23 // SDS011 RX, TX + #define HAS_LORA 1 // comment out if device shall not send data via LoRa or has no LoRa #define HAS_SPI 1 // comment out if device shall not send data via SPI // pin definitions for SPI slave interface diff --git a/src/sds011read.cpp b/src/sds011read.cpp index cf6dccdf..0b66c747 100644 --- a/src/sds011read.cpp +++ b/src/sds011read.cpp @@ -3,60 +3,58 @@ // Local logging tag static const char TAG[] = __FILE__; -#include +#include "sds011read.h" // UART(2) is unused in this project #if (HAS_IF482) #error cannot use IF482 together with SDS011 (both use UART#2) #endif -static HardwareSerial sdsSerial(2); // so we use it here -static SDS011 sdsSensor; // fine dust sensor +#ifndef SDS011_SERIAL +#error serial settings for SDS011 connection missing +#endif +static HardwareSerial sdsSerial(2); // so we use it here +static SDS011 sdsSensor; // fine dust sensor // the results of the sensor: -float pm25; +float pm25; float pm10; boolean isSDS011Active; // init -bool sds011_init() -{ - pm25 = pm10 = 0.0; - sdsSerial.begin(9600, SERIAL_8N1, ESP_PIN_RX, ESP_PIN_TX); - sdsSensor.begin (&sdsSerial); - sdsSensor.contmode(0); // for safety: no wakeup/sleep by the sensor - sds011_sleep(); // we do it by ourselves - return true; +bool sds011_init() { + pm25 = pm10 = 0.0; + sdsSerial.begin(SDS011_SERIAL); + sdsSensor.begin(&sdsSerial); + //sdsSensor.contmode(0); // for safety: no wakeup/sleep by the sensor + sds011_sleep(); // we do it by ourselves + return true; } // reading data: -void sds011_loop() -{ - if ( isSDS011Active ) { - int sdsErrorCode = sdsSensor.read(&pm25, &pm10); - if (sdsErrorCode) { - pm25 = pm10 = 0.0; - ESP_LOGI(TAG, "SDS011 error: %d", sdsErrorCode); - } - else { - ESP_LOGI(TAG, "fine-dust-values: %5.1f,%4.1f", pm10, pm25); - } - sds011_sleep(); +void sds011_loop() { + if (isSDS011Active) { + int sdsErrorCode = sdsSensor.read(&pm25, &pm10); + if (sdsErrorCode) { + pm25 = pm10 = 0.0; + ESP_LOGI(TAG, "SDS011 error: %d", sdsErrorCode); + } else { + ESP_LOGI(TAG, "fine-dust-values: %5.1f,%4.1f", pm10, pm25); } - return; + sds011_sleep(); + } + return; } // putting the SDS-sensor to sleep -void sds011_sleep(void) -{ - sdsSensor.sleep(); - isSDS011Active = false; +void sds011_sleep(void) { + sdsSensor.sleep(); + isSDS011Active = false; } // start the SDS-sensor // needs 30 seconds for warming up -void sds011_wakeup() -{ - if ( !isSDS011Active ) { - sdsSensor.wakeup(); - isSDS011Active = true; - } +void sds011_wakeup() { + if (!isSDS011Active) { + sdsSensor.wakeup(); + isSDS011Active = true; + } } diff --git a/src/senddata.cpp b/src/senddata.cpp index 00986562..166bc960 100644 --- a/src/senddata.cpp +++ b/src/senddata.cpp @@ -21,10 +21,6 @@ void SendPayload(uint8_t port, sendprio_t prio) { MessageBuffer_t SendBuffer; // contains MessageSize, MessagePort, MessagePrio, Message[] -//#if (HAS_SDS011) -// sds011_loop(); -//#endif - SendBuffer.MessageSize = payload.getSize(); SendBuffer.MessagePrio = prio; From 66e760391d9161c0c1a683aa41bd3e8c84179c17 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Thu, 6 Feb 2020 14:29:23 +0100 Subject: [PATCH 053/106] Update cyclic.cpp removed some debug-info --- src/cyclic.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/cyclic.cpp b/src/cyclic.cpp index 2fd00037..b0223381 100644 --- a/src/cyclic.cpp +++ b/src/cyclic.cpp @@ -14,9 +14,7 @@ extern boolean isSDS011Active; #endif void housekeeping() { - static int counter = 0; xTaskNotifyFromISR(irqHandlerTask, CYCLIC_IRQ, eSetBits, NULL); -ESP_LOGI( TAG, "in Housekeeping(): %d", counter++); } // do all housekeeping From 853bddd2655233a7ebf7ee07fe12c89591891b20 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Thu, 6 Feb 2020 14:30:04 +0100 Subject: [PATCH 054/106] Update senddata.cpp removed some debug-info --- src/senddata.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/senddata.cpp b/src/senddata.cpp index 00986562..577748c4 100644 --- a/src/senddata.cpp +++ b/src/senddata.cpp @@ -9,10 +9,7 @@ extern float pm25; #endif void sendcycle() { - static int counter = 0; xTaskNotifyFromISR(irqHandlerTask, SENDCYCLE_IRQ, eSetBits, NULL); -ESP_LOGI( TAG, "in sendcycle(): %d", counter++); - } // put data to send in RTos Queues used for transmit over channels Lora and SPI From fa2920340cc6df6c4a326d1a7c326f04c8137d6c Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Thu, 6 Feb 2020 16:53:50 +0100 Subject: [PATCH 055/106] SDS011read.cpp: bugfix conditional compile --- src/sds011read.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sds011read.cpp b/src/sds011read.cpp index 0b66c747..71be06d9 100644 --- a/src/sds011read.cpp +++ b/src/sds011read.cpp @@ -3,15 +3,19 @@ // Local logging tag static const char TAG[] = __FILE__; +#if (HAS_SDS) + #include "sds011read.h" // UART(2) is unused in this project #if (HAS_IF482) #error cannot use IF482 together with SDS011 (both use UART#2) #endif + #ifndef SDS011_SERIAL #error serial settings for SDS011 connection missing #endif + static HardwareSerial sdsSerial(2); // so we use it here static SDS011 sdsSensor; // fine dust sensor @@ -58,3 +62,5 @@ void sds011_wakeup() { isSDS011Active = true; } } + +#endif // HAS_SDS \ No newline at end of file From 94e1a6dc0d2f3a8fa6f71080336c998752d72aba Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Mon, 17 Feb 2020 22:43:40 +0100 Subject: [PATCH 056/106] Update platformio.ini SDS011 lib version nailed --- platformio.ini | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 538d6163..a2640cfd 100644 --- a/platformio.ini +++ b/platformio.ini @@ -73,7 +73,8 @@ lib_deps_sensors = Adafruit BME280 Library@>=2.0.0 Adafruit BMP085 Library@>=1.0.1 BSEC Software Library@1.5.1474 - SDS011 sensor Library + ;SDS011 sensor Library + https://github.com/ricki-z/SDS011.git#33fd8b6 lib_deps_basic = ArduinoJson@^5.13.1 76@>=1.2.4 ; #76 Timezone by Jack Christensen From a51deb41165fbb1adeaaa915f6e20e171e473e0a Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Wed, 19 Feb 2020 13:55:08 +0100 Subject: [PATCH 057/106] Update platformio.ini --- platformio.ini | 3 +++ 1 file changed, 3 insertions(+) diff --git a/platformio.ini b/platformio.ini index e9d5dc49..e3dabd48 100644 --- a/platformio.ini +++ b/platformio.ini @@ -71,6 +71,8 @@ lib_deps_sensors = Adafruit Unified Sensor@>=1.1.1 Adafruit BME280 Library@>=2.0.0 Adafruit BMP085 Library@>=1.0.1 +lib_deps_sds = + https://github.com/ricki-z/SDS011.git#33fd8b6 lib_deps_basic = ArduinoJson@^5.13.1 76@>=1.2.4 ; #76 Timezone by Jack Christensen @@ -86,6 +88,7 @@ lib_deps_all = ${common.lib_deps_gps} ${common.lib_deps_sensors} ${common.lib_deps_matrix_display} + ${common.lib_deps_sds} build_flags_basic = -include "src/hal/${board.halfile}" -include "src/paxcounter.conf" From 40c27f8c66bc020c7b64c12104d0510438498677 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Wed, 19 Feb 2020 13:57:21 +0100 Subject: [PATCH 058/106] Update sds011read.cpp --- src/sds011read.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/sds011read.cpp b/src/sds011read.cpp index cf6dccdf..13df56fa 100644 --- a/src/sds011read.cpp +++ b/src/sds011read.cpp @@ -21,12 +21,11 @@ boolean isSDS011Active; bool sds011_init() { pm25 = pm10 = 0.0; - sdsSerial.begin(9600, SERIAL_8N1, ESP_PIN_RX, ESP_PIN_TX); - sdsSensor.begin (&sdsSerial); - sdsSensor.contmode(0); // for safety: no wakeup/sleep by the sensor - sds011_sleep(); // we do it by ourselves - return true; + sdsSensor.begin (&sdsSerial, ESP_PIN_RX, ESP_PIN_TX); + sds011_sleep(); // we do sleep/wakup by ourselves + return true; } + // reading data: void sds011_loop() { From 25d4d9d0474b6c5f087c4ffb71ba8c99c91bf9e5 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Wed, 19 Feb 2020 13:58:58 +0100 Subject: [PATCH 059/106] Update ttgov21new.h --- src/hal/ttgov21new.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/hal/ttgov21new.h b/src/hal/ttgov21new.h index 90f93c7a..fc0b5036 100644 --- a/src/hal/ttgov21new.h +++ b/src/hal/ttgov21new.h @@ -11,6 +11,11 @@ // ATTENTION: check your board version! // This settings are for boards labeled v1.6 on pcb, NOT for v1.5 or older */ +// SDS011 dust sensor settings +#define HAS_SDS011 1 // use SDS011 +// used pins on the ESP-side: +#define ESP_PIN_TX 19 // connect to RX on the SDS011 +#define ESP_PIN_RX 23 // connect to TX on the SDS011 #define HAS_LORA 1 // comment out if device shall not send data via LoRa #define CFG_sx1276_radio 1 // HPD13A LoRa SoC @@ -48,4 +53,4 @@ #define LORA_IO1 (33) #define LORA_IO2 (32) -#endif \ No newline at end of file +#endif From 20df7ca28237c78fed8607c41af19f2d91efe3ee Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Tue, 25 Feb 2020 15:38:09 +0100 Subject: [PATCH 060/106] Update platformio.ini --- platformio.ini | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/platformio.ini b/platformio.ini index e3dabd48..a2640cfd 100644 --- a/platformio.ini +++ b/platformio.ini @@ -31,6 +31,8 @@ halfile = generic.h ;halfile = octopus32.h ;halfile = tinypico.h ;halfile = tinypicomatrix.h +;halfile = m5core.h +;halfile = m5fire.h [platformio] ; upload firmware to board with usb cable @@ -43,10 +45,10 @@ description = Paxcounter is a device for metering passenger flows in realtime. I [common] ; for release_version use max. 10 chars total, use any decimal format like "a.b.c" -release_version = 1.9.85 +release_version = 1.9.90 ; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running! ; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose -debug_level = 3 +debug_level = 5 extra_scripts = pre:build.py otakeyfile = ota.conf lorakeyfile = loraconf.h @@ -55,14 +57,13 @@ platform_espressif32 = espressif32@1.11.1 monitor_speed = 115200 upload_speed = 115200 lib_deps_lora = - ;MCCI LoRaWAN LMIC library@>=3.1.0 ; MCCI LMIC by Terrill Moore - https://github.com/mcci-catena/arduino-lmic.git#3ca90f3 + MCCI LoRaWAN LMIC library@>=3.1.0 ; MCCI LMIC by Terrill Moore lib_deps_display = - ss_oled@>=3.2.0 ; simple and small OLED lib by Larry Bank + ss_oled@>=3.3.1 ; simple and small OLED lib by Larry Bank BitBang_I2C@>=1.3.0 QRCode@>=0.0.1 lib_deps_matrix_display = - Ultrathin_LED_Matrix + Ultrathin_LED_Matrix@>=1.0.0 lib_deps_rgbled = SmartLeds@>=1.1.6 lib_deps_gps = @@ -71,7 +72,8 @@ lib_deps_sensors = Adafruit Unified Sensor@>=1.1.1 Adafruit BME280 Library@>=2.0.0 Adafruit BMP085 Library@>=1.0.1 -lib_deps_sds = + BSEC Software Library@1.5.1474 + ;SDS011 sensor Library https://github.com/ricki-z/SDS011.git#33fd8b6 lib_deps_basic = ArduinoJson@^5.13.1 @@ -88,7 +90,6 @@ lib_deps_all = ${common.lib_deps_gps} ${common.lib_deps_sensors} ${common.lib_deps_matrix_display} - ${common.lib_deps_sds} build_flags_basic = -include "src/hal/${board.halfile}" -include "src/paxcounter.conf" From 0d18feb29d66492384d48f86bb87fd9bb34750cf Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Tue, 25 Feb 2020 15:41:12 +0100 Subject: [PATCH 061/106] Update sds011read.cpp --- src/sds011read.cpp | 73 +++++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/src/sds011read.cpp b/src/sds011read.cpp index 13df56fa..e6d1c216 100644 --- a/src/sds011read.cpp +++ b/src/sds011read.cpp @@ -3,59 +3,64 @@ // Local logging tag static const char TAG[] = __FILE__; -#include +#if (HAS_SDS) + +#include "sds011read.h" // UART(2) is unused in this project #if (HAS_IF482) #error cannot use IF482 together with SDS011 (both use UART#2) #endif -static HardwareSerial sdsSerial(2); // so we use it here -static SDS011 sdsSensor; // fine dust sensor + +#ifndef SDS011_SERIAL +#error serial settings for SDS011 connection missing +#endif + +static HardwareSerial sdsSerial(2); // so we use it here +static SDS011 sdsSensor; // fine dust sensor // the results of the sensor: -float pm25; +float pm25; float pm10; boolean isSDS011Active; // init -bool sds011_init() -{ - pm25 = pm10 = 0.0; - sdsSensor.begin (&sdsSerial, ESP_PIN_RX, ESP_PIN_TX); - sds011_sleep(); // we do sleep/wakup by ourselves - return true; +bool sds011_init() { + pm25 = pm10 = 0.0; + sdsSerial.begin(SDS011_SERIAL); + sdsSensor.begin(&sdsSerial); + //sdsSensor.contmode(0); // for safety: no wakeup/sleep by the sensor + sds011_sleep(); // we do it by ourselves + return true; } - // reading data: -void sds011_loop() -{ - if ( isSDS011Active ) { - int sdsErrorCode = sdsSensor.read(&pm25, &pm10); - if (sdsErrorCode) { - pm25 = pm10 = 0.0; - ESP_LOGI(TAG, "SDS011 error: %d", sdsErrorCode); - } - else { - ESP_LOGI(TAG, "fine-dust-values: %5.1f,%4.1f", pm10, pm25); - } - sds011_sleep(); +void sds011_loop() { + if (isSDS011Active) { + int sdsErrorCode = sdsSensor.read(&pm25, &pm10); + if (sdsErrorCode) { + pm25 = pm10 = 0.0; + ESP_LOGI(TAG, "SDS011 error: %d", sdsErrorCode); + } else { + ESP_LOGI(TAG, "fine-dust-values: %5.1f,%4.1f", pm10, pm25); } - return; + sds011_sleep(); + } + return; } // putting the SDS-sensor to sleep -void sds011_sleep(void) -{ - sdsSensor.sleep(); - isSDS011Active = false; +void sds011_sleep(void) { + sdsSensor.sleep(); + isSDS011Active = false; } // start the SDS-sensor // needs 30 seconds for warming up -void sds011_wakeup() -{ - if ( !isSDS011Active ) { - sdsSensor.wakeup(); - isSDS011Active = true; - } +void sds011_wakeup() { + if (!isSDS011Active) { + sdsSensor.wakeup(); + isSDS011Active = true; + } } + +#endif // HAS_SDS From b674b5e1b075ecf29fefe334a3cba3d6bd818d89 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Tue, 25 Feb 2020 15:43:54 +0100 Subject: [PATCH 062/106] Update sds011read.cpp --- src/sds011read.cpp | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/sds011read.cpp b/src/sds011read.cpp index e6d1c216..a7c84ba6 100644 --- a/src/sds011read.cpp +++ b/src/sds011read.cpp @@ -3,19 +3,12 @@ // Local logging tag static const char TAG[] = __FILE__; -#if (HAS_SDS) - #include "sds011read.h" // UART(2) is unused in this project #if (HAS_IF482) #error cannot use IF482 together with SDS011 (both use UART#2) #endif - -#ifndef SDS011_SERIAL -#error serial settings for SDS011 connection missing -#endif - static HardwareSerial sdsSerial(2); // so we use it here static SDS011 sdsSensor; // fine dust sensor @@ -27,12 +20,14 @@ boolean isSDS011Active; // init bool sds011_init() { pm25 = pm10 = 0.0; - sdsSerial.begin(SDS011_SERIAL); - sdsSensor.begin(&sdsSerial); + sdsSensor.begin (&sdsSerial, ESP_PIN_RX, ESP_PIN_TX); + delay(100); +// sdsSerial.begin(SDS011_SERIAL); //sdsSensor.contmode(0); // for safety: no wakeup/sleep by the sensor - sds011_sleep(); // we do it by ourselves + sds011_sleep(); // we do sleep/wakup by ourselves return true; } + // reading data: void sds011_loop() { if (isSDS011Active) { @@ -62,5 +57,3 @@ void sds011_wakeup() { isSDS011Active = true; } } - -#endif // HAS_SDS From d0ffe9ab7efb1553fba10390d41309787f2aec1a Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Tue, 25 Feb 2020 15:44:56 +0100 Subject: [PATCH 063/106] Update sdcard.cpp --- src/sdcard.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sdcard.cpp b/src/sdcard.cpp index 780efef9..002cfdf8 100644 --- a/src/sdcard.cpp +++ b/src/sdcard.cpp @@ -24,6 +24,8 @@ bool sdcard_init() { useSDCard = SD.begin(SDCARD_CS, SDCARD_MOSI, SDCARD_MISO, SDCARD_SCLK); if (useSDCard) createFile(); + else + ESP_LOGD(TAG,"SD-card not found"); return useSDCard; } @@ -84,4 +86,3 @@ void createFile(void) { } #endif // (HAS_SDCARD) - From ad3cd8bf9e138ee1f9dfe56064133012955c8e77 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Tue, 25 Feb 2020 15:46:13 +0100 Subject: [PATCH 064/106] Update generic.h --- src/hal/generic.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/hal/generic.h b/src/hal/generic.h index 5d56e591..658eb7bc 100644 --- a/src/hal/generic.h +++ b/src/hal/generic.h @@ -10,6 +10,10 @@ // Hardware related definitions for generic ESP32 boards // generic.h is kitchensink with all available options +// SDS011 dust sensor settings +// #define HAS_SDS011 1 // use SDS011 +// #define SDS011_SERIAL 9600, SERIAL_8N1, GPIO_NUM_19, GPIO_NUM_23 // SDS011 RX, TX + #define HAS_LORA 1 // comment out if device shall not send data via LoRa or has no LoRa #define HAS_SPI 1 // comment out if device shall not send data via SPI // pin definitions for SPI slave interface @@ -100,4 +104,4 @@ #define MCP_24AA02E64_I2C_ADDRESS 0x50 // I2C address for the 24AA02E64 #define MCP_24AA02E64_MAC_ADDRESS 0xF8 // Memory adress of unique deveui 64 bits -#endif \ No newline at end of file +#endif From f3218b4266c8e2b2f05018656ec27ece31db77be Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Tue, 25 Feb 2020 15:47:15 +0100 Subject: [PATCH 065/106] Update ttgov21new.h --- src/hal/ttgov21new.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/hal/ttgov21new.h b/src/hal/ttgov21new.h index fc0b5036..9fa665f2 100644 --- a/src/hal/ttgov21new.h +++ b/src/hal/ttgov21new.h @@ -11,8 +11,9 @@ // ATTENTION: check your board version! // This settings are for boards labeled v1.6 on pcb, NOT for v1.5 or older */ + // SDS011 dust sensor settings -#define HAS_SDS011 1 // use SDS011 +#define HAS_SDS011 1 // use SDS011 // used pins on the ESP-side: #define ESP_PIN_TX 19 // connect to RX on the SDS011 #define ESP_PIN_RX 23 // connect to TX on the SDS011 From 3dd143f70ac73b5a6873467b71d032e2f47a8001 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Tue, 25 Feb 2020 15:53:22 +0100 Subject: [PATCH 067/106] Update sds011read.h --- include/sds011read.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/include/sds011read.h b/include/sds011read.h index d278a5fd..8c573c0d 100644 --- a/include/sds011read.h +++ b/include/sds011read.h @@ -3,10 +3,6 @@ #include -// used pins on the ESP-side: -#define ESP_PIN_TX 19 // connect to RX on the SDS011 -#define ESP_PIN_RX 23 // connect to TX on the SDS011 - #define SDCARD_FILE_HEADER_SDS011 ", PM10,PM25" bool sds011_init(); From e6ab58ff91acefcfb2c0b1ba5888a572c9cf38c4 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Tue, 25 Feb 2020 15:54:03 +0100 Subject: [PATCH 068/106] Update senddata.h --- include/senddata.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/include/senddata.h b/include/senddata.h index 324f2e1f..5022800c 100644 --- a/include/senddata.h +++ b/include/senddata.h @@ -16,10 +16,6 @@ #include "sdcard.h" #endif -#if (HAS_SDS011) -#include "sds011read.h" -#endif - extern Ticker sendcycler; void SendPayload(uint8_t port, sendprio_t prio); From 497c738405aa5e26f02d6fdb5bcfd6d0114f4492 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Tue, 25 Feb 2020 15:56:11 +0100 Subject: [PATCH 069/106] Update sds011read.cpp --- src/sds011read.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sds011read.cpp b/src/sds011read.cpp index a7c84ba6..c29a9a77 100644 --- a/src/sds011read.cpp +++ b/src/sds011read.cpp @@ -5,10 +5,10 @@ static const char TAG[] = __FILE__; #include "sds011read.h" -// UART(2) is unused in this project #if (HAS_IF482) #error cannot use IF482 together with SDS011 (both use UART#2) #endif +// UART(2) is unused in this project static HardwareSerial sdsSerial(2); // so we use it here static SDS011 sdsSensor; // fine dust sensor From 96ab12420feb4294f5f01a16054175d9a8257aa1 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Tue, 25 Feb 2020 16:01:02 +0100 Subject: [PATCH 072/106] Update senddata.cpp --- src/senddata.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/senddata.cpp b/src/senddata.cpp index 577748c4..f5b867f6 100644 --- a/src/senddata.cpp +++ b/src/senddata.cpp @@ -18,10 +18,6 @@ void SendPayload(uint8_t port, sendprio_t prio) { MessageBuffer_t SendBuffer; // contains MessageSize, MessagePort, MessagePrio, Message[] -//#if (HAS_SDS011) -// sds011_loop(); -//#endif - SendBuffer.MessageSize = payload.getSize(); SendBuffer.MessagePrio = prio; From cfe877ff70e725e67509d70726fa00ea0ebc3b65 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Tue, 25 Feb 2020 17:22:31 +0100 Subject: [PATCH 075/106] Update sds011read.cpp --- src/sds011read.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sds011read.cpp b/src/sds011read.cpp index f5a781d5..f280fbe6 100644 --- a/src/sds011read.cpp +++ b/src/sds011read.cpp @@ -56,5 +56,3 @@ void sds011_wakeup() { isSDS011Active = true; } } - -#endif // HAS_SDS \ No newline at end of file From 39f99043d6fd7298787dc9b659b7fa04f7d39c0e Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Tue, 25 Feb 2020 22:18:20 +0100 Subject: [PATCH 076/106] SDS011 code sanitizations --- include/globals.h | 5 ++ include/payload.h | 3 +- include/sds011read.h | 2 + platformio.ini | 3 +- src/gpsread.cpp | 3 +- src/hal/generic.h | 8 ++- src/hal/ttgov21new.h | 4 +- src/payload.cpp | 120 ++++++++++++++++++++----------------------- src/sdcard.cpp | 24 ++++----- src/sds011read.cpp | 19 ++++--- src/senddata.cpp | 16 +++--- 11 files changed, 107 insertions(+), 100 deletions(-) diff --git a/include/globals.h b/include/globals.h index 41002c32..22ed8954 100644 --- a/include/globals.h +++ b/include/globals.h @@ -111,6 +111,11 @@ typedef struct { float gas; // raw gas sensor signal } bmeStatus_t; +typedef struct { + float pm10; + float pm25; +} sdsStatus_t; + extern std::set, Mallocator> macs; extern std::array::iterator it; extern std::array beacons; diff --git a/include/payload.h b/include/payload.h index 719d59ec..a7de4ddd 100644 --- a/include/payload.h +++ b/include/payload.h @@ -59,8 +59,7 @@ public: void addButton(uint8_t value); void addSensor(uint8_t[]); void addTime(time_t value); - void addPM10(float value); - void addPM25(float value); + void addSDS(sdsStatus_t value); private: void addChars( char* string, int len); diff --git a/include/sds011read.h b/include/sds011read.h index 8c573c0d..97307d1c 100644 --- a/include/sds011read.h +++ b/include/sds011read.h @@ -1,6 +1,7 @@ #ifndef _SDS011READ_H #define _SDS011READ_H +#include "globals.h" #include #define SDCARD_FILE_HEADER_SDS011 ", PM10,PM25" @@ -9,5 +10,6 @@ bool sds011_init(); void sds011_loop(); void sds011_sleep(void); void sds011_wakeup(void); +void sds011_store(sdsStatus_t *sds_store); #endif // _SDS011READ_H diff --git a/platformio.ini b/platformio.ini index 704ebdc7..3a1e9cfb 100644 --- a/platformio.ini +++ b/platformio.ini @@ -73,8 +73,7 @@ lib_deps_sensors = Adafruit BME280 Library@>=2.0.0 Adafruit BMP085 Library@>=1.0.1 BSEC Software Library@1.5.1474 - ;SDS011 sensor Library - https://github.com/ricki-z/SDS011.git#33fd8b6 + https://github.com/ricki-z/SDS011.git#33fd8b6 ;SDS011 sensor Library lib_deps_basic = ArduinoJson@^5.13.1 76@>=1.2.4 ; #76 Timezone by Jack Christensen diff --git a/src/gpsread.cpp b/src/gpsread.cpp index 03318284..0205cc9d 100644 --- a/src/gpsread.cpp +++ b/src/gpsread.cpp @@ -10,8 +10,7 @@ static const char TAG[] = __FILE__; TinyGPSPlus gps; TinyGPSCustom gpstime(gps, "GPZDA", 1); // field 1 = UTC time static const String ZDA_Request = "$EIGPQ,ZDA*39\r\n"; - -gpsStatus_t gps_status = {0}; +static gpsStatus_t gps_status = {0}; TaskHandle_t GpsTask; #ifdef GPS_SERIAL diff --git a/src/hal/generic.h b/src/hal/generic.h index fa1d14c7..b63cea89 100644 --- a/src/hal/generic.h +++ b/src/hal/generic.h @@ -48,8 +48,14 @@ //#define HAS_BMP180 //#define BMP180_ADDR 0x77 +// SDS011 dust sensor settings +#define HAS_SDS011 1 // use SDS011 +// used pins on the ESP-side: +#define SDS_TX 19 // connect to RX on the SDS011 +#define SDS_RX 23 // connect to TX on the SDS011 + // user defined sensors -//#define HAS_SENSORS 1 // comment out if device has user defined sensors +#define HAS_SENSORS 1 // comment out if device has user defined sensors #define CFG_sx1276_radio 1 // select LoRa chip //#define CFG_sx1272_radio 1 // select LoRa chip diff --git a/src/hal/ttgov21new.h b/src/hal/ttgov21new.h index 9fa665f2..e7292f2a 100644 --- a/src/hal/ttgov21new.h +++ b/src/hal/ttgov21new.h @@ -15,8 +15,8 @@ // SDS011 dust sensor settings #define HAS_SDS011 1 // use SDS011 // used pins on the ESP-side: -#define ESP_PIN_TX 19 // connect to RX on the SDS011 -#define ESP_PIN_RX 23 // connect to TX on the SDS011 +#define SDS_TX 19 // connect to RX on the SDS011 +#define SDS_RX 23 // connect to TX on the SDS011 #define HAS_LORA 1 // comment out if device shall not send data via LoRa #define CFG_sx1276_radio 1 // HPD13A LoRa SoC diff --git a/src/payload.cpp b/src/payload.cpp index 57428234..b3291348 100644 --- a/src/payload.cpp +++ b/src/payload.cpp @@ -80,7 +80,7 @@ void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime, float cputemp, } void PayloadConvert::addGPS(gpsStatus_t value) { -#if(HAS_GPS) +#if (HAS_GPS) buffer[cursor++] = (byte)((value.latitude & 0xFF000000) >> 24); buffer[cursor++] = (byte)((value.latitude & 0x00FF0000) >> 16); buffer[cursor++] = (byte)((value.latitude & 0x0000FF00) >> 8); @@ -100,7 +100,7 @@ void PayloadConvert::addGPS(gpsStatus_t value) { } void PayloadConvert::addSensor(uint8_t buf[]) { -#if(HAS_SENSORS) +#if (HAS_SENSORS) uint8_t length = buf[0]; memcpy(buffer, buf + 1, length); cursor += length; // length of buffer @@ -108,7 +108,7 @@ void PayloadConvert::addSensor(uint8_t buf[]) { } void PayloadConvert::addBME(bmeStatus_t value) { -#if(HAS_BME) +#if (HAS_BME) int16_t temperature = (int16_t)(value.temperature); // float -> int uint16_t humidity = (uint16_t)(value.humidity); // float -> int uint16_t pressure = (uint16_t)(value.pressure); // float -> int @@ -124,6 +124,16 @@ void PayloadConvert::addBME(bmeStatus_t value) { #endif } +void PayloadConvert::addSDS(sdsStatus_t sds) { +#if (HAS_SDS011) + char tempBuffer[10 + 1]; + sprintf(tempBuffer, ",%5.1f", sds.pm10); + addChars(tempBuffer, strlen(tempBuffer)); + sprintf(tempBuffer, ",%5.1f", sds.pm25); + addChars(tempBuffer, strlen(tempBuffer)); +#endif // HAS_SDS011 +} + void PayloadConvert::addButton(uint8_t value) { #ifdef HAS_BUTTON buffer[cursor++] = value; @@ -193,7 +203,7 @@ void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime, float cputemp, } void PayloadConvert::addGPS(gpsStatus_t value) { -#if(HAS_GPS) +#if (HAS_GPS) writeLatLng(value.latitude, value.longitude); #if (!PAYLOAD_OPENSENSEBOX) writeUint8(value.satellites); @@ -204,7 +214,7 @@ void PayloadConvert::addGPS(gpsStatus_t value) { } void PayloadConvert::addSensor(uint8_t buf[]) { -#if(HAS_SENSORS) +#if (HAS_SENSORS) uint8_t length = buf[0]; memcpy(buffer, buf + 1, length); cursor += length; // length of buffer @@ -212,7 +222,7 @@ void PayloadConvert::addSensor(uint8_t buf[]) { } void PayloadConvert::addBME(bmeStatus_t value) { -#if(HAS_BME) +#if (HAS_BME) writeFloat(value.temperature); writePressure(value.pressure); writeUFloat(value.humidity); @@ -220,6 +230,13 @@ void PayloadConvert::addBME(bmeStatus_t value) { #endif } +void PayloadConvert::addSDS(sdsStatus_t sds) { +#if (HAS_SDS011) + writeUint16((uint16_t)(sds.pm10 * 10)); + writeUint16((uint16_t)(sds.pm25 * 10)); +#endif // HAS_SDS011 +} + void PayloadConvert::addButton(uint8_t value) { #ifdef HAS_BUTTON writeUint8(value); @@ -242,9 +259,7 @@ void PayloadConvert::uintToBytes(uint64_t value, uint8_t byteSize) { } } -void PayloadConvert::writeUptime(uint64_t uptime) { - writeUint64(uptime); -} +void PayloadConvert::writeUptime(uint64_t uptime) { writeUint64(uptime); } void PayloadConvert::writeVersion(char *version) { memcpy(buffer + cursor, version, 10); @@ -265,13 +280,9 @@ void PayloadConvert::writeUint16(uint16_t i) { uintToBytes(i, 2); } void PayloadConvert::writeUint8(uint8_t i) { uintToBytes(i, 1); } -void PayloadConvert::writeUFloat(float value) { - writeUint16(value * 100); -} +void PayloadConvert::writeUFloat(float value) { writeUint16(value * 100); } -void PayloadConvert::writePressure(float value) { - writeUint16(value * 10); -} +void PayloadConvert::writePressure(float value) { writeUint16(value * 10); } /** * Uses a 16bit two's complement with two decimals, so the range is @@ -312,10 +323,25 @@ void PayloadConvert::writeBitmap(bool a, bool b, bool c, bool d, bool e, bool f, #elif ((PAYLOAD_ENCODER == 3) || (PAYLOAD_ENCODER == 4)) -void PayloadConvert::addByte(uint8_t value) { - /* +void PayloadConvert::addByte(uint8_t value) { + /* not implemented - */ } + */ +} + +void PayloadConvert::addSDS(sdsStatus_t sds) { +#if (HAS_SDS011) + +#if (PAYLOAD_ENCODER == 3) // Cayenne LPP dynamic +#error not implemented yet +#endif + +#if (PAYLOAD_ENCODER == 4) // Cayenne LPP packed +#error not implemented yet +#endif + +#endif // HAS_SDS011 +} void PayloadConvert::addCount(uint16_t value, uint8_t snifftype) { switch (snifftype) { @@ -393,7 +419,7 @@ void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime, float celsius, } void PayloadConvert::addGPS(gpsStatus_t value) { -#if(HAS_GPS) +#if (HAS_GPS) int32_t lat = value.latitude / 100; int32_t lon = value.longitude / 100; int32_t alt = value.altitude * 100; @@ -414,18 +440,18 @@ void PayloadConvert::addGPS(gpsStatus_t value) { } void PayloadConvert::addSensor(uint8_t buf[]) { -#if(HAS_SENSORS) -// to come -/* - uint8_t length = buf[0]; - memcpy(buffer, buf+1, length); - cursor += length; // length of buffer -*/ +#if (HAS_SENSORS) + // to come + /* + uint8_t length = buf[0]; + memcpy(buffer, buf+1, length); + cursor += length; // length of buffer + */ #endif // HAS_SENSORS } void PayloadConvert::addBME(bmeStatus_t value) { -#if(HAS_BME) +#if (HAS_BME) // data value conversions to meet cayenne data type definition // 0.1°C per bit => -3276,7 .. +3276,7 °C @@ -491,39 +517,7 @@ void PayloadConvert::addTime(time_t value) { } #endif // PAYLOAD_ENCODER -void PayloadConvert::addPM10( float value) { -#if (HAS_SDS011) -#if (PAYLOAD_ENCODER == 1) // plain - char tempBuffer[10+1]; - sprintf( tempBuffer, ",%5.1f", value); - addChars(tempBuffer, strlen(tempBuffer)); -#elif (PAYLOAD_ENCODER == 2 ) // packed - writeUint16( (uint16_t) (value*10) ); -#elif (PAYLOAD_ENCODER == 3 ) // Cayenne LPP dynamic -#error not implemented yet -#elif (PAYLOAD_ENCODER == 4 ) // Cayenne LPP packed -#error not implemented yet -#endif -#endif // HAS_SDS011 -} - -void PayloadConvert::addPM25( float value) { -#if (HAS_SDS011) -#if (PAYLOAD_ENCODER == 1) // plain - char tempBuffer[10+1]; - sprintf( tempBuffer, ",%5.1f", value); - addChars(tempBuffer, strlen(tempBuffer)); -#elif (PAYLOAD_ENCODER == 2 ) // packed - writeUint16( (uint16_t) (value*10) ); -#elif (PAYLOAD_ENCODER == 3 ) // Cayenne LPP dynamic -#error not implemented yet -#elif (PAYLOAD_ENCODER == 4 ) // Cayenne LPP packed -#error not implemented yet -#endif -#endif // HAS_SDS011 -} - -void PayloadConvert::addChars( char * string, int len) { - for (int i=0; i < len; i++) - addByte(string[i]); -} +void PayloadConvert::addChars(char *string, int len) { + for (int i = 0; i < len; i++) + addByte(string[i]); +} \ No newline at end of file diff --git a/src/sdcard.cpp b/src/sdcard.cpp index 002cfdf8..5faab5cd 100644 --- a/src/sdcard.cpp +++ b/src/sdcard.cpp @@ -6,12 +6,6 @@ static const char TAG[] = __FILE__; #include "sdcard.h" -#if (HAS_SDS011) -#include -// the results of the sensor: -extern float pm25; -extern float pm10; -#endif static bool useSDCard; @@ -25,7 +19,7 @@ bool sdcard_init() { if (useSDCard) createFile(); else - ESP_LOGD(TAG,"SD-card not found"); + ESP_LOGD(TAG, "SD-card not found"); return useSDCard; } @@ -33,6 +27,9 @@ void sdcardWriteData(uint16_t noWifi, uint16_t noBle) { static int counterWrites = 0; char tempBuffer[12 + 1]; time_t t = now(); +#if (HAS_SDS011) + sdsStatus_t sds; +#endif if (!useSDCard) return; @@ -43,12 +40,13 @@ void sdcardWriteData(uint16_t noWifi, uint16_t noBle) { sprintf(tempBuffer, "%02d:%02d:%02d,", hour(t), minute(t), second(t)); fileSDCard.print(tempBuffer); sprintf(tempBuffer, "%d,%d", noWifi, noBle); - fileSDCard.print( tempBuffer); + fileSDCard.print(tempBuffer); #if (HAS_SDS011) - sprintf(tempBuffer, ",%5.1f,%4.1f", pm10, pm25); - fileSDCard.print( tempBuffer); + sds011_store(&sds); + sprintf(tempBuffer, ",%5.1f,%4.1f", sds.pm10, sds.pm25); + fileSDCard.print(tempBuffer); #endif - fileSDCard.println( ); + fileSDCard.println(); if (++counterWrites > 2) { // force writing to SD-card @@ -72,9 +70,9 @@ void createFile(void) { fileSDCard = SD.open(bufferFilename, FILE_WRITE); if (fileSDCard) { ESP_LOGD(TAG, "SD: name opened: <%s>", bufferFilename); - fileSDCard.print( SDCARD_FILE_HEADER ); + fileSDCard.print(SDCARD_FILE_HEADER); #if (HAS_SDS011) - fileSDCard.print( SDCARD_FILE_HEADER_SDS011 ); + fileSDCard.print(SDCARD_FILE_HEADER_SDS011); #endif fileSDCard.println(); useSDCard = true; diff --git a/src/sds011read.cpp b/src/sds011read.cpp index f280fbe6..a693f651 100644 --- a/src/sds011read.cpp +++ b/src/sds011read.cpp @@ -1,5 +1,7 @@ // routines for fetching data from the SDS011-sensor +#if (HAS_SDS011) + // Local logging tag static const char TAG[] = __FILE__; @@ -13,17 +15,14 @@ static HardwareSerial sdsSerial(2); // so we use it here static SDS011 sdsSensor; // fine dust sensor // the results of the sensor: -float pm25; -float pm10; +static float pm10, pm25; boolean isSDS011Active; // init bool sds011_init() { pm25 = pm10 = 0.0; -#if (HAS_SDS011) - sdsSensor.begin (&sdsSerial, ESP_PIN_RX, ESP_PIN_TX); -#endif - sds011_sleep(); // we do sleep/wakup by ourselves + sdsSensor.begin(&sdsSerial, SDS_RX, SDS_TX); + sds011_sleep(); // we do sleep/wakup by ourselves return true; } @@ -42,6 +41,12 @@ void sds011_loop() { return; } +// retrieving stored data: +void sds011_store(sdsStatus_t *sds_store) { + sds_store->pm10 = pm10; + sds_store->pm25 = pm25; +} + // putting the SDS-sensor to sleep void sds011_sleep(void) { sdsSensor.sleep(); @@ -56,3 +61,5 @@ void sds011_wakeup() { isSDS011Active = true; } } + +#endif // HAS_SDS011 diff --git a/src/senddata.cpp b/src/senddata.cpp index f5b867f6..a98c129e 100644 --- a/src/senddata.cpp +++ b/src/senddata.cpp @@ -3,11 +3,6 @@ Ticker sendcycler; -#if (HAS_SDS011) -extern float pm10; -extern float pm25; -#endif - void sendcycle() { xTaskNotifyFromISR(irqHandlerTask, SENDCYCLE_IRQ, eSetBits, NULL); } @@ -68,9 +63,12 @@ void sendData() { uint8_t bitmask = cfg.payloadmask; uint8_t mask = 1; - #if (HAS_GPS) +#if (HAS_GPS) gpsStatus_t gps_status; - #endif +#endif +#if (HAS_SDS011) + sdsStatus_t sds_status; +#endif while (bitmask) { switch (bitmask & mask) { @@ -101,8 +99,8 @@ void sendData() { payload.addCount(macs_ble, MAC_SNIFF_BLE); #endif #if (HAS_SDS011) - payload.addPM10(pm10); - payload.addPM25(pm25); + sds011_store(&sds_status); + payload.addSDS(sds_status); #endif SendPayload(COUNTERPORT, prio_normal); // clear counter if not in cumulative counter mode From c0689bea49cc62429bd8296b2d66a95683c66aed Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Tue, 25 Feb 2020 22:27:49 +0100 Subject: [PATCH 077/106] readme.md updated for SDS011 --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ca90f791..b0280c62 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ Depending on board hardware following features are supported: - Silicon unique ID - Battery voltage monitoring - GPS (Generic serial NMEA, or Quectel L76 I2C) -- Environmental sensor (Bosch BMP180/BME280/BME680 I2C) +- Environmental sensors (Bosch BMP180/BME280/BME680 I2C; SDS011 serial) - Real Time Clock (Maxim DS3231 I2C) - IF482 (serial) and DCF77 (gpio) time telegram generator - Switch external power / battery @@ -167,7 +167,7 @@ by pressing the button of the device. # Sensors and Peripherals -You can add up to 3 user defined sensors. Insert sensor's payload scheme in [*sensor.cpp*](src/sensor.cpp). Bosch BMP180 / BME280 / BME680 environment sensors are supported. Enable flag *lib_deps_sensors* for your board in [*platformio.ini*](src/platformio.ini) and configure BME in board's hal file before build. If you need Bosch's proprietary BSEC libraray (e.g. to get indoor air quality value from BME680) further enable *build_flags_sensors*, which comes on the price of reduced RAM and increased build size. RTC DS3231, generic serial NMEA GPS, I2C LoPy GPS are supported, and to be configured in board's hal file. See [*generic.h*](src/hal/generic.h) for all options and for proper configuration of BME280/BME680. +You can add up to 3 user defined sensors. Insert sensor's payload scheme in [*sensor.cpp*](src/sensor.cpp). Bosch BMP180 / BME280 / BME680 environment sensors are supported. Enable flag *lib_deps_sensors* for your board in [*platformio.ini*](src/platformio.ini) and configure BME in board's hal file before build. If you need Bosch's proprietary BSEC libraray (e.g. to get indoor air quality value from BME680) further enable *build_flags_sensors*, which comes on the price of reduced RAM and increased build size. Furthermore, SDS011, RTC DS3231, generic serial NMEA GPS, I2C LoPy GPS are supported, and to be configured in board's hal file. See [*generic.h*](src/hal/generic.h) for all options and for proper configuration of BME280/BME680. Output of user sensor data can be switched by user remote control command 0x14 sent to Port 2. @@ -548,4 +548,4 @@ Thanks to - [terrillmoore](https://github.com/mcci-catena) for maintaining the LMIC for arduino LoRaWAN stack - [sbamueller](https://github.com/sbamueller) for writing the tutorial in Make Magazine - [Stefan](https://github.com/nerdyscout) for paxcounter opensensebox integration -- [August Quint](https://github.com/AugustQu) for adding SD card data logger support +- [August Quint](https://github.com/AugustQu) for adding SD card data logger and SDS011 support From 4e788ac7202dcc0cc32baf9e61ffb53fd9185633 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Wed, 26 Feb 2020 15:58:10 +0100 Subject: [PATCH 078/106] sds011 lib link --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 3a1e9cfb..012bef14 100644 --- a/platformio.ini +++ b/platformio.ini @@ -73,7 +73,7 @@ lib_deps_sensors = Adafruit BME280 Library@>=2.0.0 Adafruit BMP085 Library@>=1.0.1 BSEC Software Library@1.5.1474 - https://github.com/ricki-z/SDS011.git#33fd8b6 ;SDS011 sensor Library + https://github.com/ricki-z/SDS011.git lib_deps_basic = ArduinoJson@^5.13.1 76@>=1.2.4 ; #76 Timezone by Jack Christensen From 3382f5507a0782fa20ea3dc2530b79f1a43016e7 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Fri, 28 Feb 2020 16:04:22 +0100 Subject: [PATCH 079/106] Cayenne: added constant for PM-values --- include/payload.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/payload.h b/include/payload.h index 719d59ec..0f968df7 100644 --- a/include/payload.h +++ b/include/payload.h @@ -23,6 +23,7 @@ #define LPP_HUMIDITY_CHANNEL 29 #define LPP_BAROMETER_CHANNEL 30 #define LPP_AIR_CHANNEL 31 +#define LPP_PARTMATTER_CHANNEL 32 // particular matter // MyDevices CayenneLPP 2.0 types for Packed Sensor Payload, not using channels, // but different FPorts From d18403a84a30b8c2b604cca755ea05aa8f330ece Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Fri, 28 Feb 2020 16:12:16 +0100 Subject: [PATCH 080/106] Cayenne: added code for PM-values --- src/payload.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/payload.cpp b/src/payload.cpp index 57428234..06bbf5bd 100644 --- a/src/payload.cpp +++ b/src/payload.cpp @@ -317,6 +317,19 @@ void PayloadConvert::addByte(uint8_t value) { not implemented */ } +void PayloadConvert::addSDS(sdsStatus_t sds) { +#if (HAS_SDS011) +#if (PAYLOAD_ENCODER == 3) // Cayenne LPP dynamic + buffer[cursor++] = LPP_PARTMATTER_CHANNEL; // for PM10 and PM25 +#endif + buffer[cursor++] = LPP_LUMINOSITY; // workaround since cayenne has no data type meter + buffer[cursor++] = highByte(sds.pm10); + buffer[cursor++] = lowByte(sds.pm10); + buffer[cursor++] = highByte(sds.pm25); + buffer[cursor++] = lowByte(sds.pm25); +#endif // HAS_SDS011 +} + void PayloadConvert::addCount(uint16_t value, uint8_t snifftype) { switch (snifftype) { case MAC_SNIFF_WIFI: From a2a53f761738ae990d3baedaa3022d6a4048d6c5 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Fri, 28 Feb 2020 16:13:20 +0100 Subject: [PATCH 081/106] Update payload.h --- include/payload.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/payload.h b/include/payload.h index 0f968df7..29d1f88e 100644 --- a/include/payload.h +++ b/include/payload.h @@ -60,8 +60,7 @@ public: void addButton(uint8_t value); void addSensor(uint8_t[]); void addTime(time_t value); - void addPM10(float value); - void addPM25(float value); + void addSDS(sdsStatus_t value); private: void addChars( char* string, int len); From 2b76107ff599276bdd1287be864bd144f54f4eea Mon Sep 17 00:00:00 2001 From: Julian Flake Date: Thu, 5 Mar 2020 14:07:57 +0100 Subject: [PATCH 082/106] Implemented ABP configuration --- README.md | 2 +- src/loraconf.sample.h | 53 +++++++++++++++++++++++++++++++++++++- src/loraconf_abp.cpp | 60 +++++++++++++++++++++++++++++++++++++++++++ src/lorawan.cpp | 45 ++++++++++++++++++++++---------- 4 files changed, 145 insertions(+), 15 deletions(-) create mode 100644 src/loraconf_abp.cpp diff --git a/README.md b/README.md index ca90f791..525adfb0 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ Before compiling the code, - **create file ota.conf in your local /src directory** using the template [ota.sample.conf](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/ota.sample.conf) and enter your WIFI network&key. These settings are used for downloading updates. If you want to push own OTA updates you need a Bintray account. Enter your Bintray user account data in ota.conf. If you don't need wireless firmware updates just rename ota.sample.conf to ota.conf. -To join the network only method OTAA is supported, not ABP. The DEVEUI for OTAA will be derived from the device's MAC adress during device startup and is shown on the device's display (if it has one). It is also printed on the serial console for copying it, if you set *verbose 1* in paxcounter.conf and *debug_level 3* in platformio.ini. +To join the network you have to configure either the preferred OTAA method or the ABP method. The DEVEUI for OTAA will be derived from the device's MAC adress during device startup and is shown on the device's display (if it has one). It is also printed on the serial console for copying it, if you set *verbose 1* in paxcounter.conf and *debug_level 3* in platformio.ini. If your device has a fixed DEVEUI enter this in your local loraconf.h file. During compile time this DEVEUI will be grabbed from loraconf.h and inserted in the code. diff --git a/src/loraconf.sample.h b/src/loraconf.sample.h index 5e60c0a5..3eacd18e 100644 --- a/src/loraconf.sample.h +++ b/src/loraconf.sample.h @@ -1,3 +1,6 @@ +#ifndef __LORACONF_H__ +#define __LORACONF_H__ + #if(HAS_LORA) /************************************************************ @@ -6,6 +9,17 @@ * Read the values from TTN console (or whatever applies), insert them here, * and rename this file to src/loraconf.h * + * You can configure you PaxCounter to activate via OTAA (recommended) or ABP. + * In order to use ABP, uncomment (enable) the following line, + * otherwise, leave the line commented (disabled). + * + *************************************************************/ + +//#define LORA_ABP + +/************************************************************ + * OTAA configuration + * * Note that DEVEUI, APPEUI and APPKEY should all be specified in MSB format. * (This is different from standard LMIC-Arduino which expects DEVEUI and APPEUI * in LSB format.) @@ -22,6 +36,7 @@ * the DEVEUI will be overwriten by the one contained in the Microchip module * ************************************************************/ +#ifndef LORA_ABP static const u1_t DEVEUI[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; @@ -29,5 +44,41 @@ static const u1_t APPEUI[8] = {0x70, 0xB3, 0xD5, 0x00, 0x00, 0x00, 0x00, 0x00}; static const u1_t APPKEY[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +#endif -#endif // HAS_LORA \ No newline at end of file + +/************************************************************ + * ABP configuration (for development) + * + * Get your + * - Network Session Key (NWKSKEY) + * - App Session Key and your (APPSKEY) + * - Device Address (DEVADDR) + * from TTN console and replace the example values below. + * + * NOTE: Use MSB format (as displayed in TTN console, so you can cut & paste + * from there) + * + ************************************************************/ +#ifdef LORA_ABP + +// ID of LoRaAlliance assigned Network (for a list, see e.g. here https://www.thethingsnetwork.org/docs/lorawan/prefix-assignments.html) +static const u1_t NETID = 0x13; // TTN + +static const u1_t NWKSKEY[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const u1_t APPSKEY[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const u4_t DEVADDR = 0x00000000; // <-- Change this address for every node! + +// set additional ABP parameters in loraconf_abp.cpp +void setABPParamaters(); + +#endif + +#endif // HAS_LORA + +#endif // __LORACONF_H__ \ No newline at end of file diff --git a/src/loraconf_abp.cpp b/src/loraconf_abp.cpp new file mode 100644 index 00000000..4a4971bf --- /dev/null +++ b/src/loraconf_abp.cpp @@ -0,0 +1,60 @@ +#include "lorawan.h" +#include "loraconf.h" + +#ifdef LORA_ABP + +/** ****************************************************************** + * + * See LoRaWAN Regional Parameters Document, or here: + * https://www.thethingsnetwork.org/docs/lorawan/frequency-plans.html + * + * Parameters fro TTN and comments below are taken + * from MCCI LoRaWAN LMIC library example 'ttn-abp.ino' + * + *********************************************************************/ + +void setABPParamaters() { + + /** ************************************************************** + * ************************************************************* */ + #if defined(CFG_eu868) + // Set up the channels used by the Things Network, which corresponds + // to the defaults of most gateways. Without this, only three base + // channels from the LoRaWAN specification are used, which certainly + // works, so it is good for debugging, but can overload those + // frequencies, so be sure to configure the full frequency range of + // your network here (unless your network autoconfigures them). + // Setting up channels should happen after LMIC_setSession, as that + // configures the minimal channel set. + LMIC_setupChannel(0, 868100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(1, 868300000, DR_RANGE_MAP(DR_SF12, DR_SF7B), BAND_CENTI); // g-band + LMIC_setupChannel(2, 868500000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(3, 867100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(4, 867300000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(5, 867500000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(6, 867700000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(7, 867900000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(8, 868800000, DR_RANGE_MAP(DR_FSK, DR_FSK), BAND_MILLI); // g2-band + // TTN defines an additional channel at 869.525Mhz using SF9 for class B + // devices' ping slots. LMIC does not have an easy way to define set this + // frequency and support for class B is spotty and untested, so this + // frequency is not configured here. + #elif defined(CFG_us915) + // NA-US channels 0-71 are configured automatically + // but only one group of 8 should (a subband) should be active + // TTN recommends the second sub band, 1 in a zero based count. + // https://github.com/TheThingsNetwork/gateway-conf/blob/master/US-global_conf.json + LMIC_selectSubBand(1); + #endif + + // Disable link check validation + LMIC_setLinkCheckMode(0); + + // TTN uses SF9 for its RX2 window. + LMIC.dn2Dr = DR_SF9; + + // Set data rate and transmit power for uplink + LMIC_setDrTxpow(DR_SF7,14); +} + +#endif \ No newline at end of file diff --git a/src/lorawan.cpp b/src/lorawan.cpp index 853832aa..196cff04 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -159,14 +159,21 @@ void RevBytes(unsigned char *b, size_t c) { } // LMIC callback functions -void os_getDevKey(u1_t *buf) { memcpy(buf, APPKEY, 16); } +void os_getDevKey(u1_t *buf) { +#ifndef LORA_ABP + memcpy(buf, APPKEY, 16); +#endif +} void os_getArtEui(u1_t *buf) { +#ifndef LORA_ABP memcpy(buf, APPEUI, 8); RevBytes(buf, 8); // TTN requires it in LSB First order, so we swap bytes +#endif } void os_getDevEui(u1_t *buf) { +#ifndef LORA_ABP int i = 0, k = 0; memcpy(buf, DEVEUI, 8); // get fixed DEVEUI from loraconf.h for (i = 0; i < 8; i++) { @@ -183,6 +190,7 @@ void os_getDevEui(u1_t *buf) { get_hard_deveui(buf); RevBytes(buf, 8); // swap bytes to LSB format #endif +#endif } void get_hard_deveui(uint8_t *pdeveui) { @@ -323,19 +331,30 @@ esp_err_t lora_stack_init(bool do_join) { &lmicTask, // task handle 1); // CPU core - // Start join procedure if not already joined, - // lora_setupForNetwork(true) is called by eventhandler when joined - // else continue current session - if (do_join) { - if (!LMIC_startJoining()) - ESP_LOGI(TAG, "Already joined"); - } else { + #ifdef LORA_ABP + // Pass ABP parameters to LMIC_setSession LMIC_reset(); - LMIC_setSession(RTCnetid, RTCdevaddr, RTCnwkKey, RTCartKey); - LMIC.seqnoUp = RTCseqnoUp; - LMIC.seqnoDn = RTCseqnoDn; - } - + uint8_t appskey[sizeof(APPSKEY)]; + uint8_t nwkskey[sizeof(NWKSKEY)]; + memcpy_P(appskey, APPSKEY, sizeof(APPSKEY)); + memcpy_P(nwkskey, NWKSKEY, sizeof(NWKSKEY)); + LMIC_setSession (NETID, DEVADDR, nwkskey, appskey); + // These parameters are defined as macro in loraconf.h + setABPParamaters(); + #else + // Start join procedure if not already joined, + // lora_setupForNetwork(true) is called by eventhandler when joined + // else continue current session + if (do_join) { + if (!LMIC_startJoining()) + ESP_LOGI(TAG, "Already joined"); + } else { + LMIC_reset(); + LMIC_setSession(RTCnetid, RTCdevaddr, RTCnwkKey, RTCartKey); + LMIC.seqnoUp = RTCseqnoUp; + LMIC.seqnoDn = RTCseqnoDn; + } + #endif // start lmic send task xTaskCreatePinnedToCore(lora_send, // task function "lorasendtask", // name of task From 7b4c9c613f089c0a211188e1a1c8d4fdb136e21d Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Mon, 9 Mar 2020 12:18:43 +0100 Subject: [PATCH 083/106] Update platformio.ini --- platformio.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platformio.ini b/platformio.ini index 67094d96..0a8e5eca 100644 --- a/platformio.ini +++ b/platformio.ini @@ -59,8 +59,8 @@ upload_speed = 115200 lib_deps_lora = MCCI LoRaWAN LMIC library@>=3.1.0 ; MCCI LMIC by Terrill Moore lib_deps_display = - ss_oled@>=3.3.1 ; simple and small OLED lib by Larry Bank - BitBang_I2C@>=1.3.0 + ss_oled@3.3.1 ; simple and small OLED lib by Larry Bank + BitBang_I2C@1.3.0 QRCode@>=0.0.1 lib_deps_matrix_display = Ultrathin_LED_Matrix@>=1.0.0 From aa042f69682b1015ee5fc9d28efcb19fdf490536 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Wed, 11 Mar 2020 23:47:16 +0100 Subject: [PATCH 084/106] update display lib --- include/display.h | 22 ++++++++++++++ platformio.ini | 6 ++-- src/display.cpp | 74 ++++++++++++++++++++++------------------------- 3 files changed, 59 insertions(+), 43 deletions(-) diff --git a/include/display.h b/include/display.h index 4091f0a9..670ed30c 100644 --- a/include/display.h +++ b/include/display.h @@ -3,15 +3,37 @@ #include "cyclic.h" #include "qrcode.h" +#define DISPLAY_PAGES (7) // number of paxcounter display pages + +// settings for oled display library +#define USE_BACKBUFFER 1 +#define MY_OLED OLED_128x64 +#define OLED_ADDR -1 +#define OLED_INVERT 0 +#define USE_HW_I2C 1 + +#ifndef DISPLAY_FLIP +#define DISPLAY_FLIP 0 +#endif + +// settings for qr code generator +#define QR_VERSION 3 // 29 x 29px +#define QR_SCALEFACTOR 2 // 29 -> 58x < 64px + +// settings for curve plotter +#define DISPLAY_WIDTH 128 // Width in pixels of OLED-display, must be 32X +#define DISPLAY_HEIGHT 64 // Height in pixels of OLED-display, must be 64X extern uint8_t DisplayIsOn, displaybuf[]; +void setup_display(int contrast = 0); void refreshTheDisplay(bool nextPage = false); void init_display(bool verbose = false); void shutdown_display(void); void draw_page(time_t t, bool nextpage); void dp_printf(uint16_t x, uint16_t y, uint8_t font, uint8_t inv, const char *format, ...); +void dp_dump(uint8_t *pBuffer); void dp_printqr(uint16_t offset_x, uint16_t offset_y, const char *Message); void oledfillRect(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t bRender); diff --git a/platformio.ini b/platformio.ini index 67094d96..0ee61057 100644 --- a/platformio.ini +++ b/platformio.ini @@ -45,7 +45,7 @@ description = Paxcounter is a device for metering passenger flows in realtime. I [common] ; for release_version use max. 10 chars total, use any decimal format like "a.b.c" -release_version = 1.9.95 +release_version = 1.9.96 ; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running! ; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose debug_level = 3 @@ -59,8 +59,8 @@ upload_speed = 115200 lib_deps_lora = MCCI LoRaWAN LMIC library@>=3.1.0 ; MCCI LMIC by Terrill Moore lib_deps_display = - ss_oled@>=3.3.1 ; simple and small OLED lib by Larry Bank - BitBang_I2C@>=1.3.0 + ss_oled@4.0.1 ; simple and small OLED lib by Larry Bank + BitBang_I2C@2.0.0 QRCode@>=0.0.1 lib_deps_matrix_display = Ultrathin_LED_Matrix@>=1.0.0 diff --git a/src/display.cpp b/src/display.cpp index 0765b452..b10fb7d9 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -39,19 +39,6 @@ FONT_STRETCHED: 16x32px = 8 chars / line // local Tag for logging static const char TAG[] = __FILE__; -#define DISPLAY_PAGES (7) // number of paxcounter display pages - -// settings for oled display library -#define USE_BACKBUFFER - -// settings for qr code generator -#define QR_VERSION 3 // 29 x 29px -#define QR_SCALEFACTOR 2 // 29 -> 58x < 64px - -// settings for curve plotter -#define DISPLAY_WIDTH 128 // Width in pixels of OLED-display, must be 32X -#define DISPLAY_HEIGHT 64 // Height in pixels of OLED-display, must be 64X - // helper array for converting month values to text const char *printmonth[] = {"xxx", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; @@ -60,6 +47,23 @@ uint8_t displaybuf[DISPLAY_WIDTH * DISPLAY_HEIGHT / 8] = {0}; static uint8_t plotbuf[DISPLAY_WIDTH * DISPLAY_HEIGHT / 8] = {0}; QRCode qrcode; +SSOLED ssoled; + +void setup_display(int contrast) { + int rc = oledInit(&ssoled, MY_OLED, OLED_ADDR, DISPLAY_FLIP, OLED_INVERT, + USE_HW_I2C, MY_OLED_SDA, MY_OLED_SCL, MY_OLED_RST, + 400000L); // use standard I2C bus at 400Khz + + assert(rc != OLED_NOT_FOUND); + + // set display buffer + oledSetBackBuffer(&ssoled, displaybuf); + if (contrast) + oledSetContrast(&ssoled, contrast); + + // clear display + oledFill(&ssoled, 0, 1); +} void init_display(bool verbose) { @@ -68,19 +72,7 @@ void init_display(bool verbose) { ESP_LOGV(TAG, "[%0.3f] i2c mutex lock failed", millis() / 1000.0); else { - // init display -#ifndef DISPLAY_FLIP - oledInit(OLED_128x64, false, false, -1, -1, MY_OLED_RST, 400000L); -#else - oledInit(OLED_128x64, true, false, -1, -1, MY_OLED_RST, 400000L); -#endif - - // set display buffer - oledSetBackBuffer(displaybuf); - - // clear display - oledSetContrast(DISPLAYCONTRAST); - oledFill(0, 1); + setup_display(DISPLAYCONTRAST); if (verbose) { @@ -104,9 +96,9 @@ void init_display(bool verbose) { : "ext."); // give user some time to read or take picture - oledDumpBuffer(displaybuf); + dp_dump(displaybuf); delay(2000); - oledFill(0x00, 1); + oledFill(&ssoled, 0x00, 1); #endif // VERBOSE #if (HAS_LORA) @@ -117,7 +109,7 @@ void init_display(bool verbose) { snprintf(deveui, 17, "%016llX", *((uint64_t *)&buf)); // display DEVEUI as QR code on the left - oledSetContrast(30); + oledSetContrast(&ssoled, 30); dp_printqr(3, 3, deveui); // display DEVEUI as plain text on the right @@ -127,15 +119,15 @@ void init_display(bool verbose) { dp_printf(80, i + 3, FONT_NORMAL, 0, "%4.4s", deveui + i * 4); // give user some time to read or take picture - oledDumpBuffer(displaybuf); + dp_dump(displaybuf); delay(8000); - oledSetContrast(DISPLAYCONTRAST); - oledFill(0x00, 1); + oledSetContrast(&ssoled, DISPLAYCONTRAST); + oledFill(&ssoled, 0x00, 1); #endif // HAS_LORA } // verbose - oledPower(cfg.screenon); // set display off if disabled + oledPower(&ssoled, cfg.screenon); // set display off if disabled I2C_MUTEX_UNLOCK(); // release i2c bus access } // mutex @@ -164,7 +156,7 @@ void refreshTheDisplay(bool nextPage) { // set display on/off according to current device configuration if (DisplayIsOn != cfg.screenon) { DisplayIsOn = cfg.screenon; - oledPower(cfg.screenon); + oledPower(&ssoled, cfg.screenon); } #ifndef HAS_BUTTON @@ -176,7 +168,7 @@ void refreshTheDisplay(bool nextPage) { #endif draw_page(t, nextPage); - oledDumpBuffer(displaybuf); + dp_dump(displaybuf); I2C_MUTEX_UNLOCK(); // release i2c bus access @@ -214,7 +206,7 @@ start: if (nextpage) { DisplayPage = (DisplayPage >= DISPLAY_PAGES - 1) ? 0 : (DisplayPage + 1); - oledFill(0, 1); + oledFill(&ssoled, 0, 1); } switch (DisplayPage) { @@ -307,7 +299,7 @@ start: // page 1: pax graph case 1: - oledDumpBuffer(plotbuf); + dp_dump(plotbuf); break; // page1 // page 2: GPS @@ -391,7 +383,7 @@ start: // page 6: blank screen case 6: #ifdef HAS_BUTTON - oledFill(0, 1); + oledFill(&ssoled, 0, 1); break; #else // don't show blank page if we are unattended DisplayPage++; // next page @@ -428,12 +420,14 @@ void dp_printf(uint16_t x, uint16_t y, uint8_t font, uint8_t inv, len = vsnprintf(temp, len + 1, format, arg); } va_end(arg); - oledWriteString(0, x, y, temp, font, inv, false); + oledWriteString(&ssoled, 0, x, y, temp, font, inv, false); if (temp != loc_buf) { free(temp); } } +void dp_dump(uint8_t *pBuffer) { oledDumpBuffer(&ssoled, pBuffer); } + void dp_printqr(uint16_t offset_x, uint16_t offset_y, const char *Message) { uint8_t qrcodeData[qrcode_getBufferSize(QR_VERSION)]; qrcode_initText(&qrcode, qrcodeData, QR_VERSION, ECC_HIGH, Message); @@ -460,7 +454,7 @@ void dp_printqr(uint16_t offset_x, uint16_t offset_y, const char *Message) { void oledfillRect(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t bRender) { for (uint16_t xi = x; xi < x + width; xi++) - oledDrawLine(xi, y, xi, y + height - 1, bRender); + oledDrawLine(&ssoled, xi, y, xi, y + height - 1, bRender); } int oledDrawPixel(uint8_t *buf, const uint16_t x, const uint16_t y, From 1057a25fc4e005f0924c8755e2d4f69e03741201 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Wed, 11 Mar 2020 23:47:33 +0100 Subject: [PATCH 085/106] gpsread code sanitizations --- include/gpsread.h | 4 ++-- src/gpsread.cpp | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/gpsread.h b/include/gpsread.h index edabf8a4..a3cc7747 100644 --- a/include/gpsread.h +++ b/include/gpsread.h @@ -20,7 +20,7 @@ int gps_config(); bool gps_hasfix(); void gps_storelocation(gpsStatus_t *gps_store); void gps_loop(void *pvParameters); -time_t fetch_gpsTime(uint16_t *msec); -time_t fetch_gpsTime(void); +time_t get_gpstime(uint16_t *msec); +time_t get_gpstime(void); #endif \ No newline at end of file diff --git a/src/gpsread.cpp b/src/gpsread.cpp index 03318284..b1f6db81 100644 --- a/src/gpsread.cpp +++ b/src/gpsread.cpp @@ -92,7 +92,7 @@ bool gps_hasfix() { } // function to fetch current time from struct; note: this is costly -time_t fetch_gpsTime(uint16_t *msec) { +time_t get_gpstime(uint16_t *msec) { time_t time_sec = 0; @@ -133,11 +133,11 @@ time_t fetch_gpsTime(uint16_t *msec) { return timeIsValid(time_sec); -} // fetch_gpsTime() +} // get_gpstime() -time_t fetch_gpsTime(void) { +time_t get_gpstime(void) { uint16_t msec; - return fetch_gpsTime(&msec); + return get_gpstime(&msec); } // GPS serial feed FreeRTos Task From e8664c8505272d3ee95202c22c8c57f8488385a4 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Wed, 11 Mar 2020 23:47:56 +0100 Subject: [PATCH 086/106] update display lib --- src/ota.cpp | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/ota.cpp b/src/ota.cpp index fb2161ee..9f84bdf8 100644 --- a/src/ota.cpp +++ b/src/ota.cpp @@ -45,23 +45,15 @@ void start_ota_update() { // init display #ifdef HAS_DISPLAY -#ifndef DISPLAY_FLIP - oledInit(OLED_128x64, false, false, -1, -1, MY_OLED_RST, 400000L); -#else - oledInit(OLED_128x64, true, false, -1, -1, MY_OLED_RST, 400000L); -#endif + setup_display(); - // set display buffer - oledSetBackBuffer(displaybuf); - - oledFill(0, 1); dp_printf(0, 0, 0, 1, "SOFTWARE UPDATE"); dp_printf(0, 1, 0, 0, "WiFi connect .."); dp_printf(0, 2, 0, 0, "Has Update? .."); dp_printf(0, 3, 0, 0, "Fetching .."); dp_printf(0, 4, 0, 0, "Downloading .."); dp_printf(0, 5, 0, 0, "Rebooting .."); - oledDumpBuffer(displaybuf); + dp_dump(displaybuf); #endif ESP_LOGI(TAG, "Starting Wifi OTA update"); @@ -314,7 +306,7 @@ void ota_display(const uint8_t row, const std::string status, dp_printf(0, 7, 0, 0, " "); dp_printf(0, 7, 0, 0, msg.substr(0, 16).c_str()); } - oledDumpBuffer(displaybuf); + dp_dump(displaybuf); #endif } From 42ee21a2f78689078a01f9e223774a83868f0622 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Wed, 11 Mar 2020 23:49:06 +0100 Subject: [PATCH 087/106] timesync code sanitziations --- include/timesync.h | 4 ++-- src/lorawan.cpp | 2 +- src/timekeeper.cpp | 25 +++++++++---------------- src/timesync.cpp | 22 +++++++++++++--------- 4 files changed, 25 insertions(+), 28 deletions(-) diff --git a/include/timesync.h b/include/timesync.h index dd385029..e2a8d9c4 100644 --- a/include/timesync.h +++ b/include/timesync.h @@ -20,8 +20,8 @@ enum timesync_t { }; void timesync_init(void); -void timesync_sendReq(void); -void timesync_storeReq(uint32_t timestamp, timesync_t timestamp_type); +void timesync_request(void); +void timesync_store(uint32_t timestamp, timesync_t timestamp_type); void IRAM_ATTR timesync_processReq(void *taskparameter); void IRAM_ATTR timesync_serverAnswer(void *pUserData, int flag); diff --git a/src/lorawan.cpp b/src/lorawan.cpp index 933a1ffd..ee9c525c 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -251,7 +251,7 @@ void lora_send(void *pvParameters) { // if last packet sent was a timesync request, store TX timestamp if (SendBuffer.MessagePort == TIMEPORT) // store LMIC time when we started transmit of timesync request - timesync_storeReq(osticks2ms(os_getTime()), timesync_tx); + timesync_store(osticks2ms(os_getTime()), timesync_tx); #endif ESP_LOGI(TAG, "%d byte(s) sent to LORA", SendBuffer.MessageSize); diff --git a/src/timekeeper.cpp b/src/timekeeper.cpp index 4e788d04..ada140a6 100644 --- a/src/timekeeper.cpp +++ b/src/timekeeper.cpp @@ -30,30 +30,27 @@ void calibrateTime(void) { // kick off asychronous lora timesync if we have #if (HAS_LORA) && (TIME_SYNC_LORASERVER) || (TIME_SYNC_LORAWAN) - timesync_sendReq(); + timesync_request(); #endif - // only if we lost time, we try to fallback to local time source RTS or GPS + // (only!) if we lost time, we try to fallback to local time source RTS or GPS if (timeSource == _unsynced) { // has RTC -> fallback to RTC time #ifdef HAS_RTC t = get_rtctime(); - if (t) { - timeSource = _rtc; - goto finish; - } + timeSource = _rtc; #endif // no RTC -> fallback to GPS time #if (HAS_GPS) - // fetch recent time from last NMEA record - t = fetch_gpsTime(&t_msec); - if (t) { - timeSource = _gps; - goto finish; - } + t = get_gpstime(&t_msec); + timeSource = _gps; #endif + + if (t) + setMyTime((uint32_t)t, t_msec, timeSource); // set time + } // fallback else @@ -61,10 +58,6 @@ void calibrateTime(void) { // no fallback time source available -> we can't set time return; -finish: - - setMyTime((uint32_t)t, t_msec, timeSource); // set time - } // calibrateTime() // adjust system time, calibrate RTC and RTC_INT pps diff --git a/src/timesync.cpp b/src/timesync.cpp index ce2c5e5a..d9937922 100644 --- a/src/timesync.cpp +++ b/src/timesync.cpp @@ -42,7 +42,7 @@ void timesync_init() { } // kickoff asnychronous timesync handshake -void timesync_sendReq(void) { +void timesync_request(void) { // exit if a timesync handshake is already running if (timeSyncPending) return; @@ -60,7 +60,7 @@ void IRAM_ATTR timesync_processReq(void *taskparameter) { uint32_t rcv_seqNo = TIME_SYNC_END_FLAG, time_offset_ms; // this task is an endless loop, waiting in blocked mode, until it is - // unblocked by timesync_sendReq(). It then waits to be notified from + // unblocked by timesync_request(). It then waits to be notified from // timesync_serverAnswer(), which is called from LMIC each time a timestamp // from the timesource via LORAWAN arrived. @@ -158,7 +158,7 @@ void IRAM_ATTR timesync_processReq(void *taskparameter) { } // store incoming timestamps -void timesync_storeReq(uint32_t timestamp, timesync_t timestamp_type) { +void timesync_store(uint32_t timestamp, timesync_t timestamp_type) { ESP_LOGD(TAG, "[%0.3f] seq#%d[%d]: timestamp(t%d)=%d", millis() / 1000.0, time_sync_seqNo, sample_idx, timestamp_type, timestamp); timesync_timestamp[sample_idx][timestamp_type] = timestamp; @@ -174,6 +174,9 @@ void IRAM_ATTR timesync_serverAnswer(void *pUserData, int flag) { // mask application irq to ensure accurate timing mask_user_IRQ(); + // store LMIC time when we received the timesync answer + ostime_t rxTime = osticks2ms(os_getTime()); + int rc = 0; uint8_t rcv_seqNo = *(uint8_t *)pUserData; uint16_t timestamp_msec = 0; @@ -184,8 +187,8 @@ void IRAM_ATTR timesync_serverAnswer(void *pUserData, int flag) { // pUserData: contains pointer to payload buffer // flag: length of buffer - // store LMIC time when we received the timesync answer - timesync_storeReq(osticks2ms(os_getTime()), timesync_rx); + // Store the instant the time request of the node was received on the gateway + timesync_store(rxTime, timesync_rx); // parse timesync_answer: // byte meaning @@ -240,8 +243,9 @@ void IRAM_ATTR timesync_serverAnswer(void *pUserData, int flag) { // Calculate UTCTime, considering the difference between GPS and UTC time timestamp_sec = lmicTime.tNetwork + GPS_UTC_DIFF; - // Add delay between the instant the time was transmitted and the current time - timestamp_msec = osticks2ms(os_getTime() - lmicTime.tLocal); + // Add delay between the instant the time was received on the gateway and the + // current time on the node + timestamp_msec = rxTime - lmicTime.tLocal; goto Finish; #endif // (TIME_SYNC_LORAWAN) @@ -250,8 +254,8 @@ Finish: // check if calculated time is recent if (timeIsValid(timestamp_sec)) { // store time received from gateway - timesync_storeReq(timestamp_sec, gwtime_sec); - timesync_storeReq(timestamp_msec, gwtime_msec); + timesync_store(timestamp_sec, gwtime_sec); + timesync_store(timestamp_msec, gwtime_msec); // success rc = 1; } else { From aaf127f5e8120b00245ae9cde6661c7f4e9bf854 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Wed, 11 Mar 2020 23:49:24 +0100 Subject: [PATCH 088/106] gpsread code sanitizations --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index f191be06..7dd14a81 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -449,7 +449,7 @@ void setup() { // initialize gps time #if (HAS_GPS) - fetch_gpsTime(); + get_gpstime(); #endif #if (defined HAS_IF482 || defined HAS_DCF77) From 4127588752b0622a20091d7a5962247b477af8e3 Mon Sep 17 00:00:00 2001 From: Julian Flake Date: Thu, 5 Mar 2020 14:07:57 +0100 Subject: [PATCH 089/106] Implemented ABP configuration --- README.md | 2 +- src/loraconf.sample.h | 53 +++++++++++++++++++++++++++++++++++++- src/loraconf_abp.cpp | 60 +++++++++++++++++++++++++++++++++++++++++++ src/lorawan.cpp | 45 ++++++++++++++++++++++---------- 4 files changed, 145 insertions(+), 15 deletions(-) create mode 100644 src/loraconf_abp.cpp diff --git a/README.md b/README.md index 78d6fdd8..20ee14e3 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ Before compiling the code, - **create file ota.conf in your local /src directory** using the template [ota.sample.conf](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/ota.sample.conf) and enter your WIFI network&key. These settings are used for downloading updates. If you want to push own OTA updates you need a Bintray account. Enter your Bintray user account data in ota.conf. If you don't need wireless firmware updates just rename ota.sample.conf to ota.conf. -To join the network only method OTAA is supported, not ABP. The DEVEUI for OTAA will be derived from the device's MAC adress during device startup and is shown on the device's display (if it has one). It is also printed on the serial console for copying it, if you set *verbose 1* in paxcounter.conf and *debug_level 3* in platformio.ini. +To join the network you have to configure either the preferred OTAA method or the ABP method. The DEVEUI for OTAA will be derived from the device's MAC adress during device startup and is shown on the device's display (if it has one). It is also printed on the serial console for copying it, if you set *verbose 1* in paxcounter.conf and *debug_level 3* in platformio.ini. If your device has a fixed DEVEUI enter this in your local loraconf.h file. During compile time this DEVEUI will be grabbed from loraconf.h and inserted in the code. diff --git a/src/loraconf.sample.h b/src/loraconf.sample.h index 5e60c0a5..3eacd18e 100644 --- a/src/loraconf.sample.h +++ b/src/loraconf.sample.h @@ -1,3 +1,6 @@ +#ifndef __LORACONF_H__ +#define __LORACONF_H__ + #if(HAS_LORA) /************************************************************ @@ -6,6 +9,17 @@ * Read the values from TTN console (or whatever applies), insert them here, * and rename this file to src/loraconf.h * + * You can configure you PaxCounter to activate via OTAA (recommended) or ABP. + * In order to use ABP, uncomment (enable) the following line, + * otherwise, leave the line commented (disabled). + * + *************************************************************/ + +//#define LORA_ABP + +/************************************************************ + * OTAA configuration + * * Note that DEVEUI, APPEUI and APPKEY should all be specified in MSB format. * (This is different from standard LMIC-Arduino which expects DEVEUI and APPEUI * in LSB format.) @@ -22,6 +36,7 @@ * the DEVEUI will be overwriten by the one contained in the Microchip module * ************************************************************/ +#ifndef LORA_ABP static const u1_t DEVEUI[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; @@ -29,5 +44,41 @@ static const u1_t APPEUI[8] = {0x70, 0xB3, 0xD5, 0x00, 0x00, 0x00, 0x00, 0x00}; static const u1_t APPKEY[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +#endif -#endif // HAS_LORA \ No newline at end of file + +/************************************************************ + * ABP configuration (for development) + * + * Get your + * - Network Session Key (NWKSKEY) + * - App Session Key and your (APPSKEY) + * - Device Address (DEVADDR) + * from TTN console and replace the example values below. + * + * NOTE: Use MSB format (as displayed in TTN console, so you can cut & paste + * from there) + * + ************************************************************/ +#ifdef LORA_ABP + +// ID of LoRaAlliance assigned Network (for a list, see e.g. here https://www.thethingsnetwork.org/docs/lorawan/prefix-assignments.html) +static const u1_t NETID = 0x13; // TTN + +static const u1_t NWKSKEY[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const u1_t APPSKEY[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const u4_t DEVADDR = 0x00000000; // <-- Change this address for every node! + +// set additional ABP parameters in loraconf_abp.cpp +void setABPParamaters(); + +#endif + +#endif // HAS_LORA + +#endif // __LORACONF_H__ \ No newline at end of file diff --git a/src/loraconf_abp.cpp b/src/loraconf_abp.cpp new file mode 100644 index 00000000..4a4971bf --- /dev/null +++ b/src/loraconf_abp.cpp @@ -0,0 +1,60 @@ +#include "lorawan.h" +#include "loraconf.h" + +#ifdef LORA_ABP + +/** ****************************************************************** + * + * See LoRaWAN Regional Parameters Document, or here: + * https://www.thethingsnetwork.org/docs/lorawan/frequency-plans.html + * + * Parameters fro TTN and comments below are taken + * from MCCI LoRaWAN LMIC library example 'ttn-abp.ino' + * + *********************************************************************/ + +void setABPParamaters() { + + /** ************************************************************** + * ************************************************************* */ + #if defined(CFG_eu868) + // Set up the channels used by the Things Network, which corresponds + // to the defaults of most gateways. Without this, only three base + // channels from the LoRaWAN specification are used, which certainly + // works, so it is good for debugging, but can overload those + // frequencies, so be sure to configure the full frequency range of + // your network here (unless your network autoconfigures them). + // Setting up channels should happen after LMIC_setSession, as that + // configures the minimal channel set. + LMIC_setupChannel(0, 868100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(1, 868300000, DR_RANGE_MAP(DR_SF12, DR_SF7B), BAND_CENTI); // g-band + LMIC_setupChannel(2, 868500000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(3, 867100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(4, 867300000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(5, 867500000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(6, 867700000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(7, 867900000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(8, 868800000, DR_RANGE_MAP(DR_FSK, DR_FSK), BAND_MILLI); // g2-band + // TTN defines an additional channel at 869.525Mhz using SF9 for class B + // devices' ping slots. LMIC does not have an easy way to define set this + // frequency and support for class B is spotty and untested, so this + // frequency is not configured here. + #elif defined(CFG_us915) + // NA-US channels 0-71 are configured automatically + // but only one group of 8 should (a subband) should be active + // TTN recommends the second sub band, 1 in a zero based count. + // https://github.com/TheThingsNetwork/gateway-conf/blob/master/US-global_conf.json + LMIC_selectSubBand(1); + #endif + + // Disable link check validation + LMIC_setLinkCheckMode(0); + + // TTN uses SF9 for its RX2 window. + LMIC.dn2Dr = DR_SF9; + + // Set data rate and transmit power for uplink + LMIC_setDrTxpow(DR_SF7,14); +} + +#endif \ No newline at end of file diff --git a/src/lorawan.cpp b/src/lorawan.cpp index ee9c525c..040e9805 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -136,14 +136,21 @@ void RevBytes(unsigned char *b, size_t c) { } // LMIC callback functions -void os_getDevKey(u1_t *buf) { memcpy(buf, APPKEY, 16); } +void os_getDevKey(u1_t *buf) { +#ifndef LORA_ABP + memcpy(buf, APPKEY, 16); +#endif +} void os_getArtEui(u1_t *buf) { +#ifndef LORA_ABP memcpy(buf, APPEUI, 8); RevBytes(buf, 8); // TTN requires it in LSB First order, so we swap bytes +#endif } void os_getDevEui(u1_t *buf) { +#ifndef LORA_ABP int i = 0, k = 0; memcpy(buf, DEVEUI, 8); // get fixed DEVEUI from loraconf.h for (i = 0; i < 8; i++) { @@ -160,6 +167,7 @@ void os_getDevEui(u1_t *buf) { get_hard_deveui(buf); RevBytes(buf, 8); // swap bytes to LSB format #endif +#endif } void get_hard_deveui(uint8_t *pdeveui) { @@ -298,19 +306,30 @@ esp_err_t lora_stack_init(bool do_join) { &lmicTask, // task handle 1); // CPU core - // Start join procedure if not already joined, - // lora_setupForNetwork(true) is called by eventhandler when joined - // else continue current session - if (do_join) { - if (!LMIC_startJoining()) - ESP_LOGI(TAG, "Already joined"); - } else { + #ifdef LORA_ABP + // Pass ABP parameters to LMIC_setSession LMIC_reset(); - LMIC_setSession(RTCnetid, RTCdevaddr, RTCnwkKey, RTCartKey); - LMIC.seqnoUp = RTCseqnoUp; - LMIC.seqnoDn = RTCseqnoDn; - } - + uint8_t appskey[sizeof(APPSKEY)]; + uint8_t nwkskey[sizeof(NWKSKEY)]; + memcpy_P(appskey, APPSKEY, sizeof(APPSKEY)); + memcpy_P(nwkskey, NWKSKEY, sizeof(NWKSKEY)); + LMIC_setSession (NETID, DEVADDR, nwkskey, appskey); + // These parameters are defined as macro in loraconf.h + setABPParamaters(); + #else + // Start join procedure if not already joined, + // lora_setupForNetwork(true) is called by eventhandler when joined + // else continue current session + if (do_join) { + if (!LMIC_startJoining()) + ESP_LOGI(TAG, "Already joined"); + } else { + LMIC_reset(); + LMIC_setSession(RTCnetid, RTCdevaddr, RTCnwkKey, RTCartKey); + LMIC.seqnoUp = RTCseqnoUp; + LMIC.seqnoDn = RTCseqnoDn; + } + #endif // start lmic send task xTaskCreatePinnedToCore(lora_send, // task function "lorasendtask", // name of task From 406f71eea567108a6be15ce425b0560215418776 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Fri, 13 Mar 2020 15:23:22 +0100 Subject: [PATCH 090/106] Update payload.h --- include/payload.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/payload.h b/include/payload.h index 29d1f88e..889c164d 100644 --- a/include/payload.h +++ b/include/payload.h @@ -23,7 +23,8 @@ #define LPP_HUMIDITY_CHANNEL 29 #define LPP_BAROMETER_CHANNEL 30 #define LPP_AIR_CHANNEL 31 -#define LPP_PARTMATTER_CHANNEL 32 // particular matter +#define LPP_PARTMATTER10_CHANNEL 32 // particular matter for PM 10 +#define LPP_PARTMATTER25_CHANNEL 33 // particular matter for PM 2.5 // MyDevices CayenneLPP 2.0 types for Packed Sensor Payload, not using channels, // but different FPorts From 8520afcf8d2502c786b053e950ce148e75e78464 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Fri, 13 Mar 2020 15:25:18 +0100 Subject: [PATCH 091/106] Update payload.cpp --- src/payload.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/payload.cpp b/src/payload.cpp index 06bbf5bd..b565c3b4 100644 --- a/src/payload.cpp +++ b/src/payload.cpp @@ -319,14 +319,20 @@ void PayloadConvert::addByte(uint8_t value) { void PayloadConvert::addSDS(sdsStatus_t sds) { #if (HAS_SDS011) +// value of PM10 #if (PAYLOAD_ENCODER == 3) // Cayenne LPP dynamic - buffer[cursor++] = LPP_PARTMATTER_CHANNEL; // for PM10 and PM25 + buffer[cursor++] = LPP_PARTMATTER10_CHANNEL; // for PM10 #endif buffer[cursor++] = LPP_LUMINOSITY; // workaround since cayenne has no data type meter - buffer[cursor++] = highByte(sds.pm10); - buffer[cursor++] = lowByte(sds.pm10); - buffer[cursor++] = highByte(sds.pm25); - buffer[cursor++] = lowByte(sds.pm25); + buffer[cursor++] = highByte((uint16_t)(sds.pm10 * 10)); + buffer[cursor++] = lowByte((uint16_t)(sds.pm10 * 10)); +// value of PM2.5 +#if (PAYLOAD_ENCODER == 3) // Cayenne LPP dynamic + buffer[cursor++] = LPP_PARTMATTER25_CHANNEL; // for PM2.5 +#endif + buffer[cursor++] = LPP_LUMINOSITY; // workaround since cayenne has no data type meter + buffer[cursor++] = highByte((uint16_t)(sds.pm25 * 10)); + buffer[cursor++] = lowByte((uint16_t)(sds.pm25 * 10)); #endif // HAS_SDS011 } From 0c91c55cd4f98f3b3bfc563c7d37112488bab9ef Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Fri, 13 Mar 2020 15:29:20 +0100 Subject: [PATCH 092/106] Update payload.cpp --- src/payload.cpp | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/src/payload.cpp b/src/payload.cpp index b565c3b4..ceb971fa 100644 --- a/src/payload.cpp +++ b/src/payload.cpp @@ -510,38 +510,6 @@ void PayloadConvert::addTime(time_t value) { } #endif // PAYLOAD_ENCODER -void PayloadConvert::addPM10( float value) { -#if (HAS_SDS011) -#if (PAYLOAD_ENCODER == 1) // plain - char tempBuffer[10+1]; - sprintf( tempBuffer, ",%5.1f", value); - addChars(tempBuffer, strlen(tempBuffer)); -#elif (PAYLOAD_ENCODER == 2 ) // packed - writeUint16( (uint16_t) (value*10) ); -#elif (PAYLOAD_ENCODER == 3 ) // Cayenne LPP dynamic -#error not implemented yet -#elif (PAYLOAD_ENCODER == 4 ) // Cayenne LPP packed -#error not implemented yet -#endif -#endif // HAS_SDS011 -} - -void PayloadConvert::addPM25( float value) { -#if (HAS_SDS011) -#if (PAYLOAD_ENCODER == 1) // plain - char tempBuffer[10+1]; - sprintf( tempBuffer, ",%5.1f", value); - addChars(tempBuffer, strlen(tempBuffer)); -#elif (PAYLOAD_ENCODER == 2 ) // packed - writeUint16( (uint16_t) (value*10) ); -#elif (PAYLOAD_ENCODER == 3 ) // Cayenne LPP dynamic -#error not implemented yet -#elif (PAYLOAD_ENCODER == 4 ) // Cayenne LPP packed -#error not implemented yet -#endif -#endif // HAS_SDS011 -} - void PayloadConvert::addChars( char * string, int len) { for (int i=0; i < len; i++) addByte(string[i]); From 8f7ba2943fff99371a699dd12992f6746f31c346 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Fri, 13 Mar 2020 15:33:47 +0100 Subject: [PATCH 093/106] Update payload.h --- include/payload.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/payload.h b/include/payload.h index 889c164d..79b8c341 100644 --- a/include/payload.h +++ b/include/payload.h @@ -84,7 +84,8 @@ private: void writeUint16(uint16_t i); void writeUint8(uint8_t i); void writeFloat(float value); - void writeUFloat(float value); + void wri +teUFloat(float value); void writePressure(float value); void writeVersion(char *version); void writeBitmap(bool a, bool b, bool c, bool d, bool e, bool f, bool g, From f33494b84f5fe44069f906c1f6a5e26db8c5f7b7 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Fri, 13 Mar 2020 15:35:42 +0100 Subject: [PATCH 094/106] Update payload.h --- include/payload.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/payload.h b/include/payload.h index 79b8c341..889c164d 100644 --- a/include/payload.h +++ b/include/payload.h @@ -84,8 +84,7 @@ private: void writeUint16(uint16_t i); void writeUint8(uint8_t i); void writeFloat(float value); - void wri -teUFloat(float value); + void writeUFloat(float value); void writePressure(float value); void writeVersion(char *version); void writeBitmap(bool a, bool b, bool c, bool d, bool e, bool f, bool g, From d818b6ecf4781f161b4282f3d1f06dfeedeb1a65 Mon Sep 17 00:00:00 2001 From: August Quint <49277349+AugustQu@users.noreply.github.com> Date: Fri, 13 Mar 2020 15:41:07 +0100 Subject: [PATCH 095/106] Update payload.cpp --- src/payload.cpp | 72 ++++++++++++++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 30 deletions(-) diff --git a/src/payload.cpp b/src/payload.cpp index ceb971fa..8ab9f91c 100644 --- a/src/payload.cpp +++ b/src/payload.cpp @@ -80,7 +80,7 @@ void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime, float cputemp, } void PayloadConvert::addGPS(gpsStatus_t value) { -#if(HAS_GPS) +#if (HAS_GPS) buffer[cursor++] = (byte)((value.latitude & 0xFF000000) >> 24); buffer[cursor++] = (byte)((value.latitude & 0x00FF0000) >> 16); buffer[cursor++] = (byte)((value.latitude & 0x0000FF00) >> 8); @@ -100,7 +100,7 @@ void PayloadConvert::addGPS(gpsStatus_t value) { } void PayloadConvert::addSensor(uint8_t buf[]) { -#if(HAS_SENSORS) +#if (HAS_SENSORS) uint8_t length = buf[0]; memcpy(buffer, buf + 1, length); cursor += length; // length of buffer @@ -108,7 +108,7 @@ void PayloadConvert::addSensor(uint8_t buf[]) { } void PayloadConvert::addBME(bmeStatus_t value) { -#if(HAS_BME) +#if (HAS_BME) int16_t temperature = (int16_t)(value.temperature); // float -> int uint16_t humidity = (uint16_t)(value.humidity); // float -> int uint16_t pressure = (uint16_t)(value.pressure); // float -> int @@ -124,6 +124,16 @@ void PayloadConvert::addBME(bmeStatus_t value) { #endif } +void PayloadConvert::addSDS(sdsStatus_t sds) { +#if (HAS_SDS011) + char tempBuffer[10 + 1]; + sprintf(tempBuffer, ",%5.1f", sds.pm10); + addChars(tempBuffer, strlen(tempBuffer)); + sprintf(tempBuffer, ",%5.1f", sds.pm25); + addChars(tempBuffer, strlen(tempBuffer)); +#endif // HAS_SDS011 +} + void PayloadConvert::addButton(uint8_t value) { #ifdef HAS_BUTTON buffer[cursor++] = value; @@ -193,7 +203,7 @@ void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime, float cputemp, } void PayloadConvert::addGPS(gpsStatus_t value) { -#if(HAS_GPS) +#if (HAS_GPS) writeLatLng(value.latitude, value.longitude); #if (!PAYLOAD_OPENSENSEBOX) writeUint8(value.satellites); @@ -204,7 +214,7 @@ void PayloadConvert::addGPS(gpsStatus_t value) { } void PayloadConvert::addSensor(uint8_t buf[]) { -#if(HAS_SENSORS) +#if (HAS_SENSORS) uint8_t length = buf[0]; memcpy(buffer, buf + 1, length); cursor += length; // length of buffer @@ -212,7 +222,7 @@ void PayloadConvert::addSensor(uint8_t buf[]) { } void PayloadConvert::addBME(bmeStatus_t value) { -#if(HAS_BME) +#if (HAS_BME) writeFloat(value.temperature); writePressure(value.pressure); writeUFloat(value.humidity); @@ -220,6 +230,13 @@ void PayloadConvert::addBME(bmeStatus_t value) { #endif } +void PayloadConvert::addSDS(sdsStatus_t sds) { +#if (HAS_SDS011) + writeUint16((uint16_t)(sds.pm10 * 10)); + writeUint16((uint16_t)(sds.pm25 * 10)); +#endif // HAS_SDS011 +} + void PayloadConvert::addButton(uint8_t value) { #ifdef HAS_BUTTON writeUint8(value); @@ -242,9 +259,7 @@ void PayloadConvert::uintToBytes(uint64_t value, uint8_t byteSize) { } } -void PayloadConvert::writeUptime(uint64_t uptime) { - writeUint64(uptime); -} +void PayloadConvert::writeUptime(uint64_t uptime) { writeUint64(uptime); } void PayloadConvert::writeVersion(char *version) { memcpy(buffer + cursor, version, 10); @@ -265,13 +280,9 @@ void PayloadConvert::writeUint16(uint16_t i) { uintToBytes(i, 2); } void PayloadConvert::writeUint8(uint8_t i) { uintToBytes(i, 1); } -void PayloadConvert::writeUFloat(float value) { - writeUint16(value * 100); -} +void PayloadConvert::writeUFloat(float value) { writeUint16(value * 100); } -void PayloadConvert::writePressure(float value) { - writeUint16(value * 10); -} +void PayloadConvert::writePressure(float value) { writeUint16(value * 10); } /** * Uses a 16bit two's complement with two decimals, so the range is @@ -312,10 +323,11 @@ void PayloadConvert::writeBitmap(bool a, bool b, bool c, bool d, bool e, bool f, #elif ((PAYLOAD_ENCODER == 3) || (PAYLOAD_ENCODER == 4)) -void PayloadConvert::addByte(uint8_t value) { - /* +void PayloadConvert::addByte(uint8_t value) { + /* not implemented - */ } + */ +} void PayloadConvert::addSDS(sdsStatus_t sds) { #if (HAS_SDS011) @@ -412,7 +424,7 @@ void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime, float celsius, } void PayloadConvert::addGPS(gpsStatus_t value) { -#if(HAS_GPS) +#if (HAS_GPS) int32_t lat = value.latitude / 100; int32_t lon = value.longitude / 100; int32_t alt = value.altitude * 100; @@ -433,18 +445,18 @@ void PayloadConvert::addGPS(gpsStatus_t value) { } void PayloadConvert::addSensor(uint8_t buf[]) { -#if(HAS_SENSORS) -// to come -/* - uint8_t length = buf[0]; - memcpy(buffer, buf+1, length); - cursor += length; // length of buffer -*/ +#if (HAS_SENSORS) + // to come + /* + uint8_t length = buf[0]; + memcpy(buffer, buf+1, length); + cursor += length; // length of buffer + */ #endif // HAS_SENSORS } void PayloadConvert::addBME(bmeStatus_t value) { -#if(HAS_BME) +#if (HAS_BME) // data value conversions to meet cayenne data type definition // 0.1°C per bit => -3276,7 .. +3276,7 °C @@ -510,7 +522,7 @@ void PayloadConvert::addTime(time_t value) { } #endif // PAYLOAD_ENCODER -void PayloadConvert::addChars( char * string, int len) { - for (int i=0; i < len; i++) - addByte(string[i]); +void PayloadConvert::addChars(char *string, int len) { + for (int i = 0; i < len; i++) + addByte(string[i]); } From 25eaeab2b56702c2edb6721263b6dfecb789cfbf Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sat, 14 Mar 2020 08:20:51 +0100 Subject: [PATCH 096/106] ttgov21new.h sds & bme settings removed --- src/hal/ttgov21new.h | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/hal/ttgov21new.h b/src/hal/ttgov21new.h index e7292f2a..1d95d8fe 100644 --- a/src/hal/ttgov21new.h +++ b/src/hal/ttgov21new.h @@ -12,12 +12,6 @@ // This settings are for boards labeled v1.6 on pcb, NOT for v1.5 or older */ -// SDS011 dust sensor settings -#define HAS_SDS011 1 // use SDS011 -// used pins on the ESP-side: -#define SDS_TX 19 // connect to RX on the SDS011 -#define SDS_RX 23 // connect to TX on the SDS011 - #define HAS_LORA 1 // comment out if device shall not send data via LoRa #define CFG_sx1276_radio 1 // HPD13A LoRa SoC @@ -29,11 +23,6 @@ #define SDCARD_MISO (2) #define SDCARD_SCLK (14) -// enable only if device has these sensors, otherwise comment these lines -// BME280 sensor on I2C bus -//#define HAS_BME 1 // Enable BME sensors in general -//#define HAS_BME280 GPIO_NUM_21, GPIO_NUM_22 // SDA, SCL - #define HAS_DISPLAY 1 #define HAS_LED (25) // green on board LED #define BAT_MEASURE_ADC ADC1_GPIO35_CHANNEL // battery probe GPIO pin -> ADC1_CHANNEL_7 From a85f84ede444eed69ae6e5aedaaf6a66ee2d1209 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sat, 14 Mar 2020 18:46:39 +0100 Subject: [PATCH 097/106] m5core.h m4fire.h bugfix lora pins --- src/hal/m5core.h | 11 +++-------- src/hal/m5fire.h | 13 ++++--------- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/src/hal/m5core.h b/src/hal/m5core.h index 1f3371ce..85b79f3b 100644 --- a/src/hal/m5core.h +++ b/src/hal/m5core.h @@ -2,26 +2,21 @@ // upload_speed 921600 // board m5stack-core-esp32 -// EXPERIMENTAL VERSION - NOT TESTED ON M5 HARDWARE YET - #ifndef _M5CORE_H #define _M5CORE_H #include #define HAS_LORA 1 // comment out if device shall not send data via LoRa or has no M5 RA01 LoRa module - -// Pins for LORA chip SPI interface, reset line and interrupt lines #define LORA_SCK SCK #define LORA_CS SS #define LORA_MISO MISO #define LORA_MOSI MOSI -#define LORA_RST GPIO_NUM_36 -#define LORA_IRQ GPIO_NUM_26 -#define LORA_IO1 GPIO_NUM_34 // must be externally wired on PCB! +#define LORA_RST GPIO_NUM_26 +#define LORA_IRQ GPIO_NUM_36 +#define LORA_IO1 GPIO_NUM_34 // must be wired by you on PCB! #define LORA_IO2 LMIC_UNUSED_PIN - // enable only if you want to store a local paxcount table on the device #define HAS_SDCARD 1 // this board has an SD-card-reader/writer // Pins for SD-card diff --git a/src/hal/m5fire.h b/src/hal/m5fire.h index 0064799c..e02ede3a 100644 --- a/src/hal/m5fire.h +++ b/src/hal/m5fire.h @@ -2,29 +2,24 @@ // upload_speed 921600 // board m5stack-fire -// EXPERIMENTAL VERSION - NOT TESTED ON M5 HARDWARE YET - #ifndef _M5FIRE_H #define _M5FIRE_H #include -// #define HAS_LORA 1 // comment out if device shall not send data via LoRa or has no M5 RA01 LoRa module - -// Pins for LORA chip SPI interface, reset line and interrupt lines +#define HAS_LORA 1 // comment out if device shall not send data via LoRa or has no M5 RA01 LoRa module #define LORA_SCK SCK #define LORA_CS SS #define LORA_MISO MISO #define LORA_MOSI MOSI -#define LORA_RST GPIO_NUM_36 -#define LORA_IRQ GPIO_NUM_26 -#define LORA_IO1 GPIO_NUM_34 // must be externally wired on PCB! +#define LORA_RST GPIO_NUM_26 +#define LORA_IRQ GPIO_NUM_36 +#define LORA_IO1 GPIO_NUM_34 // must be wired by you on PCB! #define LORA_IO2 LMIC_UNUSED_PIN // enable only if you want to store a local paxcount table on the device #define HAS_SDCARD 1 // this board has an SD-card-reader/writer -// Pins for SD-card #define SDCARD_CS GPIO_NUM_4 #define SDCARD_MOSI MOSI #define SDCARD_MISO MISO From 10c96759f451b5588bc7754c632a68168f80ce89 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sun, 15 Mar 2020 13:04:03 +0100 Subject: [PATCH 098/106] code sanitizations (eleminate compiler warnings) --- src/display.cpp | 4 ++-- src/gpsread.cpp | 1 - src/timesync.cpp | 22 +++++++++++----------- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/display.cpp b/src/display.cpp index b10fb7d9..ee8bd98f 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -103,10 +103,10 @@ void init_display(bool verbose) { #if (HAS_LORA) // generate DEVEUI as QR code and text - uint8_t buf[8]; + uint8_t buf[8], *p = buf; char deveui[17]; os_getDevEui((u1_t *)buf); - snprintf(deveui, 17, "%016llX", *((uint64_t *)&buf)); + snprintf(deveui, 17, "%016llX", (*(uint64_t *)(p))); // display DEVEUI as QR code on the left oledSetContrast(&ssoled, 30); diff --git a/src/gpsread.cpp b/src/gpsread.cpp index 8530bbca..49a579f4 100644 --- a/src/gpsread.cpp +++ b/src/gpsread.cpp @@ -10,7 +10,6 @@ static const char TAG[] = __FILE__; TinyGPSPlus gps; TinyGPSCustom gpstime(gps, "GPZDA", 1); // field 1 = UTC time static const String ZDA_Request = "$EIGPQ,ZDA*39\r\n"; -static gpsStatus_t gps_status = {0}; TaskHandle_t GpsTask; #ifdef GPS_SERIAL diff --git a/src/timesync.cpp b/src/timesync.cpp index d9937922..1228a4ad 100644 --- a/src/timesync.cpp +++ b/src/timesync.cpp @@ -178,30 +178,30 @@ void IRAM_ATTR timesync_serverAnswer(void *pUserData, int flag) { ostime_t rxTime = osticks2ms(os_getTime()); int rc = 0; - uint8_t rcv_seqNo = *(uint8_t *)pUserData; + // cast back function void parameter to a pointer + uint8_t *p = (uint8_t *)pUserData, rcv_seqNo = *p; uint16_t timestamp_msec = 0; uint32_t timestamp_sec = 0; #if (TIME_SYNC_LORASERVER) - // pUserData: contains pointer to payload buffer + // pUserData: contains pointer (32bit) to payload buffer // flag: length of buffer // Store the instant the time request of the node was received on the gateway timesync_store(rxTime, timesync_rx); - // parse timesync_answer: - // byte meaning - // 0 sequence number (taken from node's time_sync_req) - // 1..4 current second (from UTC epoch) - // 5 1/250ths fractions of current second + // parse pUserData: + // p type meaning + // +0 uint8_t sequence number (taken from node's time_sync_req) + // +1 uint32_t current second (from UTC epoch) + // +4 uint8_t 1/250ths fractions of current second // swap byte order from msb to lsb, note: this is a platform dependent hack - timestamp_sec = __builtin_bswap32(*(uint32_t *)(pUserData + 1)); + timestamp_sec = __builtin_bswap32(*(uint32_t *)(++p)); // one step being 1/250th sec * 1000 = 4msec - timestamp_msec = *(uint8_t *)(pUserData + 5); - timestamp_msec *= 4; + timestamp_msec = *(p += 4) * 4; // if no time is available or spurious buffer then exit if (flag != TIME_SYNC_FRAME_LENGTH) { @@ -218,7 +218,7 @@ void IRAM_ATTR timesync_serverAnswer(void *pUserData, int flag) { #elif (TIME_SYNC_LORAWAN) - // pUserData: contains pointer to SeqNo + // pUserData: contains pointer to SeqNo (not needed here) // flag: indicates if we got a recent time from the network if (flag != 1) { From 9abd0e7a0716a611ee817d6cb9a4c74c9e065e34 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sun, 15 Mar 2020 18:09:41 +0100 Subject: [PATCH 099/106] switch AES to avoid compiler warnings in lmic.c --- src/lmic_config.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lmic_config.h b/src/lmic_config.h index f888ac0e..df3c5668 100644 --- a/src/lmic_config.h +++ b/src/lmic_config.h @@ -87,12 +87,12 @@ // implementation is optimized for speed on 32-bit processors using // fairly big lookup tables, but it takes up big amounts of flash on the // AVR architecture. -#define USE_ORIGINAL_AES +//#define USE_ORIGINAL_AES // // This selects the AES implementation written by Ideetroon for their // own LoRaWAN library. It also uses lookup tables, but smaller // byte-oriented ones, making it use a lot less flash space (but it is // also about twice as slow as the original). -// #define USE_IDEETRON_AES +#define USE_IDEETRON_AES // //#define USE_MBEDTLS_AES From 64b3a2c491961ec72327bd8c9442fcdc45e25b6d Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sun, 15 Mar 2020 20:21:26 +0100 Subject: [PATCH 100/106] timesync.cpp fixes --- src/timesync.cpp | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/src/timesync.cpp b/src/timesync.cpp index 1228a4ad..62aa9478 100644 --- a/src/timesync.cpp +++ b/src/timesync.cpp @@ -57,7 +57,8 @@ void timesync_request(void) { // task for processing time sync request void IRAM_ATTR timesync_processReq(void *taskparameter) { - uint32_t rcv_seqNo = TIME_SYNC_END_FLAG, time_offset_ms; + uint32_t rcv_seqNo = TIME_SYNC_END_FLAG; + uint32_t time_offset_sec = 0, time_offset_ms = 0; // this task is an endless loop, waiting in blocked mode, until it is // unblocked by timesync_request(). It then waits to be notified from @@ -88,13 +89,14 @@ void IRAM_ATTR timesync_processReq(void *taskparameter) { SendPayload(TIMEPORT, prio_high); #elif (TIME_SYNC_LORAWAN) // ask network (requires LoRAWAN >= 1.0.3) LMIC_requestNetworkTime(timesync_serverAnswer, &time_sync_seqNo); - // trigger send to immediately get DevTimeAns on class A device + // trigger to immediately get DevTimeAns from class A device LMIC_sendAlive(); #endif // wait until a timestamp was received if (xTaskNotifyWait(0x00, ULONG_MAX, &rcv_seqNo, pdMS_TO_TICKS(TIME_SYNC_TIMEOUT * 1000)) == pdFALSE) { - ESP_LOGW(TAG, "[%0.3f] Timesync aborted: timed out", millis() / 1000.0); + ESP_LOGW(TAG, "[d%0.3f] Timesync aborted: timed out", + millis() / 1000.0); goto Fail; // no timestamp received before timeout } @@ -128,17 +130,20 @@ void IRAM_ATTR timesync_processReq(void *taskparameter) { mask_user_IRQ(); // calculate average time offset over the summed up difference - // add msec from latest gateway time, and apply a compensation constant for - // processing times on node and gateway time_offset_ms /= TIME_SYNC_SAMPLES; - time_offset_ms += - TIME_SYNC_FIXUP + timesync_timestamp[sample_idx - 1][gwtime_msec]; - // calculate absolute UTC time: take latest timestamp received from - // gateway, convert to whole seconds, round to ceil, add fraction seconds - setMyTime(timesync_timestamp[sample_idx - 1][gwtime_sec] + - time_offset_ms / 1000, - time_offset_ms % 1000, _lora); + // take latest timestamp received from gateway + // and add time difference rounded to whole seconds + time_offset_sec = timesync_timestamp[sample_idx - 1][gwtime_sec]; + time_offset_sec += time_offset_ms / 1000; + + // add milliseconds from latest gateway time, and apply a compensation + // constant for processing times on node and gateway, strip full seconds + time_offset_ms += timesync_timestamp[sample_idx - 1][gwtime_msec]; + time_offset_ms += TIME_SYNC_FIXUP; + time_offset_ms %= 1000; + + setMyTime(time_offset_sec, time_offset_ms, _lora); // send timesync end char to show timesync was successful payload.reset(); @@ -159,7 +164,7 @@ void IRAM_ATTR timesync_processReq(void *taskparameter) { // store incoming timestamps void timesync_store(uint32_t timestamp, timesync_t timestamp_type) { - ESP_LOGD(TAG, "[%0.3f] seq#%d[%d]: timestamp(t%d)=%d", millis() / 1000.0, + ESP_LOGD(TAG, "[%0.3f] seq#%d[%d]: t%d=%d", millis() / 1000.0, time_sync_seqNo, sample_idx, timestamp_type, timestamp); timesync_timestamp[sample_idx][timestamp_type] = timestamp; } @@ -171,14 +176,14 @@ void IRAM_ATTR timesync_serverAnswer(void *pUserData, int flag) { if (!timeSyncPending) return; - // mask application irq to ensure accurate timing - mask_user_IRQ(); - // store LMIC time when we received the timesync answer ostime_t rxTime = osticks2ms(os_getTime()); + // mask application irq to ensure accurate timing + mask_user_IRQ(); + int rc = 0; - // cast back function void parameter to a pointer + // cast back void parameter to a pointer uint8_t *p = (uint8_t *)pUserData, rcv_seqNo = *p; uint16_t timestamp_msec = 0; uint32_t timestamp_sec = 0; @@ -246,6 +251,7 @@ void IRAM_ATTR timesync_serverAnswer(void *pUserData, int flag) { // Add delay between the instant the time was received on the gateway and the // current time on the node timestamp_msec = rxTime - lmicTime.tLocal; + goto Finish; #endif // (TIME_SYNC_LORAWAN) From 8e41894a0b9a875b73defea18b2ca16dbc7e25ec Mon Sep 17 00:00:00 2001 From: Julian Flake Date: Mon, 16 Mar 2020 00:03:22 +0100 Subject: [PATCH 101/106] Updated README.md and comments in loraconf.h.sample to reflect ABP modifications --- README.md | 27 +++++++++++++++------------ src/loraconf.sample.h | 40 +++++++++++++++++++++------------------- 2 files changed, 36 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 12e7d59b..c5684f42 100644 --- a/README.md +++ b/README.md @@ -82,25 +82,28 @@ By default bluetooth sniffing is disabled (#define *BLECOUNTER* 0 in paxcounter. # Preparing -Before compiling the code, +Compile time configuration is spread across several files. Before compiling the code, edit or create the following files: -- **edit platformio.ini** and select desired hardware target in section boards. To add a new board, create an appropriate hardware abstraction layer file in hal subdirectory, and add a pointer to this file in sections boards. +## platformio.ini +Edit `platformio.ini` and select desired hardware target in section boards. To add a new board, create an appropriate hardware abstraction layer file in hal subdirectory, and add a pointer to this file in sections boards. -- **edit src/paxcounter.conf** and tailor settings in this file according to your needs and use case. Please take care of the duty cycle regulations of the LoRaWAN network you're going to use. +## src/paxcounter.conf +Edit `src/paxcounter.conf` and tailor settings in this file according to your needs and use case. Please take care of the duty cycle regulations of the LoRaWAN network you're going to use. -- **edit src/lmic_config.h** and tailor settings in this file according to your country and device hardware. Please take care of national regulations when selecting the frequency band for LoRaWAN. +If your device has a **real time clock** it can be updated bei either LoRaWAN network or GPS time, according to settings *TIME_SYNC_INTERVAL* and *TIME_SYNC_LORAWAN* in `paxcounter.conf`. -- **create file loraconf.h in your local /src directory** using the template [loraconf.sample.h](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/loraconf.sample.h) and populate it with your personal APPEUI und APPKEY for the LoRaWAN network. If you're using popular TheThingsNetwork you can copy&paste the keys from TTN console or output of ttnctl. +## src/lmic_config.h +Edit `src/lmic_config.h` and tailor settings in this file according to your country and device hardware. Please take care of national regulations when selecting the frequency band for LoRaWAN. -- **create file ota.conf in your local /src directory** using the template [ota.sample.conf](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/ota.sample.conf) and enter your WIFI network&key. These settings are used for downloading updates. If you want to push own OTA updates you need a Bintray account. Enter your Bintray user account data in ota.conf. If you don't need wireless firmware updates just rename ota.sample.conf to ota.conf. +## src/loraconf.h +Create file `src/loraconf.h` using the template [src/loraconf.sample.h](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/loraconf.sample.h) and modify it, to use your personal values. +To join the network and activate your paxcounter, you have to configure either the preferred OTAA method or the ABP method. You should use OTAA, whenever possible. To understand the differences of the two methods, [this article](https://www.thethingsnetwork.org/docs/devices/registration.html) may be useful. -To join the network you have to configure either the preferred OTAA method or the ABP method. The DEVEUI for OTAA will be derived from the device's MAC adress during device startup and is shown on the device's display (if it has one). It is also printed on the serial console for copying it, if you set *verbose 1* in paxcounter.conf and *debug_level 3* in platformio.ini. +To configure OTAA, leave `#define LORA_ABP` deactivated (commented). To use ABP, activate (uncomment) `#define LORA_ABP` in the file `src/loraconf.h`. +The file `src/loraconf.h.sample` contains more information about the values to provide. -If your device has a fixed DEVEUI enter this in your local loraconf.h file. During compile time this DEVEUI will be grabbed from loraconf.h and inserted in the code. - -If your device has silicon **Unique ID** which is stored in serial EEPROM Microchip 24AA02E64 you don't need to change anything. The Unique ID will be read during startup and DEVEUI will be generated from it, overriding settings in loraconf.h. - -If your device has a **real time clock** it can be updated bei either LoRaWAN network or GPS time, according to settings *TIME_SYNC_INTERVAL* and *TIME_SYNC_LORAWAN* in paxcounter.conf. +## src/ota.conf +Create file `src/ota.conf` using the template [src/ota.sample.conf](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/ota.sample.conf) and enter your WIFI network&key. These settings are used for downloading updates. If you want to push own OTA updates you need a Bintray account. Enter your Bintray user account data in ota.conf. If you don't need wireless firmware updates just rename ota.sample.conf to ota.conf. # Building diff --git a/src/loraconf.sample.h b/src/loraconf.sample.h index 3eacd18e..b5ecedaf 100644 --- a/src/loraconf.sample.h +++ b/src/loraconf.sample.h @@ -9,34 +9,32 @@ * Read the values from TTN console (or whatever applies), insert them here, * and rename this file to src/loraconf.h * - * You can configure you PaxCounter to activate via OTAA (recommended) or ABP. - * In order to use ABP, uncomment (enable) the following line, - * otherwise, leave the line commented (disabled). + * You can configure OTAA or ABP Activation. In order to use ABP, uncomment + * (enable) the following line, but you should only do so, if you have good + * reasons for not using OTAA. * *************************************************************/ //#define LORA_ABP +#ifndef LORA_ABP /************************************************************ * OTAA configuration * - * Note that DEVEUI, APPEUI and APPKEY should all be specified in MSB format. - * (This is different from standard LMIC-Arduino which expects DEVEUI and APPEUI - * in LSB format.) + * DEVEUI, APPEUI and APPKEY should all be specified in MSB format as + * displayed in TTN console, so you can cut & paste from there. This is different + * from standard LMIC-Arduino which expects DEVEUI and APPEUI in LSB format. + * For TTN, APPEUI in MSB format always starts with 0x70, 0xB3, 0xD5. - * Set your DEVEUI here, if you have one. You can leave this untouched, - * then the DEVEUI will be generated during runtime from device's MAC adress - * and will be displayed on device's screen as well as on serial console. - * - * NOTE: Use MSB format (as displayed in TTN console, so you can cut & paste - * from there) - * For TTN, APPEUI in MSB format always starts with 0x70, 0xB3, 0xD5 - * - * Note: If using a board with Microchip 24AA02E64 Uinique ID for deveui, - * the DEVEUI will be overwriten by the one contained in the Microchip module + * Set your DEVEUI here, if your device has have a fixed one. + * If you leave this untouched, then the DEVEUI will be derived from device's + * MAC adress during startup and will be displayed on device's screen as well as + * on serial console, if you set 'verbose 1' in paxcounter.conf and + * 'debug_level 3' in platformio.ini. + * If using a board with Microchip 24AA02E64 Uinique ID for deveui, the DEVEUI + * will be overwritten by the one contained in the Microchip module. * ************************************************************/ -#ifndef LORA_ABP static const u1_t DEVEUI[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; @@ -48,6 +46,7 @@ static const u1_t APPKEY[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, #endif +#ifdef LORA_ABP /************************************************************ * ABP configuration (for development) * @@ -55,13 +54,16 @@ static const u1_t APPKEY[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, * - Network Session Key (NWKSKEY) * - App Session Key and your (APPSKEY) * - Device Address (DEVADDR) - * from TTN console and replace the example values below. + * from e.g. TTN console and replace the example values below. * * NOTE: Use MSB format (as displayed in TTN console, so you can cut & paste * from there) * + * NOTE: You may also need to adjust lorawan_abp.cpp in order to configure + * different channels and data rate channels to match your country's regulations + * and your network's settings. + * ************************************************************/ -#ifdef LORA_ABP // ID of LoRaAlliance assigned Network (for a list, see e.g. here https://www.thethingsnetwork.org/docs/lorawan/prefix-assignments.html) static const u1_t NETID = 0x13; // TTN From 5096cd8751053c70fb220ef82632507427a93372 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Mon, 16 Mar 2020 19:13:01 +0100 Subject: [PATCH 102/106] loraconf.sample.h edited --- src/loraconf.sample.h | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/loraconf.sample.h b/src/loraconf.sample.h index 3eacd18e..bfd513ad 100644 --- a/src/loraconf.sample.h +++ b/src/loraconf.sample.h @@ -1,7 +1,7 @@ #ifndef __LORACONF_H__ #define __LORACONF_H__ -#if(HAS_LORA) +#if (HAS_LORA) /************************************************************ * LMIC LoRaWAN configuration @@ -10,16 +10,16 @@ * and rename this file to src/loraconf.h * * You can configure you PaxCounter to activate via OTAA (recommended) or ABP. - * In order to use ABP, uncomment (enable) the following line, + * In order to use ABP, uncomment (enable) the following line, * otherwise, leave the line commented (disabled). - * + * *************************************************************/ //#define LORA_ABP /************************************************************ * OTAA configuration - * + * * Note that DEVEUI, APPEUI and APPKEY should all be specified in MSB format. * (This is different from standard LMIC-Arduino which expects DEVEUI and APPEUI * in LSB format.) @@ -44,35 +44,38 @@ static const u1_t APPEUI[8] = {0x70, 0xB3, 0xD5, 0x00, 0x00, 0x00, 0x00, 0x00}; static const u1_t APPKEY[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - -#endif +#endif /************************************************************ * ABP configuration (for development) - * + * * Get your * - Network Session Key (NWKSKEY) * - App Session Key and your (APPSKEY) * - Device Address (DEVADDR) * from TTN console and replace the example values below. - * + * * NOTE: Use MSB format (as displayed in TTN console, so you can cut & paste * from there) * ************************************************************/ #ifdef LORA_ABP -// ID of LoRaAlliance assigned Network (for a list, see e.g. here https://www.thethingsnetwork.org/docs/lorawan/prefix-assignments.html) +// ID of LoRaAlliance assigned Network (for a list, see e.g. here +// https://www.thethingsnetwork.org/docs/lorawan/prefix-assignments.html) static const u1_t NETID = 0x13; // TTN -static const u1_t NWKSKEY[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const u1_t NWKSKEY[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00}; -static const u1_t APPSKEY[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const u1_t APPSKEY[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00}; -static const u4_t DEVADDR = 0x00000000; // <-- Change this address for every node! +static const u4_t DEVADDR = + 0x00000000; // <-- Change this address for every node! // set additional ABP parameters in loraconf_abp.cpp void setABPParamaters(); From 2c0312e15de292ed81ca2abba0124ace776e4a8a Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Mon, 16 Mar 2020 19:13:28 +0100 Subject: [PATCH 103/106] v1.9.971 --- platformio.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platformio.ini b/platformio.ini index 69dca5eb..15f75192 100644 --- a/platformio.ini +++ b/platformio.ini @@ -45,7 +45,7 @@ description = Paxcounter is a device for metering passenger flows in realtime. I [common] ; for release_version use max. 10 chars total, use any decimal format like "a.b.c" -release_version = 1.9.96 +release_version = 1.9.971 ; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running! ; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose debug_level = 3 @@ -59,7 +59,7 @@ upload_speed = 115200 lib_deps_lora = MCCI LoRaWAN LMIC library@>=3.1.0 ; MCCI LMIC by Terrill Moore lib_deps_display = - ss_oled@4.0.1 ; simple and small OLED lib by Larry Bank + ss_oled@4.1.0 ; simple and small OLED lib by Larry Bank BitBang_I2C@2.0.0 QRCode@>=0.0.1 lib_deps_matrix_display = From 8dde6bb13b2f1157502fbd0cdd79b5d482181406 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Tue, 17 Mar 2020 20:45:17 +0100 Subject: [PATCH 104/106] fixed error in Lorawan network timesync --- platformio.ini | 4 ++-- src/timesync.cpp | 21 ++++++++++----------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/platformio.ini b/platformio.ini index 15f75192..a6696a34 100644 --- a/platformio.ini +++ b/platformio.ini @@ -45,7 +45,7 @@ description = Paxcounter is a device for metering passenger flows in realtime. I [common] ; for release_version use max. 10 chars total, use any decimal format like "a.b.c" -release_version = 1.9.971 +release_version = 1.9.972 ; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running! ; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose debug_level = 3 @@ -65,7 +65,7 @@ lib_deps_display = lib_deps_matrix_display = Ultrathin_LED_Matrix@>=1.0.0 lib_deps_rgbled = - SmartLeds@>=1.1.6 + SmartLeds@>=1.2.0 lib_deps_gps = 1655@>=1.0.2 ; #1655 TinyGPSPlus by Mikal Hart lib_deps_sensors = diff --git a/src/timesync.cpp b/src/timesync.cpp index 62aa9478..ce364dcd 100644 --- a/src/timesync.cpp +++ b/src/timesync.cpp @@ -226,20 +226,15 @@ void IRAM_ATTR timesync_serverAnswer(void *pUserData, int flag) { // pUserData: contains pointer to SeqNo (not needed here) // flag: indicates if we got a recent time from the network + uint32_t delay_msec; + lmic_time_reference_t lmicTime; + if (flag != 1) { ESP_LOGW(TAG, "[%0.3f] Network did not answer time request", millis() / 1000.0); goto Exit; } - // A struct that will be populated by LMIC_getNetworkTimeReference. - // It contains the following fields: - // - tLocal: the value returned by os_GetTime() when the time - // request was sent to the gateway, and - // - tNetwork: the seconds between the GPS epoch and the time - // the gateway received the time request - lmic_time_reference_t lmicTime; - // Populate lmic_time_reference if ((LMIC_getNetworkTimeReference(&lmicTime)) != 1) { ESP_LOGW(TAG, "[%0.3f] Network time request failed", millis() / 1000.0); @@ -247,10 +242,14 @@ void IRAM_ATTR timesync_serverAnswer(void *pUserData, int flag) { } // Calculate UTCTime, considering the difference between GPS and UTC time + // epoch, and the leap seconds timestamp_sec = lmicTime.tNetwork + GPS_UTC_DIFF; - // Add delay between the instant the time was received on the gateway and the - // current time on the node - timestamp_msec = rxTime - lmicTime.tLocal; + + // Add the delay between the instant the time was transmitted and + // the current time + delay_msec = osticks2ms(os_getTime() - lmicTime.tLocal); + timestamp_sec += delay_msec / 1000; + timestamp_msec += delay_msec % 1000; goto Finish; From e7d5df6f4b9a5fadd22ab5a9274ee8cc04d9ff1e Mon Sep 17 00:00:00 2001 From: Julian Flake Date: Tue, 17 Mar 2020 22:31:34 +0100 Subject: [PATCH 105/106] Fix i2c address for OLED on TTGOv1 (use 0x3C instead of 0x3D) --- include/display.h | 6 +++++- src/hal/ttgov1.h | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/include/display.h b/include/display.h index 670ed30c..4d41ce08 100644 --- a/include/display.h +++ b/include/display.h @@ -8,7 +8,11 @@ // settings for oled display library #define USE_BACKBUFFER 1 #define MY_OLED OLED_128x64 -#define OLED_ADDR -1 +#ifdef MY_OLED_ADDR + #define OLED_ADDR MY_OLED_ADDR +#else + #define OLED_ADDR -1 +#endif #define OLED_INVERT 0 #define USE_HW_I2C 1 diff --git a/src/hal/ttgov1.h b/src/hal/ttgov1.h index 7d96ab54..ac906860 100644 --- a/src/hal/ttgov1.h +++ b/src/hal/ttgov1.h @@ -31,6 +31,9 @@ #define MY_OLED_SCL (15) #define MY_OLED_RST (16) +// This board reports back the wrong I2C address, so we overwrite it here +#define MY_OLED_ADDR 0x3C + // Pins for LORA chip SPI interface come from board file, we need some // additional definitions for LMIC #define LORA_IO1 (33) From 0800bef3b5f42656a699aee82a9fcfa8e5d7f07a Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Fri, 20 Mar 2020 12:24:09 +0100 Subject: [PATCH 106/106] v1.9.98 --- platformio.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platformio.ini b/platformio.ini index a6696a34..0f54964e 100644 --- a/platformio.ini +++ b/platformio.ini @@ -45,7 +45,7 @@ description = Paxcounter is a device for metering passenger flows in realtime. I [common] ; for release_version use max. 10 chars total, use any decimal format like "a.b.c" -release_version = 1.9.972 +release_version = 1.9.98 ; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running! ; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose debug_level = 3 @@ -60,7 +60,7 @@ lib_deps_lora = MCCI LoRaWAN LMIC library@>=3.1.0 ; MCCI LMIC by Terrill Moore lib_deps_display = ss_oled@4.1.0 ; simple and small OLED lib by Larry Bank - BitBang_I2C@2.0.0 + BitBang_I2C@2.0.1 QRCode@>=0.0.1 lib_deps_matrix_display = Ultrathin_LED_Matrix@>=1.0.0