From a49cc13bc872eb58254080a137035c8255b9792c Mon Sep 17 00:00:00 2001 From: peterj Date: Fri, 29 May 2020 14:35:50 +0100 Subject: [PATCH 01/96] [Issue #65] CRC implementation. --- src/SDI12.cpp | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/SDI12.h | 5 ++++ 2 files changed, 81 insertions(+) diff --git a/src/SDI12.cpp b/src/SDI12.cpp index 10fcfb2..f212d05 100644 --- a/src/SDI12.cpp +++ b/src/SDI12.cpp @@ -848,6 +848,82 @@ void SDI12::sendResponse(FlashString resp) { } setState(SDI12_LISTENING); // return to listening state } +ifdef USE_CRC +#define POLY 0xa001 +String SDI12::addCRCResponse(String &resp) { + char crcStr[3] = {0}; + uint16_t crc = 0; + + for(int i = 0; i < resp.length(); i++) { + crc ^= (uint16_t)resp[i]; //Set the CRC equal to the exclusive OR of the character and itself + for (int j = 0; j < 8; j++){ //count = 1 to 8 + if (crc & 0x0001){ //if the least significant bit of the CRC is one + crc >>= 1; //right shift the CRC one bit + crc ^= POLY; //set CRC equal to the exclusive OR of POLY and itself + } + else { + crc >>= 1; //right shift the CRC one bit + } + } + } + crcStr[0] = (char)( 0x0040 | (crc >> 12)); + crcStr[1] = (char)( 0x0040 | ((crc >> 6) & 0x003F)); + crcStr[2] = (char)( 0x0040 | (crc & 0x003F)); + return (resp + String(crcStr[0]) + String(crcStr[1]) + String(crcStr[2])); +} + +char * SDI12::addCRCResponse(char *resp) { + char *crcStr = "\0"; + uint16_t crc = 0; + + for(int i = 0; i < strlen(resp); i++) { + crc ^= (uint16_t)resp[i]; //Set the CRC equal to the exclusive OR of the character and itself + for (int j = 0; j < 8; j++){ //count = 1 to 8 + if (crc & 0x0001){ //if the least significant bit of the CRC is one + crc >>= 1; //right shift the CRC one bit + crc ^= POLY; //set CRC equal to the exclusive OR of POLY and itself + } + else { + crc >>= 1; //right shift the CRC one bit + } + } + } + crcStr[0] = (char)( 0x0040 | (crc >> 12)); + crcStr[1] = (char)( 0x0040 | ((crc >> 6) & 0x003F)); + crcStr[2] = (char)( 0x0040 | (crc & 0x003F)); + return (strncat(resp, crcStr,3)); +} + +String SDI12::addCRCResponse(FlashString resp) { + char crcStr[3] = {0}; + char respBuffer[SDI12_BUFFER_SIZE - 5]; // don't need space for the CRC or CR/LF + uint16_t crc = 0; + int i = 0; + char responsechar ; + + + for(i = 0; i < strlen_P((PGM_P)resp); i++) { + responsechar = (char)pgm_read_byte((const char *)resp + i); + crc ^= (uint16_t)responsechar; //Set the CRC equal to the exclusive OR of the character and itself + for (int j = 0; j < 8; j++){ //count = 1 to 8 + if (crc & 0x0001){ //if the least significant bit of the CRC is one + crc >>= 1; //right shift the CRC one bit + crc ^= POLY; //set CRC equal to the exclusive OR of POLY and itself + } + else { + crc >>= 1; //right shift the CRC one bit + } + } + respBuffer[i] = responsechar; + } + respBuffer[++i] = '\0'; + String outResp = respBuffer; + crcStr[0] = (char)( 0x0040 | (crc >> 12)); + crcStr[1] = (char)( 0x0040 | ((crc >> 6) & 0x003F)); + crcStr[2] = (char)( 0x0040 | (crc & 0x003F)); + return (outResp + String(crcStr[0]) + String(crcStr[1]) + String(crcStr[2])); +} +#endif //USE_CRC /* ============== 7. Interrupt Service Routine =================== diff --git a/src/SDI12.h b/src/SDI12.h index 52a3edc..1cfcbc9 100644 --- a/src/SDI12.h +++ b/src/SDI12.h @@ -130,6 +130,11 @@ class SDI12 : public Stream void sendResponse(String &resp); // sends the String resp out on the data line (for slave use) void sendResponse(const char *resp); // sends the String resp out on the data line (for slave use) void sendResponse(FlashString resp); // sends the String resp out on the data line (for slave use) + #ifdef USE_CRC + String addCRCResponse(String &resp); // Add CRC to the resp string (for slave use) + char * addCRCResponse( char *resp); // Add CRC to the resp string (for slave use) + String addCRCResponse(FlashString resp); // Add CRC to the resp string (for slave use) + #endif int available(); // returns the number of bytes available in buffer int peek(); // reveals next byte in buffer without consuming From 96feec02c99bb8e1a56a79392e86f7a6bfd5bbb8 Mon Sep 17 00:00:00 2001 From: DalesLandNet Date: Mon, 12 Jul 2021 16:08:32 +0100 Subject: [PATCH 02/96] Correct missing #, Also add CRC calculate function and correct one of the overloaded methods --- src/SDI12.cpp | 44 ++++++++++++++++++++++++++++++++++---------- src/SDI12.h | 5 +++++ 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/src/SDI12.cpp b/src/SDI12.cpp index f212d05..58e1d5c 100644 --- a/src/SDI12.cpp +++ b/src/SDI12.cpp @@ -848,7 +848,8 @@ void SDI12::sendResponse(FlashString resp) { } setState(SDI12_LISTENING); // return to listening state } -ifdef USE_CRC + +#ifdef USE_CRC #define POLY 0xa001 String SDI12::addCRCResponse(String &resp) { char crcStr[3] = {0}; @@ -857,12 +858,12 @@ String SDI12::addCRCResponse(String &resp) { for(int i = 0; i < resp.length(); i++) { crc ^= (uint16_t)resp[i]; //Set the CRC equal to the exclusive OR of the character and itself for (int j = 0; j < 8; j++){ //count = 1 to 8 - if (crc & 0x0001){ //if the least significant bit of the CRC is one - crc >>= 1; //right shift the CRC one bit - crc ^= POLY; //set CRC equal to the exclusive OR of POLY and itself + if (crc & 0x0001){ //if the least significant bit of the CRC is one + crc >>= 1; //right shift the CRC one bit + crc ^= POLY; //set CRC equal to the exclusive OR of POLY and itself } else { - crc >>= 1; //right shift the CRC one bit + crc >>= 1; //right shift the CRC one bit } } } @@ -873,15 +874,15 @@ String SDI12::addCRCResponse(String &resp) { } char * SDI12::addCRCResponse(char *resp) { - char *crcStr = "\0"; - uint16_t crc = 0; + char *crcStr[3] = {0}; + uint16_t crc = 0; for(int i = 0; i < strlen(resp); i++) { crc ^= (uint16_t)resp[i]; //Set the CRC equal to the exclusive OR of the character and itself for (int j = 0; j < 8; j++){ //count = 1 to 8 - if (crc & 0x0001){ //if the least significant bit of the CRC is one - crc >>= 1; //right shift the CRC one bit - crc ^= POLY; //set CRC equal to the exclusive OR of POLY and itself + if (crc & 0x0001){ //if the least significant bit of the CRC is one + crc >>= 1; //right shift the CRC one bit + crc ^= POLY; //set CRC equal to the exclusive OR of POLY and itself } else { crc >>= 1; //right shift the CRC one bit @@ -923,6 +924,29 @@ String SDI12::addCRCResponse(FlashString resp) { crcStr[2] = (char)( 0x0040 | (crc & 0x003F)); return (outResp + String(crcStr[0]) + String(crcStr[1]) + String(crcStr[2])); } + +String SDI12::calculateCRC(String &resp){ + char crcStr[3] = {0}; + uint16_t crc = 0; + + for(int i = 0; i < resp.length(); i++) { + crc ^= (uint16_t)resp[i]; //Set the CRC equal to the exclusive OR of the character and itself + for (int j = 0; j < 8; j++){ //count = 1 to 8 + if (crc & 0x0001){ //if the least significant bit of the CRC is one + crc >>= 1; //right shift the CRC one bit + crc ^= POLY; //set CRC equal to the exclusive OR of POLY and itself + } + else { + crc >>= 1; //right shift the CRC one bit + } + } + } + crcStr[0] = (char)( 0x0040 | (crc >> 12)); + crcStr[1] = (char)( 0x0040 | ((crc >> 6) & 0x003F)); + crcStr[2] = (char)( 0x0040 | (crc & 0x003F)); + return (String(crcStr[0]) + String(crcStr[1]) + String(crcStr[2])); +} + #endif //USE_CRC diff --git a/src/SDI12.h b/src/SDI12.h index 1cfcbc9..d6291f4 100644 --- a/src/SDI12.h +++ b/src/SDI12.h @@ -51,6 +51,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA typedef const __FlashStringHelper *FlashString; + +//#define USE_CRC + #define NO_IGNORE_CHAR '\x01' // a char not found in a valid ASCII numeric field #define SDI12_BUFFER_SIZE 81 //
is a single character // + has a maximum value of 75 characters. @@ -65,6 +68,7 @@ enum LookaheadMode SKIP_NONE, // Nothing is skipped, and the stream is not touched unless the first waiting character is valid. SKIP_WHITESPACE // Only tabs, spaces, line feeds & carriage returns are skipped. }; + #define READTIME sdi12timer.SDI12TimerRead() #else #define READTIME TCNTX @@ -134,6 +138,7 @@ class SDI12 : public Stream String addCRCResponse(String &resp); // Add CRC to the resp string (for slave use) char * addCRCResponse( char *resp); // Add CRC to the resp string (for slave use) String addCRCResponse(FlashString resp); // Add CRC to the resp string (for slave use) + String calculateCRC(String &resp); // Calculate the CRC for a response #endif int available(); // returns the number of bytes available in buffer From f543a69022de16664e8a429b6ee73d0163da0f6e Mon Sep 17 00:00:00 2001 From: DalesLandNet Date: Tue, 13 Jul 2021 11:34:45 +0100 Subject: [PATCH 03/96] [Issue #79] ESP32- Core panic when using in conjunction with the "preferences" library. --- src/SDI12.cpp | 8 ++++---- src/SDI12_boards.cpp | 2 +- src/SDI12_boards.h | 8 ++++++++ 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/SDI12.cpp b/src/SDI12.cpp index 58e1d5c..31d1105 100644 --- a/src/SDI12.cpp +++ b/src/SDI12.cpp @@ -874,7 +874,7 @@ String SDI12::addCRCResponse(String &resp) { } char * SDI12::addCRCResponse(char *resp) { - char *crcStr[3] = {0}; + char crcStr[3] = {0}; uint16_t crc = 0; for(int i = 0; i < strlen(resp); i++) { @@ -979,12 +979,12 @@ takes to either a HIGH vs a LOW, and helps maintain a constant timing. the ISR is instructed to call handleInterrupt() when they trigger. */ // 7.1 - Passes off responsibility for the interrupt to the active object. -void SDI12::handleInterrupt(){ +void USE_INSTRUCTION_RAM SDI12::handleInterrupt(){ if (_activeObject) _activeObject->receiveISR(); } // 7.2 - Creates a blank slate of bits for an incoming character -void SDI12::startChar() +void USE_INSTRUCTION_RAM SDI12::startChar() { rxState = 0; // got a start bit rxMask = 0x01; // 0b00000001, bit mask, lsb first @@ -992,7 +992,7 @@ void SDI12::startChar() } // startChar // 7.3 - The actual interrupt service routine -void SDI12::receiveISR() +void USE_INSTRUCTION_RAM SDI12::receiveISR() { sdi12timer_t thisBitTCNT = READTIME; // time of this data transition (plus ISR latency) diff --git a/src/SDI12_boards.cpp b/src/SDI12_boards.cpp index d3eacae..1298c7f 100644 --- a/src/SDI12_boards.cpp +++ b/src/SDI12_boards.cpp @@ -228,7 +228,7 @@ SDI12Timer::SDI12Timer(){} void SDI12Timer::configSDI12TimerPrescale(void) { } void SDI12Timer::resetSDI12TimerPrescale(void) { } - sdi12timer_t SDI12Timer::SDI12TimerRead(void) + sdi12timer_t USE_INSTRUCTION_RAM SDI12Timer::SDI12TimerRead(void) { // Its a one microsecond clock but we want 64uS ticks so divide by 64 i.e. right shift 6 return((sdi12timer_t) (micros() >> 6)); diff --git a/src/SDI12_boards.h b/src/SDI12_boards.h index 542a005..7dcd054 100644 --- a/src/SDI12_boards.h +++ b/src/SDI12_boards.h @@ -14,6 +14,14 @@ sensors. This library provides a general software solution, without requiring typedef uint8_t sdi12timer_t; #endif +#if defined(ESP32) +#define USE_INSTRUCTION_RAM IRAM_ATTR +#elif defined(ESP8266) +#define USE_INSTRUCTION_RAM ICACHE_RAM_ATTR +#else +#define USE_INSTRUCTION_RAM +#endif + class SDI12Timer { public: From 02d84cd78c471ba8077792d9d1f425effd03b593 Mon Sep 17 00:00:00 2001 From: DalesLandNet Date: Wed, 14 Jul 2021 15:19:49 +0100 Subject: [PATCH 04/96] Update with suggestions from the pull request --- src/SDI12.cpp | 12 ++++++------ src/SDI12.h | 4 ++-- src/SDI12_boards.cpp | 2 +- src/SDI12_boards.h | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/SDI12.cpp b/src/SDI12.cpp index 31d1105..2832ca6 100644 --- a/src/SDI12.cpp +++ b/src/SDI12.cpp @@ -849,7 +849,7 @@ void SDI12::sendResponse(FlashString resp) { setState(SDI12_LISTENING); // return to listening state } -#ifdef USE_CRC +#ifdef ENVIRODIY_SDI12_USE_CRC #define POLY 0xa001 String SDI12::addCRCResponse(String &resp) { char crcStr[3] = {0}; @@ -889,7 +889,7 @@ char * SDI12::addCRCResponse(char *resp) { } } } - crcStr[0] = (char)( 0x0040 | (crc >> 12)); + crcStr[1] = (char)( 0x0040 | ((crc >> 6) & 0x003F)); crcStr[2] = (char)( 0x0040 | (crc & 0x003F)); return (strncat(resp, crcStr,3)); @@ -947,7 +947,7 @@ String SDI12::calculateCRC(String &resp){ return (String(crcStr[0]) + String(crcStr[1]) + String(crcStr[2])); } -#endif //USE_CRC +#endif //ENVIRODIY_SDI12_USE_CRC /* ============== 7. Interrupt Service Routine =================== @@ -979,12 +979,12 @@ takes to either a HIGH vs a LOW, and helps maintain a constant timing. the ISR is instructed to call handleInterrupt() when they trigger. */ // 7.1 - Passes off responsibility for the interrupt to the active object. -void USE_INSTRUCTION_RAM SDI12::handleInterrupt(){ +void ESPFAMILY_USE_INSTRUCTION_RAM SDI12::handleInterrupt(){ if (_activeObject) _activeObject->receiveISR(); } // 7.2 - Creates a blank slate of bits for an incoming character -void USE_INSTRUCTION_RAM SDI12::startChar() +void ESPFAMILY_USE_INSTRUCTION_RAM SDI12::startChar() { rxState = 0; // got a start bit rxMask = 0x01; // 0b00000001, bit mask, lsb first @@ -992,7 +992,7 @@ void USE_INSTRUCTION_RAM SDI12::startChar() } // startChar // 7.3 - The actual interrupt service routine -void USE_INSTRUCTION_RAM SDI12::receiveISR() +void ESPFAMILY_USE_INSTRUCTION_RAM SDI12::receiveISR() { sdi12timer_t thisBitTCNT = READTIME; // time of this data transition (plus ISR latency) diff --git a/src/SDI12.h b/src/SDI12.h index d6291f4..9495bb1 100644 --- a/src/SDI12.h +++ b/src/SDI12.h @@ -52,7 +52,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA typedef const __FlashStringHelper *FlashString; -//#define USE_CRC +//#define ENVIRODIY_SDI12_USE_CRC #define NO_IGNORE_CHAR '\x01' // a char not found in a valid ASCII numeric field #define SDI12_BUFFER_SIZE 81 //
is a single character @@ -134,7 +134,7 @@ class SDI12 : public Stream void sendResponse(String &resp); // sends the String resp out on the data line (for slave use) void sendResponse(const char *resp); // sends the String resp out on the data line (for slave use) void sendResponse(FlashString resp); // sends the String resp out on the data line (for slave use) - #ifdef USE_CRC + #ifdef ENVIRODIY_SDI12_USE_CRC String addCRCResponse(String &resp); // Add CRC to the resp string (for slave use) char * addCRCResponse( char *resp); // Add CRC to the resp string (for slave use) String addCRCResponse(FlashString resp); // Add CRC to the resp string (for slave use) diff --git a/src/SDI12_boards.cpp b/src/SDI12_boards.cpp index 1298c7f..e18dd62 100644 --- a/src/SDI12_boards.cpp +++ b/src/SDI12_boards.cpp @@ -228,7 +228,7 @@ SDI12Timer::SDI12Timer(){} void SDI12Timer::configSDI12TimerPrescale(void) { } void SDI12Timer::resetSDI12TimerPrescale(void) { } - sdi12timer_t USE_INSTRUCTION_RAM SDI12Timer::SDI12TimerRead(void) + sdi12timer_t ESPFAMILY_USE_INSTRUCTION_RAM SDI12Timer::SDI12TimerRead(void) { // Its a one microsecond clock but we want 64uS ticks so divide by 64 i.e. right shift 6 return((sdi12timer_t) (micros() >> 6)); diff --git a/src/SDI12_boards.h b/src/SDI12_boards.h index 7dcd054..bd3f2b7 100644 --- a/src/SDI12_boards.h +++ b/src/SDI12_boards.h @@ -15,11 +15,11 @@ sensors. This library provides a general software solution, without requiring #endif #if defined(ESP32) -#define USE_INSTRUCTION_RAM IRAM_ATTR +#define ESPFAMILY_USE_INSTRUCTION_RAM IRAM_ATTR #elif defined(ESP8266) -#define USE_INSTRUCTION_RAM ICACHE_RAM_ATTR +#define ESPFAMILY_USE_INSTRUCTION_RAM ICACHE_RAM_ATTR #else -#define USE_INSTRUCTION_RAM +#define ESPFAMILY_USE_INSTRUCTION_RAM #endif class SDI12Timer From 19449f106fb046b491f78e27a39b65f35258dc1a Mon Sep 17 00:00:00 2001 From: DalesLandNet Date: Mon, 12 Jul 2021 16:08:32 +0100 Subject: [PATCH 05/96] Correct missing #, Also add CRC calculate function and correct one of the overloaded methods --- src/SDI12.cpp | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/SDI12.h | 6 +++ 2 files changed, 106 insertions(+) diff --git a/src/SDI12.cpp b/src/SDI12.cpp index c71e998..8b50599 100644 --- a/src/SDI12.cpp +++ b/src/SDI12.cpp @@ -547,6 +547,106 @@ void SDI12::sendResponse(FlashString resp) { setState(SDI12_LISTENING); // return to listening state } +#ifdef USE_CRC +#define POLY 0xa001 +String SDI12::addCRCResponse(String &resp) { + char crcStr[3] = {0}; + uint16_t crc = 0; + + for(int i = 0; i < resp.length(); i++) { + crc ^= (uint16_t)resp[i]; //Set the CRC equal to the exclusive OR of the character and itself + for (int j = 0; j < 8; j++){ //count = 1 to 8 + if (crc & 0x0001){ //if the least significant bit of the CRC is one + crc >>= 1; //right shift the CRC one bit + crc ^= POLY; //set CRC equal to the exclusive OR of POLY and itself + } + else { + crc >>= 1; //right shift the CRC one bit + } + } + } + crcStr[0] = (char)( 0x0040 | (crc >> 12)); + crcStr[1] = (char)( 0x0040 | ((crc >> 6) & 0x003F)); + crcStr[2] = (char)( 0x0040 | (crc & 0x003F)); + return (resp + String(crcStr[0]) + String(crcStr[1]) + String(crcStr[2])); +} + +char * SDI12::addCRCResponse(char *resp) { + char *crcStr[3] = {0}; + uint16_t crc = 0; + + for(int i = 0; i < strlen(resp); i++) { + crc ^= (uint16_t)resp[i]; //Set the CRC equal to the exclusive OR of the character and itself + for (int j = 0; j < 8; j++){ //count = 1 to 8 + if (crc & 0x0001){ //if the least significant bit of the CRC is one + crc >>= 1; //right shift the CRC one bit + crc ^= POLY; //set CRC equal to the exclusive OR of POLY and itself + } + else { + crc >>= 1; //right shift the CRC one bit + } + } + } + crcStr[0] = (char)( 0x0040 | (crc >> 12)); + crcStr[1] = (char)( 0x0040 | ((crc >> 6) & 0x003F)); + crcStr[2] = (char)( 0x0040 | (crc & 0x003F)); + return (strncat(resp, crcStr,3)); +} + +String SDI12::addCRCResponse(FlashString resp) { + char crcStr[3] = {0}; + char respBuffer[SDI12_BUFFER_SIZE - 5]; // don't need space for the CRC or CR/LF + uint16_t crc = 0; + int i = 0; + char responsechar ; + + + for(i = 0; i < strlen_P((PGM_P)resp); i++) { + responsechar = (char)pgm_read_byte((const char *)resp + i); + crc ^= (uint16_t)responsechar; //Set the CRC equal to the exclusive OR of the character and itself + for (int j = 0; j < 8; j++){ //count = 1 to 8 + if (crc & 0x0001){ //if the least significant bit of the CRC is one + crc >>= 1; //right shift the CRC one bit + crc ^= POLY; //set CRC equal to the exclusive OR of POLY and itself + } + else { + crc >>= 1; //right shift the CRC one bit + } + } + respBuffer[i] = responsechar; + } + respBuffer[++i] = '\0'; + String outResp = respBuffer; + crcStr[0] = (char)( 0x0040 | (crc >> 12)); + crcStr[1] = (char)( 0x0040 | ((crc >> 6) & 0x003F)); + crcStr[2] = (char)( 0x0040 | (crc & 0x003F)); + return (outResp + String(crcStr[0]) + String(crcStr[1]) + String(crcStr[2])); +} + +String SDI12::calculateCRC(String &resp){ + char crcStr[3] = {0}; + uint16_t crc = 0; + + for(int i = 0; i < resp.length(); i++) { + crc ^= (uint16_t)resp[i]; //Set the CRC equal to the exclusive OR of the character and itself + for (int j = 0; j < 8; j++){ //count = 1 to 8 + if (crc & 0x0001){ //if the least significant bit of the CRC is one + crc >>= 1; //right shift the CRC one bit + crc ^= POLY; //set CRC equal to the exclusive OR of POLY and itself + } + else { + crc >>= 1; //right shift the CRC one bit + } + } + } + crcStr[0] = (char)( 0x0040 | (crc >> 12)); + crcStr[1] = (char)( 0x0040 | ((crc >> 6) & 0x003F)); + crcStr[2] = (char)( 0x0040 | (crc & 0x003F)); + return (String(crcStr[0]) + String(crcStr[1]) + String(crcStr[2])); +} + +#endif //USE_CRC + /* ================ Interrupt Service Routine =======================================*/ diff --git a/src/SDI12.h b/src/SDI12.h index ec949e3..c749e5b 100644 --- a/src/SDI12.h +++ b/src/SDI12.h @@ -909,6 +909,12 @@ class SDI12 : public Stream { void sendCommand(const char* cmd, int8_t extraWakeTime = SDI12_WAKE_DELAY); /// @copydoc SDI12::sendCommand(String&, int8_t) void sendCommand(FlashString cmd, int8_t extraWakeTime = SDI12_WAKE_DELAY); + #ifdef USE_CRC + String addCRCResponse(String &resp); // Add CRC to the resp string (for slave use) + char * addCRCResponse( char *resp); // Add CRC to the resp string (for slave use) + String addCRCResponse(FlashString resp); // Add CRC to the resp string (for slave use) + String calculateCRC(String &resp); // Calculate the CRC for a response + #endif /** * @brief Send a response out on the data line (for slave use) From 793ed697b8effe0defe32ff355ad796757c6f477 Mon Sep 17 00:00:00 2001 From: DalesLandNet Date: Tue, 13 Jul 2021 11:34:45 +0100 Subject: [PATCH 06/96] [Issue #79] ESP32- Core panic when using in conjunction with the "preferences" library. --- src/SDI12.cpp | 12 +++--------- src/SDI12_boards.cpp | 10 ++++++++++ src/SDI12_boards.h | 8 ++++++++ 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/SDI12.cpp b/src/SDI12.cpp index 8b50599..aff0995 100644 --- a/src/SDI12.cpp +++ b/src/SDI12.cpp @@ -652,25 +652,19 @@ String SDI12::calculateCRC(String &resp){ // Passes off responsibility for the interrupt to the active object. // On espressif boards (ESP8266 and ESP32), the ISR must be stored in IRAM -#if defined(ESP32) || defined(ESP8266) -void ICACHE_RAM_ATTR SDI12::handleInterrupt() { +void USE_INSTRUCTION_RAM SDI12::handleInterrupt(){ if (_activeObject) _activeObject->receiveISR(); } -#else -void SDI12::handleInterrupt() { - if (_activeObject) _activeObject->receiveISR(); -} -#endif // Creates a blank slate of bits for an incoming character -void SDI12::startChar() { +void USE_INSTRUCTION_RAM SDI12::startChar() { rxState = 0x00; // 0b00000000, got a start bit rxMask = 0x01; // 0b00000001, bit mask, lsb first rxValue = 0x00; // 0b00000000, RX character to be, a blank slate } // startChar // The actual interrupt service routine -void SDI12::receiveISR() { +void USE_INSTRUCTION_RAM SDI12::receiveISR() { // time of this data transition (plus ISR latency) sdi12timer_t thisBitTCNT = READTIME; diff --git a/src/SDI12_boards.cpp b/src/SDI12_boards.cpp index beb28e8..91c0cf5 100644 --- a/src/SDI12_boards.cpp +++ b/src/SDI12_boards.cpp @@ -262,6 +262,7 @@ void SDI12Timer::resetSDI12TimerPrescale(void) { while (GCLK->STATUS.bit.SYNCBUSY) {} // Wait for synchronization } +<<<<<<< Upstream, based on fd9699b390edeac3a8681e2a6d4fe2ba8b1f9a51 // Espressif ESP32/ESP8266 boards // #elif defined(ESP32) || defined(ESP8266) @@ -273,6 +274,15 @@ sdi12timer_t SDI12Timer::SDI12TimerRead(void) { // 6 return ((sdi12timer_t)(micros() >> 6)); } +======= + void SDI12Timer::configSDI12TimerPrescale(void) { } + void SDI12Timer::resetSDI12TimerPrescale(void) { } + sdi12timer_t USE_INSTRUCTION_RAM SDI12Timer::SDI12TimerRead(void) + { + // Its a one microsecond clock but we want 64uS ticks so divide by 64 i.e. right shift 6 + return((sdi12timer_t) (micros() >> 6)); + } +>>>>>>> f543a69 [Issue #79] ESP32- Core panic when using in conjunction with the "preferences" library. // Unknown board #else #error "Please define your board timer and pins" diff --git a/src/SDI12_boards.h b/src/SDI12_boards.h index a2a533e..a0d8ff4 100644 --- a/src/SDI12_boards.h +++ b/src/SDI12_boards.h @@ -26,6 +26,14 @@ typedef uint32_t sdi12timer_t; typedef uint8_t sdi12timer_t; #endif +#if defined(ESP32) +#define USE_INSTRUCTION_RAM IRAM_ATTR +#elif defined(ESP8266) +#define USE_INSTRUCTION_RAM ICACHE_RAM_ATTR +#else +#define USE_INSTRUCTION_RAM +#endif + /** * @brief The class used to define the processor timer for the SDI-12 serial emulation. */ From 14c5182e9e4e750d2869057dae54c1c56779d0c6 Mon Sep 17 00:00:00 2001 From: DalesLandNet Date: Wed, 14 Jul 2021 15:19:49 +0100 Subject: [PATCH 07/96] Update with suggestions from the pull request --- src/SDI12_boards.cpp | 11 ++--------- src/SDI12_boards.h | 1 + 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/SDI12_boards.cpp b/src/SDI12_boards.cpp index 91c0cf5..260247d 100644 --- a/src/SDI12_boards.cpp +++ b/src/SDI12_boards.cpp @@ -262,7 +262,6 @@ void SDI12Timer::resetSDI12TimerPrescale(void) { while (GCLK->STATUS.bit.SYNCBUSY) {} // Wait for synchronization } -<<<<<<< Upstream, based on fd9699b390edeac3a8681e2a6d4fe2ba8b1f9a51 // Espressif ESP32/ESP8266 boards // #elif defined(ESP32) || defined(ESP8266) @@ -272,17 +271,11 @@ void SDI12Timer::resetSDI12TimerPrescale(void) {} sdi12timer_t SDI12Timer::SDI12TimerRead(void) { // Its a one microsecond clock but we want 64uS ticks so divide by 64 i.e. right shift // 6 - return ((sdi12timer_t)(micros() >> 6)); -} -======= - void SDI12Timer::configSDI12TimerPrescale(void) { } - void SDI12Timer::resetSDI12TimerPrescale(void) { } sdi12timer_t USE_INSTRUCTION_RAM SDI12Timer::SDI12TimerRead(void) { // Its a one microsecond clock but we want 64uS ticks so divide by 64 i.e. right shift 6 - return((sdi12timer_t) (micros() >> 6)); - } ->>>>>>> f543a69 [Issue #79] ESP32- Core panic when using in conjunction with the "preferences" library. + return ((sdi12timer_t)(micros() >> 6)); +} // Unknown board #else #error "Please define your board timer and pins" diff --git a/src/SDI12_boards.h b/src/SDI12_boards.h index a0d8ff4..0ce053f 100644 --- a/src/SDI12_boards.h +++ b/src/SDI12_boards.h @@ -26,6 +26,7 @@ typedef uint32_t sdi12timer_t; typedef uint8_t sdi12timer_t; #endif +// On espressif boards (ESP8266 and ESP32), the ISR must be stored in IRAM #if defined(ESP32) #define USE_INSTRUCTION_RAM IRAM_ATTR #elif defined(ESP8266) From 014d58fb8bbd5fccd0c12cae906c0f945a1e7d3f Mon Sep 17 00:00:00 2001 From: DalesLandNet Date: Mon, 19 Jul 2021 10:05:28 +0100 Subject: [PATCH 08/96] Change depricated symbol ICACHE_RAM_ATTR that had been used for the ESP8266 this is now the same as the ESP32 --- src/SDI12_boards.h | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/SDI12_boards.h b/src/SDI12_boards.h index f918959..f5e044a 100644 --- a/src/SDI12_boards.h +++ b/src/SDI12_boards.h @@ -19,23 +19,17 @@ sensors. This library provides a general software solution, without requiring #include #if defined(ESP32) || defined(ESP8266) +// On espressif boards (ESP8266 and ESP32), the ISR must be stored in IRAM +#define ESPFAMILY_USE_INSTRUCTION_RAM IRAM_ATTR /** The interger type (size) of the timer return value */ typedef uint32_t sdi12timer_t; -#else -/** The interger type (size) of the timer return value */ -typedef uint8_t sdi12timer_t; -#endif -// On espressif boards (ESP8266 and ESP32), the ISR must be stored in IRAM -#if defined(ESP32) -#define ESPFAMILY_USE_INSTRUCTION_RAM IRAM_ATTR -#elif defined(ESP8266) -#define ESPFAMILY_USE_INSTRUCTION_RAM ICACHE_RAM_ATTR #else #define ESPFAMILY_USE_INSTRUCTION_RAM +/** The interger type (size) of the timer return value */ +typedef uint8_t sdi12timer_t; #endif - /** * @brief The class used to define the processor timer for the SDI-12 serial emulation. */ From 6404c3899d4c9c13174cdce9d3fe946dda5869aa Mon Sep 17 00:00:00 2001 From: manhere <580122+manhere@users.noreply.github.com> Date: Wed, 13 Jul 2022 15:48:56 +0800 Subject: [PATCH 09/96] Update library.properties Eliminating compilation warnings in Arduino IDE --- library.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.properties b/library.properties index 83a2da9..68bdb80 100644 --- a/library.properties +++ b/library.properties @@ -6,5 +6,5 @@ sentence=An Arduino library for SDI-12 communication with a wide variety of envi paragraph=This library provides a general software solution, without requiring any additional hardware. category=Communication url=https://github.com/EnviroDIY/Arduino-SDI-12 -architectures=avr,sam,samd,espressif +architectures=avr,sam,samd,esp8266,esp32 includes=SDI12.h From 36970167f2e2df879bbb8e93132e67d8f3a75f8d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Oct 2022 20:34:07 +0000 Subject: [PATCH 10/96] ci: bump actions/setup-node from 3.3.0 to 3.5.1 Bumps [actions/setup-node](https://github.com/actions/setup-node) from 3.3.0 to 3.5.1. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/v3.3.0...v3.5.1) --- updated-dependencies: - dependency-name: actions/setup-node dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/verify_library_json.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/verify_library_json.yaml b/.github/workflows/verify_library_json.yaml index d0c779e..d4970a0 100644 --- a/.github/workflows/verify_library_json.yaml +++ b/.github/workflows/verify_library_json.yaml @@ -12,7 +12,7 @@ jobs: - uses: actions/checkout@v3 - name: Setup Node.js - uses: actions/setup-node@v3.3.0 + uses: actions/setup-node@v3.5.1 - name: Cache Node.js modules uses: actions/cache@v3.0.4 From 538e9a51525464779b6c00779684a053a272281b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Oct 2022 20:34:10 +0000 Subject: [PATCH 11/96] ci: bump actions/cache from 3.0.4 to 3.0.11 Bumps [actions/cache](https://github.com/actions/cache) from 3.0.4 to 3.0.11. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v3.0.4...v3.0.11) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/build_documentation.yaml | 6 +++--- .github/workflows/build_examples.yaml | 4 ++-- .github/workflows/prepare_release.yaml | 2 +- .github/workflows/verify_library_json.yaml | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build_documentation.yaml b/.github/workflows/build_documentation.yaml index 6b53906..1928229 100644 --- a/.github/workflows/build_documentation.yaml +++ b/.github/workflows/build_documentation.yaml @@ -27,7 +27,7 @@ jobs: path: code_docs/Arduino-SDI-12 - name: Restore or Cache pip - uses: actions/cache@v3.0.4 + uses: actions/cache@v3.0.11 id: cache_pip with: path: ~/.cache/pip @@ -37,7 +37,7 @@ jobs: restore-keys: ${{ runner.os }}-pip- - name: Restore or Cache PlatformIO and Libraries - uses: actions/cache@v3.0.4 + uses: actions/cache@v3.0.11 id: cache_pio with: path: ~/.platformio @@ -70,7 +70,7 @@ jobs: - name: Restore or Cache Doxygen id: cache_doxygen - uses: actions/cache@v3.0.4 + uses: actions/cache@v3.0.11 with: path: doxygen-src key: ${{ runner.os }}-doxygen-${{ env.DOXYGEN_VERSION }} diff --git a/.github/workflows/build_examples.yaml b/.github/workflows/build_examples.yaml index 53c965e..1e315a3 100644 --- a/.github/workflows/build_examples.yaml +++ b/.github/workflows/build_examples.yaml @@ -39,7 +39,7 @@ jobs: fi - name: Restore or Cache pip - uses: actions/cache@v3.0.4 + uses: actions/cache@v3.0.11 with: path: ~/.cache/pip # if requirements.txt hasn't changed, then it will be a "cache hit" and pip will be restored @@ -48,7 +48,7 @@ jobs: restore-keys: ${{ runner.os }}-pip- - name: Restore or Cache PlatformIO and Libraries - uses: actions/cache@v3.0.4 + uses: actions/cache@v3.0.11 with: path: ~/.platformio # if nothing in the lock files has changed, then it will be a "cache hit" and pip will be restored diff --git a/.github/workflows/prepare_release.yaml b/.github/workflows/prepare_release.yaml index ccb27e3..09121af 100644 --- a/.github/workflows/prepare_release.yaml +++ b/.github/workflows/prepare_release.yaml @@ -28,7 +28,7 @@ jobs: echo "VERSION=$VER" >> $GITHUB_ENV - name: Restore or Cache pip - uses: actions/cache@v3.0.4 + uses: actions/cache@v3.0.11 with: path: ~/.cache/pip # if requirements.txt hasn't changed, then it will be a "cache hit" and pip will be restored diff --git a/.github/workflows/verify_library_json.yaml b/.github/workflows/verify_library_json.yaml index d0c779e..fb1051c 100644 --- a/.github/workflows/verify_library_json.yaml +++ b/.github/workflows/verify_library_json.yaml @@ -15,7 +15,7 @@ jobs: uses: actions/setup-node@v3.3.0 - name: Cache Node.js modules - uses: actions/cache@v3.0.4 + uses: actions/cache@v3.0.11 with: # npm cache files are stored in `~/.npm` on Linux/macOS path: ~/.npm From adf47d96d6048f1dc52efbb135800cf8b2795b17 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Jan 2023 20:07:52 +0000 Subject: [PATCH 12/96] ci: bump actions/setup-node from 3.5.1 to 3.6.0 Bumps [actions/setup-node](https://github.com/actions/setup-node) from 3.5.1 to 3.6.0. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/v3.5.1...v3.6.0) --- updated-dependencies: - dependency-name: actions/setup-node dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/verify_library_json.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/verify_library_json.yaml b/.github/workflows/verify_library_json.yaml index bf61b48..dd282b8 100644 --- a/.github/workflows/verify_library_json.yaml +++ b/.github/workflows/verify_library_json.yaml @@ -12,7 +12,7 @@ jobs: - uses: actions/checkout@v3 - name: Setup Node.js - uses: actions/setup-node@v3.5.1 + uses: actions/setup-node@v3.6.0 - name: Cache Node.js modules uses: actions/cache@v3.0.11 From d1e6f2d4d7653f7c1eb279f5ccd376fba6c4db7c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Jan 2023 20:17:32 +0000 Subject: [PATCH 13/96] ci: bump actions/cache from 3.0.11 to 3.2.3 Bumps [actions/cache](https://github.com/actions/cache) from 3.0.11 to 3.2.3. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v3.0.11...v3.2.3) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/build_documentation.yaml | 6 +++--- .github/workflows/build_examples.yaml | 4 ++-- .github/workflows/prepare_release.yaml | 2 +- .github/workflows/verify_library_json.yaml | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build_documentation.yaml b/.github/workflows/build_documentation.yaml index 1928229..756199e 100644 --- a/.github/workflows/build_documentation.yaml +++ b/.github/workflows/build_documentation.yaml @@ -27,7 +27,7 @@ jobs: path: code_docs/Arduino-SDI-12 - name: Restore or Cache pip - uses: actions/cache@v3.0.11 + uses: actions/cache@v3.2.3 id: cache_pip with: path: ~/.cache/pip @@ -37,7 +37,7 @@ jobs: restore-keys: ${{ runner.os }}-pip- - name: Restore or Cache PlatformIO and Libraries - uses: actions/cache@v3.0.11 + uses: actions/cache@v3.2.3 id: cache_pio with: path: ~/.platformio @@ -70,7 +70,7 @@ jobs: - name: Restore or Cache Doxygen id: cache_doxygen - uses: actions/cache@v3.0.11 + uses: actions/cache@v3.2.3 with: path: doxygen-src key: ${{ runner.os }}-doxygen-${{ env.DOXYGEN_VERSION }} diff --git a/.github/workflows/build_examples.yaml b/.github/workflows/build_examples.yaml index 1e315a3..6e63f88 100644 --- a/.github/workflows/build_examples.yaml +++ b/.github/workflows/build_examples.yaml @@ -39,7 +39,7 @@ jobs: fi - name: Restore or Cache pip - uses: actions/cache@v3.0.11 + uses: actions/cache@v3.2.3 with: path: ~/.cache/pip # if requirements.txt hasn't changed, then it will be a "cache hit" and pip will be restored @@ -48,7 +48,7 @@ jobs: restore-keys: ${{ runner.os }}-pip- - name: Restore or Cache PlatformIO and Libraries - uses: actions/cache@v3.0.11 + uses: actions/cache@v3.2.3 with: path: ~/.platformio # if nothing in the lock files has changed, then it will be a "cache hit" and pip will be restored diff --git a/.github/workflows/prepare_release.yaml b/.github/workflows/prepare_release.yaml index 09121af..bfc8e7d 100644 --- a/.github/workflows/prepare_release.yaml +++ b/.github/workflows/prepare_release.yaml @@ -28,7 +28,7 @@ jobs: echo "VERSION=$VER" >> $GITHUB_ENV - name: Restore or Cache pip - uses: actions/cache@v3.0.11 + uses: actions/cache@v3.2.3 with: path: ~/.cache/pip # if requirements.txt hasn't changed, then it will be a "cache hit" and pip will be restored diff --git a/.github/workflows/verify_library_json.yaml b/.github/workflows/verify_library_json.yaml index bf61b48..2e018d0 100644 --- a/.github/workflows/verify_library_json.yaml +++ b/.github/workflows/verify_library_json.yaml @@ -15,7 +15,7 @@ jobs: uses: actions/setup-node@v3.5.1 - name: Cache Node.js modules - uses: actions/cache@v3.0.11 + uses: actions/cache@v3.2.3 with: # npm cache files are stored in `~/.npm` on Linux/macOS path: ~/.npm From 3e51ed2d42309c64eb730794c7105b35808b26b4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Mar 2023 21:21:09 +0000 Subject: [PATCH 14/96] ci: bump actions/cache from 3.2.3 to 3.3.1 Bumps [actions/cache](https://github.com/actions/cache) from 3.2.3 to 3.3.1. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v3.2.3...v3.3.1) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/build_documentation.yaml | 6 +++--- .github/workflows/build_examples.yaml | 4 ++-- .github/workflows/prepare_release.yaml | 2 +- .github/workflows/verify_library_json.yaml | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build_documentation.yaml b/.github/workflows/build_documentation.yaml index 756199e..79b17b7 100644 --- a/.github/workflows/build_documentation.yaml +++ b/.github/workflows/build_documentation.yaml @@ -27,7 +27,7 @@ jobs: path: code_docs/Arduino-SDI-12 - name: Restore or Cache pip - uses: actions/cache@v3.2.3 + uses: actions/cache@v3.3.1 id: cache_pip with: path: ~/.cache/pip @@ -37,7 +37,7 @@ jobs: restore-keys: ${{ runner.os }}-pip- - name: Restore or Cache PlatformIO and Libraries - uses: actions/cache@v3.2.3 + uses: actions/cache@v3.3.1 id: cache_pio with: path: ~/.platformio @@ -70,7 +70,7 @@ jobs: - name: Restore or Cache Doxygen id: cache_doxygen - uses: actions/cache@v3.2.3 + uses: actions/cache@v3.3.1 with: path: doxygen-src key: ${{ runner.os }}-doxygen-${{ env.DOXYGEN_VERSION }} diff --git a/.github/workflows/build_examples.yaml b/.github/workflows/build_examples.yaml index 6e63f88..ca57b83 100644 --- a/.github/workflows/build_examples.yaml +++ b/.github/workflows/build_examples.yaml @@ -39,7 +39,7 @@ jobs: fi - name: Restore or Cache pip - uses: actions/cache@v3.2.3 + uses: actions/cache@v3.3.1 with: path: ~/.cache/pip # if requirements.txt hasn't changed, then it will be a "cache hit" and pip will be restored @@ -48,7 +48,7 @@ jobs: restore-keys: ${{ runner.os }}-pip- - name: Restore or Cache PlatformIO and Libraries - uses: actions/cache@v3.2.3 + uses: actions/cache@v3.3.1 with: path: ~/.platformio # if nothing in the lock files has changed, then it will be a "cache hit" and pip will be restored diff --git a/.github/workflows/prepare_release.yaml b/.github/workflows/prepare_release.yaml index bfc8e7d..42f67bb 100644 --- a/.github/workflows/prepare_release.yaml +++ b/.github/workflows/prepare_release.yaml @@ -28,7 +28,7 @@ jobs: echo "VERSION=$VER" >> $GITHUB_ENV - name: Restore or Cache pip - uses: actions/cache@v3.2.3 + uses: actions/cache@v3.3.1 with: path: ~/.cache/pip # if requirements.txt hasn't changed, then it will be a "cache hit" and pip will be restored diff --git a/.github/workflows/verify_library_json.yaml b/.github/workflows/verify_library_json.yaml index 3db1cbf..8a6102f 100644 --- a/.github/workflows/verify_library_json.yaml +++ b/.github/workflows/verify_library_json.yaml @@ -15,7 +15,7 @@ jobs: uses: actions/setup-node@v3.6.0 - name: Cache Node.js modules - uses: actions/cache@v3.2.3 + uses: actions/cache@v3.3.1 with: # npm cache files are stored in `~/.npm` on Linux/macOS path: ~/.npm From d381605a4e8daa7bb2a0a64f018f45f3e5dd5377 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Aug 2023 20:46:04 +0000 Subject: [PATCH 15/96] ci: bump actions/setup-node from 3.6.0 to 3.8.1 Bumps [actions/setup-node](https://github.com/actions/setup-node) from 3.6.0 to 3.8.1. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/v3.6.0...v3.8.1) --- updated-dependencies: - dependency-name: actions/setup-node dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/verify_library_json.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/verify_library_json.yaml b/.github/workflows/verify_library_json.yaml index 8a6102f..1a1b32b 100644 --- a/.github/workflows/verify_library_json.yaml +++ b/.github/workflows/verify_library_json.yaml @@ -12,7 +12,7 @@ jobs: - uses: actions/checkout@v3 - name: Setup Node.js - uses: actions/setup-node@v3.6.0 + uses: actions/setup-node@v3.8.1 - name: Cache Node.js modules uses: actions/cache@v3.3.1 From e504faf0382db41e92f5ccb7a5544a4b7815562f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 20:07:59 +0000 Subject: [PATCH 16/96] ci: bump actions/checkout from 3 to 4 Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build_documentation.yaml | 4 ++-- .github/workflows/build_examples.yaml | 2 +- .github/workflows/changelog_reminder.yaml | 2 +- .github/workflows/prepare_release.yaml | 2 +- .github/workflows/verify_library_json.yaml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build_documentation.yaml b/.github/workflows/build_documentation.yaml index 79b17b7..eae1a36 100644 --- a/.github/workflows/build_documentation.yaml +++ b/.github/workflows/build_documentation.yaml @@ -22,7 +22,7 @@ jobs: steps: # check out the Arduino-SDI-12 repo - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: path: code_docs/Arduino-SDI-12 @@ -90,7 +90,7 @@ jobs: # check out my fork of m.css, for processing Doxygen output - name: Checkout m.css - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: # Repository name with owner. For example, actions/checkout repository: SRGDamia1/m.css diff --git a/.github/workflows/build_examples.yaml b/.github/workflows/build_examples.yaml index ca57b83..d7b05aa 100644 --- a/.github/workflows/build_examples.yaml +++ b/.github/workflows/build_examples.yaml @@ -26,7 +26,7 @@ jobs: ] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set variables run: | diff --git a/.github/workflows/changelog_reminder.yaml b/.github/workflows/changelog_reminder.yaml index e6bcdb3..a1b4115 100644 --- a/.github/workflows/changelog_reminder.yaml +++ b/.github/workflows/changelog_reminder.yaml @@ -5,7 +5,7 @@ jobs: name: Changelog Reminder runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: persist-credentials: false # otherwise, the token used is the GITHUB_TOKEN, instead of your personal token diff --git a/.github/workflows/prepare_release.yaml b/.github/workflows/prepare_release.yaml index 42f67bb..734a6bb 100644 --- a/.github/workflows/prepare_release.yaml +++ b/.github/workflows/prepare_release.yaml @@ -19,7 +19,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set variables run: | diff --git a/.github/workflows/verify_library_json.yaml b/.github/workflows/verify_library_json.yaml index 8a6102f..cb4a5ca 100644 --- a/.github/workflows/verify_library_json.yaml +++ b/.github/workflows/verify_library_json.yaml @@ -9,7 +9,7 @@ jobs: if: "!contains(github.event.head_commit.message, 'ci skip')" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v3.6.0 From b1e260aa4b2541a465fa37d0dfd43c1b4148caff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Sep 2023 20:41:34 +0000 Subject: [PATCH 17/96] ci: bump actions/cache from 3.3.1 to 3.3.2 Bumps [actions/cache](https://github.com/actions/cache) from 3.3.1 to 3.3.2. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v3.3.1...v3.3.2) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/build_documentation.yaml | 6 +++--- .github/workflows/build_examples.yaml | 4 ++-- .github/workflows/prepare_release.yaml | 2 +- .github/workflows/verify_library_json.yaml | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build_documentation.yaml b/.github/workflows/build_documentation.yaml index 79b17b7..c935085 100644 --- a/.github/workflows/build_documentation.yaml +++ b/.github/workflows/build_documentation.yaml @@ -27,7 +27,7 @@ jobs: path: code_docs/Arduino-SDI-12 - name: Restore or Cache pip - uses: actions/cache@v3.3.1 + uses: actions/cache@v3.3.2 id: cache_pip with: path: ~/.cache/pip @@ -37,7 +37,7 @@ jobs: restore-keys: ${{ runner.os }}-pip- - name: Restore or Cache PlatformIO and Libraries - uses: actions/cache@v3.3.1 + uses: actions/cache@v3.3.2 id: cache_pio with: path: ~/.platformio @@ -70,7 +70,7 @@ jobs: - name: Restore or Cache Doxygen id: cache_doxygen - uses: actions/cache@v3.3.1 + uses: actions/cache@v3.3.2 with: path: doxygen-src key: ${{ runner.os }}-doxygen-${{ env.DOXYGEN_VERSION }} diff --git a/.github/workflows/build_examples.yaml b/.github/workflows/build_examples.yaml index ca57b83..3aaf10c 100644 --- a/.github/workflows/build_examples.yaml +++ b/.github/workflows/build_examples.yaml @@ -39,7 +39,7 @@ jobs: fi - name: Restore or Cache pip - uses: actions/cache@v3.3.1 + uses: actions/cache@v3.3.2 with: path: ~/.cache/pip # if requirements.txt hasn't changed, then it will be a "cache hit" and pip will be restored @@ -48,7 +48,7 @@ jobs: restore-keys: ${{ runner.os }}-pip- - name: Restore or Cache PlatformIO and Libraries - uses: actions/cache@v3.3.1 + uses: actions/cache@v3.3.2 with: path: ~/.platformio # if nothing in the lock files has changed, then it will be a "cache hit" and pip will be restored diff --git a/.github/workflows/prepare_release.yaml b/.github/workflows/prepare_release.yaml index 42f67bb..ab2e3a7 100644 --- a/.github/workflows/prepare_release.yaml +++ b/.github/workflows/prepare_release.yaml @@ -28,7 +28,7 @@ jobs: echo "VERSION=$VER" >> $GITHUB_ENV - name: Restore or Cache pip - uses: actions/cache@v3.3.1 + uses: actions/cache@v3.3.2 with: path: ~/.cache/pip # if requirements.txt hasn't changed, then it will be a "cache hit" and pip will be restored diff --git a/.github/workflows/verify_library_json.yaml b/.github/workflows/verify_library_json.yaml index 8a6102f..903b683 100644 --- a/.github/workflows/verify_library_json.yaml +++ b/.github/workflows/verify_library_json.yaml @@ -15,7 +15,7 @@ jobs: uses: actions/setup-node@v3.6.0 - name: Cache Node.js modules - uses: actions/cache@v3.3.1 + uses: actions/cache@v3.3.2 with: # npm cache files are stored in `~/.npm` on Linux/macOS path: ~/.npm From ee41b8b8cb21ed102214a83c7b820275d546ce3c Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Thu, 21 Sep 2023 14:58:32 -0400 Subject: [PATCH 18/96] Clang format Signed-off-by: Sara Damiano --- src/SDI12.cpp | 234 +++++++++++++++++++++---------------------- src/SDI12.h | 14 +-- src/SDI12_boards.cpp | 22 ++-- 3 files changed, 140 insertions(+), 130 deletions(-) diff --git a/src/SDI12.cpp b/src/SDI12.cpp index 18048cf..676dbee 100644 --- a/src/SDI12.cpp +++ b/src/SDI12.cpp @@ -356,34 +356,37 @@ void SDI12::setPinInterrupts(bool enable) { // sets the state of the SDI-12 object. void SDI12::setState(SDI12_STATES state) { switch (state) { - case SDI12_HOLDING: { - pinMode(_dataPin, INPUT); // Turn off the pull-up resistor - pinMode(_dataPin, OUTPUT); // Pin mode = output - digitalWrite(_dataPin, LOW); // Pin state = low - marking - setPinInterrupts(false); // Interrupts disabled on data pin - break; - } - case SDI12_TRANSMITTING: { - pinMode(_dataPin, INPUT); // Turn off the pull-up resistor - pinMode(_dataPin, OUTPUT); // Pin mode = output - setPinInterrupts(false); // Interrupts disabled on data pin - break; - } - case SDI12_LISTENING: { - digitalWrite(_dataPin, LOW); // Pin state = low (turns off pull-up) - pinMode(_dataPin, INPUT); // Pin mode = input, pull-up resistor off - interrupts(); // Enable general interrupts - setPinInterrupts(true); // Enable Rx interrupts on data pin - rxState = WAITING_FOR_START_BIT; - break; - } + case SDI12_HOLDING: + { + pinMode(_dataPin, INPUT); // Turn off the pull-up resistor + pinMode(_dataPin, OUTPUT); // Pin mode = output + digitalWrite(_dataPin, LOW); // Pin state = low - marking + setPinInterrupts(false); // Interrupts disabled on data pin + break; + } + case SDI12_TRANSMITTING: + { + pinMode(_dataPin, INPUT); // Turn off the pull-up resistor + pinMode(_dataPin, OUTPUT); // Pin mode = output + setPinInterrupts(false); // Interrupts disabled on data pin + break; + } + case SDI12_LISTENING: + { + digitalWrite(_dataPin, LOW); // Pin state = low (turns off pull-up) + pinMode(_dataPin, INPUT); // Pin mode = input, pull-up resistor off + interrupts(); // Enable general interrupts + setPinInterrupts(true); // Enable Rx interrupts on data pin + rxState = WAITING_FOR_START_BIT; + break; + } default: // SDI12_DISABLED or SDI12_ENABLED - { - digitalWrite(_dataPin, LOW); // Pin state = low (turns off pull-up) - pinMode(_dataPin, INPUT); // Pin mode = input, pull-up resistor off - setPinInterrupts(false); // Interrupts disabled on data pin - break; - } + { + digitalWrite(_dataPin, LOW); // Pin state = low (turns off pull-up) + pinMode(_dataPin, INPUT); // Pin mode = input, pull-up resistor off + setPinInterrupts(false); // Interrupts disabled on data pin + break; + } } } @@ -545,127 +548,124 @@ void SDI12::sendResponse(FlashString resp) { #ifdef ENVIRODIY_SDI12_USE_CRC #define POLY 0xa001 -String SDI12::addCRCResponse(String &resp) { - char crcStr[3] = {0}; - uint16_t crc = 0; - - for(int i = 0; i < resp.length(); i++) { - crc ^= (uint16_t)resp[i]; //Set the CRC equal to the exclusive OR of the character and itself - for (int j = 0; j < 8; j++){ //count = 1 to 8 - if (crc & 0x0001){ //if the least significant bit of the CRC is one - crc >>= 1; //right shift the CRC one bit - crc ^= POLY; //set CRC equal to the exclusive OR of POLY and itself - } - else { - crc >>= 1; //right shift the CRC one bit + +String SDI12::addCRCResponse(String& resp) { + char crcStr[3] = {0}; + uint16_t crc = 0; + + for (int i = 0; i < resp.length(); i++) { + crc ^= (uint16_t) + resp[i]; // Set the CRC equal to the exclusive OR of the character and itself + for (int j = 0; j < 8; j++) { // count = 1 to 8 + if (crc & 0x0001) { // if the least significant bit of the CRC is one + crc >>= 1; // right shift the CRC one bit + crc ^= POLY; // set CRC equal to the exclusive OR of POLY and itself + } else { + crc >>= 1; // right shift the CRC one bit } } } - crcStr[0] = (char)( 0x0040 | (crc >> 12)); - crcStr[1] = (char)( 0x0040 | ((crc >> 6) & 0x003F)); - crcStr[2] = (char)( 0x0040 | (crc & 0x003F)); - return (resp + String(crcStr[0]) + String(crcStr[1]) + String(crcStr[2])); -} - - -char * SDI12::addCRCResponse(char *resp) { - char crcStr[3] = {0}; - uint16_t crc = 0; - - for(int i = 0; i < strlen(resp); i++) { - crc ^= (uint16_t)resp[i]; //Set the CRC equal to the exclusive OR of the character and itself - for (int j = 0; j < 8; j++){ //count = 1 to 8 - if (crc & 0x0001){ //if the least significant bit of the CRC is one - crc >>= 1; //right shift the CRC one bit - crc ^= POLY; //set CRC equal to the exclusive OR of POLY and itself - } - else { - crc >>= 1; //right shift the CRC one bit + crcStr[0] = (char)(0x0040 | (crc >> 12)); + crcStr[1] = (char)(0x0040 | ((crc >> 6) & 0x003F)); + crcStr[2] = (char)(0x0040 | (crc & 0x003F)); + return (resp + String(crcStr[0]) + String(crcStr[1]) + String(crcStr[2])); +} + +char* SDI12::addCRCResponse(char* resp) { + char crcStr[3] = {0}; + uint16_t crc = 0; + + for (int i = 0; i < strlen(resp); i++) { + crc ^= (uint16_t) + resp[i]; // Set the CRC equal to the exclusive OR of the character and itself + for (int j = 0; j < 8; j++) { // count = 1 to 8 + if (crc & 0x0001) { // if the least significant bit of the CRC is one + crc >>= 1; // right shift the CRC one bit + crc ^= POLY; // set CRC equal to the exclusive OR of POLY and itself + } else { + crc >>= 1; // right shift the CRC one bit } } } - crcStr[1] = (char)( 0x0040 | ((crc >> 6) & 0x003F)); - crcStr[2] = (char)( 0x0040 | (crc & 0x003F)); - return (strncat(resp, crcStr,3)); + crcStr[1] = (char)(0x0040 | ((crc >> 6) & 0x003F)); + crcStr[2] = (char)(0x0040 | (crc & 0x003F)); + return (strncat(resp, crcStr, 3)); } String SDI12::addCRCResponse(FlashString resp) { - char crcStr[3] = {0}; - char respBuffer[SDI12_BUFFER_SIZE - 5]; // don't need space for the CRC or CR/LF - uint16_t crc = 0; - int i = 0; - char responsechar ; - - - for(i = 0; i < strlen_P((PGM_P)resp); i++) { - responsechar = (char)pgm_read_byte((const char *)resp + i); - crc ^= (uint16_t)responsechar; //Set the CRC equal to the exclusive OR of the character and itself - for (int j = 0; j < 8; j++){ //count = 1 to 8 - if (crc & 0x0001){ //if the least significant bit of the CRC is one - crc >>= 1; //right shift the CRC one bit - crc ^= POLY; //set CRC equal to the exclusive OR of POLY and itself - } - else { - crc >>= 1; //right shift the CRC one bit - } + char crcStr[3] = {0}; + char respBuffer[SDI12_BUFFER_SIZE - 5]; // don't need space for the CRC or CR/LF + uint16_t crc = 0; + int i = 0; + char responsechar; + + + for (i = 0; i < strlen_P((PGM_P)resp); i++) { + responsechar = (char)pgm_read_byte((const char*)resp + i); + crc ^= (uint16_t)responsechar; // Set the CRC equal to the exclusive OR of the + // character and itself + for (int j = 0; j < 8; j++) { // count = 1 to 8 + if (crc & 0x0001) { // if the least significant bit of the CRC is one + crc >>= 1; // right shift the CRC one bit + crc ^= POLY; // set CRC equal to the exclusive OR of POLY and itself + } else { + crc >>= 1; // right shift the CRC one bit + } } respBuffer[i] = responsechar; } respBuffer[++i] = '\0'; - String outResp = respBuffer; - crcStr[0] = (char)( 0x0040 | (crc >> 12)); - crcStr[1] = (char)( 0x0040 | ((crc >> 6) & 0x003F)); - crcStr[2] = (char)( 0x0040 | (crc & 0x003F)); - return (outResp + String(crcStr[0]) + String(crcStr[1]) + String(crcStr[2])); -} - -String SDI12::calculateCRC(String &resp){ - char crcStr[3] = {0}; - uint16_t crc = 0; - - for(int i = 0; i < resp.length(); i++) { - crc ^= (uint16_t)resp[i]; //Set the CRC equal to the exclusive OR of the character and itself - for (int j = 0; j < 8; j++){ //count = 1 to 8 - if (crc & 0x0001){ //if the least significant bit of the CRC is one - crc >>= 1; //right shift the CRC one bit - crc ^= POLY; //set CRC equal to the exclusive OR of POLY and itself - } - else { - crc >>= 1; //right shift the CRC one bit - } + String outResp = respBuffer; + crcStr[0] = (char)(0x0040 | (crc >> 12)); + crcStr[1] = (char)(0x0040 | ((crc >> 6) & 0x003F)); + crcStr[2] = (char)(0x0040 | (crc & 0x003F)); + return (outResp + String(crcStr[0]) + String(crcStr[1]) + String(crcStr[2])); +} + +String SDI12::calculateCRC(String& resp) { + char crcStr[3] = {0}; + uint16_t crc = 0; + + for (int i = 0; i < resp.length(); i++) { + crc ^= (uint16_t) + resp[i]; // Set the CRC equal to the exclusive OR of the character and itself + for (int j = 0; j < 8; j++) { // count = 1 to 8 + if (crc & 0x0001) { // if the least significant bit of the CRC is one + crc >>= 1; // right shift the CRC one bit + crc ^= POLY; // set CRC equal to the exclusive OR of POLY and itself + } else { + crc >>= 1; // right shift the CRC one bit } - } - crcStr[0] = (char)( 0x0040 | (crc >> 12)); - crcStr[1] = (char)( 0x0040 | ((crc >> 6) & 0x003F)); - crcStr[2] = (char)( 0x0040 | (crc & 0x003F)); - return (String(crcStr[0]) + String(crcStr[1]) + String(crcStr[2])); + } + } + crcStr[0] = (char)(0x0040 | (crc >> 12)); + crcStr[1] = (char)(0x0040 | ((crc >> 6) & 0x003F)); + crcStr[2] = (char)(0x0040 | (crc & 0x003F)); + return (String(crcStr[0]) + String(crcStr[1]) + String(crcStr[2])); } -#endif //ENVIRODIY_SDI12_USE_CRC - +#endif // ENVIRODIY_SDI12_USE_CRC /* ================ Interrupt Service Routine =======================================*/ // 7.1 - Passes off responsibility for the interrupt to the active object. -void ESPFAMILY_USE_INSTRUCTION_RAM SDI12::handleInterrupt(){ +void ESPFAMILY_USE_INSTRUCTION_RAM SDI12::handleInterrupt() { if (_activeObject) _activeObject->receiveISR(); } // 7.2 - Creates a blank slate of bits for an incoming character -void ESPFAMILY_USE_INSTRUCTION_RAM SDI12::startChar() -{ - rxState = 0; // got a start bit +void ESPFAMILY_USE_INSTRUCTION_RAM SDI12::startChar() { + rxState = 0; // got a start bit rxMask = 0x01; // 0b00000001, bit mask, lsb first rxValue = 0x00; // 0b00000000, RX character to be, a blank slate } // startChar // 7.3 - The actual interrupt service routine -void ESPFAMILY_USE_INSTRUCTION_RAM SDI12::receiveISR() -{ - - sdi12timer_t thisBitTCNT = READTIME; // time of this data transition (plus ISR latency) +void ESPFAMILY_USE_INSTRUCTION_RAM SDI12::receiveISR() { + sdi12timer_t thisBitTCNT = + READTIME; // time of this data transition (plus ISR latency) uint8_t pinLevel = digitalRead(_dataPin); // current RX data level diff --git a/src/SDI12.h b/src/SDI12.h index 881f20a..5f1dce5 100644 --- a/src/SDI12.h +++ b/src/SDI12.h @@ -914,12 +914,13 @@ class SDI12 : public Stream { /// @copydoc SDI12::sendCommand(String&, int8_t) void sendCommand(FlashString cmd, int8_t extraWakeTime = SDI12_WAKE_DELAY); - #ifdef ENVIRODIY_SDI12_USE_CRC - String addCRCResponse(String &resp); // Add CRC to the resp string (for slave use) - char * addCRCResponse( char *resp); // Add CRC to the resp string (for slave use) - String addCRCResponse(FlashString resp); // Add CRC to the resp string (for slave use) - String calculateCRC(String &resp); // Calculate the CRC for a response - #endif +#ifdef ENVIRODIY_SDI12_USE_CRC + String addCRCResponse(String& resp); // Add CRC to the resp string (for slave use) + char* addCRCResponse(char* resp); // Add CRC to the resp string (for slave use) + String + addCRCResponse(FlashString resp); // Add CRC to the resp string (for slave use) + String calculateCRC(String& resp); // Calculate the CRC for a response +#endif /** * @brief Send a response out on the data line (for slave use) @@ -986,7 +987,6 @@ class SDI12 : public Stream { /** on AVR boards, uncomment to use your own PCINT ISRs */ // #define SDI12_EXTERNAL_PCINT /**@}*/ - }; #endif // SRC_SDI12_H_ diff --git a/src/SDI12_boards.cpp b/src/SDI12_boards.cpp index ac7c6bf..3a21879 100644 --- a/src/SDI12_boards.cpp +++ b/src/SDI12_boards.cpp @@ -44,6 +44,7 @@ void SDI12Timer::configSDI12TimerPrescale(void) { TCCR2B = 0x07; // TCCR2B = 0x07 = 0b00000111 - Clock Select bits 22, 21, & 20 on - // prescaler set to CK/1024 } + void SDI12Timer::resetSDI12TimerPrescale(void) { TCCR2A = preSDI12_TCCR2A; TCCR2B = preSDI12_TCCR2B; @@ -59,6 +60,7 @@ void SDI12Timer::configSDI12TimerPrescale(void) { TCCR2B = 0x07; // TCCR2B = 0x07 = 0b00000111 - Clock Select bits 22, 21, & 20 on - // prescaler set to CK/1024 } + void SDI12Timer::resetSDI12TimerPrescale(void) { TCCR2A = preSDI12_TCCR2A; TCCR2B = preSDI12_TCCR2B; @@ -74,6 +76,7 @@ void SDI12Timer::configSDI12TimerPrescale(void) { TCCR2B = 0x06; // TCCR2B = 0x06 = 0b00000110 - Clock Select bits 22 & 20 on - // prescaler set to CK/256 } + void SDI12Timer::resetSDI12TimerPrescale(void) { TCCR2A = preSDI12_TCCR2A; TCCR2B = preSDI12_TCCR2B; @@ -108,8 +111,9 @@ static uint8_t preSDI12_TCCR1A; void SDI12Timer::configSDI12TimerPrescale(void) { preSDI12_TCCR1A = TCCR1; - TCCR1 = 0b00001011; // Set the prescaler to 1024 + TCCR1 = 0b00001011; // Set the prescaler to 1024 } + void SDI12Timer::resetSDI12TimerPrescale(void) { TCCR1 = preSDI12_TCCR1A; } @@ -119,8 +123,9 @@ void SDI12Timer::resetSDI12TimerPrescale(void) { void SDI12Timer::configSDI12TimerPrescale(void) { preSDI12_TCCR1A = TCCR1; - TCCR1 = 0b00001010; // Set the prescaler to 512 + TCCR1 = 0b00001010; // Set the prescaler to 512 } + void SDI12Timer::resetSDI12TimerPrescale(void) { TCCR1 = preSDI12_TCCR1A; } @@ -170,6 +175,7 @@ void SDI12Timer::configSDI12TimerPrescale(void) { TCCR4D = 0x00; // TCCR4D = 0x00 = No fault protection TCCR4E = 0x00; // TCCR4E = 0x00 = No register locks or overrides } + void SDI12Timer::resetSDI12TimerPrescale(void) { TCCR4A = preSDI12_TCCR4A; TCCR4B = preSDI12_TCCR4B; @@ -194,6 +200,7 @@ void SDI12Timer::configSDI12TimerPrescale(void) { TCCR4D = 0x00; // TCCR4D = 0x00 = No fault protection TCCR4E = 0x00; // TCCR4E = 0x00 = No register locks or overrides } + void SDI12Timer::resetSDI12TimerPrescale(void) { TCCR4A = preSDI12_TCCR4A; TCCR4B = preSDI12_TCCR4B; @@ -245,6 +252,7 @@ void SDI12Timer::configSDI12TimerPrescale(void) { TC_CTRLA_ENABLE; // Enable TC3 while (TC3->COUNT16.STATUS.bit.SYNCBUSY) {} // Wait for synchronization } + // NOT resetting the SAMD timer settings void SDI12Timer::resetSDI12TimerPrescale(void) { // Disable TCx @@ -267,13 +275,15 @@ void SDI12Timer::resetSDI12TimerPrescale(void) { #elif defined(ESP32) || defined(ESP8266) void SDI12Timer::configSDI12TimerPrescale(void) {} + void SDI12Timer::resetSDI12TimerPrescale(void) {} -sdi12timer_t ESPFAMILY_USE_INSTRUCTION_RAM SDI12Timer::SDI12TimerRead(void) -{ - // Its a one microsecond clock but we want 64uS ticks so divide by 64 i.e. right shift 6 - return ((sdi12timer_t)(micros() >> 6)); +sdi12timer_t ESPFAMILY_USE_INSTRUCTION_RAM SDI12Timer::SDI12TimerRead(void) { + // Its a one microsecond clock but we want 64uS ticks so divide by 64 i.e. right shift + // 6 + return ((sdi12timer_t)(micros() >> 6)); } + // Unknown board #else #error "Please define your board timer and pins" From 9389a7b9a920a7e24e2d330f5592cb7284181966 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Fri, 22 Sep 2023 12:40:28 -0400 Subject: [PATCH 19/96] Refactor addCRCResponse into part of sendResponse Signed-off-by: Sara Damiano --- src/SDI12.cpp | 92 +++++++++++++++++++++------------------------------ src/SDI12.h | 33 ++++++++++++------ 2 files changed, 61 insertions(+), 64 deletions(-) diff --git a/src/SDI12.cpp b/src/SDI12.cpp index 676dbee..35abc20 100644 --- a/src/SDI12.cpp +++ b/src/SDI12.cpp @@ -514,27 +514,41 @@ void SDI12::sendCommand(FlashString cmd, int8_t extraWakeTime) { // marking and then sending out the characters of resp one by one (for slave-side use, // that is, when the Arduino itself is acting as an SDI-12 device rather than a // recorder). -void SDI12::sendResponse(String& resp) { +void SDI12::sendResponse(String& resp, bool addCRC) { setState(SDI12_TRANSMITTING); // Get ready to send data to the recorder digitalWrite(_dataPin, LOW); // marking is LOW delayMicroseconds(marking_micros); // 8.33 ms marking before response for (int unsigned i = 0; i < resp.length(); i++) { writeChar(resp[i]); // write each character } + // tack on the CRC if requested + if (addCRC) { + String crc = crcToString(calculateCRC(resp)); + for (int unsigned i = 0; i < 3; i++) { + writeChar(crc[i]); // write each character + } + } setState(SDI12_LISTENING); // return to listening state } -void SDI12::sendResponse(const char* resp) { +void SDI12::sendResponse(const char* resp, bool addCRC) { setState(SDI12_TRANSMITTING); // Get ready to send data to the recorder digitalWrite(_dataPin, LOW); // marking is LOW delayMicroseconds(marking_micros); // 8.33 ms marking before response for (int unsigned i = 0; i < strlen(resp); i++) { writeChar(resp[i]); // write each character } + // tack on the CRC if requested + if (addCRC) { + String crc = crcToString(calculateCRC(resp)); + for (int unsigned i = 0; i < 3; i++) { + writeChar(crc[i]); // write each character + } + } setState(SDI12_LISTENING); // return to listening state } -void SDI12::sendResponse(FlashString resp) { +void SDI12::sendResponse(FlashString resp, bool addCRC) { setState(SDI12_TRANSMITTING); // Get ready to send data to the recorder digitalWrite(_dataPin, LOW); // marking is LOW delayMicroseconds(marking_micros); // 8.33 ms marking before response @@ -542,16 +556,20 @@ void SDI12::sendResponse(FlashString resp) { // write each character writeChar(static_cast(pgm_read_byte((const char*)resp + i))); } + // tack on the CRC if requested + if (addCRC) { + String crc = crcToString(calculateCRC(resp)); + for (int unsigned i = 0; i < 3; i++) { + writeChar(crc[i]); // write each character + } + } setState(SDI12_LISTENING); // return to listening state } -#ifdef ENVIRODIY_SDI12_USE_CRC - #define POLY 0xa001 -String SDI12::addCRCResponse(String& resp) { - char crcStr[3] = {0}; - uint16_t crc = 0; +uint16_t SDI12::calculateCRC(String& resp) { + uint16_t crc = 0; for (int i = 0; i < resp.length(); i++) { crc ^= (uint16_t) @@ -565,15 +583,11 @@ String SDI12::addCRCResponse(String& resp) { } } } - crcStr[0] = (char)(0x0040 | (crc >> 12)); - crcStr[1] = (char)(0x0040 | ((crc >> 6) & 0x003F)); - crcStr[2] = (char)(0x0040 | (crc & 0x003F)); - return (resp + String(crcStr[0]) + String(crcStr[1]) + String(crcStr[2])); + return crc; } -char* SDI12::addCRCResponse(char* resp) { - char crcStr[3] = {0}; - uint16_t crc = 0; +uint16_t SDI12::calculateCRC(const char* resp) { + uint16_t crc = 0; for (int i = 0; i < strlen(resp); i++) { crc ^= (uint16_t) @@ -587,21 +601,14 @@ char* SDI12::addCRCResponse(char* resp) { } } } - - crcStr[1] = (char)(0x0040 | ((crc >> 6) & 0x003F)); - crcStr[2] = (char)(0x0040 | (crc & 0x003F)); - return (strncat(resp, crcStr, 3)); + return crc; } -String SDI12::addCRCResponse(FlashString resp) { - char crcStr[3] = {0}; - char respBuffer[SDI12_BUFFER_SIZE - 5]; // don't need space for the CRC or CR/LF +uint16_t SDI12::calculateCRC(FlashString resp) { uint16_t crc = 0; - int i = 0; char responsechar; - - for (i = 0; i < strlen_P((PGM_P)resp); i++) { + for (int i = 0; i < strlen_P((PGM_P)resp); i++) { responsechar = (char)pgm_read_byte((const char*)resp + i); crc ^= (uint16_t)responsechar; // Set the CRC equal to the exclusive OR of the // character and itself @@ -613,41 +620,18 @@ String SDI12::addCRCResponse(FlashString resp) { crc >>= 1; // right shift the CRC one bit } } - respBuffer[i] = responsechar; } - respBuffer[++i] = '\0'; - String outResp = respBuffer; - crcStr[0] = (char)(0x0040 | (crc >> 12)); - crcStr[1] = (char)(0x0040 | ((crc >> 6) & 0x003F)); - crcStr[2] = (char)(0x0040 | (crc & 0x003F)); - return (outResp + String(crcStr[0]) + String(crcStr[1]) + String(crcStr[2])); + return crc; } -String SDI12::calculateCRC(String& resp) { - char crcStr[3] = {0}; - uint16_t crc = 0; - - for (int i = 0; i < resp.length(); i++) { - crc ^= (uint16_t) - resp[i]; // Set the CRC equal to the exclusive OR of the character and itself - for (int j = 0; j < 8; j++) { // count = 1 to 8 - if (crc & 0x0001) { // if the least significant bit of the CRC is one - crc >>= 1; // right shift the CRC one bit - crc ^= POLY; // set CRC equal to the exclusive OR of POLY and itself - } else { - crc >>= 1; // right shift the CRC one bit - } - } - } - crcStr[0] = (char)(0x0040 | (crc >> 12)); - crcStr[1] = (char)(0x0040 | ((crc >> 6) & 0x003F)); - crcStr[2] = (char)(0x0040 | (crc & 0x003F)); +String SDI12::crcToString(uint16_t crc) { + char crcStr[3] = {0}; + crcStr[0] = (char)(0x0040 | (crc >> 12)); + crcStr[1] = (char)(0x0040 | ((crc >> 6) & 0x003F)); + crcStr[2] = (char)(0x0040 | (crc & 0x003F)); return (String(crcStr[0]) + String(crcStr[1]) + String(crcStr[2])); } -#endif // ENVIRODIY_SDI12_USE_CRC - - /* ================ Interrupt Service Routine =======================================*/ // 7.1 - Passes off responsibility for the interrupt to the active object. diff --git a/src/SDI12.h b/src/SDI12.h index 5f1dce5..bbb8ab2 100644 --- a/src/SDI12.h +++ b/src/SDI12.h @@ -914,28 +914,41 @@ class SDI12 : public Stream { /// @copydoc SDI12::sendCommand(String&, int8_t) void sendCommand(FlashString cmd, int8_t extraWakeTime = SDI12_WAKE_DELAY); -#ifdef ENVIRODIY_SDI12_USE_CRC - String addCRCResponse(String& resp); // Add CRC to the resp string (for slave use) - char* addCRCResponse(char* resp); // Add CRC to the resp string (for slave use) - String - addCRCResponse(FlashString resp); // Add CRC to the resp string (for slave use) - String calculateCRC(String& resp); // Calculate the CRC for a response -#endif + /** + * @brief Calculates the 16-bit Cyclic Redundancy Check (CRC) for an SDI-12 message. + * + * @param resp The message to calculate the CRC for. + * @return *uint16_t* The calculated CRC + */ + uint16_t calculateCRC(String& resp); + /// @copydoc SDI12::calculateCRC(String&) + uint16_t calculateCRC(const char* resp); + /// @copydoc SDI12::calculateCRC(String&) + uint16_t calculateCRC(FlashString resp); + + /** + * @brief Converts a numeric 16-bit CRC to an ASCII String. + * + * @param crc The 16-bit CRC + * @return *String* An ASCII string for the CRC + */ + String crcToString(uint16_t crc); /** * @brief Send a response out on the data line (for slave use) * * @param resp the response to send + * @param addCRC True to append a CRC to the outgoing response * * A publicly accessible function that sends out an 8.33 ms marking and a response * byte by byte on the data line. This is needed if the Arduino is acting as an * SDI-12 device itself, not as a recorder for another SDI-12 device. */ - void sendResponse(String& resp); + void sendResponse(String& resp, bool addCRC = false); /// @copydoc SDI12::sendResponse(String& resp) - void sendResponse(const char* resp); + void sendResponse(const char* resp, bool addCRC = false); /// @copydoc SDI12::sendResponse(String& resp) - void sendResponse(FlashString resp); + void sendResponse(FlashString resp, bool addCRC = false); ///@} /** From f525628ef13bb6fefd52485c6cdaa3303ea7fb4d Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Fri, 22 Sep 2023 12:42:28 -0400 Subject: [PATCH 20/96] Function to verify CRC, #75 Signed-off-by: Sara Damiano --- src/SDI12.cpp | 23 +++++++++++++++++++++++ src/SDI12.h | 10 ++++++++++ 2 files changed, 33 insertions(+) diff --git a/src/SDI12.cpp b/src/SDI12.cpp index 35abc20..5c73dc6 100644 --- a/src/SDI12.cpp +++ b/src/SDI12.cpp @@ -632,6 +632,29 @@ String SDI12::crcToString(uint16_t crc) { return (String(crcStr[0]) + String(crcStr[1]) + String(crcStr[2])); } +bool SDI12::verifyCRC(String& respWithCRC) { + uint16_t nChar = respWithCRC.length() - + 2; // number of characters without and (readable string composed of + // sensor address, values separated by + and -) and the 3 characters + String recCRC = ""; // the CRC portion of the response + String recString = ""; // the data portion of the response + + // extract the data portion of the string + for (int i = 0; i < (nChar - 3); i++) recString += respWithCRC[i]; + + // extract the last 3 characters that are the CRC from the full response string + for (int i = (nChar - 3); i < nChar; i++) recCRC += respWithCRC[i]; + + // calculate the CRC for the data portion + String calcCRC = crcToString(calculateCRC(recString)); + + if (recCRC == calcCRC) { + return false; + } else { + return true; + } +} + /* ================ Interrupt Service Routine =======================================*/ // 7.1 - Passes off responsibility for the interrupt to the active object. diff --git a/src/SDI12.h b/src/SDI12.h index bbb8ab2..a376031 100644 --- a/src/SDI12.h +++ b/src/SDI12.h @@ -934,6 +934,16 @@ class SDI12 : public Stream { */ String crcToString(uint16_t crc); + /** + * @brief Verifies that the CRC returned at the end of an SDI-12 message matches that + * of the content of the message. + * + * @param respWithCRC The full SDI-12 message, including the CRC at the end. + * @return *true* The CRC matches and the message is valid + * @return *false* The CRC doesn't match; the conversation should be retried. + */ + bool verifyCRC(String& respWithCRC); + /** * @brief Send a response out on the data line (for slave use) * From b5d9da5d99181f14ce7fc7f56e5c14f291f08eb7 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Fri, 22 Sep 2023 13:56:02 -0400 Subject: [PATCH 21/96] Fix type warnings, slave for ESP8266 Signed-off-by: Sara Damiano --- .../h_SDI-12_slave_implementation.ino | 8 +++++--- src/SDI12.cpp | 12 ++++++------ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/examples/h_SDI-12_slave_implementation/h_SDI-12_slave_implementation.ino b/examples/h_SDI-12_slave_implementation/h_SDI-12_slave_implementation.ino index 49c2236..a7387bd 100644 --- a/examples/h_SDI-12_slave_implementation/h_SDI-12_slave_implementation.ino +++ b/examples/h_SDI-12_slave_implementation/h_SDI-12_slave_implementation.ino @@ -137,8 +137,9 @@ void parseSdi12Cmd(String command, String* dValues) { } } - // Issue the response speficied in the switch-case structure above. - slaveSDI12.sendResponse(String(sensorAddress) + responseStr + "\r\n"); + // Issue the response specified in the switch-case structure above. + String fullResponse = String(sensorAddress) + responseStr + "\r\n"; + slaveSDI12.sendResponse(fullResponse); } void formatOutputSDI(float* measurementValues, String* dValues, unsigned int maxChar) { @@ -241,7 +242,8 @@ void loop() { // Populate the "dValues" String array with the values in SDI-12 format formatOutputSDI(measurementValues, dValues, 35); // For aM!, Send "service request" (
) when data is ready - slaveSDI12.sendResponse(String(sensorAddress) + "\r\n"); + String fullResponse = String(sensorAddress) + "\r\n"; + slaveSDI12.sendResponse(fullResponse); state = WAIT; slaveSDI12.forceListen(); // sets SDI-12 pin as input to prepare for incoming // message AGAIN diff --git a/src/SDI12.cpp b/src/SDI12.cpp index 5c73dc6..0616b39 100644 --- a/src/SDI12.cpp +++ b/src/SDI12.cpp @@ -239,7 +239,7 @@ SDI12::SDI12(int8_t dataPin) { // Destructor SDI12::~SDI12() { setState(SDI12_DISABLED); - if (isActive()) { _activeObject = NULL; } + if (isActive()) { _activeObject = nullptr; } // Set the timer prescalers back to original values // NOTE: This does NOT reset SAMD board pre-scalers! sdi12timer.resetSDI12TimerPrescale(); @@ -571,7 +571,7 @@ void SDI12::sendResponse(FlashString resp, bool addCRC) { uint16_t SDI12::calculateCRC(String& resp) { uint16_t crc = 0; - for (int i = 0; i < resp.length(); i++) { + for (uint16_t i = 0; i < resp.length(); i++) { crc ^= (uint16_t) resp[i]; // Set the CRC equal to the exclusive OR of the character and itself for (int j = 0; j < 8; j++) { // count = 1 to 8 @@ -589,7 +589,7 @@ uint16_t SDI12::calculateCRC(String& resp) { uint16_t SDI12::calculateCRC(const char* resp) { uint16_t crc = 0; - for (int i = 0; i < strlen(resp); i++) { + for (size_t i = 0; i < strlen(resp); i++) { crc ^= (uint16_t) resp[i]; // Set the CRC equal to the exclusive OR of the character and itself for (int j = 0; j < 8; j++) { // count = 1 to 8 @@ -608,7 +608,7 @@ uint16_t SDI12::calculateCRC(FlashString resp) { uint16_t crc = 0; char responsechar; - for (int i = 0; i < strlen_P((PGM_P)resp); i++) { + for (size_t i = 0; i < strlen_P((PGM_P)resp); i++) { responsechar = (char)pgm_read_byte((const char*)resp + i); crc ^= (uint16_t)responsechar; // Set the CRC equal to the exclusive OR of the // character and itself @@ -640,10 +640,10 @@ bool SDI12::verifyCRC(String& respWithCRC) { String recString = ""; // the data portion of the response // extract the data portion of the string - for (int i = 0; i < (nChar - 3); i++) recString += respWithCRC[i]; + for (uint16_t i = 0; i < (nChar - 3); i++) recString += respWithCRC[i]; // extract the last 3 characters that are the CRC from the full response string - for (int i = (nChar - 3); i < nChar; i++) recCRC += respWithCRC[i]; + for (uint16_t i = (nChar - 3); i < nChar; i++) recCRC += respWithCRC[i]; // calculate the CRC for the data portion String calcCRC = crcToString(calculateCRC(recString)); From 667a3c311a6df048aa68051ca87397013e611f29 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Fri, 22 Sep 2023 14:03:46 -0400 Subject: [PATCH 22/96] Fix interface example for ESP8266 Signed-off-by: Sara Damiano --- examples/i_SDI-12_interface/i_SDI-12_interface.ino | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/i_SDI-12_interface/i_SDI-12_interface.ino b/examples/i_SDI-12_interface/i_SDI-12_interface.ino index 8fc6b39..15a9585 100644 --- a/examples/i_SDI-12_interface/i_SDI-12_interface.ino +++ b/examples/i_SDI-12_interface/i_SDI-12_interface.ino @@ -166,7 +166,8 @@ void loop() { mySDI12.sendCommand(serialMsgStr); } else { serialMsgStr.toUpperCase(); - mySDI12.sendCommand(serialMsgStr + "!"); + String fullCommand = serialMsgStr + "!"; + mySDI12.sendCommand(fullCommand); } } // Reset String for next serial message From af498b4019ed5e286ea92346cfdd27323ecdaac1 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Fri, 22 Sep 2023 14:57:12 -0400 Subject: [PATCH 23/96] Trim string in verify CRC Signed-off-by: Sara Damiano --- src/SDI12.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/SDI12.cpp b/src/SDI12.cpp index 0616b39..60b82ee 100644 --- a/src/SDI12.cpp +++ b/src/SDI12.cpp @@ -633,11 +633,14 @@ String SDI12::crcToString(uint16_t crc) { } bool SDI12::verifyCRC(String& respWithCRC) { - uint16_t nChar = respWithCRC.length() - - 2; // number of characters without and (readable string composed of - // sensor address, values separated by + and -) and the 3 characters - String recCRC = ""; // the CRC portion of the response - String recString = ""; // the data portion of the response + // trim trailing \r and \n ( and ) + respWithCRC.trim(); + uint16_t nChar = + respWithCRC.length(); // number of characters without (readable string composed of + // sensor address, values separated by + and -) and the 3 + // characters of the CRC + String recCRC = ""; // the CRC portion of the response + String recString = ""; // the data portion of the response // extract the data portion of the string for (uint16_t i = 0; i < (nChar - 3); i++) recString += respWithCRC[i]; From 9b727864c6612bd7314736a54f5b368dbc1dbf23 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Fri, 22 Sep 2023 16:11:16 -0400 Subject: [PATCH 24/96] Add example for CRC check Signed-off-by: Sara Damiano --- .../e_continuous_measurement/platformio.ini | 15 ++ examples/l_verify_crc/ReadMe.md | 12 ++ examples/l_verify_crc/l_verify_crc.ino | 142 ++++++++++++++++++ examples/l_verify_crc/platformio.ini | 15 ++ 4 files changed, 184 insertions(+) create mode 100644 examples/e_continuous_measurement/platformio.ini create mode 100644 examples/l_verify_crc/ReadMe.md create mode 100644 examples/l_verify_crc/l_verify_crc.ino create mode 100644 examples/l_verify_crc/platformio.ini diff --git a/examples/e_continuous_measurement/platformio.ini b/examples/e_continuous_measurement/platformio.ini new file mode 100644 index 0000000..6060cd1 --- /dev/null +++ b/examples/e_continuous_measurement/platformio.ini @@ -0,0 +1,15 @@ +; PlatformIO Project Configuration File + +[platformio] +description = SDI-12 Library Example D: Getting Data from All Attached Sensors +src_dir = .piolibdeps/Arduino-SDI-12_ID1486/examples/d_simple_logger + +[env:mayfly] +monitor_speed = 115200 +board = mayfly +platform = atmelavr +framework = arduino +lib_ldf_mode = deep+ +lib_ignore = RTCZero +lib_deps = + SDI-12 diff --git a/examples/l_verify_crc/ReadMe.md b/examples/l_verify_crc/ReadMe.md new file mode 100644 index 0000000..6411dc4 --- /dev/null +++ b/examples/l_verify_crc/ReadMe.md @@ -0,0 +1,12 @@ +[//]: # ( @page example_l_page Example L: Verifying CRC Values ) +# Example L: Verifying CRC Values + +This is a simple demonstration of the SDI-12 library for Arduino. + +This is a very basic (stripped down) example where the user initiates a measurement with a CRC check and receives and verifies the CRC response + +[//]: # ( @sectionl_verify_crc_pio PlatformIO Configuration ) + +[//]: # ( @include{lineno} l_verify_crc/platformio.ini ) + +[//]: # ( @section l_verify_crc_code The Complete Example ) diff --git a/examples/l_verify_crc/l_verify_crc.ino b/examples/l_verify_crc/l_verify_crc.ino new file mode 100644 index 0000000..12dd357 --- /dev/null +++ b/examples/l_verify_crc/l_verify_crc.ino @@ -0,0 +1,142 @@ +/** + * @file f_basic_data_request.ino + * @copyright (c) 2013-2020 Stroud Water Research Center (SWRC) + * and the EnviroDIY Development Team + * This example is published under the BSD-3 license. + * @author Ruben Kertesz or @rinnamon on twitter + * @date 2/10/2016 + * + * @brief Example F: Basic Data Request to a Single Sensor + * + * This is a very basic (stripped down) example where the user initiates a measurement + * and receives the results to a terminal window without typing numerous commands into + * the terminal. + * + * Edited by Ruben Kertesz for ISCO Nile 502 2/10/2016 + */ + +#include + +#define SERIAL_BAUD 115200 /*!< The baud rate for the output serial port */ +#define DATA_PIN 7 /*!< The pin of the SDI-12 data bus */ +#define POWER_PIN 22 /*!< The sensor power pin (or -1 if not switching power) */ +#define SENSOR_ADDRESS 2 + +/** Define the SDI-12 bus */ +SDI12 mySDI12(DATA_PIN); + +String sdiResponse = ""; +String myCommand = ""; + +void setup() { + Serial.begin(SERIAL_BAUD); + while (!Serial) + ; + + Serial.println("Opening SDI-12 bus..."); + mySDI12.begin(); + delay(500); // allow things to settle + + // Power the sensors; + if (POWER_PIN > 0) { + Serial.println("Powering up sensors..."); + pinMode(POWER_PIN, OUTPUT); + digitalWrite(POWER_PIN, HIGH); + delay(200); + } + + // print out the sensor info + String command = ""; + command += String(SENSOR_ADDRESS); + command += "I!"; + mySDI12.sendCommand(command); + Serial.print(">>>"); + Serial.println(command); + delay(100); + + sdiResponse = mySDI12.readStringUntil('\n'); + sdiResponse.trim(); + // allccccccccmmmmmmvvvxxx...xx + Serial.print("<<<"); + Serial.println(sdiResponse); + + Serial.print("Address: "); + Serial.print(sdiResponse.substring(0, 1)); // address + Serial.print(", SDI-12 Version: "); + Serial.print(sdiResponse.substring(1, 3).toFloat() / 10); // SDI-12 version number + Serial.print(", Vendor ID: "); + Serial.print(sdiResponse.substring(3, 11)); // vendor id + Serial.print(", Sensor Model: "); + Serial.print(sdiResponse.substring(11, 17)); // sensor model + Serial.print(", Sensor Version: "); + Serial.print(sdiResponse.substring(17, 20)); // sensor version + Serial.print(", Sensor ID: "); + Serial.print(sdiResponse.substring(20)); // sensor id + Serial.println(); +} + +void loop() { + // first command to take a measurement + myCommand = String(SENSOR_ADDRESS) + "MC!"; + Serial.print(">>>"); + Serial.println(myCommand); // echo command to terminal + + mySDI12.sendCommand(myCommand); + delay(5); + + // wait for acknowlegement with format [address][ttt (3 char, seconds)][number of + // measurments available, 0-9] + String sdiResponse = mySDI12.readStringUntil('\n'); + sdiResponse.trim(); + Serial.print("<<<"); + Serial.println(sdiResponse); + mySDI12.clearBuffer(); + + // find out how long we have to wait (in seconds). + uint8_t meas_time_s = sdiResponse.substring(1, 4).toInt(); + Serial.print("expected measurement time: "); + Serial.print(meas_time_s); + Serial.print(" s, "); + + // Set up the number of results to expect + int numResults = sdiResponse.substring(4).toInt(); + Serial.print("Number Results: "); + Serial.println(numResults); + + // listen for measurement to finish + unsigned long timerStart = millis(); + while ((millis() - timerStart) < (meas_time_s + 1) * 1000) { + if (mySDI12.available()) // sensor can interrupt us to let us know it is done early + { + unsigned long measTime = millis() - timerStart; + Serial.print("<<<"); + Serial.println(mySDI12.readStringUntil('\n')); + Serial.print("Completed after "); + Serial.print(measTime); + Serial.println(" ms"); + break; + } + } + + + // next command to request data from last measurement + myCommand = String(SENSOR_ADDRESS) + "D0!"; + Serial.print(">>>"); + Serial.println(myCommand); // echo command to terminal + + mySDI12.sendCommand(myCommand); + delay(30); // wait a while for a response + + + sdiResponse = mySDI12.readStringUntil('\n'); + sdiResponse.trim(); + Serial.print("<<<"); + Serial.println(sdiResponse); // write the response to the screen + bool crcMatch = mySDI12.verifyCRC(sdiResponse); + if (crcMatch) { + Serial.println("CRC matches!"); + } else { + Serial.println("CRC check failed!"); + } + mySDI12.clearBuffer(); +} diff --git a/examples/l_verify_crc/platformio.ini b/examples/l_verify_crc/platformio.ini new file mode 100644 index 0000000..81b92d6 --- /dev/null +++ b/examples/l_verify_crc/platformio.ini @@ -0,0 +1,15 @@ +; PlatformIO Project Configuration File + +[platformio] +description = SDI-12 Library Example L: Verifying CRC Values +src_dir = .piolibdeps/Arduino-SDI-12_ID1486/examples/l_verify_crc + +[env:mayfly] +monitor_speed = 115200 +board = mayfly +platform = atmelavr +framework = arduino +lib_ldf_mode = deep+ +lib_ignore = RTCZero +lib_deps = + SDI-12 From d20cd75263e4c1c218995594bd6afa10704ba629 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Fri, 22 Sep 2023 16:12:18 -0400 Subject: [PATCH 25/96] Add new example to checks Signed-off-by: Sara Damiano --- .github/workflows/build_examples.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build_examples.yaml b/.github/workflows/build_examples.yaml index 122cea6..c4e6374 100644 --- a/.github/workflows/build_examples.yaml +++ b/.github/workflows/build_examples.yaml @@ -23,6 +23,7 @@ jobs: examples/i_SDI-12_interface/, examples/j_external_pcint_library/, examples/k_concurrent_logger/, + examples/l_verify_crc/, ] steps: @@ -67,7 +68,7 @@ jobs: pip install --upgrade platformio - name: Run PlatformIO - if: matrix.example != 'examples/j_external_pcint_library/' + if: matrix.example != 'examples/j_external_pcint_library/' env: PLATFORMIO_CI_SRC: ${{ matrix.example }} run: | From 19a48fd094bced3e9116be1dfda25999dcb7cb17 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Oct 2023 20:35:55 +0000 Subject: [PATCH 26/96] ci: bump actions/setup-node from 3.8.1 to 4.0.0 Bumps [actions/setup-node](https://github.com/actions/setup-node) from 3.8.1 to 4.0.0. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/v3.8.1...v4.0.0) --- updated-dependencies: - dependency-name: actions/setup-node dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/verify_library_json.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/verify_library_json.yaml b/.github/workflows/verify_library_json.yaml index fad108e..fbb2ee5 100644 --- a/.github/workflows/verify_library_json.yaml +++ b/.github/workflows/verify_library_json.yaml @@ -12,7 +12,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup Node.js - uses: actions/setup-node@v3.8.1 + uses: actions/setup-node@v4.0.0 - name: Cache Node.js modules uses: actions/cache@v3.3.2 From 4412e37b76005bf96ba39391165930db5a192476 Mon Sep 17 00:00:00 2001 From: Stephen Catsamas Date: Fri, 2 Feb 2024 09:48:04 +1100 Subject: [PATCH 27/96] fixed issue in slave example where some commands sent slave into unresponsive state --- .../h_SDI-12_slave_implementation.ino | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/examples/h_SDI-12_slave_implementation/h_SDI-12_slave_implementation.ino b/examples/h_SDI-12_slave_implementation/h_SDI-12_slave_implementation.ino index 49c2236..33f10b3 100644 --- a/examples/h_SDI-12_slave_implementation/h_SDI-12_slave_implementation.ino +++ b/examples/h_SDI-12_slave_implementation/h_SDI-12_slave_implementation.ino @@ -39,6 +39,7 @@ int state = 0; #define WAIT 0 #define INITIATE_CONCURRENT 1 #define INITIATE_MEASUREMENT 2 +#define PROCESS_COMMAND 3 // Create object by which to communicate with the SDI-12 bus on SDIPIN SDI12 slaveSDI12(DATA_PIN); @@ -196,6 +197,7 @@ void loop() { // Character '!' indicates the end of an SDI-12 command; if the current // character is '!', stop listening and respond to the command if (charReceived == '!') { + state = PROCESS_COMMAND; // Command string is completed; do something with it parseSdi12Cmd(commandReceived, dValues); // Clear command string to reset for next command @@ -246,5 +248,9 @@ void loop() { slaveSDI12.forceListen(); // sets SDI-12 pin as input to prepare for incoming // message AGAIN break; + case PROCESS_COMMAND: + state = WAIT; + slaveSDI12.forceListen(); + break; } } From 4172915a3d555126cb79eb35da4741b2dd33a4e2 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Mon, 1 Apr 2024 11:37:54 -0400 Subject: [PATCH 28/96] replace defines in examples Signed-off-by: Sara Damiano --- examples/a_wild_card/a_wild_card.ino | 16 ++--- examples/b_address_change/ReadMe.md | 4 +- .../b_address_change/b_address_change.ino | 16 ++--- .../c_check_all_addresses.ino | 20 +++--- examples/d_simple_logger/d_simple_logger.ino | 32 +++++---- .../e_continuous_measurement.ino | 26 +++---- .../f_basic_data_request.ino | 23 ++++--- .../g_terminal_window/g_terminal_window.ino | 16 ++--- .../h_SDI-12_slave_implementation.ino | 11 ++- .../i_SDI-12_interface/i_SDI-12_interface.ino | 19 ++--- .../j_external_pcint_library.ino | 27 ++++---- .../k_concurrent_logger.ino | 29 ++++---- examples/l_verify_crc/l_verify_crc.ino | 25 +++---- tools/SDI12_spy/SDI12_spy.ino | 4 +- tools/TestCommands/TestCommands.ino | 69 ++++++++++--------- tools/TestWarmUp/TestWarmUp.ino | 31 +++++---- tools/printCommands/printCommands.ino | 51 +++++++------- 17 files changed, 217 insertions(+), 202 deletions(-) diff --git a/examples/a_wild_card/a_wild_card.ino b/examples/a_wild_card/a_wild_card.ino index 8add10a..e8efe55 100644 --- a/examples/a_wild_card/a_wild_card.ino +++ b/examples/a_wild_card/a_wild_card.ino @@ -17,12 +17,12 @@ #include -#define SERIAL_BAUD 115200 /*!< The baud rate for the output serial port */ -#define DATA_PIN 7 /*!< The pin of the SDI-12 data bus */ -#define POWER_PIN 22 /*!< The sensor power pin (or -1 if not switching power) */ +uint32_t serialBaud = 115200; /*!< The baud rate for the output serial port */ +int8_t dataPin = 7; /*!< The pin of the SDI-12 data bus */ +int8_t powerPin = 22; /*!< The sensor power pin (or -1 if not switching power) */ /** Define the SDI-12 bus */ -SDI12 mySDI12(DATA_PIN); +SDI12 mySDI12(dataPin); /** '?' is a wildcard character which asks any and all sensors to respond @@ -32,7 +32,7 @@ SDI12 mySDI12(DATA_PIN); String myCommand = "?I!"; void setup() { - Serial.begin(SERIAL_BAUD); + Serial.begin(serialBaud); while (!Serial) ; @@ -41,10 +41,10 @@ void setup() { delay(500); // allow things to settle // Power the sensors; - if (POWER_PIN > 0) { + if (powerPin > 0) { Serial.println("Powering up sensors..."); - pinMode(POWER_PIN, OUTPUT); - digitalWrite(POWER_PIN, HIGH); + pinMode(powerPin, OUTPUT); + digitalWrite(powerPin, HIGH); delay(200); } } diff --git a/examples/b_address_change/ReadMe.md b/examples/b_address_change/ReadMe.md index f220b9b..ce1dbda 100644 --- a/examples/b_address_change/ReadMe.md +++ b/examples/b_address_change/ReadMe.md @@ -7,9 +7,9 @@ First, physically connect your SDI-12 sensor to your device. Some helpful hits Once your sensor is physically connected to your board, download this library and open this sketch. -Scroll to line 54 of the sketch (`#define DATA_PIN 7`). Change the `7` to the pin number that your sensor is attached to. +Scroll to line 54 of the sketch (`int8_t dataPin = 7;`). Change the `7` to the pin number that your sensor is attached to. -Set the pin to provide power to your sensor in line 55 (`#define POWER_PIN 22`). If your sensor is continuously powered, set the power pin to -1. +Set the pin to provide power to your sensor in line 55 (`int8_t powerPin = 22;`). If your sensor is continuously powered, set the power pin to -1. Upload the sketch to your board. After the upload finishes, open up the serial port monitor at a baud rate of 115200 on line 53. diff --git a/examples/b_address_change/b_address_change.ino b/examples/b_address_change/b_address_change.ino index 2dd7096..eadb0bf 100644 --- a/examples/b_address_change/b_address_change.ino +++ b/examples/b_address_change/b_address_change.ino @@ -14,12 +14,12 @@ #include -#define SERIAL_BAUD 115200 /*!< The baud rate for the output serial port */ -#define DATA_PIN 7 /*!< The pin of the SDI-12 data bus */ -#define POWER_PIN 22 /*!< The sensor power pin (or -1 if not switching power) */ +uint32_t serialBaud = 115200; /*!< The baud rate for the output serial port */ +int8_t dataPin = 7; /*!< The pin of the SDI-12 data bus */ +int8_t powerPin = 22; /*!< The sensor power pin (or -1 if not switching power) */ /** Define the SDI-12 bus */ -SDI12 mySDI12(DATA_PIN); +SDI12 mySDI12(dataPin); String myCommand = ""; // empty to start char oldAddress = '!'; // invalid address as placeholder @@ -50,7 +50,7 @@ boolean checkActive(byte i) { // this checks for activity at a particular addre } void setup() { - Serial.begin(SERIAL_BAUD); + Serial.begin(serialBaud); while (!Serial) ; @@ -59,10 +59,10 @@ void setup() { delay(500); // allow things to settle // Power the sensors; - if (POWER_PIN > 0) { + if (powerPin > 0) { Serial.println("Powering up sensors..."); - pinMode(POWER_PIN, OUTPUT); - digitalWrite(POWER_PIN, HIGH); + pinMode(powerPin, OUTPUT); + digitalWrite(powerPin, HIGH); delay(200); } } diff --git a/examples/c_check_all_addresses/c_check_all_addresses.ino b/examples/c_check_all_addresses/c_check_all_addresses.ino index a5c0bde..8e0412d 100644 --- a/examples/c_check_all_addresses/c_check_all_addresses.ino +++ b/examples/c_check_all_addresses/c_check_all_addresses.ino @@ -22,10 +22,10 @@ #include -#define SERIAL_BAUD 115200 /*!< The baud rate for the output serial port */ -#define POWER_PIN 22 /*!< The sensor power pin (or -1 if not switching power) */ -#define FirstPin 5 /*! change to lowest pin number on your board */ -#define LastPin 24 /*! change to highest pin number on your board */ +uint32_t serialBaud = 115200; /*!< The baud rate for the output serial port */ +int8_t powerPin = 22; /*!< The sensor power pin (or -1 if not switching power) */ +#define FirstPin 5 /*! change to lowest pin number on your board */ +#define LastPin 24 /*! change to highest pin number on your board */ /** * @brief gets identification information from a sensor, and prints it to the serial @@ -85,19 +85,19 @@ void scanAddressSpace(SDI12 sdi) { } void setup() { - Serial.begin(SERIAL_BAUD); + Serial.begin(serialBaud); Serial.println("//\n// Start Search for SDI-12 Devices \n// -----------------------"); // Power the sensors; - if (POWER_PIN > 0) { + if (powerPin > 0) { Serial.println("Powering up sensors..."); - pinMode(POWER_PIN, OUTPUT); - digitalWrite(POWER_PIN, HIGH); + pinMode(powerPin, OUTPUT); + digitalWrite(powerPin, HIGH); delay(200); } for (uint8_t pin = FirstPin; pin <= LastPin; pin++) { - if (pin != POWER_PIN) { + if (pin != powerPin) { pinMode(pin, INPUT); SDI12 mySDI12(pin); mySDI12.begin(); @@ -112,7 +112,7 @@ void setup() { Serial.println("\n//\n// End Search for SDI-12 Devices \n// ---------------------"); // Cut power - digitalWrite(POWER_PIN, LOW); + digitalWrite(powerPin, LOW); } void loop() {} diff --git a/examples/d_simple_logger/d_simple_logger.ino b/examples/d_simple_logger/d_simple_logger.ino index 93b2797..1901962 100644 --- a/examples/d_simple_logger/d_simple_logger.ino +++ b/examples/d_simple_logger/d_simple_logger.ino @@ -26,13 +26,15 @@ #include -#define SERIAL_BAUD 115200 /*!< The baud rate for the output serial port */ -#define DATA_PIN 7 /*!< The pin of the SDI-12 data bus */ -#define POWER_PIN 22 /*!< The sensor power pin (or -1 if not switching power) */ -#define WAKE_DELAY 0 /*!< Extra time needed for the sensor to wake (0-100ms) */ +uint32_t serialBaud = 115200; /*!< The baud rate for the output serial port */ +int8_t dataPin = 7; /*!< The pin of the SDI-12 data bus */ +int8_t powerPin = 22; /*!< The sensor power pin (or -1 if not switching power) */ +uint32_t wakeDelay = 0; /*!< Extra time needed for the sensor to wake (0-100ms) */ +int8_t firstAddress = 0; /* The first address in the address space to check (0='0') */ +int8_t lastAddress = 62; /* The last address in the address space to check (62='z') */ /** Define the SDI-12 bus */ -SDI12 mySDI12(DATA_PIN); +SDI12 mySDI12(dataPin); // keeps track of active addresses bool isActive[64] = { @@ -80,7 +82,7 @@ void printInfo(char i) { String command = ""; command += (char)i; command += "I!"; - mySDI12.sendCommand(command, WAKE_DELAY); + mySDI12.sendCommand(command, wakeDelay); delay(100); String sdiResponse = mySDI12.readStringUntil('\n'); @@ -111,7 +113,7 @@ bool getResults(char i, int resultsExpected) { command += "D"; command += cmd_number; command += "!"; // SDI-12 command to get data [address][D][dataOption][!] - mySDI12.sendCommand(command, WAKE_DELAY); + mySDI12.sendCommand(command, wakeDelay); uint32_t start = millis(); while (mySDI12.available() < 3 && (millis() - start) < 1500) {} @@ -148,7 +150,7 @@ bool takeMeasurement(char i, String meas_type = "") { command += "M"; command += meas_type; command += "!"; // SDI-12 measurement command format [address]['M'][!] - mySDI12.sendCommand(command, WAKE_DELAY); + mySDI12.sendCommand(command, wakeDelay); delay(100); Serial.print(command); @@ -201,7 +203,7 @@ boolean checkActive(char i) { myCommand += "!"; for (int j = 0; j < 3; j++) { // goes through three rapid contact attempts - mySDI12.sendCommand(myCommand, WAKE_DELAY); + mySDI12.sendCommand(myCommand, wakeDelay); delay(100); if (mySDI12.available()) { // If we here anything, assume we have an active sensor mySDI12.clearBuffer(); @@ -213,7 +215,7 @@ boolean checkActive(char i) { } void setup() { - Serial.begin(SERIAL_BAUD); + Serial.begin(serialBaud); while (!Serial) ; @@ -225,10 +227,10 @@ void setup() { Serial.println(mySDI12.TIMEOUT); // Power the sensors; - if (POWER_PIN > 0) { + if (powerPin > 0) { Serial.println("Powering up sensors, wait..."); - pinMode(POWER_PIN, OUTPUT); - digitalWrite(POWER_PIN, HIGH); + pinMode(powerPin, OUTPUT); + digitalWrite(powerPin, HIGH); delay(15000L); // delay(200); } @@ -238,7 +240,7 @@ void setup() { Serial.println("Sensor Address, Protocol Version, Sensor Vendor, Sensor Model, " "Sensor Version, Sensor ID"); - for (byte i = 0; i < 62; i++) { + for (byte i = firstAddress; i < lastAddress; i++) { char addr = decToChar(i); if (checkActive(addr)) { numSensors++; @@ -268,7 +270,7 @@ void loop() { String commands[] = {"", "1", "2", "3", "4", "5", "6", "7", "8", "9"}; for (uint8_t a = 0; a < 1; a++) { // measure one at a time - for (byte i = 0; i < 62; i++) { + for (byte i = firstAddress; i < lastAddress; i++) { char addr = decToChar(i); if (isActive[i]) { // Serial.print(millis() / 1000); diff --git a/examples/e_continuous_measurement/e_continuous_measurement.ino b/examples/e_continuous_measurement/e_continuous_measurement.ino index c513220..6cee1f8 100644 --- a/examples/e_continuous_measurement/e_continuous_measurement.ino +++ b/examples/e_continuous_measurement/e_continuous_measurement.ino @@ -16,12 +16,14 @@ #include -#define SERIAL_BAUD 115200 /*!< The baud rate for the output serial port */ -#define DATA_PIN 7 /*!< The pin of the SDI-12 data bus */ -#define POWER_PIN 22 /*!< The sensor power pin (or -1 if not switching power) */ +uint32_t serialBaud = 115200; /*!< The baud rate for the output serial port */ +int8_t dataPin = 7; /*!< The pin of the SDI-12 data bus */ +int8_t powerPin = 22; /*!< The sensor power pin (or -1 if not switching power) */ +int8_t firstAddress = 0; /* The first address in the address space to check (0='0') */ +int8_t lastAddress = 62; /* The last address in the address space to check (62='z') */ /** Define the SDI-12 bus */ -SDI12 mySDI12(DATA_PIN); +SDI12 mySDI12(dataPin); // keeps track of active addresses bool isActive[64] = { @@ -151,7 +153,7 @@ boolean checkActive(char i) { } void setup() { - Serial.begin(SERIAL_BAUD); + Serial.begin(serialBaud); while (!Serial) ; @@ -163,11 +165,11 @@ void setup() { Serial.println(mySDI12.TIMEOUT); // Power the sensors; - if (POWER_PIN > 0) { - Serial.println("Powering up sensors..."); - pinMode(POWER_PIN, OUTPUT); - digitalWrite(POWER_PIN, HIGH); - delay(200); + if (powerPin > 0) { + Serial.println("Powering up sensors, wait..."); + pinMode(powerPin, OUTPUT); + digitalWrite(powerPin, HIGH); + delay(10000L); } // Quickly Scan the Address Space @@ -175,7 +177,7 @@ void setup() { Serial.println("Sensor Address, Protocol Version, Sensor Vendor, Sensor Model, " "Sensor Version, Sensor ID"); - for (byte i = 0; i < 62; i++) { + for (byte i = firstAddress; i < lastAddress; i++) { char addr = decToChar(i); if (checkActive(addr)) { numSensors++; @@ -203,7 +205,7 @@ void setup() { void loop() { // measure one at a time - for (byte i = 0; i < 62; i++) { + for (byte i = firstAddress; i < lastAddress; i++) { char addr = decToChar(i); if (isActive[i]) { // Serial.print(millis() / 1000); diff --git a/examples/f_basic_data_request/f_basic_data_request.ino b/examples/f_basic_data_request/f_basic_data_request.ino index 8f66b65..3d7b5b5 100644 --- a/examples/f_basic_data_request/f_basic_data_request.ino +++ b/examples/f_basic_data_request/f_basic_data_request.ino @@ -17,19 +17,20 @@ #include -#define SERIAL_BAUD 115200 /*!< The baud rate for the output serial port */ -#define DATA_PIN 7 /*!< The pin of the SDI-12 data bus */ -#define POWER_PIN 22 /*!< The sensor power pin (or -1 if not switching power) */ -#define SENSOR_ADDRESS 1 +/* connection information */ +uint32_t serialBaud = 115200; /*!< The baud rate for the output serial port */ +int8_t dataPin = 7; /*!< The pin of the SDI-12 data bus */ +int8_t powerPin = 22; /*!< The sensor power pin (or -1 if not switching power) */ +char sensorAddress = '\1'; /*!< The address of the SDI-12 sensor */ /** Define the SDI-12 bus */ -SDI12 mySDI12(DATA_PIN); +SDI12 mySDI12(dataPin); String sdiResponse = ""; String myCommand = ""; void setup() { - Serial.begin(SERIAL_BAUD); + Serial.begin(serialBaud); while (!Serial) ; @@ -38,10 +39,10 @@ void setup() { delay(500); // allow things to settle // Power the sensors; - if (POWER_PIN > 0) { + if (powerPin > 0) { Serial.println("Powering up sensors..."); - pinMode(POWER_PIN, OUTPUT); - digitalWrite(POWER_PIN, HIGH); + pinMode(powerPin, OUTPUT); + digitalWrite(powerPin, HIGH); delay(200); } } @@ -55,7 +56,7 @@ void loop() { // characters get discarded but now the rest of the loop continues // first command to take a measurement - myCommand = String(SENSOR_ADDRESS) + "M!"; + myCommand = String(sensorAddress) + "M!"; Serial.println(myCommand); // echo command to terminal mySDI12.sendCommand(myCommand); @@ -78,7 +79,7 @@ void loop() { // next command to request data from last measurement - myCommand = String(SENSOR_ADDRESS) + "D0!"; + myCommand = String(sensorAddress) + "D0!"; Serial.println(myCommand); // echo command to terminal mySDI12.sendCommand(myCommand); diff --git a/examples/g_terminal_window/g_terminal_window.ino b/examples/g_terminal_window/g_terminal_window.ino index 9957e7a..7f1b689 100644 --- a/examples/g_terminal_window/g_terminal_window.ino +++ b/examples/g_terminal_window/g_terminal_window.ino @@ -19,19 +19,19 @@ #include -#define SERIAL_BAUD 115200 /*!< The baud rate for the output serial port */ -#define DATA_PIN 7 /*!< The pin of the SDI-12 data bus */ -#define POWER_PIN 22 /*!< The sensor power pin (or -1 if not switching power) */ +uint32_t serialBaud = 115200; /*!< The baud rate for the output serial port */ +int8_t dataPin = 7; /*!< The pin of the SDI-12 data bus */ +int8_t powerPin = 22; /*!< The sensor power pin (or -1 if not switching power) */ /** Define the SDI-12 bus */ -SDI12 mySDI12(DATA_PIN); +SDI12 mySDI12(dataPin); char inByte = 0; String sdiResponse = ""; String myCommand = ""; void setup() { - Serial.begin(SERIAL_BAUD); + Serial.begin(serialBaud); while (!Serial) ; @@ -40,10 +40,10 @@ void setup() { delay(500); // allow things to settle // Power the sensors; - if (POWER_PIN > 0) { + if (powerPin > 0) { Serial.println("Powering up sensors..."); - pinMode(POWER_PIN, OUTPUT); - digitalWrite(POWER_PIN, HIGH); + pinMode(powerPin, OUTPUT); + digitalWrite(powerPin, HIGH); delay(200); } } diff --git a/examples/h_SDI-12_slave_implementation/h_SDI-12_slave_implementation.ino b/examples/h_SDI-12_slave_implementation/h_SDI-12_slave_implementation.ino index a7387bd..4faee42 100644 --- a/examples/h_SDI-12_slave_implementation/h_SDI-12_slave_implementation.ino +++ b/examples/h_SDI-12_slave_implementation/h_SDI-12_slave_implementation.ino @@ -30,18 +30,17 @@ #include -#define DATA_PIN 7 /*!< The pin of the SDI-12 data bus */ -#define POWER_PIN 22 /*!< The sensor power pin (or -1 if not switching power) */ - -char sensorAddress = '5'; -int state = 0; +int8_t dataPin = 7; /*!< The pin of the SDI-12 data bus */ +int8_t powerPin = 22; /*!< The sensor power pin (or -1 if not switching power) */ +char sensorAddress = '5'; /*!< The address of the SDI-12 sensor */ +int state = 0; #define WAIT 0 #define INITIATE_CONCURRENT 1 #define INITIATE_MEASUREMENT 2 // Create object by which to communicate with the SDI-12 bus on SDIPIN -SDI12 slaveSDI12(DATA_PIN); +SDI12 slaveSDI12(dataPin); void pollSensor(float* measurementValues) { measurementValues[0] = 1.111111; diff --git a/examples/i_SDI-12_interface/i_SDI-12_interface.ino b/examples/i_SDI-12_interface/i_SDI-12_interface.ino index 15a9585..9b97659 100644 --- a/examples/i_SDI-12_interface/i_SDI-12_interface.ino +++ b/examples/i_SDI-12_interface/i_SDI-12_interface.ino @@ -46,24 +46,25 @@ #include -#define SERIAL_BAUD 115200 /*!< The baud rate for the output serial port */ -#define DATA_PIN 7 /*!< The pin of the SDI-12 data bus */ -#define POWER_PIN 22 /*!< The sensor power pin (or -1 if not switching power) */ -#define SENSOR_ADDRESS 1 +/* connection information */ +uint32_t serialBaud = 115200; /*!< The baud rate for the output serial port */ +int8_t dataPin = 7; /*!< The pin of the SDI-12 data bus */ +int8_t powerPin = 22; /*!< The sensor power pin (or -1 if not switching power) */ +char sensorAddress = '1'; /*!< The address of the SDI-12 sensor */ /** Define the SDI-12 bus */ -SDI12 mySDI12(DATA_PIN); +SDI12 mySDI12(dataPin); void setup() { - Serial.begin(SERIAL_BAUD); + Serial.begin(serialBaud); while (!Serial) ; // Power the sensors; - if (POWER_PIN > 0) { + if (powerPin > 0) { Serial.println("Powering up sensors..."); - pinMode(POWER_PIN, OUTPUT); - digitalWrite(POWER_PIN, HIGH); + pinMode(powerPin, OUTPUT); + digitalWrite(powerPin, HIGH); delay(200); } diff --git a/examples/j_external_pcint_library/j_external_pcint_library.ino b/examples/j_external_pcint_library/j_external_pcint_library.ino index fd90537..7319d26 100644 --- a/examples/j_external_pcint_library/j_external_pcint_library.ino +++ b/examples/j_external_pcint_library/j_external_pcint_library.ino @@ -19,12 +19,15 @@ #include #include -#define SERIAL_BAUD 115200 /*!< The baud rate for the output serial port */ -#define DATA_PIN 7 /*!< The pin of the SDI-12 data bus */ -#define POWER_PIN 22 /*!< The sensor power pin (or -1 if not switching power) */ +/* connection information */ +uint32_t serialBaud = 115200; /*!< The baud rate for the output serial port */ +int8_t dataPin = 7; /*!< The pin of the SDI-12 data bus */ +int8_t powerPin = 22; /*!< The sensor power pin (or -1 if not switching power) */ +int8_t firstAddress = 0; /* The first address in the address space to check (0='0') */ +int8_t lastAddress = 62; /* The last address in the address space to check (62='z') */ /** Define the SDI-12 bus */ -SDI12 mySDI12(DATA_PIN); +SDI12 mySDI12(dataPin); // keeps track of active addresses bool isActive[64] = { @@ -202,7 +205,7 @@ boolean checkActive(char i) { } void setup() { - Serial.begin(SERIAL_BAUD); + Serial.begin(serialBaud); while (!Serial) ; @@ -214,23 +217,23 @@ void setup() { Serial.println(mySDI12.TIMEOUT); // Power the sensors; - if (POWER_PIN > 0) { + if (powerPin > 0) { Serial.println("Powering up sensors..."); - pinMode(POWER_PIN, OUTPUT); - digitalWrite(POWER_PIN, HIGH); + pinMode(powerPin, OUTPUT); + digitalWrite(powerPin, HIGH); delay(200); } // Enable interrupts for the recieve pin - pinMode(DATA_PIN, INPUT_PULLUP); - enableInterrupt(DATA_PIN, SDI12::handleInterrupt, CHANGE); + pinMode(dataPin, INPUT_PULLUP); + enableInterrupt(dataPin, SDI12::handleInterrupt, CHANGE); // Quickly Scan the Address Space Serial.println("Scanning all addresses, please wait..."); Serial.println("Protocol Version, Sensor Address, Sensor Vendor, Sensor Model, " "Sensor Version, Sensor ID"); - for (byte i = 0; i < 62; i++) { + for (byte i = firstAddress; i < lastAddress; i++) { char addr = decToChar(i); if (checkActive(addr)) { numSensors++; @@ -257,7 +260,7 @@ void setup() { void loop() { // measure one at a time - for (byte i = 0; i < 62; i++) { + for (byte i = firstAddress; i < lastAddress; i++) { char addr = decToChar(i); if (isActive[i]) { Serial.print(millis() / 1000); diff --git a/examples/k_concurrent_logger/k_concurrent_logger.ino b/examples/k_concurrent_logger/k_concurrent_logger.ino index ff383f5..f7da769 100644 --- a/examples/k_concurrent_logger/k_concurrent_logger.ino +++ b/examples/k_concurrent_logger/k_concurrent_logger.ino @@ -16,12 +16,15 @@ #include -#define SERIAL_BAUD 115200 /*!< The baud rate for the output serial port */ -#define DATA_PIN 7 /*!< The pin of the SDI-12 data bus */ -#define POWER_PIN 22 /*!< The sensor power pin (or -1 if not switching power) */ +/* connection information */ +uint32_t serialBaud = 115200; /*!< The baud rate for the output serial port */ +int8_t dataPin = 7; /*!< The pin of the SDI-12 data bus */ +int8_t powerPin = 22; /*!< The sensor power pin (or -1 if not switching power) */ +int8_t firstAddress = 0; /* The first address in the address space to check (0='0') */ +int8_t lastAddress = 62; /* The last address in the address space to check (62='z') */ /** Define the SDI-12 bus */ -SDI12 mySDI12(DATA_PIN); +SDI12 mySDI12(dataPin); // keeps track of active addresses bool isActive[64] = { @@ -205,7 +208,7 @@ boolean checkActive(char i) { } void setup() { - Serial.begin(SERIAL_BAUD); + Serial.begin(serialBaud); while (!Serial) ; @@ -217,11 +220,11 @@ void setup() { Serial.println(mySDI12.TIMEOUT); // Power the sensors; - if (POWER_PIN > 0) { - Serial.println("Powering up sensors..."); - pinMode(POWER_PIN, OUTPUT); - digitalWrite(POWER_PIN, HIGH); - delay(200); + if (powerPin > 0) { + Serial.println("Powering up sensors, wait..."); + pinMode(powerPin, OUTPUT); + digitalWrite(powerPin, HIGH); + delay(10000L); } // Quickly Scan the Address Space @@ -229,7 +232,7 @@ void setup() { Serial.println("Protocol Version, Sensor Address, Sensor Vendor, Sensor Model, " "Sensor Version, Sensor ID"); - for (byte i = 0; i < 62; i++) { + for (byte i = firstAddress; i < lastAddress; i++) { char addr = decToChar(i); if (checkActive(addr)) { numSensors++; @@ -255,7 +258,7 @@ void setup() { void loop() { // start all sensors measuring concurrently - for (byte i = 0; i < 62; i++) { + for (byte i = firstAddress; i < lastAddress; i++) { char addr = decToChar(i); if (isActive[i]) { startConcurrentMeasurement(addr); } } @@ -263,7 +266,7 @@ void loop() { // get all readings uint8_t numReadingsRecorded = 0; while (numReadingsRecorded < numSensors) { - for (byte i = 0; i < 62; i++) { + for (byte i = firstAddress; i < lastAddress; i++) { char addr = decToChar(i); if (isActive[i]) { if (millis() > millisReady[i]) { diff --git a/examples/l_verify_crc/l_verify_crc.ino b/examples/l_verify_crc/l_verify_crc.ino index 12dd357..fa6a3a5 100644 --- a/examples/l_verify_crc/l_verify_crc.ino +++ b/examples/l_verify_crc/l_verify_crc.ino @@ -17,19 +17,20 @@ #include -#define SERIAL_BAUD 115200 /*!< The baud rate for the output serial port */ -#define DATA_PIN 7 /*!< The pin of the SDI-12 data bus */ -#define POWER_PIN 22 /*!< The sensor power pin (or -1 if not switching power) */ -#define SENSOR_ADDRESS 2 +/* connection information */ +uint32_t serialBaud = 115200; /*!< The baud rate for the output serial port */ +int8_t dataPin = 7; /*!< The pin of the SDI-12 data bus */ +int8_t powerPin = 22; /*!< The sensor power pin (or -1 if not switching power) */ +char sensorAddress = '2'; /*!< The address of the SDI-12 sensor */ /** Define the SDI-12 bus */ -SDI12 mySDI12(DATA_PIN); +SDI12 mySDI12(dataPin); String sdiResponse = ""; String myCommand = ""; void setup() { - Serial.begin(SERIAL_BAUD); + Serial.begin(serialBaud); while (!Serial) ; @@ -38,16 +39,16 @@ void setup() { delay(500); // allow things to settle // Power the sensors; - if (POWER_PIN > 0) { + if (powerPin > 0) { Serial.println("Powering up sensors..."); - pinMode(POWER_PIN, OUTPUT); - digitalWrite(POWER_PIN, HIGH); + pinMode(powerPin, OUTPUT); + digitalWrite(powerPin, HIGH); delay(200); } // print out the sensor info String command = ""; - command += String(SENSOR_ADDRESS); + command += String(sensorAddress); command += "I!"; mySDI12.sendCommand(command); Serial.print(">>>"); @@ -77,7 +78,7 @@ void setup() { void loop() { // first command to take a measurement - myCommand = String(SENSOR_ADDRESS) + "MC!"; + myCommand = String(sensorAddress) + "MC!"; Serial.print(">>>"); Serial.println(myCommand); // echo command to terminal @@ -120,7 +121,7 @@ void loop() { // next command to request data from last measurement - myCommand = String(SENSOR_ADDRESS) + "D0!"; + myCommand = String(sensorAddress) + "D0!"; Serial.print(">>>"); Serial.println(myCommand); // echo command to terminal diff --git a/tools/SDI12_spy/SDI12_spy.ino b/tools/SDI12_spy/SDI12_spy.ino index a914eea..b39c3d3 100644 --- a/tools/SDI12_spy/SDI12_spy.ino +++ b/tools/SDI12_spy/SDI12_spy.ino @@ -30,10 +30,10 @@ #include -#define DATA_PIN 7 /*!< The pin of the SDI-12 data bus */ +int8_t dataPin = 7; /*!< The pin of the SDI-12 data bus */ // Create object by which to communicate with the SDI-12 bus on SDIPIN -SDI12 slaveSDI12(DATA_PIN); +SDI12 slaveSDI12(dataPin); void setup() { Serial.begin(115200); diff --git a/tools/TestCommands/TestCommands.ino b/tools/TestCommands/TestCommands.ino index 12ec9aa..40cef66 100644 --- a/tools/TestCommands/TestCommands.ino +++ b/tools/TestCommands/TestCommands.ino @@ -26,17 +26,18 @@ #include -uint32_t SERIAL_BAUD = 115200; /*!< The baud rate for the output serial port */ -uint8_t DATA_PIN = 7; /*!< The pin of the SDI-12 data bus */ -uint8_t POWER_PIN = 22; /*!< The sensor power pin (or -1 if not switching power) */ -int8_t FIRST_ADDRESS = 1; -int8_t LAST_ADDRESS = 6; // 62 -int8_t WAKE_DELAY = 0; /*!< The extra time needed for the board to wake. */ -int8_t COMMANDS_TO_TEST = +/* connection information */ +uint32_t serialBaud = 115200; /*!< The baud rate for the output serial port */ +uint8_t dataPin = 7; /*!< The pin of the SDI-12 data bus */ +uint8_t powerPin = 22; /*!< The sensor power pin (or -1 if not switching power) */ +uint32_t wakeDelay = 0; /*!< Extra time needed for the sensor to wake (0-100ms) */ +int8_t firstAddress = 0; /* The first address in the address space to check (0='0') */ +int8_t lastAddress = 62; /* The last address in the address space to check (62='z') */ +int8_t commandsToTest = 1; /*!< The number of measurement commands to test, between 1 and 11. */ /** Define the SDI-12 bus */ -SDI12 mySDI12(DATA_PIN); +SDI12 mySDI12(dataPin); /// variable that alternates output type back and forth between parsed and raw boolean flip = 0; @@ -101,7 +102,7 @@ void printInfo(char i, bool printCommands = true) { String command = ""; command += (char)i; command += "I!"; - mySDI12.sendCommand(command, WAKE_DELAY); + mySDI12.sendCommand(command, wakeDelay); if (printCommands) { Serial.print(">>>"); Serial.println(command); @@ -144,7 +145,7 @@ bool getResults(char addr, int resultsExpected, bool printCommands = true) { command += "D"; command += cmd_number; command += "!"; // SDI-12 command to get data [address][D][dataOption][!] - mySDI12.sendCommand(command, WAKE_DELAY); + mySDI12.sendCommand(command, wakeDelay); if (printCommands) { Serial.print(">>>"); Serial.println(command); @@ -226,7 +227,7 @@ bool getContinuousResults(char i, int resultsExpected, bool printCommands = true command += "R"; command += cmd_number; command += "!"; // SDI-12 command to get data [address][D][dataOption][!] - mySDI12.sendCommand(command, WAKE_DELAY); + mySDI12.sendCommand(command, wakeDelay); if (printCommands) { Serial.print(">>>"); Serial.println(command); @@ -273,7 +274,7 @@ int startConcurrentMeasurement(char i, String meas_type = "", command += "C"; command += meas_type; command += "!"; // SDI-12 concurrent measurement command format [address]['C'][!] - mySDI12.sendCommand(command, WAKE_DELAY); + mySDI12.sendCommand(command, wakeDelay); if (printCommands) { Serial.print(">>>"); Serial.println(command); @@ -325,7 +326,7 @@ bool takeMeasurement(char i, String meas_type = "", bool printCommands = true) { command += "M"; command += meas_type; command += "!"; // SDI-12 measurement command format [address]['M'][!] - mySDI12.sendCommand(command, WAKE_DELAY); + mySDI12.sendCommand(command, wakeDelay); if (printCommands) { Serial.print(">>>"); Serial.println(command); @@ -393,7 +394,7 @@ boolean checkActive(char i, int8_t numPings = 3, bool printCommands = false) { Serial.print(">>>"); Serial.println(command); } - mySDI12.sendCommand(command, WAKE_DELAY); + mySDI12.sendCommand(command, wakeDelay); delay(100); if (mySDI12.available()) { // If we here anything, assume we have an active sensor if (printCommands) { @@ -413,7 +414,7 @@ boolean checkActive(char i, int8_t numPings = 3, bool printCommands = false) { } void setup() { - Serial.begin(SERIAL_BAUD); + Serial.begin(serialBaud); while (!Serial) ; @@ -424,7 +425,7 @@ void setup() { Serial.println("Timeout value: "); Serial.println(mySDI12.TIMEOUT); - for (int8_t i = 0; i < 62; i++) { + for (int8_t i = firstAddress; i < lastAddress; i++) { isActive[i] = false; meas_time_ms[i] = 0; millisStarted[i] = 0; @@ -435,19 +436,19 @@ void setup() { } // Power the sensors; - if (POWER_PIN > 0) { + if (powerPin > 0) { Serial.println("Powering down sensors..."); - pinMode(POWER_PIN, OUTPUT); - digitalWrite(POWER_PIN, LOW); + pinMode(powerPin, OUTPUT); + digitalWrite(powerPin, LOW); // delay(2500L); delay(250L); } // Power the sensors; - if (POWER_PIN > 0) { + if (powerPin > 0) { Serial.println("Powering up sensors, wait..."); - pinMode(POWER_PIN, OUTPUT); - digitalWrite(POWER_PIN, HIGH); + pinMode(powerPin, OUTPUT); + digitalWrite(powerPin, HIGH); // delay(10000L); delay(125); } @@ -455,7 +456,7 @@ void setup() { // Quickly Scan the Address Space Serial.println("Scanning all addresses, please wait..."); - for (int8_t i = FIRST_ADDRESS; i <= LAST_ADDRESS; i++) { + for (int8_t i = firstAddress; i <= lastAddress; i++) { char addr = decToChar(i); Serial.print("i: "); Serial.print(i); @@ -497,27 +498,27 @@ void loop() { // Serial.println(flip); // // Power the sensors; - // if (POWER_PIN > 0) { + // if (powerPin > 0) { // Serial.println("Powering down sensors..."); - // pinMode(POWER_PIN, OUTPUT); - // digitalWrite(POWER_PIN, LOW); + // pinMode(powerPin, OUTPUT); + // digitalWrite(powerPin, LOW); // delay(5000L); // } // // Power the sensors; - // if (POWER_PIN > 0) { + // if (powerPin > 0) { // Serial.println("Powering up sensors..."); - // pinMode(POWER_PIN, OUTPUT); - // digitalWrite(POWER_PIN, HIGH); + // pinMode(powerPin, OUTPUT); + // digitalWrite(powerPin, HIGH); // delay(125); // } if (flip) { // measure one at a time - for (int8_t i = 0; i < 62; i++) { + for (int8_t i = firstAddress; i < lastAddress; i++) { char addr = decToChar(i); if (isActive[i]) { - for (uint8_t a = 0; a < COMMANDS_TO_TEST; a++) { + for (uint8_t a = 0; a < commandsToTest; a++) { Serial.print("Command "); Serial.print(i); Serial.print("M"); @@ -536,12 +537,12 @@ void loop() { Serial.print("Total Time for Individual Measurements: "); Serial.println(millis() - start); } else { - for (uint8_t a = 0; a < COMMANDS_TO_TEST; a++) { + for (uint8_t a = 0; a < commandsToTest; a++) { uint32_t min_wait = 60000L; uint32_t max_wait = 0; uint32_t for_start = millis(); // start all sensors measuring concurrently - for (int8_t i = 0; i < 62; i++) { + for (int8_t i = firstAddress; i < lastAddress; i++) { char addr = decToChar(i); if (isActive[i]) { Serial.print("Command "); @@ -571,7 +572,7 @@ void loop() { delay(min_wait); do { // get all readings - for (int8_t i = 0; i < 62; i++) { + for (int8_t i = firstAddress; i < lastAddress; i++) { uint32_t timeWaited = millis() - millisStarted[i]; if (this_result[i] != "") { prev_result[i] = this_result[i]; } diff --git a/tools/TestWarmUp/TestWarmUp.ino b/tools/TestWarmUp/TestWarmUp.ino index 60104ad..5faca7c 100644 --- a/tools/TestWarmUp/TestWarmUp.ino +++ b/tools/TestWarmUp/TestWarmUp.ino @@ -9,13 +9,14 @@ #include -#define SERIAL_BAUD 115200 /*!< The baud rate for the output serial port */ -#define DATA_PIN 7 /*!< The pin of the SDI-12 data bus */ -#define SENSOR_ADDRESS '2' /*!< The address of the SDI-12 sensor */ -#define POWER_PIN 22 /*!< The sensor power pin (or -1 if not switching power) */ +/* connection information */ +uint32_t serialBaud = 115200; /*!< The baud rate for the output serial port */ +int8_t dataPin = 7; /*!< The pin of the SDI-12 data bus */ +char sensorAddress = '\1'; /*!< The address of the SDI-12 sensor */ +int8_t powerPin = 22; /*!< The sensor power pin (or -1 if not switching power) */ /** Define the SDI-12 bus */ -SDI12 mySDI12(DATA_PIN); +SDI12 mySDI12(dataPin); int32_t min_wake_delay = 0; /*!< The min time to test wake after a line break. */ int32_t increment_wake = 100; /*!< The time to lengthen waits between reps. */ int32_t max_wake_delay = 100; /*!< The max time to test wake (should be <=100). */ @@ -101,7 +102,7 @@ boolean checkActive(char i, int8_t numPings = 3, bool printCommands = false) { } void setup() { - Serial.begin(SERIAL_BAUD); + Serial.begin(serialBaud); while (!Serial) ; @@ -115,28 +116,28 @@ void setup() { void loop() { // Power the sensors; - if (POWER_PIN > 0) { + if (powerPin > 0) { Serial.println("Powering down sensors..."); - pinMode(POWER_PIN, OUTPUT); - digitalWrite(POWER_PIN, LOW); - delay(2500L); + pinMode(powerPin, OUTPUT); + digitalWrite(powerPin, LOW); + delay(10000L); } // Power the sensors; - if (POWER_PIN > 0) { + if (powerPin > 0) { Serial.println("Powering up sensors..."); - pinMode(POWER_PIN, OUTPUT); - digitalWrite(POWER_PIN, HIGH); + pinMode(powerPin, OUTPUT); + digitalWrite(powerPin, HIGH); delay(power_delay); } - if (checkActive(SENSOR_ADDRESS, 5, true)) { + if (checkActive(sensorAddress, 5, true)) { Serial.print("Got some response after "); Serial.print(power_delay); Serial.print("ms after power with "); Serial.print(wake_delay); Serial.println("ms with wake delay"); - if (printInfo(SENSOR_ADDRESS, true)) { + if (printInfo(sensorAddress, true)) { // if we got sensor info, stop Serial.println("Looks good. Stopping."); while (1) diff --git a/tools/printCommands/printCommands.ino b/tools/printCommands/printCommands.ino index b8690fd..a4c40a5 100644 --- a/tools/printCommands/printCommands.ino +++ b/tools/printCommands/printCommands.ino @@ -26,17 +26,18 @@ #include -uint32_t SERIAL_BAUD = 115200; /*!< The baud rate for the output serial port */ -uint8_t DATA_PIN = 7; /*!< The pin of the SDI-12 data bus */ -uint8_t POWER_PIN = 22; /*!< The sensor power pin (or -1 if not switching power) */ -int8_t FIRST_ADDRESS = 1; -int8_t LAST_ADDRESS = 6; // 62 -int8_t WAKE_DELAY = 0; /*!< The extra time needed for the board to wake. */ -int8_t COMMANDS_TO_TEST = +/* connection information */ +uint32_t serialBaud = 115200; /*!< The baud rate for the output serial port */ +uint8_t dataPin = 7; /*!< The pin of the SDI-12 data bus */ +uint8_t powerPin = 22; /*!< The sensor power pin (or -1 if not switching power) */ +uint32_t wakeDelay = 0; /*!< Extra time needed for the sensor to wake (0-100ms) */ +int8_t firstAddress = 0; /* The first address in the address space to check (0='0') */ +int8_t lastAddress = 62; /* The last address in the address space to check (62='z') */ +int8_t commandsToTest = 1; /*!< The number of measurement commands to test, between 1 and 11. */ /** Define the SDI-12 bus */ -SDI12 mySDI12(DATA_PIN); +SDI12 mySDI12(dataPin); /// variable that alternates output type back and forth between parsed and raw boolean flip = 0; @@ -101,7 +102,7 @@ void printInfo(char i) { String command = ""; command += (char)i; command += "I!"; - mySDI12.sendCommand(command, WAKE_DELAY); + mySDI12.sendCommand(command, wakeDelay); Serial.print("\n>>>"); Serial.println(command); delay(15); @@ -126,7 +127,7 @@ bool getResults(char addr, int resultsExpected) { command += "D"; command += cmd_number; command += "!"; // SDI-12 command to get data [address][D][dataOption][!] - mySDI12.sendCommand(command, WAKE_DELAY); + mySDI12.sendCommand(command, wakeDelay); Serial.print("\n>>>"); Serial.println(command); @@ -173,7 +174,7 @@ bool getContinuousResults(char i, int resultsExpected) { command += "R"; command += cmd_number; command += "!"; // SDI-12 command to get data [address][D][dataOption][!] - mySDI12.sendCommand(command, WAKE_DELAY); + mySDI12.sendCommand(command, wakeDelay); Serial.print("\n>>>"); Serial.println(command); @@ -210,7 +211,7 @@ int startConcurrentMeasurement(char i, String meas_type = "") { command += "C"; command += meas_type; command += "!"; // SDI-12 concurrent measurement command format [address]['C'][!] - mySDI12.sendCommand(command, WAKE_DELAY); + mySDI12.sendCommand(command, wakeDelay); Serial.print("\n>>>"); Serial.println(command); delay(5); @@ -248,7 +249,7 @@ bool takeMeasurement(char i, String meas_type = "") { command += "M"; command += meas_type; command += "!"; // SDI-12 measurement command format [address]['M'][!] - mySDI12.sendCommand(command, WAKE_DELAY); + mySDI12.sendCommand(command, wakeDelay); Serial.print("\n>>>"); Serial.println(command); delay(15); @@ -294,7 +295,7 @@ boolean checkActive(char i, int8_t numPings = 3) { for (int j = 0; j < numPings; j++) { // goes through three rapid contact attempts Serial.print("\n>>>"); Serial.println(command); - mySDI12.sendCommand(command, WAKE_DELAY); + mySDI12.sendCommand(command, wakeDelay); delay(15); if (mySDI12.available()) { // If we here anything, assume we have an active sensor Serial.print("\n<<<"); @@ -312,7 +313,7 @@ boolean checkActive(char i, int8_t numPings = 3) { } void setup() { - Serial.begin(SERIAL_BAUD); + Serial.begin(serialBaud); while (!Serial) ; @@ -323,7 +324,7 @@ void setup() { Serial.println("Timeout value: "); Serial.println(mySDI12.TIMEOUT); - for (int8_t i = 0; i < 62; i++) { + for (int8_t i = firstAddress; i < lastAddress; i++) { isActive[i] = false; meas_time_ms[i] = 0; millisStarted[i] = 0; @@ -334,19 +335,19 @@ void setup() { } // Power the sensors; - if (POWER_PIN > 0) { + if (powerPin > 0) { Serial.println("Powering down sensors..."); - pinMode(POWER_PIN, OUTPUT); - digitalWrite(POWER_PIN, LOW); + pinMode(powerPin, OUTPUT); + digitalWrite(powerPin, LOW); // delay(2500L); delay(250L); } // Power the sensors; - if (POWER_PIN > 0) { + if (powerPin > 0) { Serial.println("Powering up sensors, wait..."); - pinMode(POWER_PIN, OUTPUT); - digitalWrite(POWER_PIN, HIGH); + pinMode(powerPin, OUTPUT); + digitalWrite(powerPin, HIGH); // delay(10000L); delay(125); } @@ -354,7 +355,7 @@ void setup() { // Quickly Scan the Address Space Serial.println("Scanning all addresses, please wait..."); - for (int8_t i = FIRST_ADDRESS; i <= LAST_ADDRESS; i++) { + for (int8_t i = firstAddress; i <= lastAddress; i++) { char addr = decToChar(i); if (checkActive(addr, 5)) { numSensors++; @@ -382,11 +383,11 @@ void setup() { void loop() { uint32_t start = millis(); // measure one at a time - for (int8_t i = 0; i < 62; i++) { + for (int8_t i = firstAddress; i < lastAddress; i++) { char addr = decToChar(i); if (isActive[i]) { printInfo(addr); - for (uint8_t a = 0; a < COMMANDS_TO_TEST; a++) { + for (uint8_t a = 0; a < commandsToTest; a++) { takeMeasurement(addr, commands[a]); } Serial.println(); From aaeb9f55478010e0755ff3ecdfa7db4bead1e976 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Mon, 1 Apr 2024 11:38:16 -0400 Subject: [PATCH 29/96] BREAKING CHANGE: Change output of CRC verify Signed-off-by: Sara Damiano --- src/SDI12.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SDI12.cpp b/src/SDI12.cpp index 60b82ee..21ecb1e 100644 --- a/src/SDI12.cpp +++ b/src/SDI12.cpp @@ -652,9 +652,9 @@ bool SDI12::verifyCRC(String& respWithCRC) { String calcCRC = crcToString(calculateCRC(recString)); if (recCRC == calcCRC) { - return false; - } else { return true; + } else { + return false; } } From b2352537ffe9e4d8bdff956431248228bfb66650 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Mon, 1 Apr 2024 11:40:43 -0400 Subject: [PATCH 30/96] Fix some stray slashes Signed-off-by: Sara Damiano --- examples/f_basic_data_request/f_basic_data_request.ino | 2 +- tools/TestWarmUp/TestWarmUp.ino | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/f_basic_data_request/f_basic_data_request.ino b/examples/f_basic_data_request/f_basic_data_request.ino index 3d7b5b5..deb1239 100644 --- a/examples/f_basic_data_request/f_basic_data_request.ino +++ b/examples/f_basic_data_request/f_basic_data_request.ino @@ -21,7 +21,7 @@ uint32_t serialBaud = 115200; /*!< The baud rate for the output serial port */ int8_t dataPin = 7; /*!< The pin of the SDI-12 data bus */ int8_t powerPin = 22; /*!< The sensor power pin (or -1 if not switching power) */ -char sensorAddress = '\1'; /*!< The address of the SDI-12 sensor */ +char sensorAddress = '1'; /*!< The address of the SDI-12 sensor */ /** Define the SDI-12 bus */ SDI12 mySDI12(dataPin); diff --git a/tools/TestWarmUp/TestWarmUp.ino b/tools/TestWarmUp/TestWarmUp.ino index 5faca7c..7728316 100644 --- a/tools/TestWarmUp/TestWarmUp.ino +++ b/tools/TestWarmUp/TestWarmUp.ino @@ -12,7 +12,7 @@ /* connection information */ uint32_t serialBaud = 115200; /*!< The baud rate for the output serial port */ int8_t dataPin = 7; /*!< The pin of the SDI-12 data bus */ -char sensorAddress = '\1'; /*!< The address of the SDI-12 sensor */ +char sensorAddress = '1'; /*!< The address of the SDI-12 sensor */ int8_t powerPin = 22; /*!< The sensor power pin (or -1 if not switching power) */ /** Define the SDI-12 bus */ From 561dd430b7a2d8c0bc8b9e870c60dcf59b166921 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Mon, 1 Apr 2024 14:17:46 -0400 Subject: [PATCH 31/96] Allow power pin to be 0 in examples Signed-off-by: Sara Damiano --- examples/a_wild_card/a_wild_card.ino | 2 +- examples/b_address_change/b_address_change.ino | 2 +- .../c_check_all_addresses/c_check_all_addresses.ino | 2 +- examples/d_simple_logger/d_simple_logger.ino | 2 +- .../e_continuous_measurement.ino | 2 +- examples/f_basic_data_request/f_basic_data_request.ino | 2 +- examples/g_terminal_window/g_terminal_window.ino | 2 +- examples/i_SDI-12_interface/i_SDI-12_interface.ino | 2 +- .../j_external_pcint_library.ino | 2 +- examples/k_concurrent_logger/k_concurrent_logger.ino | 2 +- examples/l_verify_crc/l_verify_crc.ino | 2 +- tools/TestCommands/TestCommands.ino | 10 +++++----- tools/TestWarmUp/TestWarmUp.ino | 4 ++-- tools/printCommands/printCommands.ino | 4 ++-- 14 files changed, 20 insertions(+), 20 deletions(-) diff --git a/examples/a_wild_card/a_wild_card.ino b/examples/a_wild_card/a_wild_card.ino index e8efe55..7fbe76b 100644 --- a/examples/a_wild_card/a_wild_card.ino +++ b/examples/a_wild_card/a_wild_card.ino @@ -41,7 +41,7 @@ void setup() { delay(500); // allow things to settle // Power the sensors; - if (powerPin > 0) { + if (powerPin >= 0) { Serial.println("Powering up sensors..."); pinMode(powerPin, OUTPUT); digitalWrite(powerPin, HIGH); diff --git a/examples/b_address_change/b_address_change.ino b/examples/b_address_change/b_address_change.ino index eadb0bf..add0878 100644 --- a/examples/b_address_change/b_address_change.ino +++ b/examples/b_address_change/b_address_change.ino @@ -59,7 +59,7 @@ void setup() { delay(500); // allow things to settle // Power the sensors; - if (powerPin > 0) { + if (powerPin >= 0) { Serial.println("Powering up sensors..."); pinMode(powerPin, OUTPUT); digitalWrite(powerPin, HIGH); diff --git a/examples/c_check_all_addresses/c_check_all_addresses.ino b/examples/c_check_all_addresses/c_check_all_addresses.ino index 8e0412d..7203ab2 100644 --- a/examples/c_check_all_addresses/c_check_all_addresses.ino +++ b/examples/c_check_all_addresses/c_check_all_addresses.ino @@ -89,7 +89,7 @@ void setup() { Serial.println("//\n// Start Search for SDI-12 Devices \n// -----------------------"); // Power the sensors; - if (powerPin > 0) { + if (powerPin >= 0) { Serial.println("Powering up sensors..."); pinMode(powerPin, OUTPUT); digitalWrite(powerPin, HIGH); diff --git a/examples/d_simple_logger/d_simple_logger.ino b/examples/d_simple_logger/d_simple_logger.ino index 1901962..1dcbe3b 100644 --- a/examples/d_simple_logger/d_simple_logger.ino +++ b/examples/d_simple_logger/d_simple_logger.ino @@ -227,7 +227,7 @@ void setup() { Serial.println(mySDI12.TIMEOUT); // Power the sensors; - if (powerPin > 0) { + if (powerPin >= 0) { Serial.println("Powering up sensors, wait..."); pinMode(powerPin, OUTPUT); digitalWrite(powerPin, HIGH); diff --git a/examples/e_continuous_measurement/e_continuous_measurement.ino b/examples/e_continuous_measurement/e_continuous_measurement.ino index 6cee1f8..2026973 100644 --- a/examples/e_continuous_measurement/e_continuous_measurement.ino +++ b/examples/e_continuous_measurement/e_continuous_measurement.ino @@ -165,7 +165,7 @@ void setup() { Serial.println(mySDI12.TIMEOUT); // Power the sensors; - if (powerPin > 0) { + if (powerPin >= 0) { Serial.println("Powering up sensors, wait..."); pinMode(powerPin, OUTPUT); digitalWrite(powerPin, HIGH); diff --git a/examples/f_basic_data_request/f_basic_data_request.ino b/examples/f_basic_data_request/f_basic_data_request.ino index deb1239..86ed163 100644 --- a/examples/f_basic_data_request/f_basic_data_request.ino +++ b/examples/f_basic_data_request/f_basic_data_request.ino @@ -39,7 +39,7 @@ void setup() { delay(500); // allow things to settle // Power the sensors; - if (powerPin > 0) { + if (powerPin >= 0) { Serial.println("Powering up sensors..."); pinMode(powerPin, OUTPUT); digitalWrite(powerPin, HIGH); diff --git a/examples/g_terminal_window/g_terminal_window.ino b/examples/g_terminal_window/g_terminal_window.ino index 7f1b689..0268349 100644 --- a/examples/g_terminal_window/g_terminal_window.ino +++ b/examples/g_terminal_window/g_terminal_window.ino @@ -40,7 +40,7 @@ void setup() { delay(500); // allow things to settle // Power the sensors; - if (powerPin > 0) { + if (powerPin >= 0) { Serial.println("Powering up sensors..."); pinMode(powerPin, OUTPUT); digitalWrite(powerPin, HIGH); diff --git a/examples/i_SDI-12_interface/i_SDI-12_interface.ino b/examples/i_SDI-12_interface/i_SDI-12_interface.ino index 9b97659..1ad7e0f 100644 --- a/examples/i_SDI-12_interface/i_SDI-12_interface.ino +++ b/examples/i_SDI-12_interface/i_SDI-12_interface.ino @@ -61,7 +61,7 @@ void setup() { ; // Power the sensors; - if (powerPin > 0) { + if (powerPin >= 0) { Serial.println("Powering up sensors..."); pinMode(powerPin, OUTPUT); digitalWrite(powerPin, HIGH); diff --git a/examples/j_external_pcint_library/j_external_pcint_library.ino b/examples/j_external_pcint_library/j_external_pcint_library.ino index 7319d26..b801f0e 100644 --- a/examples/j_external_pcint_library/j_external_pcint_library.ino +++ b/examples/j_external_pcint_library/j_external_pcint_library.ino @@ -217,7 +217,7 @@ void setup() { Serial.println(mySDI12.TIMEOUT); // Power the sensors; - if (powerPin > 0) { + if (powerPin >= 0) { Serial.println("Powering up sensors..."); pinMode(powerPin, OUTPUT); digitalWrite(powerPin, HIGH); diff --git a/examples/k_concurrent_logger/k_concurrent_logger.ino b/examples/k_concurrent_logger/k_concurrent_logger.ino index f7da769..54e58fd 100644 --- a/examples/k_concurrent_logger/k_concurrent_logger.ino +++ b/examples/k_concurrent_logger/k_concurrent_logger.ino @@ -220,7 +220,7 @@ void setup() { Serial.println(mySDI12.TIMEOUT); // Power the sensors; - if (powerPin > 0) { + if (powerPin >= 0) { Serial.println("Powering up sensors, wait..."); pinMode(powerPin, OUTPUT); digitalWrite(powerPin, HIGH); diff --git a/examples/l_verify_crc/l_verify_crc.ino b/examples/l_verify_crc/l_verify_crc.ino index fa6a3a5..5ee4065 100644 --- a/examples/l_verify_crc/l_verify_crc.ino +++ b/examples/l_verify_crc/l_verify_crc.ino @@ -39,7 +39,7 @@ void setup() { delay(500); // allow things to settle // Power the sensors; - if (powerPin > 0) { + if (powerPin >= 0) { Serial.println("Powering up sensors..."); pinMode(powerPin, OUTPUT); digitalWrite(powerPin, HIGH); diff --git a/tools/TestCommands/TestCommands.ino b/tools/TestCommands/TestCommands.ino index 40cef66..a4b112d 100644 --- a/tools/TestCommands/TestCommands.ino +++ b/tools/TestCommands/TestCommands.ino @@ -29,7 +29,7 @@ /* connection information */ uint32_t serialBaud = 115200; /*!< The baud rate for the output serial port */ uint8_t dataPin = 7; /*!< The pin of the SDI-12 data bus */ -uint8_t powerPin = 22; /*!< The sensor power pin (or -1 if not switching power) */ +int8_t powerPin = 22; /*!< The sensor power pin (or -1 if not switching power) */ uint32_t wakeDelay = 0; /*!< Extra time needed for the sensor to wake (0-100ms) */ int8_t firstAddress = 0; /* The first address in the address space to check (0='0') */ int8_t lastAddress = 62; /* The last address in the address space to check (62='z') */ @@ -436,7 +436,7 @@ void setup() { } // Power the sensors; - if (powerPin > 0) { + if (powerPin >= 0) { Serial.println("Powering down sensors..."); pinMode(powerPin, OUTPUT); digitalWrite(powerPin, LOW); @@ -445,7 +445,7 @@ void setup() { } // Power the sensors; - if (powerPin > 0) { + if (powerPin >= 0) { Serial.println("Powering up sensors, wait..."); pinMode(powerPin, OUTPUT); digitalWrite(powerPin, HIGH); @@ -498,7 +498,7 @@ void loop() { // Serial.println(flip); // // Power the sensors; - // if (powerPin > 0) { + // if (powerPin >= 0) { // Serial.println("Powering down sensors..."); // pinMode(powerPin, OUTPUT); // digitalWrite(powerPin, LOW); @@ -506,7 +506,7 @@ void loop() { // } // // Power the sensors; - // if (powerPin > 0) { + // if (powerPin >= 0) { // Serial.println("Powering up sensors..."); // pinMode(powerPin, OUTPUT); // digitalWrite(powerPin, HIGH); diff --git a/tools/TestWarmUp/TestWarmUp.ino b/tools/TestWarmUp/TestWarmUp.ino index 7728316..4fd70a2 100644 --- a/tools/TestWarmUp/TestWarmUp.ino +++ b/tools/TestWarmUp/TestWarmUp.ino @@ -116,7 +116,7 @@ void setup() { void loop() { // Power the sensors; - if (powerPin > 0) { + if (powerPin >= 0) { Serial.println("Powering down sensors..."); pinMode(powerPin, OUTPUT); digitalWrite(powerPin, LOW); @@ -124,7 +124,7 @@ void loop() { } // Power the sensors; - if (powerPin > 0) { + if (powerPin >= 0) { Serial.println("Powering up sensors..."); pinMode(powerPin, OUTPUT); digitalWrite(powerPin, HIGH); diff --git a/tools/printCommands/printCommands.ino b/tools/printCommands/printCommands.ino index a4c40a5..bfdfba6 100644 --- a/tools/printCommands/printCommands.ino +++ b/tools/printCommands/printCommands.ino @@ -335,7 +335,7 @@ void setup() { } // Power the sensors; - if (powerPin > 0) { + if (powerPin >= 0) { Serial.println("Powering down sensors..."); pinMode(powerPin, OUTPUT); digitalWrite(powerPin, LOW); @@ -344,7 +344,7 @@ void setup() { } // Power the sensors; - if (powerPin > 0) { + if (powerPin >= 0) { Serial.println("Powering up sensors, wait..."); pinMode(powerPin, OUTPUT); digitalWrite(powerPin, HIGH); From 83f6ac90adc9ebeebabf116f2e69f21998120cfa Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Mon, 1 Apr 2024 14:21:04 -0400 Subject: [PATCH 32/96] Remove year in examples Signed-off-by: Sara Damiano --- examples/a_wild_card/a_wild_card.ino | 5 ++--- examples/b_address_change/b_address_change.ino | 5 ++--- examples/c_check_all_addresses/c_check_all_addresses.ino | 5 ++--- examples/d_simple_logger/d_simple_logger.ino | 5 ++--- .../e_continuous_measurement/e_continuous_measurement.ino | 5 ++--- examples/g_terminal_window/g_terminal_window.ino | 5 ++--- .../h_SDI-12_slave_implementation.ino | 5 ++--- examples/i_SDI-12_interface/i_SDI-12_interface.ino | 5 ++--- .../j_external_pcint_library/j_external_pcint_library.ino | 5 ++--- examples/k_concurrent_logger/k_concurrent_logger.ino | 5 ++--- examples/l_verify_crc/l_verify_crc.ino | 5 ++--- tools/SDI12_spy/SDI12_spy.ino | 5 ++--- tools/TestCommands/TestCommands.ino | 5 ++--- tools/TestWarmUp/TestWarmUp.ino | 5 ++--- tools/printCommands/printCommands.ino | 5 ++--- 15 files changed, 30 insertions(+), 45 deletions(-) diff --git a/examples/a_wild_card/a_wild_card.ino b/examples/a_wild_card/a_wild_card.ino index 7fbe76b..613c97f 100644 --- a/examples/a_wild_card/a_wild_card.ino +++ b/examples/a_wild_card/a_wild_card.ino @@ -1,9 +1,8 @@ /** * @file a_wild_card.ino - * @copyright (c) 2013-2020 Stroud Water Research Center (SWRC) - * and the EnviroDIY Development Team - * This example is published under the BSD-3 license. + * @copyright Stroud Water Research Center + * This example is published under the BSD-3 license. * @author Kevin M.Smith * @date August 2013 * diff --git a/examples/b_address_change/b_address_change.ino b/examples/b_address_change/b_address_change.ino index add0878..a358112 100644 --- a/examples/b_address_change/b_address_change.ino +++ b/examples/b_address_change/b_address_change.ino @@ -1,8 +1,7 @@ /** * @file b_address_change.ino - * @copyright (c) 2013-2020 Stroud Water Research Center (SWRC) - * and the EnviroDIY Development Team - * This example is published under the BSD-3 license. + * @copyright Stroud Water Research Center + * This example is published under the BSD-3 license. * @author Kevin M.Smith * @date August 2013 * diff --git a/examples/c_check_all_addresses/c_check_all_addresses.ino b/examples/c_check_all_addresses/c_check_all_addresses.ino index 7203ab2..78a73e6 100644 --- a/examples/c_check_all_addresses/c_check_all_addresses.ino +++ b/examples/c_check_all_addresses/c_check_all_addresses.ino @@ -1,8 +1,7 @@ /** * @file c_check_all_addresses.ino - * @copyright (c) 2013-2020 Stroud Water Research Center (SWRC) - * and the EnviroDIY Development Team - * This example is published under the BSD-3 license. + * @copyright Stroud Water Research Center + * This example is published under the BSD-3 license. * @author Kevin M.Smith * @date August 2013 * diff --git a/examples/d_simple_logger/d_simple_logger.ino b/examples/d_simple_logger/d_simple_logger.ino index 1dcbe3b..a6ac0d1 100644 --- a/examples/d_simple_logger/d_simple_logger.ino +++ b/examples/d_simple_logger/d_simple_logger.ino @@ -1,8 +1,7 @@ /** * @file d_simple_logger.ino - * @copyright (c) 2013-2020 Stroud Water Research Center (SWRC) - * and the EnviroDIY Development Team - * This example is published under the BSD-3 license. + * @copyright Stroud Water Research Center + * This example is published under the BSD-3 license. * @author Kevin M.Smith * @date August 2013 * diff --git a/examples/e_continuous_measurement/e_continuous_measurement.ino b/examples/e_continuous_measurement/e_continuous_measurement.ino index 2026973..d6e0ffb 100644 --- a/examples/e_continuous_measurement/e_continuous_measurement.ino +++ b/examples/e_continuous_measurement/e_continuous_measurement.ino @@ -1,8 +1,7 @@ /** * @file d_simple_logger.ino - * @copyright (c) 2013-2020 Stroud Water Research Center (SWRC) - * and the EnviroDIY Development Team - * This example is published under the BSD-3 license. + * @copyright Stroud Water Research Center + * This example is published under the BSD-3 license. * @author Kevin M.Smith * @date August 2013 * diff --git a/examples/g_terminal_window/g_terminal_window.ino b/examples/g_terminal_window/g_terminal_window.ino index 0268349..a18f279 100644 --- a/examples/g_terminal_window/g_terminal_window.ino +++ b/examples/g_terminal_window/g_terminal_window.ino @@ -1,8 +1,7 @@ /** * @file g_terminal_window.ino - * @copyright (c) 2013-2020 Stroud Water Research Center (SWRC) - * and the EnviroDIY Development Team - * This example is published under the BSD-3 license. + * @copyright Stroud Water Research Center + * This example is published under the BSD-3 license. * @author Kevin M.Smith * @date August 2013 * @author Ruben Kertesz or @rinnamon on twitter diff --git a/examples/h_SDI-12_slave_implementation/h_SDI-12_slave_implementation.ino b/examples/h_SDI-12_slave_implementation/h_SDI-12_slave_implementation.ino index 4faee42..1cd2b3b 100644 --- a/examples/h_SDI-12_slave_implementation/h_SDI-12_slave_implementation.ino +++ b/examples/h_SDI-12_slave_implementation/h_SDI-12_slave_implementation.ino @@ -1,8 +1,7 @@ /** * @file h_SDI-12_slave_implementation.ino - * @copyright (c) 2013-2020 Stroud Water Research Center (SWRC) - * and the EnviroDIY Development Team - * This example is published under the BSD-3 license. + * @copyright Stroud Water Research Center + * This example is published under the BSD-3 license. * @date 2016 * @author D. Wasielewski * diff --git a/examples/i_SDI-12_interface/i_SDI-12_interface.ino b/examples/i_SDI-12_interface/i_SDI-12_interface.ino index 1ad7e0f..54b3a7a 100644 --- a/examples/i_SDI-12_interface/i_SDI-12_interface.ino +++ b/examples/i_SDI-12_interface/i_SDI-12_interface.ino @@ -1,8 +1,7 @@ /** * @file h_SDI-12_slave_implementation.ino - * @copyright (c) 2013-2020 Stroud Water Research Center (SWRC) - * and the EnviroDIY Development Team - * This example is published under the BSD-3 license. + * @copyright Stroud Water Research Center + * This example is published under the BSD-3 license. * @date 2016 * @author D. Wasielewski * diff --git a/examples/j_external_pcint_library/j_external_pcint_library.ino b/examples/j_external_pcint_library/j_external_pcint_library.ino index b801f0e..8e02f3b 100644 --- a/examples/j_external_pcint_library/j_external_pcint_library.ino +++ b/examples/j_external_pcint_library/j_external_pcint_library.ino @@ -1,8 +1,7 @@ /** * @file j_external_pcint_library.ino - * @copyright (c) 2013-2020 Stroud Water Research Center (SWRC) - * and the EnviroDIY Development Team - * This example is published under the BSD-3 license. + * @copyright Stroud Water Research Center + * This example is published under the BSD-3 license. * @author Kevin M.Smith * * @brief Example J: Using External Interrupts diff --git a/examples/k_concurrent_logger/k_concurrent_logger.ino b/examples/k_concurrent_logger/k_concurrent_logger.ino index 54e58fd..7929b0d 100644 --- a/examples/k_concurrent_logger/k_concurrent_logger.ino +++ b/examples/k_concurrent_logger/k_concurrent_logger.ino @@ -1,8 +1,7 @@ /** * @file k_concurrent_logger.ino - * @copyright (c) 2013-2020 Stroud Water Research Center (SWRC) - * and the EnviroDIY Development Team - * This example is published under the BSD-3 license. + * @copyright Stroud Water Research Center + * This example is published under the BSD-3 license. * @author Sara Geleskie Damiano * * @brief Example K: Concurrent Measurements diff --git a/examples/l_verify_crc/l_verify_crc.ino b/examples/l_verify_crc/l_verify_crc.ino index 5ee4065..15d1459 100644 --- a/examples/l_verify_crc/l_verify_crc.ino +++ b/examples/l_verify_crc/l_verify_crc.ino @@ -1,8 +1,7 @@ /** * @file f_basic_data_request.ino - * @copyright (c) 2013-2020 Stroud Water Research Center (SWRC) - * and the EnviroDIY Development Team - * This example is published under the BSD-3 license. + * @copyright Stroud Water Research Center + * This example is published under the BSD-3 license. * @author Ruben Kertesz or @rinnamon on twitter * @date 2/10/2016 * diff --git a/tools/SDI12_spy/SDI12_spy.ino b/tools/SDI12_spy/SDI12_spy.ino index b39c3d3..b02540f 100644 --- a/tools/SDI12_spy/SDI12_spy.ino +++ b/tools/SDI12_spy/SDI12_spy.ino @@ -1,8 +1,7 @@ /** * @file h_SDI-12_slave_implementation.ino - * @copyright (c) 2013-2020 Stroud Water Research Center (SWRC) - * and the EnviroDIY Development Team - * This example is published under the BSD-3 license. + * @copyright Stroud Water Research Center + * This example is published under the BSD-3 license. * @date 2016 * @author D. Wasielewski * diff --git a/tools/TestCommands/TestCommands.ino b/tools/TestCommands/TestCommands.ino index a4b112d..8db31ab 100644 --- a/tools/TestCommands/TestCommands.ino +++ b/tools/TestCommands/TestCommands.ino @@ -1,8 +1,7 @@ /** * @file d_simple_logger.ino - * @copyright (c) 2013-2020 Stroud Water Research Center (SWRC) - * and the EnviroDIY Development Team - * This example is published under the BSD-3 license. + * @copyright Stroud Water Research Center + * This example is published under the BSD-3 license. * @author Kevin M.Smith * @date August 2013 * diff --git a/tools/TestWarmUp/TestWarmUp.ino b/tools/TestWarmUp/TestWarmUp.ino index 4fd70a2..e0a8f89 100644 --- a/tools/TestWarmUp/TestWarmUp.ino +++ b/tools/TestWarmUp/TestWarmUp.ino @@ -1,8 +1,7 @@ /** * @file TestWarmUp.ino - * @copyright (c) 2013-2020 Stroud Water Research Center (SWRC) - * and the EnviroDIY Development Team - * This example is published under the BSD-3 license. + * @copyright Stroud Water Research Center + * This example is published under the BSD-3 license. * @author Sara Damiano * @date March 2021 */ diff --git a/tools/printCommands/printCommands.ino b/tools/printCommands/printCommands.ino index bfdfba6..f07c5f1 100644 --- a/tools/printCommands/printCommands.ino +++ b/tools/printCommands/printCommands.ino @@ -1,8 +1,7 @@ /** * @file d_simple_logger.ino - * @copyright (c) 2013-2020 Stroud Water Research Center (SWRC) - * and the EnviroDIY Development Team - * This example is published under the BSD-3 license. + * @copyright Stroud Water Research Center + * This example is published under the BSD-3 license. * @author Kevin M.Smith * @date August 2013 * From 24a18c2c7f1bea78a128a57b6f5d863a8234632b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 18 May 2024 12:46:54 +0000 Subject: [PATCH 33/96] ci: bump actions/setup-node from 4.0.0 to 4.0.2 Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4.0.0 to 4.0.2. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/v4.0.0...v4.0.2) --- updated-dependencies: - dependency-name: actions/setup-node dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/verify_library_json.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/verify_library_json.yaml b/.github/workflows/verify_library_json.yaml index fbb2ee5..cc01a0c 100644 --- a/.github/workflows/verify_library_json.yaml +++ b/.github/workflows/verify_library_json.yaml @@ -12,7 +12,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup Node.js - uses: actions/setup-node@v4.0.0 + uses: actions/setup-node@v4.0.2 - name: Cache Node.js modules uses: actions/cache@v3.3.2 From 6f26975c7e54e6bb28139c6abcb4c4480356a486 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 18 May 2024 12:48:17 +0000 Subject: [PATCH 34/96] ci: bump actions/setup-python from 4 to 5 Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4 to 5. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build_documentation.yaml | 2 +- .github/workflows/build_examples.yaml | 2 +- .github/workflows/prepare_release.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build_documentation.yaml b/.github/workflows/build_documentation.yaml index 1a59eb2..90d12dc 100644 --- a/.github/workflows/build_documentation.yaml +++ b/.github/workflows/build_documentation.yaml @@ -45,7 +45,7 @@ jobs: key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '3.x' diff --git a/.github/workflows/build_examples.yaml b/.github/workflows/build_examples.yaml index 122cea6..b126ea2 100644 --- a/.github/workflows/build_examples.yaml +++ b/.github/workflows/build_examples.yaml @@ -56,7 +56,7 @@ jobs: key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '3.x' diff --git a/.github/workflows/prepare_release.yaml b/.github/workflows/prepare_release.yaml index 6adcf03..0687c8a 100644 --- a/.github/workflows/prepare_release.yaml +++ b/.github/workflows/prepare_release.yaml @@ -37,7 +37,7 @@ jobs: restore-keys: ${{ runner.os }}-pip- - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '3.x' From fa42eba4362e36a2dc1721b338b0f20e0752347c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 18 May 2024 12:49:15 +0000 Subject: [PATCH 35/96] ci: bump actions/cache from 3.3.2 to 4.0.2 Bumps [actions/cache](https://github.com/actions/cache) from 3.3.2 to 4.0.2. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v3.3.2...v4.0.2) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build_documentation.yaml | 6 +++--- .github/workflows/build_examples.yaml | 4 ++-- .github/workflows/prepare_release.yaml | 2 +- .github/workflows/verify_library_json.yaml | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build_documentation.yaml b/.github/workflows/build_documentation.yaml index 1a59eb2..5872d10 100644 --- a/.github/workflows/build_documentation.yaml +++ b/.github/workflows/build_documentation.yaml @@ -27,7 +27,7 @@ jobs: path: code_docs/Arduino-SDI-12 - name: Restore or Cache pip - uses: actions/cache@v3.3.2 + uses: actions/cache@v4.0.2 id: cache_pip with: path: ~/.cache/pip @@ -37,7 +37,7 @@ jobs: restore-keys: ${{ runner.os }}-pip- - name: Restore or Cache PlatformIO and Libraries - uses: actions/cache@v3.3.2 + uses: actions/cache@v4.0.2 id: cache_pio with: path: ~/.platformio @@ -70,7 +70,7 @@ jobs: - name: Restore or Cache Doxygen id: cache_doxygen - uses: actions/cache@v3.3.2 + uses: actions/cache@v4.0.2 with: path: doxygen-src key: ${{ runner.os }}-doxygen-${{ env.DOXYGEN_VERSION }} diff --git a/.github/workflows/build_examples.yaml b/.github/workflows/build_examples.yaml index 122cea6..e4429c4 100644 --- a/.github/workflows/build_examples.yaml +++ b/.github/workflows/build_examples.yaml @@ -39,7 +39,7 @@ jobs: fi - name: Restore or Cache pip - uses: actions/cache@v3.3.2 + uses: actions/cache@v4.0.2 with: path: ~/.cache/pip # if requirements.txt hasn't changed, then it will be a "cache hit" and pip will be restored @@ -48,7 +48,7 @@ jobs: restore-keys: ${{ runner.os }}-pip- - name: Restore or Cache PlatformIO and Libraries - uses: actions/cache@v3.3.2 + uses: actions/cache@v4.0.2 with: path: ~/.platformio # if nothing in the lock files has changed, then it will be a "cache hit" and pip will be restored diff --git a/.github/workflows/prepare_release.yaml b/.github/workflows/prepare_release.yaml index 6adcf03..fc1d348 100644 --- a/.github/workflows/prepare_release.yaml +++ b/.github/workflows/prepare_release.yaml @@ -28,7 +28,7 @@ jobs: echo "VERSION=$VER" >> $GITHUB_ENV - name: Restore or Cache pip - uses: actions/cache@v3.3.2 + uses: actions/cache@v4.0.2 with: path: ~/.cache/pip # if requirements.txt hasn't changed, then it will be a "cache hit" and pip will be restored diff --git a/.github/workflows/verify_library_json.yaml b/.github/workflows/verify_library_json.yaml index cc01a0c..2684751 100644 --- a/.github/workflows/verify_library_json.yaml +++ b/.github/workflows/verify_library_json.yaml @@ -15,7 +15,7 @@ jobs: uses: actions/setup-node@v4.0.2 - name: Cache Node.js modules - uses: actions/cache@v3.3.2 + uses: actions/cache@v4.0.2 with: # npm cache files are stored in `~/.npm` on Linux/macOS path: ~/.npm From c3ddfe841697743771810b05d18681e48f61c31f Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Tue, 25 Jun 2024 17:20:42 -0400 Subject: [PATCH 36/96] Switch to reusable workflows Signed-off-by: Sara Damiano --- .gitattributes | 3 +- .github/workflows/build_documentation.yaml | 138 +- .github/workflows/build_examples.yaml | 97 +- .github/workflows/changelog_reminder.yaml | 4 +- .github/workflows/prepare_release.yaml | 87 +- .github/workflows/verify_library_json.yaml | 31 - .../workflows/verify_library_structure.yaml | 17 + .gitignore | 137 +- continuous_integration/.travis.yml_archive | 160 - .../build-install-doxygen.sh | 44 - continuous_integration/copy-doc-sources.sh | 40 - .../deploy-documentation.sh | 51 - .../generate-documentation.sh | 33 - .../install-current-doxygen.sh | 26 - .../m-EnviroDIY+documentation.compiled.css | 4316 ----------------- docs/fixXmlExampleSections.py | 58 - docs/markdown_prefilter.py | 40 - 17 files changed, 206 insertions(+), 5076 deletions(-) delete mode 100644 .github/workflows/verify_library_json.yaml create mode 100644 .github/workflows/verify_library_structure.yaml delete mode 100644 continuous_integration/.travis.yml_archive delete mode 100644 continuous_integration/build-install-doxygen.sh delete mode 100644 continuous_integration/copy-doc-sources.sh delete mode 100644 continuous_integration/deploy-documentation.sh delete mode 100644 continuous_integration/generate-documentation.sh delete mode 100644 continuous_integration/install-current-doxygen.sh delete mode 100644 docs/css/m-EnviroDIY+documentation.compiled.css delete mode 100644 docs/fixXmlExampleSections.py delete mode 100644 docs/markdown_prefilter.py diff --git a/.gitattributes b/.gitattributes index 7c8fffe..bf3d914 100644 --- a/.gitattributes +++ b/.gitattributes @@ -15,7 +15,8 @@ # Denote all files that are truly binary and should not be modified. *.png binary *.jpg binary - +*.pdf binary +*.pdf export-ignore # used to exclude files from archiving/compression diff --git a/.github/workflows/build_documentation.yaml b/.github/workflows/build_documentation.yaml index 1a59eb2..c834db0 100644 --- a/.github/workflows/build_documentation.yaml +++ b/.github/workflows/build_documentation.yaml @@ -1,111 +1,39 @@ -name: Build and Publish Documentation +name: Check, Build, and Publish Documentation on: - # Trigger the workflow on push or pull request, - # but only for the main branch + # Triggers the workflow on push or pull request events push: - branches: - - master - # Also trigger on page_build, as well as release created events - page_build: + pull_request: + # Trigger when a release is created + # NOTE: This will only trigger if the release is created from the UI or with a personal access token release: - types: # This configuration does not affect the page_build event above - - created - -env: - DOXYGEN_VERSION: Release_1_9_1 + types: + - published + # Trigger with the release workflow finishes + workflow_run: + workflows: ['Create a New Release'] + types: [completed] + branches: [master] + # Also give a manual trigger + workflow_dispatch: + inputs: + publish: + description: 'Publish Documentation to GitHub Pages' + required: false + type: boolean + default: false + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: - build: - runs-on: ubuntu-latest - if: "!contains(github.event.head_commit.message, 'ci skip')" - - steps: - # check out the Arduino-SDI-12 repo - - uses: actions/checkout@v4 - with: - path: code_docs/Arduino-SDI-12 - - - name: Restore or Cache pip - uses: actions/cache@v3.3.2 - id: cache_pip - with: - path: ~/.cache/pip - # if requirements.txt hasn't changed, then it will be a "cache hit" and pip will be restored - # if requirements.txt HAS changed, it will be a "cache miss" and a new cache of pip will be created if the job completes successfully - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} - restore-keys: ${{ runner.os }}-pip- - - - name: Restore or Cache PlatformIO and Libraries - uses: actions/cache@v3.3.2 - id: cache_pio - with: - path: ~/.platformio - # if nothing in the lock files has changed, then it will be a "cache hit" - key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} - - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.x' - - # This should be pulled from cache, if there's not a new version - - name: Install PlatformIO - run: | - python -m pip install --upgrade pip - pip install --upgrade platformio - - # Install *all* the dependencies! - # We're including the dependencies just so the includes can follow in the doxygen pre-processor - - name: Install the dependencies at global level - run: | - echo "::debug::Installing greygnome/EnableInterrupt" - pio lib -g install greygnome/EnableInterrupt - - - name: Update Libraries from Cache - run: pio lib -g update - - - name: Install GraphViz (dot) - run: sudo apt-get -y install graphviz - - - name: Restore or Cache Doxygen - id: cache_doxygen - uses: actions/cache@v3.3.2 - with: - path: doxygen-src - key: ${{ runner.os }}-doxygen-${{ env.DOXYGEN_VERSION }} - - - name: Clone and build doxygen - if: steps.cache_doxygen.outputs.cache-hit != 'true' - env: - TRAVIS_BUILD_DIR: ${{ github.workspace }} - run: | - cd ${{ github.workspace }}/code_docs/Arduino-SDI-12/ - chmod +x continuous_integration/build-install-doxygen.sh - sh continuous_integration/build-install-doxygen.sh - - # This should be pulled from cache, if there's not a new version - - name: Install Pygments and other m.css requirements - run: pip3 install jinja2 Pygments beautifulsoup4 - - # check out my fork of m.css, for processing Doxygen output - - name: Checkout m.css - uses: actions/checkout@v4 - with: - # Repository name with owner. For example, actions/checkout - repository: SRGDamia1/m.css - path: code_docs/m.css - - - name: Generate all the documentation - env: - TRAVIS_BUILD_DIR: ${{ github.workspace }} - run: | - cd ${{ github.workspace }}/code_docs/Arduino-SDI-12/ - chmod +x continuous_integration/generate-documentation.sh - sh continuous_integration/generate-documentation.sh - - - name: Deploy to github pages - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ${{ github.workspace }}/code_docs/Arduino-SDI-12Doxygen/m.css + doc_build: + if: ${{ (! contains(github.event.head_commit.message, 'ci skip')) && (github.event_name != 'workflow_run' || (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success')) }} + name: Build documentation + uses: EnviroDIY/workflows/.github/workflows/build_documentation.yaml@main + with: + use_graphviz: false + publish: ${{ (github.event_name == 'release' && github.event.action == 'published') || (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success') || (github.event_name == 'workflow_dispatch' && github.event.inputs.publish == 'true')}} + rebuild_cache_number: 1 + secrets: inherit diff --git a/.github/workflows/build_examples.yaml b/.github/workflows/build_examples.yaml index c4e6374..7a5505d 100644 --- a/.github/workflows/build_examples.yaml +++ b/.github/workflows/build_examples.yaml @@ -3,89 +3,16 @@ name: Build Examples # Triggers the workflow on push or pull request events on: [push, pull_request] -jobs: - build: - runs-on: ubuntu-latest - if: "!contains(github.event.head_commit.message, 'ci skip')" - - strategy: - matrix: - example: - [ - examples/a_wild_card/, - examples/b_address_change/, - examples/c_check_all_addresses/, - examples/d_simple_logger/, - examples/e_continuous_measurement/, - examples/f_basic_data_request/, - examples/g_terminal_window/, - examples/h_SDI-12_slave_implementation/, - examples/i_SDI-12_interface/, - examples/j_external_pcint_library/, - examples/k_concurrent_logger/, - examples/l_verify_crc/, - ] - - steps: - - uses: actions/checkout@v4 - - - name: Set variables - run: | - if [[ -z "${GITHUB_HEAD_REF}" ]]; then - echo "::debug::Push to commit ${GITHUB_SHA}" - echo "LIBRARY_INSTALL_SOURCE=https://github.com/${GITHUB_REPOSITORY}.git#${GITHUB_SHA}" >> $GITHUB_ENV - else - echo "::debug::Pull Request from the ${GITHUB_HEAD_REF} branch" - echo "LIBRARY_INSTALL_SOURCE=https://github.com/${GITHUB_REPOSITORY}.git#${GITHUB_HEAD_REF}" >> $GITHUB_ENV - fi - - - name: Restore or Cache pip - uses: actions/cache@v3.3.2 - with: - path: ~/.cache/pip - # if requirements.txt hasn't changed, then it will be a "cache hit" and pip will be restored - # if requirements.txt HAS changed, it will be a "cache miss" and a new cache of pip will be created if the job completes successfully - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} - restore-keys: ${{ runner.os }}-pip- +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true - - name: Restore or Cache PlatformIO and Libraries - uses: actions/cache@v3.3.2 - with: - path: ~/.platformio - # if nothing in the lock files has changed, then it will be a "cache hit" and pip will be restored - # otherwise, it will be a "cache miss" and a new cache of libraries will be created if the job completes successfully - key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} - - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.x' - - # This should be pulled from cache, if there's not a new version - - name: Install PlatformIO - run: | - python -m pip install --upgrade pip - pip install --upgrade platformio - - - name: Run PlatformIO - if: matrix.example != 'examples/j_external_pcint_library/' - env: - PLATFORMIO_CI_SRC: ${{ matrix.example }} - run: | - echo "${{ env.LIBRARY_INSTALL_SOURCE }}" - pio lib --global install ${{ env.LIBRARY_INSTALL_SOURCE }} - pio lib --global install EnableInterrupt - platformio ci --board=mayfly --board=feather32u4 --board=adafruit_feather_m0 --board=uno --board=megaatmega2560 --board=huzzah --board=featheresp32 - pio lib --global uninstall SDI-12 - - - name: Run PlatformIO - if: matrix.example == 'examples/j_external_pcint_library/' - env: - PLATFORMIO_CI_SRC: ${{ matrix.example }} - PLATFORMIO_BUILD_FLAGS: -DSDI12_EXTERNAL_PCINT - run: | - echo "${{ env.LIBRARY_INSTALL_SOURCE }}" - pio lib --global install ${{ env.LIBRARY_INSTALL_SOURCE }} - pio lib --global install EnableInterrupt - platformio ci --board=mayfly --board=feather32u4 --board=adafruit_feather_m0 --board=uno --board=megaatmega2560 - pio lib --global uninstall SDI-12 +jobs: + build_examples: + name: Build all examples with PlatformIO and the Arduino CLI + if: ${{ ! contains(github.event.head_commit.message, 'ci skip') }} + uses: EnviroDIY/workflows/.github/workflows/build_examples.yaml@main + with: + boards_to_build: 'mayfly,uno,megaatmega2560,leonardo,zeroUSB,arduino_nano_esp32,feather328p,feather32u4,adafruit_feather_m0,adafruit_feather_m4,adafruit_grandcentral_m4,huzzah,featheresp32,nodemcu,nodemcuv2,esp32dev,esp32-c3-devkitm-1,esp32-s3-devkitm-1' + examples_to_build: 'examples/a_wild_card,examples/b_address_change,examples/c_check_all_addresses,examples/d_simple_logger,examples/e_continuous_measurement,examples/f_basic_data_request,examples/g_terminal_window,examples/h_SDI-12_slave_implementation,examples/i_SDI-12_interface,examples/j_external_pcint_library,examples/k_concurrent_logger,examples/l_verify_crc/,' + secrets: inherit diff --git a/.github/workflows/changelog_reminder.yaml b/.github/workflows/changelog_reminder.yaml index a1b4115..cf35380 100644 --- a/.github/workflows/changelog_reminder.yaml +++ b/.github/workflows/changelog_reminder.yaml @@ -12,7 +12,7 @@ jobs: - name: Changelog Reminder uses: peterjgrainger/action-changelog-reminder@v1.3.0 with: - changelog_regex: '/ChangeLog\/.*\/*.md' + changelog_regex: '/CHANGELOG\/.*\/*.md/i' customPrMessage: 'Please add your changes to the change log!' env: - GITHUB_TOKEN: ${{ secrets.SARA_PUSH_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/prepare_release.yaml b/.github/workflows/prepare_release.yaml index 6adcf03..62f618b 100644 --- a/.github/workflows/prepare_release.yaml +++ b/.github/workflows/prepare_release.yaml @@ -7,68 +7,43 @@ on: - 'VERSION' # Push events when the VERSION file changes workflow_dispatch: -name: Prepare a new release +name: Create a New Release env: PLATFORMIO_AUTH_TOKEN: ${{ secrets.PLATFORMIO_AUTH_TOKEN }} jobs: - release: - name: Prepare a new release + wait_for_checks: + if: ${{ github.event_name != 'workflow_dispatch' }} + strategy: + matrix: + req_workflow: + [ + verify_library_structure.yaml, + build_examples.yaml, + build_documentation.yaml, + ] + name: Wait for Checks to complete runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set variables - run: | - echo "::debug::Get the current version number" - VER=$(cat VERSION) - echo "VERSION=$VER" >> $GITHUB_ENV - - - name: Restore or Cache pip - uses: actions/cache@v3.3.2 - with: - path: ~/.cache/pip - # if requirements.txt hasn't changed, then it will be a "cache hit" and pip will be restored - # if requirements.txt HAS changed, it will be a "cache miss" and a new cache of pip will be created if the job completes successfully - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} - restore-keys: ${{ runner.os }}-pip- - - - name: Set up Python - uses: actions/setup-python@v4 + - name: Wait on Workflow + uses: ArcticLampyrid/action-wait-for-workflow@v1.2.0 with: - python-version: '3.x' + workflow: ${{ matrix.req_workflow }} + sha: ${{ github.sha || github.event.pull_request.head.sha || github.event.pull_request.head.ref }} # optional + allowed-conclusions: | + success + cancelled + skipped - # This should be pulled from cache, if there's not a new version - - name: Install PlatformIO - run: | - python -m pip install --upgrade pip - pip install --upgrade platformio - - - name: Get notes - id: generate_notes - uses: anmarkoulis/commitizen-changelog-reader@master - with: - # NOTE: Need to add the refs/tags to work with the generate notes action - tag_name: ${{ format('refs/tags/{0}', env.VERSION) }} - changelog: ChangeLog.md - - # Create a new release - - name: Create Release - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ env.VERSION }} - release_name: ${{ env.VERSION }} - draft: false - prerelease: false - body: ${{join(fromJson(steps.generate_notes.outputs.notes).notes, '')}} - - # Publish the new release to the pio package manager - - name: Publish release to PIO - id: publish-pio - run: pio package publish + release: + name: Prepare a new release + needs: [wait_for_checks] + if: | + always() && + (needs.wait_for_checks.result == 'success' || needs.wait_for_checks.result == 'skipped') + uses: EnviroDIY/workflows/.github/workflows/prepare_release.yaml@main + secrets: inherit + with: + library-manager: 'update' + library-compliance: 'strict' diff --git a/.github/workflows/verify_library_json.yaml b/.github/workflows/verify_library_json.yaml deleted file mode 100644 index fad108e..0000000 --- a/.github/workflows/verify_library_json.yaml +++ /dev/null @@ -1,31 +0,0 @@ -name: Verify JSON structure for library manifest - -# Triggers the workflow on push or pull request events -on: [push, pull_request] - -jobs: - build: - runs-on: ubuntu-latest - if: "!contains(github.event.head_commit.message, 'ci skip')" - - steps: - - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: actions/setup-node@v3.8.1 - - - name: Cache Node.js modules - uses: actions/cache@v3.3.2 - with: - # npm cache files are stored in `~/.npm` on Linux/macOS - path: ~/.npm - key: ${{ runner.OS }}-node-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.OS }}-node- - ${{ runner.OS }}- - - - name: install jsonlint - run: npm install -g jsonlint - - - name: run jsonlint - run: jsonlint -q library.json diff --git a/.github/workflows/verify_library_structure.yaml b/.github/workflows/verify_library_structure.yaml new file mode 100644 index 0000000..bf99587 --- /dev/null +++ b/.github/workflows/verify_library_structure.yaml @@ -0,0 +1,17 @@ +name: Verify library manifest and structure + +# Triggers the workflow on push or pull request events +on: [push, pull_request] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + verify_library_structure: + name: Validate library structure + if: ${{ ! contains(github.event.head_commit.message, 'ci skip') }} + uses: EnviroDIY/workflows/.github/workflows/verify_library_structure.yaml@main + with: + library-manager: 'update' + library-compliance: 'strict' diff --git a/.gitignore b/.gitignore index 6b2d960..d230dfa 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,9 @@ $RECYCLE.BIN/ # Windows shortcuts *.lnk +# Microsoft Office temp files +~$* + # ========================= # Operating System Files # ========================= @@ -49,36 +52,114 @@ Temporary Items # PyCharm .idea/ - -# VSCode -.history -.vscode - -# Atom / PlatformIO +# Python +__pycache__/ + +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# Ignore list for Eagle, a PCB layout tool + +# Backup files +*.s#? +*.b#? +*.l#? +*.b$? +*.s$? +*.l$? + +# Eagle project file +# It contains a serial number and references to the file structure +# on your computer. +# comment the following line if you want to have your project file included. +eagle.epf + +# Autorouter files +*.pro +*.job + +# CAM files +*.$$$ +*.cmp +*.ly2 +*.l15 +*.sol +*.plc +*.stc +*.sts +*.crc +*.crs + +*.dri +*.drl +*.gpi +*.pls +*.ger +*.xln + +*.drd +*.drd.* + +*.s#* +*.b#* + +*.info + +*.eps + +# file locks introduced since 7.x +*.lck + +# PlatformIO .pio +.pioenvs +.piolibdeps .clang_complete .gcc-flags.json lib/readme.txt -include/readme.txt -platformio.ini - - -examples/debug_print/ -doxygen/doxygenOutput.log -Doxyfile-mcss -examples/debugging_prints/* -doxygen/mcss-theme.css -doxygen/mcss-theme-compiled.css -doxygen/__pycache__/conf.cpython-37.pyc -doxygen/conf.py -SDI12 Repo Token.txt -__pycache__ -docs/LocalDoxyfile -runDoxygen.bat -docs/examples/* -docs/doxygenOutput.log -docs/mcss-doxy-output.log -docs/mcss.log -doygen-run.log -examples/TestCommands/TestCommands.ino +/platformio.ini +tests/* + +# VSCode +.vscode +.history compile_commands.json + +# Other Stuff +pioScripts +runDoxygen.bat +generateKeywords.bat +output_*.log +sync.ffs_db +docs/Doxyfile.bak +SDI12 Repo Token.txt diff --git a/continuous_integration/.travis.yml_archive b/continuous_integration/.travis.yml_archive deleted file mode 100644 index f84a321..0000000 --- a/continuous_integration/.travis.yml_archive +++ /dev/null @@ -1,160 +0,0 @@ -before_install: - - git config --global user.email "sdamiano@stroudcenter.org" - - git config --global user.name "SRGDamia1" - -sudo: false -git: - depth: 1 -branches: - except: - - gh-pages - -cache: - pip: true - directories: - - "~/.platformio" - - $TRAVIS_BUILD_DIR/doxygen-src - -language: python -python: - - "2.7" - -install: - # Remove the cloned repo to emulate a user library installation - - git rm library.json - # - git rm library.properties - # - git rm -r pioScripts - # - git rm -r src - # Install PlatformIO (this should be cached!) - - pip install -U platformio - - pio upgrade - # Uninstall any old version of the current library from the Travis cache - - if pio lib --global uninstall EnviroDIY_Arduino-SDI-12; then - echo "Uninstalled cached version of Arduino-SDI-12"; - fi - - if pio lib --global uninstall EnviroDIY_SDI-12; then - echo "Uninstalled cached version of SDI-12"; - fi - # Install this library from the branch we're working on - # echo "Installing SDI-12 from https://github.com/$TRAVIS_REPO_SLUG.git#$TRAVIS_BRANCH"; - # echo "Installing SDI-12 from https://github.com/$TRAVIS_PULL_REQUEST_SLUG.git#$TRAVIS_PULL_REQUEST_BRANCH"; - - if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then - echo "Installing SDI-12 from https://github.com/$TRAVIS_REPO_SLUG.git#$TRAVIS_COMMIT"; - else - echo "Installing SDI-12 from https://github.com/$TRAVIS_PULL_REQUEST_SLUG.git#$TRAVIS_PULL_REQUEST_SHA"; - fi - # pio lib --global install https://github.com/$TRAVIS_REPO_SLUG.git#$BRANCH; - # pio lib --global install https://github.com/$TRAVIS_PULL_REQUEST_SLUG.git#$TRAVIS_PULL_REQUEST_BRANCH; - - if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then - pio lib --global install https://github.com/$TRAVIS_REPO_SLUG.git#$TRAVIS_COMMIT; - else - pio lib --global install https://github.com/$TRAVIS_PULL_REQUEST_SLUG.git#$TRAVIS_PULL_REQUEST_SHA; - fi - - pio update - -script: - - platformio ci --board=mayfly --board=feather32u4 --board=adafruit_feather_m0 --board=uno --board=megaatmega2560 --board=huzzah --board=featheresp32 - -jobs: - include: - - name: "Verify library JSON format" - language: node_js - install: npm install -g jsonlint - script: jsonlint -q library.json - after_success: | - echo "TRAVIS_BRANCH=$TRAVIS_BRANCH TRAVIS_PULL_REQUEST=$TRAVIS_PULL_REQUEST" - if [[ ($TRAVIS_BRANCH == master) && - ($TRAVIS_PULL_REQUEST == false) ]] ; then - curl -LO --retry 3 https://raw.github.com/mernst/plume-lib/master/bin/trigger-travis.sh - sh trigger-travis.sh EnviroDIY Libraries $TRAVIS_ACCESS_TOKEN - fi - - - name: "Build Doxygen Documentation" - if: branch = master AND type != pull_request - language: python - python: - - "3.7" - before_install: - - git config --global user.email "sdamiano@stroudcenter.org" - - git config --global user.name "SRGDamia1" - - git config --global push.default simple - - sudo apt-get update - - sudo apt-get -y install build-essential - - sudo apt-get -y install graphviz - - sudo apt-get -y install flex - - sudo apt-get -y install bison - - sudo apt-get -y install texlive-base - - sudo apt-get -y install texlive-latex-extra - - sudo apt-get -y install texlive-fonts-extra - - sudo apt-get -y install texlive-fonts-recommended - - pip3 install jinja2 Pygments - install: - - cd $TRAVIS_BUILD_DIR - - chmod +x travis/copy-doc-sources.sh - - sh travis/copy-doc-sources.sh - - cd $TRAVIS_BUILD_DIR - - chmod +x travis/build-install-doxygen.sh - - sh travis/build-install-doxygen.sh - script: - - cd $TRAVIS_BUILD_DIR - - chmod +x travis/generate-documentation.sh - - sh travis/generate-documentation.sh - # after_success: - # - cd $TRAVIS_BUILD_DIR - # - chmod +x travis/deploy-documentation.sh - # - sh travis/deploy-documentation.sh - deploy: - provider: pages:git - token: $GH_REPO_TOKEN - edge: true # opt in to dpl v2 - keep_history: false - local_dir: $TRAVIS_BUILD_DIR/code_docs/Arduino-SDI-12Doxygen/m.css - project_name: Arduino-SDI-12 - - - name: "a_wild_card" - env: - - PLATFORMIO_CI_SRC=examples/a_wild_card/a_wild_card.ino - - - name: "b_address_change" - env: - - PLATFORMIO_CI_SRC=examples/b_address_change/b_address_change.ino - - - name: "c_check_all_addresses" - env: - - PLATFORMIO_CI_SRC=examples/c_check_all_addresses/c_check_all_addresses.ino - - - name: "d_simple_logger" - env: - - PLATFORMIO_CI_SRC=examples/d_simple_logger/d_simple_logger.ino - - - name: "e_simple_parsing" - env: - - PLATFORMIO_CI_SRC=examples/e_simple_parsing/e_simple_parsing.ino - - - name: "f_basic_data_request" - env: - - PLATFORMIO_CI_SRC=examples/f_basic_data_request/f_basic_data_request.ino - - - name: "g_terminal_window" - env: - - PLATFORMIO_CI_SRC=examples/g_terminal_window/g_terminal_window.ino - - - name: "h_SDI-12_slave_implementation" - env: - - PLATFORMIO_CI_SRC=examples/h_SDI-12_slave_implementation/h_SDI-12_slave_implementation.ino - - - name: "i_SDI-12_interface" - env: - - PLATFORMIO_CI_SRC=examples/i_SDI-12_interface/i_SDI-12_interface.ino - - - name: "j_external_pcint_library" - env: - - PLATFORMIO_CI_SRC=examples/j_external_pcint_library/j_external_pcint_library.ino - - PLATFORMIO_BUILD_FLAGS=-DSDI12_EXTERNAL_PCINT - script: - - pio lib --global install EnableInterrupt - - platformio ci --board=mayfly --board=feather32u4 --board=adafruit_feather_m0 --board=uno --board=megaatmega2560 - - - name: "k_concurrent_logger" - env: - - PLATFORMIO_CI_SRC=examples/k_concurrent_logger/k_concurrent_logger.ino diff --git a/continuous_integration/build-install-doxygen.sh b/continuous_integration/build-install-doxygen.sh deleted file mode 100644 index 1129871..0000000 --- a/continuous_integration/build-install-doxygen.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/sh - -# Exit with nonzero exit code if anything fails -set -e - -# install all the dependencies for make for Doxygen -sudo apt-get update -sudo apt-get -y install build-essential -sudo apt-get -y install flex -sudo apt-get -y install bison -sudo apt-get -y install texlive-base -sudo apt-get -y install texlive-latex-extra -sudo apt-get -y install texlive-fonts-extra -sudo apt-get -y install texlive-fonts-recommended - -cd $TRAVIS_BUILD_DIR - -if [ ! -f $TRAVIS_BUILD_DIR/doxygen-src/build/bin/doxygen ]; then - - # Build instructions from: https://www.stack.nl/~dimitri/doxygen/download.html - echo "Cloning doxygen repository..." - git clone https://github.com/doxygen/doxygen.git doxygen-src --branch $DOXYGEN_VERSION --depth 1 - - cd doxygen-src - - echo "Create build folder..." - mkdir build - cd build - - echo "Make..." - cmake -G "Unix Makefiles" .. - make - echo "Done building doxygen." - echo "doxygen path: " $(pwd) -fi - -echo "Current Doxygen version..." -$TRAVIS_BUILD_DIR/doxygen-src/build/bin/doxygen -v - -# echo "Move Doxygen to working directory" -# cp $TRAVIS_BUILD_DIR/doxygen-src/build/bin/* $TRAVIS_BUILD_DIR/code_docs/Arduino-SDI-12/docs -#make install - -cd $TRAVIS_BUILD_DIR/code_docs/Arduino-SDI-12 diff --git a/continuous_integration/copy-doc-sources.sh b/continuous_integration/copy-doc-sources.sh deleted file mode 100644 index 8082ca9..0000000 --- a/continuous_integration/copy-doc-sources.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/sh - -# Script modified from scripts by Jeroen de Bruijn, thephez, and Adafruit -# https://gist.github.com/vidavidorra/548ffbcdae99d752da02 -# https://github.com/thephez/doxygen-travis-build -# https://learn.adafruit.com/the-well-automated-arduino-library/travis-ci - -# Exit with nonzero exit code if anything fails -set -e - -# Create a clean working directory for this script. -mkdir $TRAVIS_BUILD_DIR/code_docs -cd $TRAVIS_BUILD_DIR/code_docs - -# Re-clone the main repo, not sparsely -git clone -b master --depth 1 https://github.com/EnviroDIY/Arduino-SDI-12 Arduino-SDI-12 - -# Clone m.css for its theming -git clone --depth 1 https://github.com/SRGDamia1/m.css m.css - -# Get the current gh-pages branch -# git clone -b gh-pages https://git@$GH_REPO_REF -# cd $GH_REPO_NAME -git clone -b gh-pages --depth 1 https://github.com/EnviroDIY/Arduino-SDI-12 Arduino-SDI-12Doxygen -cd Arduino-SDI-12Doxygen -echo "Documentation path: " $(pwd) - -# Remove everything currently in the gh-pages branch. -# GitHub is smart enough to know which files have changed and which files have -# stayed the same and will only update the changed files. So the gh-pages branch -# can be safely cleaned, and it is sure that everything pushed later is the new -# documentation. -rm -rf * - -# Need to create a .nojekyll file to allow filenames starting with an underscore -# to be seen on the gh-pages site. Therefore creating an empty .nojekyll file. -# Presumably this is only needed when the SHORT_NAMES option in Doxygen is set -# to NO, which it is by default. So creating the file just in case. -echo "" > .nojekyll -ls \ No newline at end of file diff --git a/continuous_integration/deploy-documentation.sh b/continuous_integration/deploy-documentation.sh deleted file mode 100644 index 4679321..0000000 --- a/continuous_integration/deploy-documentation.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/sh - -# Script modified from scripts by Jeroen de Bruijn, thephez, and Adafruit -# https://gist.github.com/vidavidorra/548ffbcdae99d752da02 -# https://github.com/thephez/doxygen-travis-build -# https://learn.adafruit.com/the-well-automated-arduino-library/travis-ci - -# Exit with nonzero exit code if anything fails -set -e -cd $TRAVIS_BUILD_DIR/code_docs -echo "Current path: " $(pwd) -echo "Contents (ls):" -ls -cd $TRAVIS_BUILD_DIR/code_docs/Arduino-SDI-12 -echo "Current path: " $(pwd) -echo "Contents (ls):" -ls -cd $TRAVIS_BUILD_DIR/code_docs/Arduino-SDI-12Doxygen -echo "Current path: " $(pwd) -echo "Contents (ls):" -ls - -################################################################################ -##### Upload the documentation to the gh-pages branch of the repository. ##### -# Only upload if Doxygen successfully created the documentation. -# Check this by verifying that the html directory and the file html/index.html -# both exist. This is a good indication that Doxygen did it's work. -if [ -d $TRAVIS_BUILD_DIR/code_docs/Arduino-SDI-12Doxygen/m.css ] && - [ -f $TRAVIS_BUILD_DIR/code_docs/Arduino-SDI-12Doxygen/m.css/index.html ]; then - - echo 'Uploading documentation to the gh-pages branch...' - # Add everything in this directory (the Doxygen code documentation) to the - # gh-pages branch. - # GitHub is smart enough to know which files have changed and which files have - # stayed the same and will only update the changed files. - git add --all - - # Commit the added files with a title and description containing the Travis CI - # build number and the GitHub commit reference that issued this build. - git commit -m "Deploy code docs to GitHub Pages Travis build: ${TRAVIS_BUILD_NUMBER}" -m "Commit: ${TRAVIS_COMMIT}" - - # Force push to the remote gh-pages branch. - # The ouput is redirected to /dev/null to hide any sensitive credential data - # that might otherwise be exposed. - git push --force "https://${GH_REPO_TOKEN}@${GH_REPO_REF}" > /dev/null 2>&1 -else - echo '' >&2 - echo 'Warning: No documentation (html) files have been found!' >&2 - echo 'Warning: Not going to push the documentation to GitHub!' >&2 - exit 1 -fi \ No newline at end of file diff --git a/continuous_integration/generate-documentation.sh b/continuous_integration/generate-documentation.sh deleted file mode 100644 index 7402c0d..0000000 --- a/continuous_integration/generate-documentation.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/sh - -# Script modified from scripts by Jeroen de Bruijn, thephez, and Adafruit -# https://gist.github.com/vidavidorra/548ffbcdae99d752da02 -# https://github.com/thephez/doxygen-travis-build -# https://learn.adafruit.com/the-well-automated-arduino-library/travis-ci - -# Exit with nonzero exit code if anything fails -set -e - -cd $TRAVIS_BUILD_DIR/code_docs/m.css -echo 'Update the style sheets' -cd $TRAVIS_BUILD_DIR/code_docs/m.css/css/EnviroDIY -python $TRAVIS_BUILD_DIR/code_docs/m.css/css/postprocess.py "m-EnviroDIY.css" -python $TRAVIS_BUILD_DIR/code_docs/m.css/css/postprocess.py "m-EnviroDIY.css" "m-documentation.css" -o "m-EnviroDIY+documentation.compiled.css" -python $TRAVIS_BUILD_DIR/code_docs/m.css/css/postprocess.py "m-EnviroDIY.css" "m-theme-EnviroDIY.css" "m-documentation.css" --no-import -o "m-EnviroDIY.documentation.compiled.css" -cp $TRAVIS_BUILD_DIR/code_docs/m.css/css/EnviroDIY/m-EnviroDIY+documentation.compiled.css $TRAVIS_BUILD_DIR/code_docs/Arduino-SDI-12/docs/css - -echo 'Creating dox files from example read-me files' -cd $TRAVIS_BUILD_DIR/code_docs/Arduino-SDI-12/docs -python documentExamples.py - -echo 'Current Doxygen version...' -$TRAVIS_BUILD_DIR/doxygen-src/build/bin/doxygen -v 2>&1 - -# Redirect both stderr and stdout to the log file AND the console. -echo 'Generating Doxygen code documentation...' -$TRAVIS_BUILD_DIR/doxygen-src/build/bin/doxygen Doxyfile 2>&1 | tee doxygen.log - -echo 'Fixing errant xml section names in examples as generated by Doxygen...' -python fixXmlExampleSections.py - -python $TRAVIS_BUILD_DIR/code_docs/m.css/documentation/doxygen.py "mcss-conf.py" --no-doxygen --output mcss.log --templates "$TRAVIS_BUILD_DIR/code_docs/m.css/documentation/templates/EnviroDIY" --debug > mcss-doxy-output.log diff --git a/continuous_integration/install-current-doxygen.sh b/continuous_integration/install-current-doxygen.sh deleted file mode 100644 index 7640771..0000000 --- a/continuous_integration/install-current-doxygen.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh - -# Script modified from scripts by Jeroen de Bruijn, thephez, and Adafruit -# https://gist.github.com/vidavidorra/548ffbcdae99d752da02 -# https://github.com/thephez/doxygen-travis-build -# https://learn.adafruit.com/the-well-automated-arduino-library/travis-ci - -# Exit with nonzero exit code if anything fails -set -e - -cd $TRAVIS_BUILD_DIR/code_docs/Arduino-SDI-12 - -wget -q http://doxygen.nl/files/doxygen-1.8.18.linux.bin.tar.gz - -echo "Decompressing..." -tar -xzf doxygen-1.8.18.linux.bin.tar.gz -ls - -echo "Moving directory..." -mv doxygen-1.8.18/bin/doxygen . -chmod +x doxygen -echo "Current path: " $(pwd) -ls - -echo 'Installed Doxygen version...' -./doxygen -v 2>&1 diff --git a/docs/css/m-EnviroDIY+documentation.compiled.css b/docs/css/m-EnviroDIY+documentation.compiled.css deleted file mode 100644 index 1008522..0000000 --- a/docs/css/m-EnviroDIY+documentation.compiled.css +++ /dev/null @@ -1,4316 +0,0 @@ -/* Generated using `./postprocess.py m-EnviroDIY.css m-documentation.css -o m-EnviroDIY+documentation.compiled.css`. Do not edit. */ - -/* - This file is part of m.css. - - Copyright © 2017, 2018, 2019, 2020 Vladimír Vondruš - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included - in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - DEALINGS IN THE SOFTWARE. -*/ - -*, ::before, ::after { box-sizing: border-box; } -body { margin: 0; } -.m-container { - width: 100%; - margin: auto; - padding-left: 1rem; - padding-right: 1rem; -} -.m-row { - margin-left: -1rem; - margin-right: -1rem; -} -.m-row:after { - content: ' '; - clear: both; - display: table; -} -.m-row > [class*='m-col-'] { - position: relative; - padding: 1rem; -} -[class*='m-clearfix-']::after { - display: block; - content: ' '; - clear: both; -} -[class*='m-show-'] { - display: none; -} -.m-container-inflate, :not(.m-row) > [class*='m-col-'] { - margin-bottom: 1rem; -} -.m-container-inflate:last-child, :not(.m-row) > [class*='m-col-']:last-child { - margin-bottom: 0; -} -.m-container.m-nopad, [class*='m-col-'].m-nopad, -.m-container.m-nopadx, [class*='m-col-'].m-nopadx, -.m-container.m-nopadl, [class*='m-col-'].m-nopadl { - padding-left: 0; -} -.m-container.m-nopad, [class*='m-col-'].m-nopad, -.m-container.m-nopadx, [class*='m-col-'].m-nopadx, -.m-container.m-nopadr, [class*='m-col-'].m-nopadr { - padding-right: 0; -} -[class*='m-col-'].m-nopad, [class*='m-col-'].m-nopady, [class*='m-col-'].m-nopadt { - padding-top: 0; -} -[class*='m-col-'].m-nopad, [class*='m-col-'].m-nopady, [class*='m-col-'].m-nopadb, -.m-container-inflate.m-nopadb { - padding-bottom: 0; -} -[class*='m-col-t-'] { float: left; } -.m-left-t { - padding-right: 1rem; - float: left; -} -.m-right-t, [class*='m-col-t-'].m-right-t { - padding-left: 1rem; - float: right; -} -.m-center-t, [class*='m-col-t-'].m-center-t { - float: none; -} -.m-center-t, [class*='m-col-t-'].m-center-t { - margin-left: auto; - margin-right: auto; - float: none; -} -.m-col-t-1 { width: calc(1 * 100% / 12); } -.m-col-t-2 { width: calc(2 * 100% / 12); } -.m-col-t-3 { width: calc(3 * 100% / 12); } -.m-col-t-4 { width: calc(4 * 100% / 12); } -.m-col-t-5 { width: calc(5 * 100% / 12); } -.m-col-t-6 { width: calc(6 * 100% / 12); } -.m-col-t-7 { width: calc(7 * 100% / 12); } -.m-col-t-8 { width: calc(8 * 100% / 12); } -.m-col-t-9 { width: calc(9 * 100% / 12); } -.m-col-t-10 { width: calc(10 * 100% / 12); } -.m-col-t-11 { width: calc(11 * 100% / 12); } -.m-col-t-12 { width: calc(12 * 100% / 12); } -.m-push-t-1 { left: calc(1 * 100% / 12); } -.m-push-t-2 { left: calc(2 * 100% / 12); } -.m-push-t-3 { left: calc(3 * 100% / 12); } -.m-push-t-4 { left: calc(4 * 100% / 12); } -.m-push-t-5 { left: calc(5 * 100% / 12); } -.m-push-t-6 { left: calc(6 * 100% / 12); } -.m-push-t-7 { left: calc(7 * 100% / 12); } -.m-push-t-8 { left: calc(8 * 100% / 12); } -.m-push-t-9 { left: calc(9 * 100% / 12); } -.m-push-t-10 { left: calc(10 * 100% / 12); } -.m-push-t-11 { left: calc(11 * 100% / 12); } -.m-pull-t-1 { right: calc(1 * 100% / 12); } -.m-pull-t-2 { right: calc(2 * 100% / 12); } -.m-pull-t-3 { right: calc(3 * 100% / 12); } -.m-pull-t-4 { right: calc(4 * 100% / 12); } -.m-pull-t-5 { right: calc(5 * 100% / 12); } -.m-pull-t-6 { right: calc(6 * 100% / 12); } -.m-pull-t-7 { right: calc(7 * 100% / 12); } -.m-pull-t-8 { right: calc(8 * 100% / 12); } -.m-pull-t-9 { right: calc(9 * 100% / 12); } -.m-pull-t-10 { right: calc(10 * 100% / 12); } -.m-pull-t-11 { right: calc(11 * 100% / 12); } -@media screen and (min-width: 576px) { - .m-container { width: 560px; } - .m-container-inflatable .m-col-s-10 .m-container-inflate:not([class*='m-left-']):not([class*='m-right-']) { - margin-left: -10%; - margin-right: -10%; - } - .m-container-inflatable .m-col-s-10 .m-container-inflate.m-left-s { - margin-left: -10%; - } - .m-container-inflatable .m-col-s-10 .m-container-inflate.m-right-s { - margin-right: -10%; - } - [class*='m-col-s-'] { float: left; } - .m-left-s { - padding-right: 1rem; - float: left; - } - .m-right-s, [class*='m-col-s-'].m-right-s { - padding-left: 1rem; - float: right; - } - .m-center-s, [class*='m-col-s-'].m-center-s { - margin-left: auto; - margin-right: auto; - float: none; - } - .m-col-s-1 { width: calc(1 * 100% / 12); } - .m-col-s-2 { width: calc(2 * 100% / 12); } - .m-col-s-3 { width: calc(3 * 100% / 12); } - .m-col-s-4 { width: calc(4 * 100% / 12); } - .m-col-s-5 { width: calc(5 * 100% / 12); } - .m-col-s-6 { width: calc(6 * 100% / 12); } - .m-col-s-7 { width: calc(7 * 100% / 12); } - .m-col-s-8 { width: calc(8 * 100% / 12); } - .m-col-s-9 { width: calc(9 * 100% / 12); } - .m-col-s-10 { width: calc(10 * 100% / 12); } - .m-col-s-11 { width: calc(11 * 100% / 12); } - .m-col-s-12 { width: calc(12 * 100% / 12); } - .m-push-s-0 { left: calc(0 * 100% / 12); } - .m-push-s-1 { left: calc(1 * 100% / 12); } - .m-push-s-2 { left: calc(2 * 100% / 12); } - .m-push-s-3 { left: calc(3 * 100% / 12); } - .m-push-s-4 { left: calc(4 * 100% / 12); } - .m-push-s-5 { left: calc(5 * 100% / 12); } - .m-push-s-6 { left: calc(6 * 100% / 12); } - .m-push-s-7 { left: calc(7 * 100% / 12); } - .m-push-s-8 { left: calc(8 * 100% / 12); } - .m-push-s-9 { left: calc(9 * 100% / 12); } - .m-push-s-10 { left: calc(10 * 100% / 12); } - .m-push-s-11 { left: calc(11 * 100% / 12); } - .m-pull-s-0 { right: calc(0 * 100% / 12); } - .m-pull-s-1 { right: calc(1 * 100% / 12); } - .m-pull-s-2 { right: calc(2 * 100% / 12); } - .m-pull-s-3 { right: calc(3 * 100% / 12); } - .m-pull-s-4 { right: calc(4 * 100% / 12); } - .m-pull-s-5 { right: calc(5 * 100% / 12); } - .m-pull-s-6 { right: calc(6 * 100% / 12); } - .m-pull-s-7 { right: calc(7 * 100% / 12); } - .m-pull-s-8 { right: calc(8 * 100% / 12); } - .m-pull-s-9 { right: calc(9 * 100% / 12); } - .m-pull-s-10 { right: calc(10 * 100% / 12); } - .m-pull-s-11 { right: calc(11 * 100% / 12); } - .m-clearfix-t::after { display: none; } - .m-hide-s { display: none; } - .m-show-s { display: block; } - .m-col-s-none { - width: auto; - float: none; - } -} -@media screen and (min-width: 768px) { - .m-container { width: 750px; } - .m-container-inflatable .m-col-m-10 .m-container-inflate:not([class*='m-left-']):not([class*='m-right-']) { - margin-left: -10%; - margin-right: -10%; - } - .m-container-inflatable .m-col-m-10 .m-container-inflate.m-left-m { - margin-left: -10%; - } - .m-container-inflatable .m-col-m-10 .m-container-inflate.m-right-m { - margin-right: -10%; - } - [class*='m-col-m-'] { float: left; } - .m-left-m { - padding-right: 1rem; - float: left; - } - .m-right-m, [class*='m-col-m-'].m-right-m { - padding-left: 1rem; - float: right; - } - .m-center-m, [class*='m-col-m-'].m-center-m { - margin-left: auto; - margin-right: auto; - float: none; - } - .m-col-m-1 { width: calc(1 * 100% / 12); } - .m-col-m-2 { width: calc(2 * 100% / 12); } - .m-col-m-3 { width: calc(3 * 100% / 12); } - .m-col-m-4 { width: calc(4 * 100% / 12); } - .m-col-m-5 { width: calc(5 * 100% / 12); } - .m-col-m-6 { width: calc(6 * 100% / 12); } - .m-col-m-7 { width: calc(7 * 100% / 12); } - .m-col-m-8 { width: calc(8 * 100% / 12); } - .m-col-m-9 { width: calc(9 * 100% / 12); } - .m-col-m-10 { width: calc(10 * 100% / 12); } - .m-col-m-11 { width: calc(11 * 100% / 12); } - .m-col-m-12 { width: calc(12 * 100% / 12); } - .m-push-m-0 { left: calc(0 * 100% / 12); } - .m-push-m-1 { left: calc(1 * 100% / 12); } - .m-push-m-2 { left: calc(2 * 100% / 12); } - .m-push-m-3 { left: calc(3 * 100% / 12); } - .m-push-m-4 { left: calc(4 * 100% / 12); } - .m-push-m-5 { left: calc(5 * 100% / 12); } - .m-push-m-6 { left: calc(6 * 100% / 12); } - .m-push-m-7 { left: calc(7 * 100% / 12); } - .m-push-m-8 { left: calc(8 * 100% / 12); } - .m-push-m-9 { left: calc(9 * 100% / 12); } - .m-push-m-10 { left: calc(10 * 100% / 12); } - .m-push-m-11 { left: calc(11 * 100% / 12); } - .m-pull-m-0 { right: calc(0 * 100% / 12); } - .m-pull-m-1 { right: calc(1 * 100% / 12); } - .m-pull-m-2 { right: calc(2 * 100% / 12); } - .m-pull-m-3 { right: calc(3 * 100% / 12); } - .m-pull-m-4 { right: calc(4 * 100% / 12); } - .m-pull-m-5 { right: calc(5 * 100% / 12); } - .m-pull-m-6 { right: calc(6 * 100% / 12); } - .m-pull-m-7 { right: calc(7 * 100% / 12); } - .m-pull-m-8 { right: calc(8 * 100% / 12); } - .m-pull-m-9 { right: calc(9 * 100% / 12); } - .m-pull-m-10 { right: calc(10 * 100% / 12); } - .m-pull-m-11 { right: calc(11 * 100% / 12); } - .m-clearfix-s::after { display: none; } - .m-hide-m { display: none; } - .m-show-m { display: block; } - .m-col-m-none { - width: auto; - float: none; - } -} -@media screen and (min-width: 992px) { - .m-container { width: 960px; } - .m-container-inflatable .m-col-l-10 .m-container-inflate:not([class*='m-left-']):not([class*='m-right-']) { - margin-left: -10%; - margin-right: -10%; - } - .m-container-inflatable .m-col-l-10 .m-container-inflate.m-left-l { - margin-left: -10%; - } - .m-container-inflatable .m-col-l-10 .m-container-inflate.m-right-l { - margin-right: -10%; - } - [class*='m-col-l-'] { float: left; } - .m-left-l { - padding-right: 1rem; - float: left; - } - .m-right-l, [class*='m-col-l-'].m-right-l { - padding-left: 1rem; - float: right; - } - .m-center-l, [class*='m-col-l-'].m-center-l { - margin-left: auto; - margin-right: auto; - float: none; - } - .m-col-l-1 { width: calc(1 * 100% / 12); } - .m-col-l-2 { width: calc(2 * 100% / 12); } - .m-col-l-3 { width: calc(3 * 100% / 12); } - .m-col-l-4 { width: calc(4 * 100% / 12); } - .m-col-l-5 { width: calc(5 * 100% / 12); } - .m-col-l-6 { width: calc(6 * 100% / 12); } - .m-col-l-7 { width: calc(7 * 100% / 12); } - .m-col-l-8 { width: calc(8 * 100% / 12); } - .m-col-l-9 { width: calc(9 * 100% / 12); } - .m-col-l-10 { width: calc(10 * 100% / 12); } - .m-col-l-11 { width: calc(11 * 100% / 12); } - .m-col-l-12 { width: calc(12 * 100% / 12); } - .m-push-l-0 { left: calc(0 * 100% / 12); } - .m-push-l-1 { left: calc(1 * 100% / 12); } - .m-push-l-2 { left: calc(2 * 100% / 12); } - .m-push-l-3 { left: calc(3 * 100% / 12); } - .m-push-l-4 { left: calc(4 * 100% / 12); } - .m-push-l-5 { left: calc(5 * 100% / 12); } - .m-push-l-6 { left: calc(6 * 100% / 12); } - .m-push-l-7 { left: calc(7 * 100% / 12); } - .m-push-l-8 { left: calc(8 * 100% / 12); } - .m-push-l-9 { left: calc(9 * 100% / 12); } - .m-push-l-10 { left: calc(10 * 100% / 12); } - .m-push-l-11 { left: calc(11 * 100% / 12); } - .m-pull-l-0 { right: calc(0 * 100% / 12); } - .m-pull-l-1 { right: calc(1 * 100% / 12); } - .m-pull-l-2 { right: calc(2 * 100% / 12); } - .m-pull-l-3 { right: calc(3 * 100% / 12); } - .m-pull-l-4 { right: calc(4 * 100% / 12); } - .m-pull-l-5 { right: calc(5 * 100% / 12); } - .m-pull-l-6 { right: calc(6 * 100% / 12); } - .m-pull-l-7 { right: calc(7 * 100% / 12); } - .m-pull-l-8 { right: calc(8 * 100% / 12); } - .m-pull-l-9 { right: calc(9 * 100% / 12); } - .m-pull-l-10 { right: calc(10 * 100% / 12); } - .m-pull-l-11 { right: calc(11 * 100% / 12); } - .m-clearfix-m::after { display: none; } - .m-hide-l { display: none; } - .m-show-l { display: block; } - .m-col-l-none { - width: auto; - float: none; - } -} - -html { - font-size: 14px; - background-color: #e8e8e8; -} -body { - font-family: 'Roboto', sans-serif; - font-size: 14px; - line-height: normal; - color: #000000; -} -h1, -h2, -h3, -h4, -h5, -h6 { - margin-top: 0; - font-weight: 300; -} -h1 { - margin-bottom: 1rem; -} -h2, -h3, -h4, -h5, -h6 { - margin-bottom: 0.5rem; -} -p, -ul, -ol, -dl { - margin-top: 0; -} -ul, -ol { - padding-left: 2rem; -} -ul ol, -ul ul, -ol ol, -ol ul { - margin-bottom: 0; -} -main p { - text-indent: 1.5rem; - text-align: justify; -} -main p.m-noindent, -li > p, -dd > p, -table.m-table td > p { - text-indent: 0; - text-align: left; -} -blockquote { - margin-top: 0; - margin-left: 1rem; - margin-right: 1rem; - padding: 1rem; - border-left-style: solid; - border-left-width: 0.25rem; -} -hr { - width: 75%; - border-width: 0.0625rem; - border-style: solid; -} -blockquote, -hr { - border-color: #92d050; -} -strong, -.m-text.m-strong { - font-weight: bold; -} -em, -.m-text.m-em { - font-style: italic; -} -s, -.m-text.m-s { - text-decoration: line-through; -} -sub, -sup, -.m-text.m-sub, -.m-text.m-sup { - font-size: 0.75rem; - line-height: 0; - position: relative; - vertical-align: baseline; -} -sup, -.m-text.m-sup { - top: -0.35rem; -} -sub, -.m-text.m-sub { - bottom: -0.2rem; -} -abbr { - cursor: help; - text-decoration: underline dotted; -} -a { - color: #26a9e0; -} -a.m-flat { - text-decoration: none; -} -a:hover, -a:focus, -a:active { - color: #26a9e0; -} -a img { - border: 0; -} -svg a { - cursor: pointer; -} -mark { - padding: 0.0625rem; - background-color: #e6e69c; - color: #4c93d3; -} -.m-link-wrap { - word-break: break-all; -} -pre, -code { - font-family: 'Monaco', monospace, monospace, monospace; - font-size: 1em; - color: #000000; - background-color: #f7f7f7; -} -pre.m-console, -code.m-console { - color: #000000; - background-color: #f7f7f7; -} -pre { - padding: 0.5rem 1rem; - border-radius: 0px; - overflow-x: auto; - margin-top: 0; -} -pre.m-console-wrap { - white-space: pre-wrap; - word-break: break-all; -} -code { - padding: 0.125rem; -} -*:focus { - outline-color: #ffffff; -} -div.m-scroll { - max-width: 100%; - overflow-x: auto; -} -.m-fullwidth { - width: 100%; -} -.m-spacing-150 { - line-height: 1.5rem; -} -.m-text-center, -.m-text-center.m-noindent, -table.m-table th.m-text-center, -.m-text-center p { - text-align: center; -} -.m-text-left, -.m-text-left.m-noindent, -table.m-table th.m-text-left, -.m-text-right p { - text-align: left; -} -.m-text-right, -.m-text-right.m-noindent, -table.m-table th.m-text-right, -.m-text-right p { - text-align: right; -} -.m-text-top, -table.m-table th.m-text-top, -table.m-table td.m-text-top { - vertical-align: top; -} -.m-text-middle, -table.m-table th.m-text-middle, -table.m-table td.m-text-middle { - vertical-align: middle; -} -.m-text-bottom, -table.m-table th.m-text-bottom, -table.m-table td.m-text-bottom { - vertical-align: bottom; -} -.m-text.m-tiny { - font-size: 50%; -} -.m-text.m-small { - font-size: 85.4%; -} -.m-text.m-big { - font-size: 117%; -} -h1 .m-thin, -h2 .m-thin, -h3 .m-thin, -h4 .m-thin, -h5 .m-thin, -h6 .m-thin { - font-weight: normal; - font-size: 75%; - color: #bdbdbd; -} -ul.m-unstyled, -ol.m-unstyled { - list-style-type: none; - padding-left: 0; -} -ul[class*="m-block-"], -ol[class*="m-block-"] { - padding-left: 0; -} -ul[class*="m-block-"] li, -ol[class*="m-block-"] li { - display: inline; -} -ul[class*="m-block-bar-"] li:not(:last-child)::after, -ol[class*="m-block-bar-"] li:not(:last-child)::after { - content: " | "; -} -ul[class*="m-block-dot-"] li:not(:last-child)::after, -ol[class*="m-block-dot-"] li:not(:last-child)::after { - content: " • "; -} -@media screen and (min-width: 576px) { - ul.m-block-bar-s, - ol.m-block-bar-s, - ul.m-block-dot-s, - ol.m-block-dot-s { - padding-left: 2rem; - } - ul.m-block-bar-s li, - ol.m-block-bar-s li, - ul.m-block-dot-s li, - ol.m-block-dot-s li { - display: list-item; - } - ul.m-block-bar-s li:not(:last-child)::after, - ol.m-block-bar-s li:not(:last-child)::after, - ul.m-block-dot-s li:not(:last-child)::after, - ol.m-block-dot-s li:not(:last-child)::after { - content: ""; - } -} -@media screen and (min-width: 768px) { - ul.m-block-bar-m, - ol.m-block-bar-m, - ul.m-block-dot-m, - ol.m-block-dot-m { - padding-left: 2rem; - } - ul.m-block-bar-m li, - ol.m-block-bar-m li, - ul.m-block-dot-m li, - ol.m-block-dot-m li { - display: list-item; - } - ul.m-block-bar-m li:not(:last-child)::after, - ol.m-block-bar-m li:not(:last-child)::after, - ul.m-block-dot-m li:not(:last-child)::after, - ol.m-block-dot-m li:not(:last-child)::after { - content: ""; - } -} -@media screen and (min-width: 992px) { - ul.m-block-bar-l, - ol.m-block-bar-l, - ul.m-block-dot-l, - ol.m-block-dot-l { - padding-left: 2rem; - } - ul.m-block-bar-l li, - ol.m-block-bar-l li, - ul.m-block-dot-l li, - ol.m-block-dot-l li { - display: list-item; - } - ul.m-block-bar-l li:not(:last-child)::after, - ol.m-block-bar-l li:not(:last-child)::after, - ul.m-block-dot-l li:not(:last-child)::after, - ol.m-block-dot-l li:not(:last-child)::after { - content: ""; - } -} -p.m-poem { - text-indent: 0; - text-align: left; - margin-left: 1.5rem; -} -p.m-transition { - color: #ddd; - text-indent: 0; - text-align: center; - font-size: 2rem; -} -dl.m-diary { - margin-bottom: 1.25rem; -} -dl.m-diary:last-child { - margin-bottom: 0.25rem; -} -dl.m-diary dt { - font-weight: bold; - width: 6rem; - float: left; - clear: both; - padding-top: 0.25rem; -} -dl.m-diary dd { - padding-top: 0.25rem; - padding-left: 6rem; - margin-left: 0; -} -a.m-footnote, -dl.m-footnote dd span.m-footnote { - top: -0.35rem; - font-size: 0.75rem; - line-height: 0; - position: relative; - vertical-align: baseline; -} -a.m-footnote, -dl.m-footnote dd span.m-footnote a { - text-decoration: none; -} -a.m-footnote::before { - content: "["; -} -a.m-footnote::after { - content: "]"; -} -dl.m-footnote dt { - width: 1.5rem; - float: left; - clear: both; -} -dl.m-footnote dd { - margin-left: 1.5rem; -} -dl.m-footnote { - font-size: 85.4%; -} -dl.m-footnote dd span.m-footnote a { - font-weight: bold; - font-style: italic; -} -.m-container-inflatable { - background-color: #ffffff; -} -.m-note { - border-radius: 0px; - padding: 1rem; - border-width: 2px; - border-style: solid; - padding-top: 0; -} -.m-frame { - background-color: #e8e8e8; - border-style: solid; - border-width: 0.125rem; - border-radius: 0px; - border-color: #ddd; - padding: 0.875rem; -} -.m-block { - border-style: solid; - border-width: 0.0625rem; - border-left-width: 0.25rem; - border-radius: 0px; - border-color: #ddd; - padding: 0.9375rem 0.9375rem 0.9375rem 0.75rem; -} -.m-block.hr { - width: 75%; - border-width: 0.0625rem; - border-style: solid; - border-color: #92d050; -} -.m-block.m-row.hr { - width: 75%; - border-width: 0.0625rem; - border-style: solid; - border-color: #92d050; -} -.m-block.m-badge::after { - content: " "; - display: block; - clear: both; -} -.m-block.m-badge h3 { - margin-left: 5rem; -} -.m-block.m-badge p { - margin-left: 5rem; - text-indent: 0; -} -.m-block.m-badge img { - width: 4rem; - height: 4rem; - border-radius: 0.5rem; - float: left; -} -div.m-button { - text-align: center; -} -div.m-button a { - display: inline-block; - border-radius: 0.5rem; - padding-top: 0.75rem; - padding-bottom: 0.75rem; - padding-left: 1.5rem; - padding-right: 1.5rem; - text-decoration: none; - font-size: 1.17rem; -} -div.m-button.m-fullwidth a { - display: block; - padding-left: 0.5rem; - padding-right: 0.5rem; -} -div.m-button a .m-big:first-child { - font-size: 1.37rem; - font-weight: bold; -} -div.m-button a .m-small:last-child { - font-size: 0.854rem; -} -.m-label { - border-radius: 0.5rem; - font-size: 75%; - font-weight: normal; - padding: 0.125rem 0.25rem; - vertical-align: 7.5%; -} -.m-label.m-flat { - border-width: 0.0625rem; - border-style: solid; - border-color: #bdbdbd; - padding: 0.0625rem 0.1875rem; -} -table.m-table { - border-collapse: collapse; - margin-left: auto; - margin-right: auto; -} -table.m-table.m-big { - margin-top: 1.75rem; -} -div.m-scroll > table.m-table:last-child { - margin-bottom: 0.0625rem; -} -table.m-table:not(.m-flat) tbody tr:hover { - background-color: #ddd; -} -table.m-table tr, -table.m-table th, -table.m-table td { - vertical-align: top; - border-style: solid; - border-top-width: 0.0625rem; - border-left-width: 0; - border-right-width: 0; - border-bottom-width: 0; - border-color: #ddd; -} -table.m-table tr:first-child th, -table.m-table tr:first-child td { - vertical-align: top; - border-style: solid; - border-top-width: 0.0625rem; - border-left-width: 0; - border-right-width: 0; - border-bottom-width: 0; - border-color: #ddd; -} -table.m-table tr:last-child th, -table.m-table tr:last-child td { - vertical-align: top; - border-style: solid; - border-top-width: 0.0625rem; - border-bottom-width: 0.0625rem; - border-left-width: 0; - border-right-width: 0; - border-color: #ddd; -} -table.m-table caption { - padding-bottom: 0.5rem; -} -table.m-table thead tr:first-child th, -table.m-table thead tr:first-child td { - border-top-width: 0.125rem; - border-left-width: 0; - border-right-width: 0; -} -table.m-table thead th, -table.m-table thead td { - border-bottom-width: 0.125rem; - vertical-align: bottom; - border-left-width: 0; - border-right-width: 0; -} -table.m-table tfoot th, -table.m-table tfoot td { - border-top-width: 0.125rem; -} -table.m-table th, -table.m-table td { - padding: 0.5rem; -} -table.m-table.m-big th, -table.m-table.m-big td { - padding: 0.75rem 1rem; -} -table.m-table th { - text-align: left; -} -table.m-table th.m-thin { - font-weight: normal; -} -table.m-table td.m-default, -table.m-table th.m-default, -table.m-table td.m-primary, -table.m-table th.m-primary, -table.m-table td.m-success, -table.m-table th.m-success, -table.m-table td.m-warning, -table.m-table th.m-warning, -table.m-table td.m-danger, -table.m-table th.m-danger, -table.m-table td.m-info, -table.m-table th.m-info, -table.m-table td.m-dim, -table.m-table th.m-dim, -table.m-table td.m-type, -table.m-table th.m-type { - padding-left: 0.4375rem; - padding-right: 0.4375rem; - border-left-width: 0.0625rem; -} -table.m-table.m-big td.m-default, -table.m-table.m-big th.m-default, -table.m-table.m-big td.m-primary, -table.m-table.m-big th.m-primary, -table.m-table.m-big td.m-success, -table.m-table.m-big th.m-success, -table.m-table.m-big td.m-warning, -table.m-table.m-big th.m-warning, -table.m-table.m-big td.m-danger, -table.m-table.m-big th.m-danger, -table.m-table.m-big td.m-info, -table.m-table.m-big th.m-info, -table.m-table.m-big td.m-dim, -table.m-table.m-big th.m-dim, -table.m-table.m-big td.m-type, -table.m-table.m-big th.m-type { - padding-left: 0.9375rem; - padding-right: 0.9375rem; - border-left-width: 0.0625rem; -} -table.m-table tr.m-default td, -table.m-table td.m-default, -table.m-table tr.m-default th, -table.m-table th.m-default, -table.m-table tr.m-primary td, -table.m-table td.m-primary, -table.m-table tr.m-primary th, -table.m-table th.m-primary, -table.m-table tr.m-success td, -table.m-table td.m-success, -table.m-table tr.m-success th, -table.m-table th.m-success, -table.m-table tr.m-warning td, -table.m-table td.m-warning, -table.m-table tr.m-warning th, -table.m-table th.m-warning, -table.m-table tr.m-danger td, -table.m-table td.m-danger, -table.m-table tr.m-danger th, -table.m-table th.m-danger, -table.m-table tr.m-info td, -table.m-table td.m-info, -table.m-table tr.m-info th, -table.m-table th.m-info, -table.m-table tr.m-dim td, -table.m-table td.m-dim, -table.m-table tr.m-dim th, -table.m-table th.m-dim, -table.m-table tr.m-type td, -table.m-table td.m-type, -table.m-table tr.m-type th, -table.m-table th.m-type { - border-color: #e8e8e8; -} -.m-note pre, -.m-note code, -table.m-table tr.m-default pre, -table.m-table tr.m-default code, -table.m-table td.m-default pre, -table.m-table td.m-default code, -table.m-table th.m-default pre, -table.m-table th.m-default code, -table.m-table tr.m-primary pre, -table.m-table tr.m-primary code, -table.m-table td.m-primary pre, -table.m-table td.m-primary code, -table.m-table th.m-primary pre, -table.m-table th.m-primary code, -table.m-table tr.m-success pre, -table.m-table tr.m-success code, -table.m-table td.m-success pre, -table.m-table td.m-success code, -table.m-table th.m-success pre, -table.m-table th.m-success code, -table.m-table tr.m-warning pre, -table.m-table tr.m-warning code, -table.m-table td.m-warning pre, -table.m-table td.m-warning code, -table.m-table th.m-warning pre, -table.m-table th.m-warning code, -table.m-table tr.m-danger pre, -table.m-table tr.m-danger code, -table.m-table td.m-danger pre, -table.m-table td.m-danger code, -table.m-table th.m-danger pre, -table.m-table th.m-danger code, -table.m-table tr.m-info pre, -table.m-table tr.m-info code, -table.m-table td.m-info pre, -table.m-table td.m-info code, -table.m-table th.m-info pre, -table.m-table th.m-info code, -table.m-table tr.m-dim pre, -table.m-table tr.m-dim code, -table.m-table td.m-dim pre, -table.m-table td.m-dim code, -table.m-table th.m-dim pre, -table.m-table th.m-dim code, -table.m-table tr.m-type pre, -table.m-table tr.m-type code, -table.m-table td.m-type pre, -table.m-table td.m-type code, -table.m-table th.m-type pre, -table.m-table th.m-type code { - background-color: rgba(251, 240, 236, 0.5); -} -img.m-image, -svg.m-image { - display: block; - margin-left: auto; - margin-right: auto; -} -div.m-image { - text-align: center; -} -img.m-image, -svg.m-image, -div.m-image img, -div.m-image svg { - max-width: 100%; - border-radius: 0px; -} -div.m-image.m-fullwidth img, -div.m-image.m-fullwidth svg { - width: 100%; -} -img.m-image.m-badge, -div.m-image.m-badge img { - border-radius: 50%; -} -figure.m-figure { - max-width: 100%; - margin-top: 0; - margin-left: auto; - margin-right: auto; - position: relative; - display: table; -} -figure.m-figure:before { - position: absolute; - content: " "; - top: 0; - bottom: 0; - left: 0; - right: 0; - z-index: -1; - border-style: solid; - border-width: 0.125rem; - border-radius: 0px; - border-color: #ddd; -} -figure.m-figure.m-flat:before { - border-color: transparent; -} -figure.m-figure > * { - margin-left: 1rem; - margin-right: 1rem; - display: table-caption; - caption-side: bottom; -} -figure.m-figure > *:first-child { - display: inline; -} -figure.m-figure > *:last-child { - margin-bottom: 1rem !important; -} -figure.m-figure img, -figure.m-figure svg { - position: relative; - margin-left: 0; - margin-right: 0; - margin-bottom: 0; - border-top-left-radius: 0px; - border-top-right-radius: 0px; - max-width: 100%; -} -figure.m-figure.m-flat img, -figure.m-figure.m-flat svg { - border-bottom-left-radius: 0px; - border-bottom-right-radius: 0px; -} -figure.m-figure a img, -figure.m-figure a svg { - margin-left: -1rem; - margin-right: -1rem; -} -figure.m-figure.m-fullwidth, -figure.m-figure.m-fullwidth > * { - display: block; -} -figure.m-figure.m-fullwidth > *:first-child { - display: inline; -} -figure.m-figure.m-fullwidth img, -figure.m-figure.m-fullwidth svg { - width: 100%; -} -figure.m-figure.m-fullwidth:after { - content: " "; - display: block; - margin-top: 1rem; - height: 1px; -} -.m-code-figure, -.m-console-figure { - margin-top: 0; - margin-left: 0; - margin-right: 0; - position: relative; - padding: 1rem; -} -.m-code-figure:before, -.m-console-figure:before { - position: absolute; - content: " "; - top: 0; - bottom: 0; - left: 0; - right: 0; - z-index: -1; - border-style: solid; - border-width: 0.125rem; - border-radius: 0px; -} -.m-code-figure:before { - border-color: #f7f7f7; -} -.m-console-figure:before { - border-color: #f7f7f7; -} -.m-code-figure.m-flat:before, -.m-console-figure.m-flat:before { - border-color: transparent; -} -.m-code-figure > pre:first-child, -.m-console-figure > pre:first-child { - position: relative; - margin: -1rem -1rem 1rem -1rem; - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; -} -.m-code-figure > pre.m-nopad, -.m-console-figure > pre.m-nopad { - margin-left: -0.875rem; - margin-right: -0.875rem; - margin-top: -1rem; - margin-bottom: -0.875rem; - padding-left: 0.875rem; -} -figure.m-figure figcaption, -.m-code-figure figcaption, -.m-console-figure figcaption { - margin-top: 0.5rem; - margin-bottom: 0.5rem; - font-weight: 300; - font-size: 1.17rem; -} -.m-imagegrid > div { - background-color: var( - --background-color - ); -} -.m-imagegrid > div > figure { - display: block; - float: left; - position: relative; - margin: 0; -} -.m-imagegrid > div > figure > div, -.m-imagegrid > div > figure > figcaption, -.m-imagegrid > div > figure > a > div, -.m-imagegrid > div > figure > a > figcaption { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - border-color: #e8e8e8; - border-style: solid; - border-width: 0.25rem; - padding: 0.5rem; -} -.m-imagegrid > div > figure:first-child > div, -.m-imagegrid > div > figure:first-child > figcaption, -.m-imagegrid > div > figure:first-child > a > div, -.m-imagegrid > div > figure:first-child > a > figcaption { - border-left-width: 0; -} -.m-imagegrid > div > figure:last-child > div, -.m-imagegrid > div > figure:last-child > figcaption, -.m-imagegrid > div > figure:last-child > a > div, -.m-imagegrid > div > figure:last-child > a > figcaption { - border-right-width: 0; -} -.m-imagegrid > div > figure > figcaption, -.m-imagegrid > div > figure > a > figcaption { - color: transparent; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-size: 0.75rem; -} -.m-imagegrid > div > figure > div::before, -.m-imagegrid > div > figure > figcaption::before, -.m-imagegrid > div > figure > a > div::before, -.m-imagegrid > div > figure > a > figcaption::before { - content: ""; - display: inline-block; - height: 100%; - vertical-align: bottom; - width: 0; -} -.m-imagegrid > div > figure:hover > figcaption, -.m-imagegrid > div > figure:hover > a > figcaption { - background: linear-gradient( - transparent 0%, - transparent 75%, - rgba(0, 0, 0, 0.85) 100% - ); - color: #ffffff; -} -.m-imagegrid > div > figure > img, -.m-imagegrid > div > figure > a > img { - width: 100%; - height: 100%; -} -.m-imagegrid > div::after { - display: block; - content: " "; - clear: both; -} -@media screen and (max-width: 767px) { - .m-imagegrid > div > figure { - float: none; - width: 100% !important; - } - .m-imagegrid > div > figure > div, - .m-imagegrid > div > figure > figcaption, - .m-imagegrid > div > figure > a > div, - .m-imagegrid > div > figure > a > figcaption { - border-left-width: 0; - border-right-width: 0; - } -} -.m-container-inflatable > .m-row > [class*="m-col-"] > .m-note, -.m-container-inflatable > .m-row > [class*="m-col-"] > .m-frame, -.m-container-inflatable > .m-row > [class*="m-col-"] > .m-block, -.m-container-inflatable > .m-row > [class*="m-col-"] > .m-imagegrid, -.m-container-inflatable > .m-row > [class*="m-col-"] > pre, -.m-container-inflatable > .m-row > [class*="m-col-"] > .m-code-figure, -.m-container-inflatable > .m-row > [class*="m-col-"] > .m-console-figure, -.m-container-inflatable > .m-row > [class*="m-col-"] section > .m-note, -.m-container-inflatable > .m-row > [class*="m-col-"] section > .m-frame, -.m-container-inflatable > .m-row > [class*="m-col-"] section > .m-block, -.m-container-inflatable > .m-row > [class*="m-col-"] section > .m-imagegrid, -.m-container-inflatable > .m-row > [class*="m-col-"] section > pre, -.m-container-inflatable > .m-row > [class*="m-col-"] section > .m-code-figure, -.m-container-inflatable - > .m-row - > [class*="m-col-"] - section - > .m-console-figure, -.m-container-inflatable [class*="m-center-"] > .m-note, -.m-container-inflatable [class*="m-center-"] > .m-frame, -.m-container-inflatable [class*="m-center-"] > .m-block, -.m-container-inflatable [class*="m-center-"] > .m-imagegrid, -.m-container-inflatable [class*="m-center-"] > pre, -.m-container-inflatable [class*="m-center-"] > .m-code-figure, -.m-container-inflatable [class*="m-center-"] > .m-console-figure, -.m-container-inflatable [class*="m-left-"] > .m-note, -.m-container-inflatable [class*="m-left-"] > .m-frame, -.m-container-inflatable [class*="m-left-"] > .m-block, -.m-container-inflatable [class*="m-left-"] > .m-imagegrid, -.m-container-inflatable [class*="m-left-"] > pre, -.m-container-inflatable [class*="m-left-"] > .m-code-figure, -.m-container-inflatable [class*="m-left-"] > .m-console-figure, -.m-container-inflatable [class*="m-right-"] > .m-note, -.m-container-inflatable [class*="m-right-"] > .m-frame, -.m-container-inflatable [class*="m-right-"] > .m-block, -.m-container-inflatable [class*="m-right-"] > .m-imagegrid, -.m-container-inflatable [class*="m-right-"] > pre, -.m-container-inflatable [class*="m-right-"] > .m-code-figure, -.m-container-inflatable [class*="m-right-"] > .m-console-figure, -.m-container-inflatable .m-container-inflate > .m-note, -.m-container-inflatable .m-container-inflate > .m-frame, -.m-container-inflatable .m-container-inflate > .m-block, -.m-container-inflatable .m-container-inflate > .m-imagegrid, -.m-container-inflatable .m-container-inflate > pre, -.m-container-inflatable .m-container-inflate > .m-code-figure, -.m-container-inflatable .m-container-inflate > .m-console-figure { - margin-left: -1rem; - margin-right: -1rem; -} -@media screen and (min-width: 576px) { - .m-container-inflatable .m-center-s > .m-note, - .m-container-inflatable .m-center-s > .m-frame, - .m-container-inflatable .m-center-s > .m-block, - .m-container-inflatable .m-center-s > .m-imagegrid, - .m-container-inflatable .m-center-s > pre, - .m-container-inflatable .m-center-s > .m-code-figure, - .m-container-inflatable .m-center-s > .m-console-figure { - margin-left: -1rem; - margin-right: -1rem; - } - .m-container-inflatable .m-left-s > .m-note, - .m-container-inflatable .m-left-s > .m-frame, - .m-container-inflatable .m-left-s > .m-block, - .m-container-inflatable .m-left-s > .m-imagegrid, - .m-container-inflatable .m-left-s > pre, - .m-container-inflatable .m-left-s > .m-code-figure, - .m-container-inflatable .m-left-s > .m-console-figure { - margin-left: -1rem; - margin-right: 0; - } - .m-container-inflatable .m-right-s > .m-note, - .m-container-inflatable .m-right-s > .m-frame, - .m-container-inflatable .m-right-s > .m-block, - .m-container-inflatable .m-right-s > .m-imagegrid, - .m-container-inflatable .m-right-s > pre, - .m-container-inflatable .m-right-s > .m-code-figure, - .m-container-inflatable .m-right-s > .m-console-figure { - margin-left: 0; - margin-right: -1rem; - } - .m-container-inflatable - > .m-row - > .m-col-s-10 - > .m-imagegrid.m-container-inflate, - .m-container-inflatable - > .m-row - > .m-col-s-10 - section - > .m-imagegrid.m-container-inflate { - margin-left: -10%; - margin-right: -10%; - } -} -@media screen and (min-width: 768px) { - .m-container-inflatable .m-center-m > .m-note, - .m-container-inflatable .m-center-m > .m-frame, - .m-container-inflatable .m-center-m > .m-block, - .m-container-inflatable .m-center-m > .m-imagegrid, - .m-container-inflatable .m-center-m > pre, - .m-container-inflatable .m-center-m > .m-code-figure, - .m-container-inflatable .m-center-m > .m-console-figure { - margin-left: -1rem; - margin-right: -1rem; - } - .m-container-inflatable .m-left-m > .m-note, - .m-container-inflatable .m-left-m > .m-frame, - .m-container-inflatable .m-left-m > .m-block, - .m-container-inflatable .m-left-m > .m-imagegrid, - .m-container-inflatable .m-left-m > pre, - .m-container-inflatable .m-left-m > .m-code-figure, - .m-container-inflatable .m-left-m > .m-console-figure { - margin-left: -1rem; - margin-right: 0; - } - .m-container-inflatable .m-right-m > .m-note, - .m-container-inflatable .m-right-m > .m-frame, - .m-container-inflatable .m-right-m > .m-block, - .m-container-inflatable .m-right-m > .m-imagegrid, - .m-container-inflatable .m-right-m > pre, - .m-container-inflatable .m-right-m > .m-code-figure, - .m-container-inflatable .m-right-m > .m-console-figure { - margin-left: 0; - margin-right: -1rem; - } - .m-container-inflatable - > .m-row - > .m-col-m-10 - > .m-imagegrid.m-container-inflate, - .m-container-inflatable - > .m-row - > .m-col-m-10 - section - > .m-imagegrid.m-container-inflate { - margin-left: -10%; - margin-right: -10%; - } -} -@media screen and (min-width: 992px) { - .m-container-inflatable .m-center-l > .m-note, - .m-container-inflatable .m-center-l > .m-frame, - .m-container-inflatable .m-center-l > .m-block, - .m-container-inflatable .m-center-l > .m-imagegrid, - .m-container-inflatable .m-center-l > pre, - .m-container-inflatable .m-center-l > .m-code-figure, - .m-container-inflatable .m-center-l > .m-console-figure { - margin-left: -1rem; - margin-right: -1rem; - } - .m-container-inflatable .m-left-l > .m-note, - .m-container-inflatable .m-left-l > .m-frame, - .m-container-inflatable .m-left-l > .m-block, - .m-container-inflatable .m-left-l > .m-imagegrid, - .m-container-inflatable .m-left-l > pre, - .m-container-inflatable .m-left-l > .m-code-figure, - .m-container-inflatable .m-left-l > .m-console-figure { - margin-left: -1rem; - margin-right: 0; - } - .m-container-inflatable .m-right-l > .m-note, - .m-container-inflatable .m-right-l > .m-frame, - .m-container-inflatable .m-right-l > .m-block, - .m-container-inflatable .m-right-l > .m-imagegrid, - .m-container-inflatable .m-right-l > pre, - .m-container-inflatable .m-right-l > .m-code-figure, - .m-container-inflatable .m-right-l > .m-console-figure { - margin-left: 0; - margin-right: -1rem; - } - .m-container-inflatable - > .m-row - > .m-col-l-10 - > .m-imagegrid.m-container-inflate, - .m-container-inflatable - > .m-row - > .m-col-l-10 - section - > .m-imagegrid.m-container-inflate { - margin-left: -10%; - margin-right: -10%; - } -} -pre.m-code span.hll { - margin-left: -1rem; - margin-right: -1rem; - padding-left: 1rem; -} -.m-code.m-inverted > span, -.m-console.m-inverted > span { - opacity: 0.3333; -} -.m-code.m-inverted > span.hll, -.m-console.m-inverted > span.hll { - opacity: 1; - background-color: transparent; - border-color: transparent; -} -.m-code.m-inverted { - color: rgba(91, 91, 91, 0.33); -} -.m-console.m-inverted { - color: rgba(91, 91, 91, 0.33); -} -.m-code.m-inverted > span.hll { - color: #000000; -} -.m-cosole.m-inverted > span.hll { - color: #000000; -} -.m-code-color { - display: inline-block; - width: 0.75rem; - height: 0.75rem; - vertical-align: -0.05rem; - margin-left: 0.2rem; - margin-right: 0.1rem; - border-radius: 0px; -} -div.m-math { - overflow-x: auto; - overflow-y: hidden; -} -div.m-math svg { - margin-left: auto; - margin-right: auto; - display: block; -} -div.m-button a svg.m-math { - fill: #ffffff; -} -div.m-button.m-flat a svg.m-math { - fill: #000000; -} -div.m-button.m-flat a:hover svg.m-math, -div.m-button.m-default a:focus svg.m-math, -div.m-button.m-default a:active svg.m-math { - fill: #26a9e0; -} -.m-graph { - font-size: 14px; -} -div.m-plot svg, -div.m-graph svg { - max-width: 100%; - margin-left: auto; - margin-right: auto; - display: block; -} -div.m-plot .m-background { - fill: #fbf0ec; -} -div.m-plot svg .m-label { - font-size: 11px; -} -div.m-plot svg .m-title { - font-size: 13px; -} -div.m-plot svg .m-label, -div.m-plot svg .m-title { - fill: #000000; -} -div.m-plot svg .m-line { - stroke: #000000; - stroke-width: 0.8; -} -div.m-plot svg .m-error { - stroke: #000000; - stroke-width: 1.5; -} -div.m-plot svg .m-label.m-dim { - fill: #bdbdbd; -} -.m-graph g.m-edge path, -.m-graph g.m-node.m-flat ellipse, -.m-graph g.m-node.m-flat polygon { - fill: none; -} -.m-graph g.m-node:not(.m-flat) text { - fill: #ffffff; -} -figure.m-figure > svg.m-math:first-child, -figure.m-figure > svg.m-graph:first-child { - padding: 1rem; - box-sizing: content-box; -} -figure.m-figure:not(.m-flat) > svg.m-math:first-child, -figure.m-figure:not(.m-flat) > svg.m-graph:first-child { - background-color: #ddd; -} -.m-block.m-default { - border-left-color: #ddd; -} -.m-block.m-default h3, -.m-block.m-default h4, -.m-block.m-default h5, -.m-block.m-default h6, -.m-text.m-default, -.m-label.m-flat.m-default { - color: #000000; -} -.m-block.m-default h3 a, -.m-block.m-default h4 a, -.m-block.m-default h5 a, -.m-block.m-default h6 a { - color: #26a9e0; -} -.m-block.m-primary { - border-left-color: #31708f; -} -.m-block.m-primary h3, -.m-block.m-primary h4, -.m-block.m-primary h5, -.m-block.m-primary h6, -.m-block.m-primary h3 a, -.m-block.m-primary h4 a, -.m-block.m-primary h5 a, -.m-block.m-primary h6 a, -.m-text.m-primary, -.m-label.m-flat.m-primary { - color: #31708f; -} -.m-block.m-success { - border-left-color: #9ad36a; -} -.m-block.m-success h3, -.m-block.m-success h4, -.m-block.m-success h5, -.m-block.m-success h6, -.m-block.m-success h3 a, -.m-block.m-success h4 a, -.m-block.m-success h5 a, -.m-block.m-success h6 a, -.m-text.m-success, -.m-label.m-flat.m-success { - color: #9ad36a; -} -.m-block.m-warning { - border-left-color: #f9cf79; -} -.m-block.m-warning h3, -.m-block.m-warning h4, -.m-block.m-warning h5, -.m-block.m-warning h6, -.m-block.m-warning h3 a, -.m-block.m-warning h4 a, -.m-block.m-warning h5 a, -.m-block.m-warning h6 a, -.m-text.m-warning, -.m-label.m-flat.m-warning { - color: #f9cf79; -} -.m-block.m-danger { - border-left-color: #f60000; -} -.m-block.m-danger h3, -.m-block.m-danger h4, -.m-block.m-danger h5, -.m-block.m-danger h6, -.m-block.m-danger h3 a, -.m-block.m-danger h4 a, -.m-block.m-danger h5 a, -.m-block.m-danger h6 a, -.m-text.m-danger, -.m-label.m-flat.m-danger { - color: #f60000; -} -.m-block.m-info { - border-left-color: #31708f; -} -.m-block.m-info h3, -.m-block.m-info h4, -.m-block.m-info h5, -.m-block.m-info h6, -.m-block.m-info h3 a, -.m-block.m-info h4 a, -.m-block.m-info h5 a, -.m-block.m-info h6 a, -.m-text.m-info, -.m-label.m-flat.m-info { - color: #31708f; -} -.m-block.m-type h3, -.m-block.m-type h4, -.m-block.m-type h5, -.m-block.m-type h6, -.m-block.m-type h3 a, -.m-block.m-type h4 a, -.m-block.m-type h5 a, -.m-block.m-type h6 a, -.m-text.m-type, -.m-label.m-flat.m-type { - color: #9ad36a; -} -.m-block.m-dim { - border-left-color: #bdbdbd; -} -span.m-default { - color: #000000; - background-color: transparent; -} -span.m-default a { - color: var(--default-link-color); - background-color: transparent; -} -span.m-default a:hover, -span.m-default a:focus, -span.m-default a:active { - color: #26a9e0; - background-color: transparent; -} -span.m-primary { - color: #31708f; - background-color: transparent; -} -span.m-primary a { - color: var(--primary-link-color); - background-color: transparent; -} -span.m-primary a:hover, -span.m-primary a:focus, -span.m-primary a:active { - color: #31708f; - background-color: transparent; -} -span.m-success { - color: #9ad36a; - background-color: transparent; -} -span.m-success a { - color: var(--success-link-color); - background-color: transparent; -} -span.m-success a:hover, -span.m-success a:focus, -span.m-success a:active { - color: #9ad36a; - background-color: transparent; -} -span.m-warning { - color: #f9cf79; - background-color: transparent; -} -span.m-warning a { - color: var(--warning-link-color); - background-color: transparent; -} -span.m-warning a:hover, -span.m-warning a:focus, -span.m-warning a:active { - color: #f9cf79; - background-color: transparent; -} -span.m-danger { - color: #f60000; - background-color: transparent; -} -span.m-danger a { - color: var(--danger-link-color); - background-color: transparent; -} -span.m-danger a:hover, -span.m-danger a:focus, -span.m-danger a:active { - color: #f60000; - background-color: transparent; -} -span.m-info { - color: #31708f; - background-color: transparent; -} -span.m-info a { - color: var(--info-link-color); - background-color: transparent; -} -span.m-info a:hover, -span.m-info a:focus, -span.m-info a:active { - color: #67cce0; - background-color: transparent; -} -span.m-type { - color: #9ad36a; - background-color: transparent; -} -span.m-type a { - color: var(--type-link-color); - background-color: transparent; -} -span.m-type a:hover, -span.m-type a:focus, -span.m-type a:active { - color: #9ad36a; - background-color: transparent; -} -span.m-dim { - color: #bdbdbd; - background-color: transparent; -} -span.m-dim a { - color: #c0c0c0; - background-color: transparent; -} -span.m-dim a:hover, -span.m-dim a:focus, -span.m-dim a:active { - color: #949494; - background-color: transparent; -} -.m-block.m-dim, -.m-text.m-dim, -.m-label.m-flat.m-dim { - color: #bdbdbd; -} -.m-block.m-dim a, -.m-text.m-dim a { - color: #c0c0c0; -} -.m-block.m-dim a:hover, -.m-block.m-dim a:focus, -.m-block.m-dim a:active, -.m-text.m-dim a:hover, -.m-text.m-dim a:focus, -.m-text.m-dim a:active { - color: #949494; -} -.m-block.m-type { - border-left-color: #9ad36a; -} -.m-block.m-type h3, -.m-block.m-type h4, -.m-block.m-type h5, -.m-block.m-type h6, -.m-block.m-type h3 a, -.m-block.m-type h4 a, -.m-block.m-type h5 a, -.m-block.m-type h6 a, -.m-text.m-type, -.m-label.m-flat.m-type { - color: #9ad36a; -} -.m-block.m-flat { - border-color: transparent; -} -.m-block.m-flat h3, -.m-block.m-flat h4, -.m-block.m-flat h5, -.m-block.m-flat h6 { - color: #000000; -} -.m-block.m-default h3 a:hover, -.m-block.m-default h3 a:focus, -.m-block.m-default h3 a:active, -.m-block.m-default h4 a:hover, -.m-block.m-default h4 a:focus, -.m-block.m-default h4 a:active, -.m-block.m-default h5 a:hover, -.m-block.m-default h5 a:focus, -.m-block.m-default h5 a:active, -.m-block.m-default h6 a:hover, -.m-block.m-default h6 a:focus, -.m-block.m-default h6 a:active { - color: #26a9e0; -} -.m-block.m-primary h3 a:hover, -.m-block.m-primary h3 a:focus, -.m-block.m-primary h3 a:active, -.m-block.m-primary h4 a:hover, -.m-block.m-primary h4 a:focus, -.m-block.m-primary h4 a:active, -.m-block.m-primary h5 a:hover, -.m-block.m-primary h5 a:focus, -.m-block.m-primary h5 a:active, -.m-block.m-primary h6 a:hover, -.m-block.m-primary h6 a:focus, -.m-block.m-primary h6 a:active { - color: #31708f; -} -.m-block.m-success h3 a:hover, -.m-block.m-success h3 a:focus, -.m-block.m-success h3 a:active, -.m-block.m-success h4 a:hover, -.m-block.m-success h4 a:focus, -.m-block.m-success h4 a:active, -.m-block.m-success h5 a:hover, -.m-block.m-success h5 a:focus, -.m-block.m-success h5 a:active, -.m-block.m-success h6 a:hover, -.m-block.m-success h6 a:focus, -.m-block.m-success h6 a:active { - color: #9ad36a; -} -.m-block.m-warning h3 a:hover, -.m-block.m-warning h3 a:focus, -.m-block.m-warning h3 a:active, -.m-block.m-warning h4 a:hover, -.m-block.m-warning h4 a:focus, -.m-block.m-warning h4 a:active, -.m-block.m-warning h5 a:hover, -.m-block.m-warning h5 a:focus, -.m-block.m-warning h5 a:active, -.m-block.m-warning h6 a:hover, -.m-block.m-warning h6 a:focus, -.m-block.m-warning h6 a:active { - color: #f9cf79; -} -.m-block.m-danger h3 a:hover, -.m-block.m-danger h3 a:focus, -.m-block.m-danger h3 a:active, -.m-block.m-danger h4 a:hover, -.m-block.m-danger h4 a:focus, -.m-block.m-danger h4 a:active, -.m-block.m-danger h5 a:hover, -.m-block.m-danger h5 a:focus, -.m-block.m-danger h5 a:active, -.m-block.m-danger h6 a:hover, -.m-block.m-danger h6 a:focus, -.m-block.m-danger h6 a:active { - color: #f60000; -} -.m-block.m-info h3 a:hover, -.m-block.m-info h3 a:focus, -.m-block.m-info h3 a:active, -.m-block.m-info h4 a:hover, -.m-block.m-info h4 a:focus, -.m-block.m-info h4 a:active, -.m-block.m-info h5 a:hover, -.m-block.m-info h5 a:focus, -.m-block.m-info h5 a:active, -.m-block.m-info h6 a:hover, -.m-block.m-info h6 a:focus, -.m-block.m-info h6 a:active { - color: #67cce0; -} -.m-block.m-type h3 a:hover, -.m-block.m-type h3 a:focus, -.m-block.m-type h3 a:active, -.m-block.m-type h4 a:hover, -.m-block.m-type h4 a:focus, -.m-block.m-type h4 a:active, -.m-block.m-type h5 a:hover, -.m-block.m-type h5 a:focus, -.m-block.m-type h5 a:active, -.m-block.m-type h6 a:hover, -.m-block.m-type h6 a:focus, -.m-block.m-type h6 a:active { - color: #9ad36a; -} -div.m-button a, -.m-label { - color: #ffffff; -} -div.m-button.m-flat a { - color: #000000; -} -div.m-button.m-flat a:hover, -div.m-button.m-default a:focus, -div.m-button.m-default a:active { - color: #26a9e0; -} -div.m-button.m-default a, -.m-label:not(.m-flat).m-default { - background-color: #000000; - color: #000000; -} -div.m-button.m-primary a, -.m-label:not(.m-flat).m-primary { - background-color: #31708f; - color: #67cce0; -} -div.m-button.m-success a, -.m-label:not(.m-flat).m-success { - background-color: #9ad36a; - color: #3c763d; -} -div.m-button.m-warning a, -.m-label:not(.m-flat).m-warning { - background-color: #f9cf79; - color: #8a6d3b; -} -div.m-button.m-danger a, -.m-label:not(.m-flat).m-danger { - background-color: #f60000; - color: #920000; -} -div.m-button.m-info a, -.m-label:not(.m-flat).m-info { - background-color: #31708f; - color: #81bcda; -} -div.m-button.m-dim a, -.m-label:not(.m-flat).m-dim { - background-color: #bdbdbd; - color: #7c7c7c; -} -div.m-button.m-type a, -.m-label:not(.m-flat).m-type { - background-color: #9ad36a; - color: #3c763d; -} -div.m-button.m-default a:hover, -div.m-button.m-default a:focus, -div.m-button.m-default a:active { - background-color: #26a9e0; -} -div.m-button.m-primary a:hover, -div.m-button.m-primary a:focus, -div.m-button.m-primary a:active { - background-color: #31708f; -} -div.m-button.m-success a:hover, -div.m-button.m-success a:focus, -div.m-button.m-success a:active { - background-color: #9ad36a; -} -div.m-button.m-warning a:hover, -div.m-button.m-warning a:focus, -div.m-button.m-warning a:active { - background-color: #f9cf79; -} -div.m-button.m-danger a:hover, -div.m-button.m-danger a:focus, -div.m-button.m-danger a:active { - background-color: #f60000; -} -div.m-button.m-info a:hover, -div.m-button.m-info a:focus, -div.m-button.m-info a:active { - background-color: #67cce0; -} -div.m-button.m-dim a:hover, -div.m-button.m-dim a:focus, -div.m-button.m-dim a:active { - background-color: #c0c0c0; -} -div.m-button.m-type a:hover, -div.m-button.m-type a:focus, -div.m-button.m-type a:active { - background-color: #9ad36a; -} -.m-note.m-default { - background-color: transparent; - border-width: 2px; - border-style: solid; - padding-top: 0; -} -.m-note.m-default, -table.m-table tr.m-default td, -table.m-table td.m-default, -table.m-table tr.m-default th, -table.m-table th.m-default, -table.m-table tr.m-default strong, -table.m-table strong.m-default, -table.m-table tr.m-default em, -table.m-table em.m-default { - color: #000000; -} -.m-note.m-default a:hover, -table.m-table tr.m-default td a:hover, -table.m-table td.m-default a:hover, -table.m-table tr.m-default th a:hover, -table.m-table th.m-default a:hover, -.m-note.m-default a:focus, -table.m-table tr.m-default td a:focus, -table.m-table td.m-default a:focus, -table.m-table tr.m-default th a:focus, -table.m-table th.m-default a:focus, -.m-note.m-default a:active, -table.m-table tr.m-default td a:active, -table.m-table td.m-default a:active, -table.m-table tr.m-default th a:active, -table.m-table th.m-default a:active { - color: #26a9e0; -} -.m-note.m-primary { - border-color: #31708f; - border-width: 2px; - border-style: solid; - padding-top: 0; -} -.m-note.m-primary a, -table.m-table tr.m-primary td a, -table.m-table td.m-primary a, -table.m-table tr.m-primary th a, -table.m-table th.m-primary a { - color: #26a9e0; -} -.m-note.m-primary, -table.m-table tr.m-primary td, -table.m-table td.m-primary, -table.m-table tr.m-primary th, -table.m-table th.m-primary, -table.m-table tr.m-primary strong, -table.m-table strong.m-primary, -table.m-table tr.m-primary em, -table.m-table em.m-primary { - background-color: transparent; - color: #67cce0; -} -.m-note.m-primary a, -table.m-table tr.m-primary td a, -table.m-table td.m-primary a, -table.m-table tr.m-primary th a, -table.m-table th.m-primary a { - color: #31708f; -} -.m-note.m-primary a:hover, -table.m-table tr.m-primary td a:hover, -table.m-table td.m-primary a:hover, -table.m-table tr.m-primary th a:hover, -table.m-table th.m-primary a:hover, -.m-note.m-primary a:focus, -table.m-table tr.m-primary td a:focus, -table.m-table td.m-primary a:focus, -table.m-table tr.m-primary th a:focus, -table.m-table th.m-primary a:focus, -.m-note.m-primary a:active, -table.m-table tr.m-primary td a:active, -table.m-table td.m-primary a:active, -table.m-table tr.m-primary th a:active, -table.m-table th.m-primary a:active { - color: #31708f; -} -.m-note.m-success { - border-color: #9ad36a; - border-width: 2px; - border-style: solid; - padding-top: 0; -} -.m-note.m-success, -table.m-table tr.m-success td, -table.m-table td.m-success, -table.m-table tr.m-success th, -table.m-table th.m-success, -table.m-table tr.m-success strong, -table.m-table strong.m-success, -table.m-table tr.m-success em, -table.m-table em.m-success { - background-color: transparent; - color: #3c763d; -} -.m-note.m-success a, -table.m-table tr.m-success td a, -table.m-table td.m-success a, -table.m-table tr.m-success th a, -table.m-table th.m-success a { - color: #9ad36a; -} -.m-note.m-success a:hover, -table.m-table tr.m-success td a:hover, -table.m-table td.m-success a:hover, -table.m-table tr.m-success th a:hover, -table.m-table th.m-success a:hover, -.m-note.m-success a:focus, -table.m-table tr.m-success td a:focus, -table.m-table td.m-success a:focus, -table.m-table tr.m-success th a:focus, -table.m-table th.m-success a:focus, -.m-note.m-success a:active, -table.m-table tr.m-success td a:active, -table.m-table td.m-success a:active, -table.m-table tr.m-success th a:active, -table.m-table th.m-success a:active { - color: #9ad36a; -} -.m-note.m-warning { - border-color: #f9cf79; - border-width: 2px; - border-style: solid; - padding-top: 0; -} -.m-note.m-warning, -table.m-table tr.m-warning td, -table.m-table td.m-warning, -table.m-table tr.m-warning th, -table.m-table th.m-warning, -table.m-table tr.m-warning strong, -table.m-table strong.m-warning, -table.m-table tr.m-warning em, -table.m-table em.m-warning { - background-color: transparent; - color: #8a6d3b; -} -.m-note.m-warning a, -table.m-table tr.m-warning td a, -table.m-table td.m-warning a, -table.m-table tr.m-warning th a, -table.m-table th.m-warning a { - color: #f9cf79; -} -.m-note.m-warning a:hover, -table.m-table tr.m-warning td a:hover, -table.m-table td.m-warning a:hover, -table.m-table tr.m-warning th a:hover, -table.m-table th.m-warning a:hover, -.m-note.m-warning a:focus, -table.m-table tr.m-warning td a:focus, -table.m-table td.m-warning a:focus, -table.m-table tr.m-warning th a:focus, -table.m-table th.m-warning a:focus, -.m-note.m-warning a:active, -table.m-table tr.m-warning td a:active, -table.m-table td.m-warning a:active, -table.m-table tr.m-warning th a:active, -table.m-table th.m-warning a:active { - color: #f9cf79; -} -.m-note.m-danger { - border-color: #f60000; - border-width: 2px; - border-style: solid; - padding-top: 0; -} -.m-note.m-danger, -table.m-table tr.m-danger td, -table.m-table td.m-danger, -table.m-table tr.m-danger th, -table.m-table th.m-danger, -table.m-table tr.m-danger strong, -table.m-table strong.m-danger, -table.m-table tr.m-danger em, -table.m-table em.m-danger { - background-color: transparent; - color: #920000; -} -.m-note.m-danger a, -table.m-table tr.m-danger td a, -table.m-table td.m-danger a, -table.m-table tr.m-danger th a, -table.m-table th.m-danger a { - color: #f60000; -} -.m-note.m-danger a:hover, -table.m-table tr.m-danger td a:hover, -table.m-table td.m-danger a:hover, -table.m-table tr.m-danger th a:hover, -table.m-table th.m-danger a:hover, -.m-note.m-danger a:focus, -table.m-table tr.m-danger td a:focus, -table.m-table td.m-danger a:focus, -table.m-table tr.m-danger th a:focus, -table.m-table th.m-danger a:focus, -.m-note.m-danger a:active, -table.m-table tr.m-danger td a:active, -table.m-table td.m-danger a:active, -table.m-table tr.m-danger th a:active, -table.m-table th.m-danger a:active { - color: #f60000; -} -.m-note.m-info { - border-color: #31708f; - border-width: 2px; - border-style: solid; - padding-top: 0; -} -.m-note.m-info, -table.m-table tr.m-info td, -table.m-table td.m-info, -table.m-table tr.m-info th, -table.m-table th.m-info, -table.m-table tr.m-info strong, -table.m-table strong.m-info, -table.m-table tr.m-info em, -table.m-table em.m-info { - background-color: transparent; - color: #81bcda; -} -.m-note.m-info a, -table.m-table tr.m-info td a, -table.m-table td.m-info a, -table.m-table tr.m-info th a, -table.m-table th.m-info a { - color: #67cce0; -} -.m-note.m-info a:hover, -table.m-table tr.m-info td a:hover, -table.m-table td.m-info a:hover, -table.m-table tr.m-info th a:hover, -table.m-table th.m-info a:hover, -.m-note.m-info a:focus, -table.m-table tr.m-info td a:focus, -table.m-table td.m-info a:focus, -table.m-table tr.m-info th a:focus, -table.m-table th.m-info a:focus, -.m-note.m-info a:active, -table.m-table tr.m-info td a:active, -table.m-table td.m-info a:active, -table.m-table tr.m-info th a:active, -table.m-table th.m-info a:active { - color: #67cce0; -} -.m-note.m-dim { - border-color: #bdbdbd; - border-width: 2px; - border-style: solid; - padding-top: 0; -} -.m-note.m-dim, -table.m-table tr.m-dim td, -table.m-table td.m-dim, -table.m-table tr.m-dim th, -table.m-table th.m-dim, -table.m-table tr.m-dim strong, -table.m-table strong.m-dim, -table.m-table tr.m-dim em, -table.m-table em.m-dim { - background-color: transparent; - color: #7c7c7c; -} -.m-note.m-dim a, -table.m-table tr.m-dim td a, -table.m-table td.m-dim a, -table.m-table tr.m-dim th a, -table.m-table th.m-dim a { - color: #c0c0c0; -} -.m-note.m-dim a:hover, -table.m-table tr.m-dim td a:hover, -table.m-table td.m-dim a:hover, -table.m-table tr.m-dim th a:hover, -table.m-table th.m-dim a:hover, -.m-note.m-dim a:focus, -table.m-table tr.m-dim td a:focus, -table.m-table td.m-dim a:focus, -table.m-table tr.m-dim th a:focus, -table.m-table th.m-dim a:focus, -.m-note.m-dim a:active, -table.m-table tr.m-dim td a:active, -table.m-table td.m-dim a:active, -table.m-table tr.m-dim th a:active, -table.m-table th.m-dim a:active { - color: #949494; -} -.m-note.m-type { - border-color: #9ad36a; - border-width: 2px; - border-style: solid; - padding-top: 0; -} -.m-note.m-type, -table.m-table tr.m-type td, -table.m-table td.m-type, -table.m-table tr.m-type th, -table.m-table th.m-type, -table.m-table tr.m-type strong, -table.m-table strong.m-type, -table.m-table tr.m-type em, -table.m-table em.m-type { - background-color: transparent; - color: #3c763d; -} -.m-note.m-type a, -table.m-table tr.m-type td a, -table.m-table td.m-type a, -table.m-table tr.m-type th a, -table.m-table th.m-type a { - color: #9ad36a; -} -.m-note.m-type a:hover, -table.m-table tr.m-type td a:hover, -table.m-table td.m-type a:hover, -table.m-table tr.m-type th a:hover, -table.m-table th.m-type a:hover, -.m-note.m-type a:focus, -table.m-table tr.m-type td a:focus, -table.m-table td.m-type a:focus, -table.m-table tr.m-type th a:focus, -table.m-table th.m-type a:focus, -.m-note.m-type a:active, -table.m-table tr.m-type td a:active, -table.m-table td.m-type a:active, -table.m-table tr.m-type th a:active, -table.m-table th.m-type a:active { - color: #9ad36a; -} -figure.m-figure.m-default:before { - border-color: transparent; -} -figure.m-figure.m-default figcaption { - color: #000000; -} -figure.m-figure.m-primary:before { - border-color: transparent; -} -figure.m-figure.m-primary figcaption { - color: #31708f; -} -figure.m-figure.m-success:before { - border-color: transparent; -} -figure.m-figure.m-success figcaption { - color: #9ad36a; -} -figure.m-figure.m-warning:before { - border-color: transparent; -} -figure.m-figure.m-warning figcaption { - color: #f9cf79; -} -figure.m-figure.m-danger:before { - border-color: transparent; -} -figure.m-figure.m-danger figcaption { - color: #f60000; -} -figure.m-figure.m-info:before { - border-color: transparent; -} -figure.m-figure.m-info figcaption { - color: #31708f; -} -figure.m-figure.m-type:before { - border-color: transparent; -} -figure.m-figure.m-type figcaption { - color: #9ad36a; -} -figure.m-figure.m-dim:before { - border-color: transparent; -} -figure.m-figure.m-dim { - color: #bdbdbd; -} -figure.m-figure.m-dim a { - color: #c0c0c0; -} -figure.m-figure.m-dim a:hover, -figure.m-figure.m-dim a:focus, -figure.m-figure.m-dim a:active { - color: #949494; -} -.m-math { - fill: #000000; -} -.m-math.m-default, -.m-math g.m-default, -.m-math rect.m-default, -div.m-plot svg .m-bar.m-default, -.m-graph g.m-edge polygon, -.m-graph g.m-node:not(.m-flat) ellipse, -.m-graph g.m-node:not(.m-flat) polygon, -.m-graph g.m-edge text, -.m-graph g.m-node.m-flat text, -.m-graph.m-default g.m-edge polygon, -.m-graph.m-default g.m-node:not(.m-flat) ellipse, -.m-graph.m-default g.m-node:not(.m-flat) polygon, -.m-graph.m-default g.m-edge text, -.m-graph.m-default g.m-node.m-flat text { - fill: #000000; -} -.m-graph g.m-edge polygon, -.m-graph g.m-edge path, -.m-graph g.m-node ellipse, -.m-graph g.m-node polygon, -.m-graph g.m-node polyline, -.m-graph.m-default g.m-edge polygon, -.m-graph.m-default g.m-edge path, -.m-graph.m-default g.m-node ellipse, -.m-graph.m-default g.m-node polygon, -.m-graph.m-default g.m-node polyline { - stroke: #000000; -} -.m-math.m-primary, -.m-math g.m-primary, -.m-math rect.m-primary, -div.m-plot svg .m-bar.m-primary, -.m-graph.m-primary g.m-edge polygon, -.m-graph.m-primary g.m-node:not(.m-flat) ellipse, -.m-graph.m-primary g.m-node:not(.m-flat) polygon, -.m-graph.m-primary g.m-edge text, -.m-graph.m-primary g.m-node.m-flat text { - fill: #31708f; -} -.m-graph.m-primary g.m-edge polygon, -.m-graph.m-primary g.m-edge path, -.m-graph.m-primary g.m-node ellipse, -.m-graph.m-primary g.m-node polygon, -.m-graph.m-primary g.m-node polyline { - stroke: #31708f; -} -.m-math.m-success, -.m-math g.m-success, -.m-math rect.m-success, -div.m-plot svg .m-bar.m-success, -.m-graph.m-success g.m-edge polygon, -.m-graph.m-success g.m-node:not(.m-flat) ellipse, -.m-graph.m-success g.m-node:not(.m-flat) polygon, -.m-graph.m-success g.m-edge text, -.m-graph.m-success g.m-node.m-flat text { - fill: #9ad36a; -} -.m-graph.m-success g.m-edge polygon, -.m-graph.m-success g.m-edge path, -.m-graph.m-success g.m-node ellipse, -.m-graph.m-success g.m-node polygon, -.m-graph.m-success g.m-node polyline { - stroke: #9ad36a; -} -.m-math.m-warning, -.m-math g.m-warning, -.m-math rect.m-warning, -div.m-plot svg .m-bar.m-warning, -.m-graph.m-warning g.m-edge polygon, -.m-graph.m-warning g.m-node:not(.m-flat) ellipse, -.m-graph.m-warning g.m-node:not(.m-flat) polygon, -.m-graph.m-warning g.m-edge text, -.m-graph.m-warning g.m-node.m-flat text { - fill: #f9cf79; -} -.m-graph.m-warning g.m-edge polygon, -.m-graph.m-warning g.m-edge path, -.m-graph.m-warning g.m-node ellipse, -.m-graph.m-warning g.m-node polygon, -.m-graph.m-warning g.m-node polyline { - stroke: #f9cf79; -} -.m-math.m-danger, -.m-math g.m-danger, -.m-math rect.m-danger, -div.m-plot svg .m-bar.m-danger, -.m-graph.m-danger g.m-edge polygon, -.m-graph.m-danger g.m-node:not(.m-flat) ellipse, -.m-graph.m-danger g.m-node:not(.m-flat) polygon, -.m-graph.m-danger g.m-edge text, -.m-graph.m-danger g.m-node.m-flat text { - fill: #f60000; -} -.m-graph.m-danger g.m-edge polygon, -.m-graph.m-danger g.m-edge path, -.m-graph.m-danger g.m-node ellipse, -.m-graph.m-danger g.m-node polygon, -.m-graph.m-danger g.m-node polyline { - stroke: #f60000; -} -.m-math.m-info, -.m-math g.m-info, -.m-math rect.m-info, -div.m-plot svg .m-bar.m-info, -.m-graph.m-info g.m-edge polygon, -.m-graph.m-info g.m-node:not(.m-flat) ellipse, -.m-graph.m-info g.m-node:not(.m-flat) polygon, -.m-graph.m-info g.m-edge text, -.m-graph.m-info g.m-node.m-flat text { - fill: #31708f; -} -.m-graph.m-info g.m-edge polygon, -.m-graph.m-info g.m-edge path, -.m-graph.m-info g.m-node ellipse, -.m-graph.m-info g.m-node polygon, -.m-graph.m-info g.m-node polyline { - stroke: #31708f; -} -.m-math.m-dim, -.m-math g.m-dim, -.m-math rect.m-dim, -div.m-plot svg .m-bar.m-dim, -.m-graph.m-dim g.m-edge polygon, -.m-graph.m-dim g.m-node:not(.m-flat) ellipse, -.m-graph.m-dim g.m-node:not(.m-flat) polygon, -.m-graph.m-dim g.m-edge text, -.m-graph.m-dim g.m-node.m-flat text { - fill: #bdbdbd; -} -.m-graph.m-dim g.m-edge polygon, -.m-graph.m-dim g.m-edge path, -.m-graph.m-dim g.m-node ellipse, -.m-graph.m-dim g.m-node polygon, -.m-graph.m-dim g.m-node polyline { - stroke: #bdbdbd; -} -.m-math.m-type, -.m-math g.m-type, -.m-math rect.m-type, -div.m-plot svg .m-bar.m-type, -.m-graph.m-type g.m-edge polygon, -.m-graph.m-type g.m-node:not(.m-flat) ellipse, -.m-graph.m-type g.m-node:not(.m-flat) polygon, -.m-graph.m-type g.m-edge text, -.m-graph.m-type g.m-node.m-flat text { - fill: #9ad36a; -} -.m-graph.m-type g.m-edge polygon, -.m-graph.m-type g.m-edge path, -.m-graph.m-type g.m-node ellipse, -.m-graph.m-type g.m-node polygon, -.m-graph.m-type g.m-node polyline { - stroke: #9ad36a; -} -.m-graph g.m-edge.m-default polygon, -.m-graph g.m-node.m-default:not(.m-flat) ellipse, -.m-graph g.m-node.m-default:not(.m-flat) polygon, -.m-graph g.m-edge.m-default text, -.m-graph g.m-node.m-default.m-flat text { - fill: #000000; -} -.m-graph g.m-edge.m-default polygon, -.m-graph g.m-edge.m-default path, -.m-graph g.m-node.m-default ellipse, -.m-graph g.m-node.m-default polygon, -.m-graph g.m-node.m-default polyline { - stroke: #000000; -} -.m-graph g.m-edge.m-primary polygon, -.m-graph g.m-node.m-primary:not(.m-flat) ellipse, -.m-graph g.m-node.m-primary:not(.m-flat) polygon, -.m-graph g.m-edge.m-primary text, -.m-graph g.m-node.m-primary.m-flat text { - fill: #31708f; -} -.m-graph g.m-edge.m-primary polygon, -.m-graph g.m-edge.m-primary path, -.m-graph g.m-node.m-primary ellipse, -.m-graph g.m-node.m-primary polygon, -.m-graph g.m-node.m-primary polyline { - stroke: #31708f; -} -.m-graph g.m-edge.m-success polygon, -.m-graph g.m-node.m-success:not(.m-flat) ellipse, -.m-graph g.m-node.m-success:not(.m-flat) polygon, -.m-graph g.m-edge.m-success text, -.m-graph g.m-node.m-success.m-flat text { - fill: #9ad36a; -} -.m-graph g.m-edge.m-success polygon, -.m-graph g.m-edge.m-success path, -.m-graph g.m-node.m-success ellipse, -.m-graph g.m-node.m-success polygon, -.m-graph g.m-node.m-success polyline { - stroke: #9ad36a; -} -.m-graph g.m-edge.m-warning polygon, -.m-graph g.m-node.m-warning:not(.m-flat) ellipse, -.m-graph g.m-node.m-warning:not(.m-flat) polygon, -.m-graph g.m-edge.m-warning text, -.m-graph g.m-node.m-warning.m-flat text { - fill: #f9cf79; -} -.m-graph g.m-edge.m-warning polygon, -.m-graph g.m-edge.m-warning path, -.m-graph g.m-node.m-warning ellipse, -.m-graph g.m-node.m-warning polygon, -.m-graph g.m-node.m-warning polyline { - stroke: #f9cf79; -} -.m-graph g.m-edge.m-danger polygon, -.m-graph g.m-node.m-danger:not(.m-flat) ellipse, -.m-graph g.m-node.m-danger:not(.m-flat) polygon, -.m-graph g.m-edge.m-danger text, -.m-graph g.m-node.m-danger.m-flat text { - fill: #f60000; -} -.m-graph g.m-edge.m-danger polygon, -.m-graph g.m-edge.m-danger path, -.m-graph g.m-node.m-danger ellipse, -.m-graph g.m-node.m-danger polygon, -.m-graph g.m-node.m-danger polyline { - stroke: #f60000; -} -.m-graph g.m-edge.m-info polygon, -.m-graph g.m-node.m-info:not(.m-flat) ellipse, -.m-graph g.m-node.m-info:not(.m-flat) polygon, -.m-graph g.m-edge.m-info text, -.m-graph g.m-node.m-info.m-flat text { - fill: #31708f; -} -.m-graph g.m-edge.m-info polygon, -.m-graph g.m-edge.m-info path, -.m-graph g.m-node.m-info ellipse, -.m-graph g.m-node.m-info polygon, -.m-graph g.m-node.m-info polyline { - stroke: #31708f; -} -.m-graph g.m-edge.m-dim polygon, -.m-graph g.m-node.m-dim:not(.m-flat) ellipse, -.m-graph g.m-node.m-dim:not(.m-flat) polygon, -.m-graph g.m-edge.m-dim text, -.m-graph g.m-node.m-dim.m-flat text { - fill: #bdbdbd; -} -.m-graph g.m-edge.m-dim polygon, -.m-graph g.m-edge.m-dim path, -.m-graph g.m-node.m-dim ellipse, -.m-graph g.m-node.m-dim polygon, -.m-graph g.m-node.m-dim polyline { - stroke: #bdbdbd; -} -.m-graph g.m-edge.m-type polygon, -.m-graph g.m-node.m-type:not(.m-flat) ellipse, -.m-graph g.m-node.m-type:not(.m-flat) polygon, -.m-graph g.m-edge.m-type text, -.m-graph g.m-node.m-type.m-flat text { - fill: #9ad36a; -} -.m-graph g.m-edge.m-type polygon, -.m-graph g.m-edge.m-type path, -.m-graph g.m-node.m-type ellipse, -.m-graph g.m-node.m-type polygon, -.m-graph g.m-node.m-type polyline { - stroke: #9ad36a; -} -p, -ul, -ol, -dl, -blockquote, -pre, -.m-code-figure, -.m-console-figure, -hr, -.m-note, -.m-frame, -.m-block, -div.m-button, -div.m-scroll, -table.m-table, -div.m-image, -img.m-image, -svg.m-image, -figure.m-figure, -.m-imagegrid, -div.m-math, -div.m-graph, -div.m-plot { - margin-bottom: 1rem; -} -p:last-child, -p.m-nopadb, -ul:last-child, -ul.m-nopadb, -ol:last-child, -ol.m-nopadb, -dl:last-child, -dl.m-nopadb, -blockquote:last-child, -blockquote.m-nopadb, -pre:last-child, -pre.m-nopadb, -.m-code-figure:last-child, -.m-code-figure.m-nopadb, -.m-console-figure:last-child, -.m-console-figure.m-nopadb, -hr:last-child, -hr.m-nopadb, -.m-note:last-child, -.m-note.m-nopadb, -.m-frame:last-child, -.m-frame.m-nopadb, -.m-block:last-child, -.m-block.m-nopadb, -div.m-button:last-child, -div.m-button.m-nopadb, -div.m-scroll:last-child, -div.m-scroll.m-nopadb, -table.m-table:last-child, -table.m-table.m-nopadb, -img.m-image:last-child, -img.m-image.m-nopadb, -svg.m-image:last-child, -svg.m-image.m-nopadb, -div.m-image:last-child, -div.m-image.m-nopadb, -figure.m-figure:last-child, -figure.m-figure.m-nopadb, -.m-imagegrid:last-child, -.m-imagegrid.m-nopadb, -div.m-math:last-child, -div.m-math.m-nopadb, -div.m-graph:last-child, -div.m-graph.m-nopadb, -div.m-plot:last-child, -div.m-plot.m-nopadb { - margin-bottom: 0; -} -li > p:last-child, -li > blockquote:last-child, -li > pre:last-child, -li > .m-code-figure:last-child, -li > .m-console-figure:last-child, -li > .m-note:last-child, -li > .m-frame:last-child, -li > .m-block:last-child, -li > div.m-button:last-child, -li > div.m-scroll:last-child, -li > table.m-table:last-child, -li > img.m-image:last-child, -li > svg.m-image:last-child, -li > div.m-image:last-child, -li > figure.m-figure:last-child, -li > div.m-math:last-child, -li > div.m-graph:last-child, -li > div.m-plot:last-child { - margin-bottom: 1rem; -} -li:last-child > p:last-child, -li:last-child > p.m-nopadb, -li:last-child > blockquote:last-child, -li:last-child > blockquote.m-nopadb, -li:last-child > pre:last-child, -li:last-child > pre.m-nopadb, -li:last-child > .m-code-figure:last-child, -li:last-child > .m-code-figure.m-nopadb, -li:last-child > .m-console-figure:last-child, -li:last-child > .m-console-figure.m-nopadb, -li:last-child > .m-note:last-child, -li:last-child > .m-note.m-nopadb, -li:last-child > .m-frame:last-child, -li:last-child > .m-frame.m-nopadb, -li:last-child > .m-block:last-child, -li:last-child > .m-block.m-nopadb, -li:last-child > div.m-button:last-child, -li:last-child > div.m-button.m-nopadb, -li:last-child > div.m-scroll:last-child, -li:last-child > div.m-scroll.m-nopadb, -li:last-child > table.m-table:last-child, -li:last-child > table.m-table.m-nopadb, -li:last-child > img.m-image:last-child, -li:last-child > img.m-image.m-nopadb, -li:last-child > svg.m-image:last-child, -li:last-child > svg.m-image.m-nopadb, -li:last-child > div.m-image:last-child, -li:last-child > div.m-image.m-nopadb, -li:last-child > figure.m-figure:last-child, -li:last-child > figure.m-figure.m-nopadb, -li:last-child > div.m-math:last-child, -li:last-child > div.m-math.m-nopadb, -li:last-child > div.m-graph:last-child, -li:last-child > div.m-graph.m-nopadb, -li:last-child > div.m-plot:last-child, -li:last-child > div.m-plot.m-nopadb { - margin-bottom: 0; -} - -body > header > nav { - width: 100%; - background-color: #000000; - min-height: 3rem; - font-size: 16px; -} -body > header > nav.m-navbar-landing, -body > header > nav.m-navbar-cover { - background-color: transparent; - position: relative; -} -body > header > nav.m-navbar-landing { - opacity: 0.8; -} -body > header > nav.m-navbar-cover { - background-color: #000000; - opacity: 1; -} -body > header > nav.m-navbar-landing:hover, -body > header > nav.m-navbar-cover:hover { - background-color: #000000; - opacity: 1; -} -body > header > nav.m-navbar-landing:target, -body > header > nav.m-navbar-cover:target { - background-color: #000000; - opacity: 1; -} -body > header > nav.m-navbar-landing #m-navbar-brand.m-navbar-brand-hidden { - visibility: hidden; -} -body - > header - > nav.m-navbar-landing:target - #m-navbar-brand.m-navbar-brand-hidden { - visibility: visible; -} -body > header > nav { - margin-left: auto; - margin-right: auto; - color: #ffffff; -} -body > header > nav a { - text-decoration: none; - text-transform: none; - display: inline-block; - vertical-align: middle; - line-height: 2.75rem; - color: #ffffff; -} -body > header > nav #m-navbar-brand, -body > header > nav a#m-navbar-show, -body > header > nav a#m-navbar-hide { - font-weight: 300; - font-size: 1.125rem; - padding-left: 1rem; - padding-right: 1rem; -} -body > header > nav a#m-navbar-brand, -body > header > nav #m-navbar-brand a { - text-transform: none; -} -body > header > nav a#m-navbar-brand img, -body > header > nav #m-navbar-brand a img { - height: 5rem; - vertical-align: -22.5%; - margin-right: 0.5rem; -} -body > header > nav #m-navbar-brand a { - padding-left: 0; - padding-right: 0; -} -body > header > nav #m-navbar-brand .m-thin { - font-weight: normal; -} -body > header > nav #m-navbar-brand .m-breadcrumb { - color: #bdbdbd; -} -body > header > nav a#m-navbar-show:before, -body > header > nav a#m-navbar-hide:before { - content: "\2630"; -} -body > header > nav #m-navbar-collapse { - padding-bottom: 1rem; -} -body > header > nav #m-navbar-collapse li { - border-style: solid; - border-color: transparent; - border-width: 0 0 0 0.25rem; - margin-left: -1rem; -} -body > header > nav #m-navbar-collapse li a { - border-style: solid; - border-color: transparent; - line-height: 1.5rem; - margin-left: -0.25rem; - padding-left: 0.75rem; - border-width: 0 1px 1px 1px; - width: 100%; -} -body > header > nav #m-navbar-collapse li ol { - border-color: #ddd; -} -body > header > nav #m-navbar-collapse li a#m-navbar-current { - color: #ffffff; - background-color: #000000; - border-color: #000000; -} -body > header > nav #m-navbar-collapse li ol li a#m-navbar-current { - color: #000000; - background-color: #ffffff; - border-color: #ddd; -} -body > header > nav ol { - list-style-type: none; - margin: 0; -} -body > header > nav ol ol { - padding-left: 1.5rem; -} -body > header > nav .m-row > [class*="m-col-"] { - padding-top: 0; - padding-bottom: 0; -} -body > header > nav a:hover, -body > header > nav a:focus, -body > header > nav a:active { - color: #ffffff; -} -body > header > nav #m-navbar-collapse li:hover { - border-color: #ffffff; -} -body > header > nav #m-navbar-collapse li a:hover, -body > header > nav #m-navbar-collapse li a:focus, -body > header > nav #m-navbar-collapse li a:active { - border-color: #ffffff; - background-color: #000000; -} -body > header > nav.m-navbar-landing #m-navbar-collapse li a:hover, -body > header > nav.m-navbar-cover #m-navbar-collapse li a:hover, -body > header > nav.m-navbar-landing #m-navbar-collapse li a:focus, -body > header > nav.m-navbar-cover #m-navbar-collapse li a:focus, -body > header > nav.m-navbar-landing #m-navbar-collapse li a:active, -body > header > nav.m-navbar-cover #m-navbar-collapse li a:active { - background-color: var(--header-link-active-background-color-semi); -} -body > header > nav #m-navbar-hide { - display: none; -} -body > header > nav:target #m-navbar-collapse { - display: block; -} -body > header > nav:target #m-navbar-show { - display: none; -} -body > header > nav:target #m-navbar-hide { - display: inline-block; -} -@media screen and (min-width: 768px) { - body > header > nav #m-navbar-show, - body > header > nav #m-navbar-hide, - body > header > nav:target #m-navbar-show, - body > header > nav:target #m-navbar-hide { - display: none; - } - body > header > nav #m-navbar-collapse li a { - line-height: 2.75rem; - } - body > header > nav a, - body > header > nav #m-navbar-collapse li a { - margin-left: 0; - padding-left: 1rem; - padding-right: 1rem; - white-space: nowrap; - } - body > header > nav #m-navbar-collapse { - padding-bottom: 0; - } - body > header > nav #m-navbar-collapse li ol { - background-color: #000000; - } - body > header > nav #m-navbar-collapse ol ol li { - margin-left: 0; - padding-left: 0; - border-left-width: 0; - } - body > header > nav #m-navbar-collapse ol ol li a { - padding-left: 0.75rem; - color: #000000; - background-color: #ffffff; - border-color: #ddd; - } - body > header > nav #m-navbar-collapse ol ol li a:active { - padding-left: 0.75rem; - color: #ffffff; - background-color: #353535; - border-color: #ddd; - } - body > header > nav #m-navbar-collapse ol ol li a:hover { - padding-left: 0.75rem; - color: #ffffff; - background-color: #353535; - border-color: #ddd; - } - body > header > nav #m-navbar-collapse > .m-row > ol > li { - margin-left: 0; - border-left-width: 0; - } - body > header > nav #m-navbar-collapse > .m-row > ol > li > a { - border-width: 0.25rem 0 0 0; - } - body > header > nav #m-navbar-collapse ol { - padding-left: 0; - padding-right: 0; - } - body > header > nav #m-navbar-collapse > .m-row > ol, - body > header > nav #m-navbar-collapse > .m-row > ol > li { - float: left; - } - body > header > nav #m-navbar-collapse ol ol { - z-index: 99999; - position: absolute; - visibility: hidden; - } - body > header > nav #m-navbar-collapse li:hover ol { - visibility: visible; - } -} -body > footer { - width: 100%; -} -body > footer > nav { - padding-top: 1rem; - padding-bottom: 1rem; - font-size: 0.85rem; - text-align: center; - color: #777777; - background-color: #353535; -} -body > footer > nav h3, -body > footer > nav h3 a { - text-transform: capitalize; - font-weight: normal; -} -body > footer > nav ul { - list-style-type: none; - padding: 0; - margin: 0; -} -body > footer > nav a { - text-decoration: none; - text-transform: none; - color: #999; -} -body > footer > nav a:hover, -body > footer > nav a:focus, -body > footer > nav a:active { - color: #494949; -} -body > main { - padding-top: 1rem; - padding-bottom: 1rem; -} -article h1 { - font-size: 1.75rem; -} -article h1 .m-breadcrumb { - color: #bdbdbd; - font-weight: normal; - font-size: 16px; -} -article h1 .m-breadcrumb a { - color: #26a9e0; -} -article h1 .m-breadcrumb a:hover, -article h1 a:focus, -article h1 a:active { - color: #26a9e0; -} -article hr { - width: 75%; - border-width: 2px 0 0 0 ; - border-style: solid; - border-color: #92d050; - margin: auto; -} -article section hr { - width: 50%; - border-width: 1px 0 0 0; - border-style: solid; - border-color: #ddd; - margin: auto; - padding-top: 5px; - padding-bottom: 10px; -} -article > header h1 { - font-size: 2rem; - margin-bottom: 0.5rem; -} -article h1 a, -article > header h1, -article > header h1 a, -article section > h2, -article section > h2 a, -article section > h3, -article section > h3 a, -article section > h4, -article section > h4 a, -article section > h5, -article section > h5 a, -article section > h6, -article section > h6 a { - color: #000000; -} -article h1 a:hover, -article > header h1 a:hover, -article > header h1 a:focus, -article > header h1 a:active, -article section > h2 a:hover, -article section > h2 a:focus, -article section > h2 a:active, -article section > h3 a:hover, -article section > h3 a:focus, -article section > h3 a:active, -article section > h4 a:hover, -article section > h4 a:focus, -article section > h4 a:active, -article section > h5 a:hover, -article section > h5 a:focus, -article section > h5 a:active, -article section > h6 a:hover, -article section > h6 a:focus, -article section > h6 a:active { - color: #000000; -} -article > header .m-date { - display: block; - width: 2.5rem; - float: left; - text-align: center; - line-height: 95%; - font-size: 0.75rem; - font-weight: normal; - white-space: nowrap; - border-right-style: solid; - border-right-width: 0.125rem; - border-color: #000000; - padding-right: 0.75rem; - margin-top: -0.1rem; - margin-right: 0.75rem; - margin-bottom: 0.25rem; -} -article > header .m-date-day { - display: block; - font-weight: bold; - padding-top: 0.2rem; - padding-bottom: 0.15rem; - font-size: 1.25rem; -} -article > header p { - color: #7a7a7a; - font-size: 1.125rem; -} -article > header h1::after { - content: " "; - clear: both; - display: table; -} -article > footer { - color: #969696; -} -article > footer p { - font-style: italic; - font-size: 0.85rem; - text-indent: 0; -} -article h1 a, -article > header h1 a, -article section > h2 a, -article section > h3 a, -article section > h4 a, -article section > h5 a, -article section > h6 a { - text-decoration: none; -} -#m-landing-image, -#m-cover-image, -article#m-jumbo > header #m-jumbo-image { - background-size: cover; - background-color: #666666; - background-position: center center; - background-repeat: no-repeat; - margin-top: -4rem; - padding-top: 5rem; -} -#m-landing-image { - color: #ffffff; -} -#m-cover-image { - height: 30rem; - margin-bottom: -26rem; -} -#m-landing-cover h1 { - font-size: 2.8rem; - margin-top: -0.5rem; - padding-left: 1.5rem; - padding-bottom: 1rem; - text-transform: capitalize; -} -#m-landing-cover { - padding-bottom: 10rem; - margin-bottom: -6rem; -} -article#m-jumbo { - margin-top: -1rem; -} -#m-landing-cover, -#m-cover-image > div, -article#m-jumbo > header #m-jumbo-cover { - background: linear-gradient( - transparent 0%, - transparent 50%, - #e8e8e8 100% - ); - width: 100%; - height: 100%; -} -article#m-jumbo > header h1, -article#m-jumbo > header h2 { - text-align: center; - font-weight: bold; -} -article#m-jumbo > header a { - text-decoration: none; -} -article#m-jumbo > header #m-jumbo-cover { - padding-bottom: 5rem; -} -article#m-jumbo > header #m-jumbo-image { - font-size: 2.5vmin; - margin-bottom: -3rem; -} -article#m-jumbo > header h1 { - font-size: 10vmin; -} -article#m-jumbo > header h2 { - font-size: 3vmin; -} -@media screen and (max-height: 640px), screen and (max-width: 640px) { - article#m-jumbo > header h1 { - font-size: 3rem; - } - article#m-jumbo > header #m-jumbo-image, - article#m-jumbo > header h2 { - font-size: 1rem; - } -} -article#m-jumbo > header, -article#m-jumbo > header h1, -article#m-jumbo > header a { - color: #ffffff; -} -article#m-jumbo > header a:hover, -article#m-jumbo > header a:focus, -article#m-jumbo > header a:active { - color: #f0f0f0; -} -article#m-jumbo.m-inverted > header, -article#m-jumbo.m-inverted > header h1, -article#m-jumbo.m-inverted > header a { - color: #000000; -} -article#m-jumbo.m-inverted > header a:hover, -article#m-jumbo.m-inverted > header a:focus, -article#m-jumbo.m-inverted > header a:active { - color: #0f0f0f; -} -.m-landing-news h3 a { - color: #000000; - text-decoration: none; - text-transform: capitalize; -} -.m-landing-news h3 a:hover, -.m-landing-news h3 a:hover, -.m-landing-news h3 a:focus, -.m-landing-news h3 a:active { - color: #000000; -} -.m-landing-news time { - display: inline-block; - margin-left: 1rem; - float: right; -} -.m-article-pagination { - text-align: center; - padding: 1rem; -} -nav.m-navpanel { - text-align: center; -} -nav.m-navpanel h3 { - text-transform: capitalize; - font-weight: normal; -} -nav.m-navpanel ol { - text-transform: capitalize; -} -nav.m-navpanel ol, -nav.m-navpanel ul { - list-style-type: none; - padding: 0; -} -nav.m-navpanel a { - color: #292929; - text-decoration: none; -} -nav.m-navpanel a:hover, -nav.m-navpanel a:focus, -nav.m-navpanel a:active { - color: #cb4b16; -} -ul.m-tagcloud li { - display: inline; -} -ul.m-tagcloud li.m-tag-1 { - font-size: 0.75rem; -} -ul.m-tagcloud li.m-tag-2 { - font-size: 0.825rem; -} -ul.m-tagcloud li.m-tag-3 { - font-size: 1rem; -} -ul.m-tagcloud li.m-tag-4 { - font-size: 1.25rem; -} -ul.m-tagcloud li.m-tag-5 { - font-size: 1.5rem; -} -article section:target figure.m-code-figure, -article section:target figure.m-console-figure { - z-index: 1; -} -article, -article > header, -article section { - margin-bottom: 1rem; -} -article:last-child, -article section:last-child { - margin-bottom: 0; -} -.m-container-inflatable section:target > .m-note, -.m-container-inflatable section:target > .m-frame, -.m-container-inflatable section:target > .m-block, -.m-container-inflatable section:target > pre, -.m-container-inflatable section:target > .m-code-figure > pre:first-child, -.m-container-inflatable section:target > .m-console-figure > pre:first-child, -.m-container-inflatable section:target section > .m-note, -.m-container-inflatable section:target section > .m-frame, -.m-container-inflatable section:target section > .m-block, -.m-container-inflatable section:target section > pre, -.m-container-inflatable - section:target - section - > .m-code-figure - > pre:first-child, -.m-container-inflatable - section:target - section - > .m-console-figure - > pre:first-child, -.m-container-inflatable section:target [class*="m-center-"] > .m-note, -.m-container-inflatable section:target [class*="m-center-"] > .m-frame, -.m-container-inflatable section:target [class*="m-center-"] > .m-block, -.m-container-inflatable section:target [class*="m-center-"] > pre, -.m-container-inflatable - section:target - [class*="m-center-"] - > .m-code-figure - > pre:first-child, -.m-container-inflatable - section:target - [class*="m-center-"] - > .m-console-figure - > pre:first-child, -.m-container-inflatable section:target [class*="m-left-"] > .m-note, -.m-container-inflatable section:target [class*="m-left-"] > .m-frame, -.m-container-inflatable section:target [class*="m-left-"] > .m-block, -.m-container-inflatable section:target [class*="m-left-"] > pre, -.m-container-inflatable - section:target - [class*="m-left-"] - > .m-code-figure - > pre:first-child, -.m-container-inflatable - section:target - [class*="m-left-"] - > .m-console-figure - > pre:first-child, -.m-container-inflatable section:target [class*="m-right-"] > .m-note, -.m-container-inflatable section:target [class*="m-right-"] > .m-frame, -.m-container-inflatable section:target [class*="m-right-"] > .m-block, -.m-container-inflatable section:target [class*="m-right-"] > pre, -.m-container-inflatable - section:target - [class*="m-right-"] - > .m-code-figure - > pre:first-child, -.m-container-inflatable - section:target - [class*="m-right-"] - > .m-console-figure - > pre:first-child, -.m-container-inflatable section:target .m-container-inflate > .m-note, -.m-container-inflatable section:target .m-container-inflate > .m-frame, -.m-container-inflatable section:target .m-container-inflate > .m-block, -.m-container-inflatable section:target .m-container-inflate > pre, -.m-container-inflatable - section:target - .m-container-inflate - > .m-code-figure - > pre:first-child, -.m-container-inflatable - section:target - .m-container-inflate - > .m-console-figure - > pre:first-child { - margin-left: -1rem; - border-left-style: solid; - border-left-width: 0.25rem; - border-top-left-radius: 0; - border-bottom-left-radius: 0; - padding-left: 0.75rem; -} -.m-container-inflatable section:target > .m-code-figure::before, -.m-container-inflatable section:target > .m-console-figure::before, -.m-container-inflatable section:target section > .m-code-figure::before, -.m-container-inflatable section:target section > .m-console-figure::before, -.m-container-inflatable - section:target - [class*="m-center-"] - > .m-code-figure::before, -.m-container-inflatable - section:target - [class*="m-center-"] - > .m-console-figure::before, -.m-container-inflatable - section:target - [class*="m-left-"] - > .m-code-figure::before, -.m-container-inflatable - section:target - [class*="m-left-"] - > .m-console-figure::before, -.m-container-inflatable - section:target - [class*="m-right-"] - > .m-code-figure::before, -.m-container-inflatable - section:target - [class*="m-right-"] - > .m-console-figure::before, -.m-container-inflatable - section:target - .m-container-inflate - > .m-code-figure::before, -.m-container-inflatable - section:target - .m-container-inflate - > .m-console-figure::before { - border-top-left-radius: 0; - border-bottom-left-radius: 0; - border-left-width: 0.25rem; -} -.m-container-inflatable section:target > .m-code-figure > pre.m-nopad, -.m-container-inflatable section:target > .m-console-figure > pre.m-nopad { - margin-left: -0.75rem; - padding-left: -0.75rem; -} -@media screen and (min-width: 576px) { - .m-container-inflatable section:target .m-center-s > .m-note, - .m-container-inflatable section:target .m-center-s > pre, - .m-container-inflatable - section:target - .m-center-s - > figure.m-code-figure - > pre:first-child, - .m-container-inflatable - section:target - .m-center-s - > figure.m-console-figure - > pre:first-child, - .m-container-inflatable - section:target - .m-right-s - > figure.m-code-figure - > pre:first-child, - .m-container-inflatable - section:target - .m-right-s - > figure.m-console-figure - > pre:first-child { - border-left-width: 0; - border-top-left-radius: 0px; - border-bottom-left-radius: 0px; - padding-left: 1rem; - } - .m-container-inflatable section:target .m-center-s > .m-block, - .m-container-inflatable section:target .m-right-s > .m-block { - border-top-left-radius: 0px; - border-bottom-left-radius: 0px; - } - .m-container-inflatable section:target .m-center-s > .m-frame, - .m-container-inflatable section:target .m-right-s > .m-frame { - border-top-left-radius: 0px; - border-bottom-left-radius: 0px; - border-left-width: 0.125rem; - padding-left: 0.875rem; - } - .m-container-inflatable section:target .m-right-s > .m-block, - .m-container-inflatable section:target .m-right-s > .m-frame { - margin-left: 0; - } - .m-container-inflatable section:target .m-right-s > .m-note, - .m-container-inflatable section:target .m-right-s > pre { - border-top-left-radius: 0px; - border-bottom-left-radius: 0px; - margin-left: 0; - border-left-width: 0; - padding-left: 1rem; - } - .m-container-inflatable - section:target - .m-center-s - > figure.m-code-figure::before, - .m-container-inflatable - section:target - .m-center-s - > figure.m-console-figure::before, - .m-container-inflatable - section:target - .m-right-s - > figure.m-code-figure::before, - .m-container-inflatable - section:target - .m-right-s - > figure.m-console-figure::before { - border-top-left-radius: 0px; - border-bottom-left-radius: 0px; - border-left-width: 0.125rem; - } -} -@media screen and (min-width: 768px) { - .m-container-inflatable section:target .m-center-m > .m-note, - .m-container-inflatable section:target .m-center-m > pre, - .m-container-inflatable - section:target - .m-center-m - > figure.m-code-figure - > pre:first-child, - .m-container-inflatable - section:target - .m-center-m - > figure.m-console-figure - > pre:first-child, - .m-container-inflatable - section:target - .m-right-m - > figure.m-code-figure - > pre:first-child, - .m-container-inflatable - section:target - .m-right-m - > figure.m-console-figure - > pre:first-child { - border-left-width: 0; - border-top-left-radius: 0px; - border-bottom-left-radius: 0px; - padding-left: 1rem; - } - .m-container-inflatable section:target .m-center-m > .m-block, - .m-container-inflatable section:target .m-right-m > .m-block { - border-top-left-radius: 0px; - border-bottom-left-radius: 0px; - } - .m-container-inflatable section:target .m-center-m > .m-frame, - .m-container-inflatable section:target .m-right-m > .m-frame { - border-top-left-radius: 0px; - border-bottom-left-radius: 0px; - border-left-width: 0.125rem; - padding-left: 0.875rem; - } - .m-container-inflatable section:target .m-right-m > .m-block, - .m-container-inflatable section:target .m-right-m > .m-frame { - margin-left: 0; - } - .m-container-inflatable section:target .m-right-m > .m-note, - .m-container-inflatable section:target .m-right-m > pre { - border-top-left-radius: 0px; - border-bottom-left-radius: 0px; - margin-left: 0; - border-left-width: 0; - padding-left: 1rem; - } - .m-container-inflatable - section:target - .m-center-m - > figure.m-code-figure::before, - .m-container-inflatable - section:target - .m-center-m - > figure.m-console-figure::before, - .m-container-inflatable - section:target - .m-right-m - > figure.m-code-figure::before, - .m-container-inflatable - section:target - .m-right-m - > figure.m-console-figure::before { - border-top-left-radius: 0px; - border-bottom-left-radius: 0px; - border-left-width: 0.125rem; - } -} -@media screen and (min-width: 992px) { - .m-container-inflatable section:target .m-center-l > .m-note, - .m-container-inflatable section:target .m-center-l > pre, - .m-container-inflatable - section:target - .m-center-l - > figure.m-code-figure - > pre:first-child, - .m-container-inflatable - section:target - .m-center-l - > figure.m-console-figure - > pre:first-child, - .m-container-inflatable - section:target - .m-right-l - > figure.m-code-figure - > pre:first-child, - .m-container-inflatable - section:target - .m-right-l - > figure.m-console-figure - > pre:first-child { - border-left-width: 0; - border-top-left-radius: 0px; - border-bottom-left-radius: 0px; - padding-left: 1rem; - } - .m-container-inflatable section:target .m-center-l > .m-block, - .m-container-inflatable section:target .m-right-l > .m-block { - border-top-left-radius: 0px; - border-bottom-left-radius: 0px; - } - .m-container-inflatable section:target .m-center-l > .m-frame, - .m-container-inflatable section:target .m-right-l > .m-frame { - border-top-left-radius: 0px; - border-bottom-left-radius: 0px; - border-left-width: 0.125rem; - padding-left: 0.875rem; - } - .m-container-inflatable section:target .m-right-l > .m-block, - .m-container-inflatable section:target .m-right-l > .m-frame { - margin-left: 0; - } - .m-container-inflatable section:target .m-right-l > .m-note, - .m-container-inflatable section:target .m-right-l > pre { - border-top-left-radius: 0px; - border-bottom-left-radius: 0px; - margin-left: 0; - border-left-width: 0; - padding-left: 1rem; - } - .m-container-inflatable - section:target - .m-center-l - > figure.m-code-figure::before, - .m-container-inflatable - section:target - .m-center-l - > figure.m-console-figure::before, - .m-container-inflatable - section:target - .m-right-l - > figure.m-code-figure::before, - .m-container-inflatable - section:target - .m-right-l - > figure.m-console-figure::before { - border-top-left-radius: 0px; - border-bottom-left-radius: 0px; - border-left-width: 0.125rem; - } -} -.m-container-inflatable section:target > figure.m-code-figure::before, -.m-container-inflatable section:target > figure.m-console-figure::before, -.m-container-inflatable section:target section > figure.m-code-figure::before, -.m-container-inflatable - section:target - section - > figure.m-console-figure::before, -.m-container-inflatable - section:target - [class*="m-center-"] - > figure.m-code-figure::before, -.m-container-inflatable - section:target - [class*="m-center-"] - > figure.m-console-figure::before, -.m-container-inflatable - section:target - [class*="m-left-"] - > figure.m-code-figure::before, -.m-container-inflatable - section:target - [class*="m-left-"] - > figure.m-console-figure::before, -.m-container-inflatable - section:target - [class*="m-right-"] - > figure.m-code-figure::before, -.m-container-inflatable - section:target - [class*="m-right-"] - > figure.m-console-figure::before, -.m-container-inflatable - section:target - .m-container-inflatable - > figure.m-code-figure::before, -.m-container-inflatable - section:target - .m-container-inflatable - > figure.m-console-figure::before { - border-left-color: #ddd; -} -@media screen and (min-width: 576px) { - .m-container-inflatable - section:target - .m-center-s - > figure.m-code-figure::before, - .m-container-inflatable - section:target - .m-right-s - > figure.m-code-figure::before { - border-color: #f7f7f7; - } - .m-container-inflatable - section:target - .m-center-s - > figure.m-console-figure::before, - .m-container-inflatable - section:target - .m-right-s - > figure.m-console-figure::before { - border-color: #f7f7f7; - } -} -@media screen and (min-width: 768px) { - .m-container-inflatable - section:target - .m-center-m - > figure.m-code-figure::before, - .m-container-inflatable - section:target - .m-right-m - > figure.m-code-figure::before { - border-color: #f7f7f7; - } - .m-container-inflatable - section:target - .m-center-m - > figure.m-console-figure::before, - .m-container-inflatable - section:target - .m-right-m - > figure.m-console-figure::before { - border-color: #f7f7f7; - } -} -@media screen and (min-width: 992px) { - .m-container-inflatable - section:target - .m-center-l - > figure.m-code-figure::before, - .m-container-inflatable - section:target - .m-right-l - > figure.m-code-figure::before { - border-color: #f7f7f7; - } - .m-container-inflatable - section:target - .m-center-l - > figure.m-console-figure::before, - .m-container-inflatable - section:target - .m-right-l - > figure.m-console-figure::before { - border-color: #f7f7f7; - } -} -.m-container-inflatable section:target pre, -.m-container-inflatable section:target figure.m-code-figure > pre:first-child, -.m-container-inflatable - section:target - figure.m-console-figure - > pre:first-child { - border-color: #ddd; -} -.m-container-inflatable section:target .m-note.m-default { - border-color: #ddd; -} -.m-container-inflatable section:target .m-note.m-primary { - border-color: #31708f; -} -.m-container-inflatable section:target .m-note.m-success { - border-color: #9ad36a; -} -.m-container-inflatable section:target .m-note.m-warning { - border-color: #f9cf79; -} -.m-container-inflatable section:target .m-note.m-danger { - border-color: #f60000; -} -.m-container-inflatable section:target .m-note.m-info { - border-color: #31708f; -} -.m-container-inflatable section:target .m-note.m-dim { - border-color: #bdbdbd; -} - -.m-code .c { - color: #95a5a6; -} -.m-code .err { - color: #a61717; -} -.m-code .k { - color: #728e00; -} -.m-code .n { - color: #434f54; -} -.m-code .o { - color: #728e00; -} -.m-code .ch { - color: #95a5a6; -} -.m-code .cm { - color: #95a5a6; -} -.m-code .cp { - color: #728e00; -} -.m-code .cpf { - color: #95a5a6; -} -.m-code .c1 { - color: #95a5a6; -} -.m-code .cs { - color: #95a5a6; -} -.m-code .kc { - color: #00979d; -} -.m-code .kd { - color: #728e00; -} -.m-code .kn { - color: #728e00; -} -.m-code .kp { - color: #00979d; -} -.m-code .kr { - color: #00979d; -} -.m-code .kt { - color: #00979d; -} -.m-code .m { - color: #8a7b52; -} -.m-code .s { - color: #7f8c8d; -} -.m-code .na { - color: #434f54; -} -.m-code .nb { - color: #728e00; -} -.m-code .nc { - color: #434f54; -} -.m-code .no { - color: #434f54; -} -.m-code .nd { - color: #434f54; -} -.m-code .ni { - color: #434f54; -} -.m-code .ne { - color: #434f54; -} -.m-code .nf { - color: #d35400; -} -.m-code .nl { - color: #434f54; -} -.m-code .nn { - color: #434f54; -} -.m-code .nx { - color: #728e00; -} -.m-code .py { - color: #434f54; -} -.m-code .nt { - color: #434f54; -} -.m-code .nv { - color: #434f54; -} -.m-code .ow { - color: #728e00; -} -.m-code .mb { - color: #8a7b52; -} -.m-code .mf { - color: #8a7b52; -} -.m-code .mh { - color: #8a7b52; -} -.m-code .mi { - color: #8a7b52; -} -.m-code .mo { - color: #8a7b52; -} -.m-code .sa { - color: #7f8c8d; -} -.m-code .sb { - color: #7f8c8d; -} -.m-code .sc { - color: #7f8c8d; -} -.m-code .dl { - color: #7f8c8d; -} -.m-code .sd { - color: #7f8c8d; -} -.m-code .s2 { - color: #7f8c8d; -} -.m-code .se { - color: #7f8c8d; -} -.m-code .sh { - color: #7f8c8d; -} -.m-code .si { - color: #7f8c8d; -} -.m-code .sx { - color: #7f8c8d; -} -.m-code .sr { - color: #7f8c8d; -} -.m-code .s1 { - color: #7f8c8d; -} -.m-code .ss { - color: #7f8c8d; -} -.m-code .bp { - color: #728e00; -} -.m-code .fm { - color: #d35400; -} -.m-code .vc { - color: #434f54; -} -.m-code .vg { - color: #434f54; -} -.m-code .vi { - color: #434f54; -} -.m-code .vm { - color: #434f54; -} -.m-code .il { - color: #8a7b52; -} - -.m-console .hll { background-color: #ffffcc } -.m-console .g-AnsiBackgroundBlack { background-color: #232627 } -.m-console .g-AnsiBackgroundBlue { background-color: #1d99f3 } -.m-console .g-AnsiBackgroundBrightBlack { background-color: #7f8c8d } -.m-console .g-AnsiBackgroundBrightBlue { background-color: #3daee9 } -.m-console .g-AnsiBackgroundBrightCyan { background-color: #16a085 } -.m-console .g-AnsiBackgroundBrightGreen { background-color: #1cdc9a } -.m-console .g-AnsiBackgroundBrightMagenta { background-color: #8e44ad } -.m-console .g-AnsiBackgroundBrightRed { background-color: #c0392b } -.m-console .g-AnsiBackgroundBrightWhite { background-color: #ffffff } -.m-console .g-AnsiBackgroundBrightYellow { background-color: #fdbc4b } -.m-console .g-AnsiBackgroundCyan { background-color: #1abc9c } -.m-console .g-AnsiBackgroundDefault { background-color: #fcfcfc } -.m-console .g-AnsiBackgroundGreen { background-color: #11d116 } -.m-console .g-AnsiBackgroundMagenta { background-color: #9b59b6 } -.m-console .g-AnsiBackgroundRed { background-color: #ed1515 } -.m-console .g-AnsiBackgroundWhite { background-color: #fcfcfc } -.m-console .g-AnsiBackgroundYellow { background-color: #f67400 } -.m-console .g-AnsiBlack { color: #232627 } -.m-console .g-AnsiBlue { color: #1d99f3 } -.m-console .g-AnsiBrightBlack { color: #7f8c8d; font-weight: bold } -.m-console .g-AnsiBrightBlue { color: #3daee9; font-weight: bold } -.m-console .g-AnsiBrightCyan { color: #16a085; font-weight: bold } -.m-console .g-AnsiBrightDefault { color: #ffffff; font-weight: bold } -.m-console .g-AnsiBrightGreen { color: #1cdc9a; font-weight: bold } -.m-console .g-AnsiBrightMagenta { color: #8e44ad; font-weight: bold } -.m-console .g-AnsiBrightRed { color: #c0392b; font-weight: bold } -.m-console .g-AnsiBrightWhite { color: #ffffff; font-weight: bold } -.m-console .g-AnsiBrightYellow { color: #fdbc4b; font-weight: bold } -.m-console .g-AnsiCyan { color: #1abc9c } -.m-console .g-AnsiDefault { color: #fcfcfc } -.m-console .g-AnsiGreen { color: #11d116 } -.m-console .g-AnsiMagenta { color: #9b59b6 } -.m-console .g-AnsiRed { color: #ed1515 } -.m-console .g-AnsiWhite { color: #fcfcfc } -.m-console .g-AnsiYellow { color: #f67400 } -.m-console .go { color: #fcfcfc } -.m-console .gp { color: #16a085; font-weight: bold } -.m-console .w { color: #fcfcfc } - -a.m-doc, a.m-doc-self, a.m-doc-external, -ul.m-doc li.m-doc-expansible > a:first-child, ul.m-doc li.m-doc-collapsible > a:first-child, -.m-code.m-inverted.m-doc-include > a { - text-decoration: none; -} -a.m-doc, a.m-doc-self { - font-weight: bold; -} -.m-thin a.m-doc, .m-thin a.m-doc-self { - font-weight: normal; -} -ul.m-doc li.m-doc-expansible > a:first-child, -ul.m-doc li.m-doc-collapsible > a:first-child, -ul.m-doc li.m-doc-expansible > a:first-child:hover, -ul.m-doc li.m-doc-expansible > a:first-child:focus, -ul.m-doc li.m-doc-expansible > a:first-child:active, -ul.m-doc li.m-doc-collapsible > a:first-child:hover, -ul.m-doc li.m-doc-collapsible > a:first-child:focus, -ul.m-doc li.m-doc-collapsible > a:first-child:active { - color: #000000; -} -a.m-doc-self, -ul.m-doc li.m-doc-expansible > a:first-child:before, -ul.m-doc li.m-doc-collapsible > a:first-child:before { - color: #26a9e0; -} -a.m-doc-self:hover, a.m-doc-self:focus, a.m-doc-self:active, -ul.m-doc li.m-doc-expansible > a:first-child:hover::before, -ul.m-doc li.m-doc-expansible > a:first-child:focus::before, -ul.m-doc li.m-doc-expansible > a:first-child:active::before, -ul.m-doc li.m-doc-collapsible > a:first-child:hover::before, -ul.m-doc li.m-doc-collapsible > a:first-child:focus::before, -ul.m-doc li.m-doc-collapsible > a:first-child:active::before { - color: #26a9e0; -} -h3 a.m-doc-external { - font-weight: normal; -} -span.m-doc-wrap-bumper { - margin-right: -1rem; - display: inline-block; - vertical-align: text-top; -} -span.m-doc-wrap { - padding-left: 1rem; - display: inline-block; - vertical-align: text-top; - white-space: pre-line; - max-width: 100%; -} -dl.m-doc dd { - margin-bottom: 0.5rem; -} -dl.m-doc dd { - margin-left: 0; - padding-left: 2.5rem; -} -dl.m-doc dt:target, dl.m-doc dt:target + dd { - margin-left: -1.0rem; - border-left-style: solid; - border-left-width: 0.25rem; - border-color: #000000; -} -dl.m-doc dt:target { padding-left: 0.75rem; } -dl.m-doc dt:target + dd { padding-left: 3.25rem; } -ul.m-doc { - list-style: none; - margin-left: 1.0375rem; - padding-left: 0.9rem; - border-left-color: #ddd; - border-left-width: 0.0625rem; - border-left-style: solid; -} -ul.m-doc li { - text-indent: -1rem; - padding-left: 1rem; -} -ul.m-doc li.m-doc-expansible > ul { - display: none; -} -ul.m-doc li.m-doc-expansible, ul.m-doc li.m-doc-collapsible { - padding-left: 0.6rem; -} -ul.m-doc li.m-doc-expansible > ul.m-doc, ul.m-doc li.m-doc-collapsible > ul.m-doc { - margin-left: 0.5rem; -} -ul.m-doc li.m-doc-expansible > a:first-child:before, ul.m-doc li.m-doc-collapsible > a:first-child:before { - background-color: #e8e8e8; - display: inline-block; - width: 0.4rem; - font-weight: bold; -} -ul.m-doc li.m-doc-expansible > a:first-child:before { content: '⊕'; } -ul.m-doc li.m-doc-collapsible > a:first-child:before { content: '⊖'; } -h1 .m-doc-template, h1 .m-doc-include { - font-size: 1.3rem; - font-weight: normal; - float: right; -} -h1 .m-doc-include:last-child { - margin-bottom: -0.5rem; -} -h3 .m-doc-template, h3 .m-doc-include { - font-size: 1rem; - font-weight: normal; -} -.m-doc-template, dl.m-doc dd, ul.m-doc li > span.m-doc { - color: #bdbdbd; -} -dl.m-doc dd svg.m-math, ul.m-doc li > span.m-doc svg.m-math { - fill: #bdbdbd; -} -.m-doc-template a, dl.m-doc dd a, ul.m-doc li > span.m-doc a { - color: #c0c0c0; -} -.m-doc-template a:hover, .m-doc-template a:focus, .m-doc-template a:active, -dl.m-doc dd a:hover, dl.m-doc dd a:focus, dl.m-doc dd a:active, -ul.m-doc li > span.m-doc a:hover, ul.m-doc li > span.m-doc a:focus, ul.m-doc li > span.m-doc a:active { - color: #949494; -} -.m-code.m-inverted.m-doc-include > a:link, -.m-code.m-inverted.m-doc-include > a:visited { - opacity: 0.6666; -} -.m-code.m-inverted.m-doc-include > a:hover, -.m-code.m-inverted.m-doc-include > a:focus, -.m-code.m-inverted.m-doc-include > a:active { - opacity: 1; -} -article section.m-doc-details > div { - margin-top: 0; - margin-left: 0; - margin-right: 0; - position: relative; - padding: 1rem; -} -article section.m-doc-details > div::before { - position: absolute; - content: ' '; - top: 0; - bottom: 0; - left: 0; - right: 0; - z-index: -1; - border-style: solid; - border-width: 0.125rem; - border-radius: 0px; - border-color: #f7f7f7; -} -article section.m-doc-details > div > h3:first-child { - position: relative; - margin: -1rem -1rem 1rem -1rem; - padding: 0.5rem 1rem; - background-color: #f7f7f7; - border-top-left-radius: 0px; - border-top-right-radius: 0px; - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; -} -article section.m-doc-details:target { - border-color: transparent; -} -article section.m-doc-details:target > div { - z-index: 1; -} -.m-container-inflatable > .m-row > [class*='m-col-'] section.m-doc-details > div { - margin-left: -1rem; - margin-right: -1rem; -} -.m-container-inflatable section.m-doc-details:target > div > h3:first-child, -.m-container-inflatable section.m-doc-details:target section > div > h3:first-child { - margin-left: -1.0rem; - border-left-style: solid; - border-left-color: #000000; - border-left-width: 0.25rem; - padding-left: 0.75rem; -} -.m-container-inflatable section.m-doc-details:target > div::before, -.m-container-inflatable section-dox-details:target section > div.m::before { - border-left-width: 0.25rem; - border-left-color: #000000; -} -a.m-doc-search-icon { - padding-left: 1rem; - padding-right: 1rem; -} -a.m-doc-search-icon svg { - fill: #ffffff; -} -body > header > nav #m-navbar-collapse a.m-doc-search-icon svg { - vertical-align: -5%; -} -a.m-doc-search-icon:focus svg, a.m-doc-search-icon:hover svg, a.m-doc-search-icon:active svg { - fill: #ffffff; -} -.m-doc-search { - display: none; - z-index: 10; - position: fixed; - left: 0; - right: 0; - top: 0; - bottom: 0; - background-color: #000000; -} -.m-doc-search:target { - display: block; -} -.m-doc-search > a { - display: block; - position: absolute; - left: 0; - right: 0; - top: 0; - bottom: 0; -} -.m-doc-search-header { - margin-top: 2.5rem; - padding: 0.5rem 1rem; - height: 2rem; -} -.m-doc-search-header > div:first-child { - float: right; -} -.m-doc-search-content { - background-color: #000000; - border-radius: 0px; - padding: 1rem; -} -.m-doc-search input { - width: 100%; - height: 3rem; - font-size: 1.2rem; - border-width: 0; - color: #000000; - background-color: transparent; - border-radius: 0px; - margin-bottom: 1rem; - padding: 0 1rem; -} -.m-doc-search #search-notfound { - display: none; -} -.m-doc-search ul#search-results { - list-style-type: none; - padding-left: 0; - max-height: calc(100vh - 12.5rem); - overflow-y: auto; - display: none; -} -.m-doc-search ul#search-results li a { - display: block; - padding-left: 1rem; - padding-right: 1rem; - text-decoration: none; - width: 100%; - line-height: 1.5rem; - color: #000000; -} -.m-doc-search ul#search-results li a > div { - white-space: nowrap; - overflow: hidden; -} -.m-doc-search ul#search-results li a > div:not(.m-doc-search-alias) { - direction: rtl; -} -.m-doc-search ul#search-results li a .m-label { - float: right; - line-height: 1rem; - margin-top: 0.1rem; - margin-left: 0.25rem; -} -.m-doc-search ul#search-results li a .m-label.m-flat { - margin-right: -0.75rem; -} -.m-doc-search ul#search-results li#search-current a { - background-color: transparent; -} -.m-doc-search ul#search-results li#search-current.m-doc-search-copied a { - background-color: transparent; -} -.m-doc-search-typed { - color: #26a9e0; -} -.m-doc-search input[type="search"] { -webkit-appearance: textfield; } -.m-doc-search input[type="search"]::-webkit-search-decoration, -.m-doc-search input[type="search"]::-webkit-search-cancel-button, -.m-doc-search input[type="search"]::-webkit-search-results-button, -.m-doc-search input[type="search"]::-webkit-search-results-decoration { - display: none; -} diff --git a/docs/fixXmlExampleSections.py b/docs/fixXmlExampleSections.py deleted file mode 100644 index 539bdc8..0000000 --- a/docs/fixXmlExampleSections.py +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env python -import fileinput -import re -import os -import glob -import xml.etree.ElementTree as ET - -fileDir = os.path.dirname(os.path.realpath("__file__")) -# print("Program Directory: {}".format(fileDir)) -relative_dir = "../../Arduino-SDI-12Doxygen/xml/" -abs_file_path = os.path.join(fileDir, relative_dir) -abs_file_path = os.path.abspath(os.path.realpath(abs_file_path)) -# print("XML Directory: {}".format(fileDir)) - -all_files = [ - f - for f in os.listdir(abs_file_path) - if os.path.isfile(os.path.join(abs_file_path, f)) and f.endswith("8ino-example.xml") -] - -for filename in all_files: - print(filename) - - tree = ET.parse(os.path.join(abs_file_path, filename)) - root = tree.getroot() - - needs_to_be_fixed = False - for definition in root.iter("compounddef"): - # print(definition.attrib) - compound_id = definition.attrib["id"] - # print(compound_id) - # print("---") - - for i in range(6): - for section in definition.iter("sect" + str(i)): - # print(section.attrib) - section_id = section.attrib["id"] - if not section_id.startswith(compound_id): - # print("problem!") - needs_to_be_fixed = True - dox_loc = section_id.find(".dox_") - section_suffix = section_id[dox_loc + 6 :] - # print(section_suffix) - corrected_id = compound_id + "_" + section_suffix - # print(corrected_id) - section.attrib["id"] = corrected_id - - if needs_to_be_fixed: - tree.write(os.path.join(abs_file_path, filename + "_fixed")) - os.rename( - os.path.join(abs_file_path, filename), - os.path.join(abs_file_path, filename + "_original"), - ) - os.rename( - os.path.join(abs_file_path, filename + "_fixed"), - os.path.join(abs_file_path, filename), - ) - # print() diff --git a/docs/markdown_prefilter.py b/docs/markdown_prefilter.py deleted file mode 100644 index 6c2b4ee..0000000 --- a/docs/markdown_prefilter.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env python -import fileinput, re - -print_me = True -skip_me = False -i = 1 -# for line in fileinput.input(openhook=fileinput.hook_encoded("utf-8", "surrogateescape")): -for line in fileinput.input(): - # print(i, print_me, skip_me, line) - - # Remove markdown comment tags from doxygen commands within the markdown - if print_me and not skip_me: - print(re.sub(r'\[//\]: # \( @(\w+?.*) \)', r'@\1', line), end="") - - # using skip_me to skip single lines, so unset it after reading a line - if skip_me: - skip_me = False; - - # a page, section, subsection, or subsubsection commands followed - # immediately with by a markdown header leads to that section appearing - # twice in the doxygen html table of contents. - # I'm putting the section markers right above the header and then will skip the header. - if re.match(r'\[//\]: # \( @mainpage', line) is not None: - skip_me = True; - if re.match(r'\[//\]: # \( @page', line) is not None: - skip_me = True; - if re.match(r'\[//\]: # \( @.*section', line) is not None: - skip_me = True; - if re.match(r'\[//\]: # \( @paragraph', line) is not None: - skip_me = True; - - # I'm using these comments to fence off content that is only intended for - # github mardown rendering - if "[//]: # ( Start GitHub Only )" in line: - print_me = False - - if "[//]: # ( End GitHub Only )" in line: - print_me = True - - i += 1 From 2a7d93672fcdbc48c519d4a10c75644bcf75b1d7 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Tue, 25 Jun 2024 17:51:35 -0400 Subject: [PATCH 37/96] First round of updates for newer doxygen Signed-off-by: Sara Damiano --- docs/Doxyfile | 898 +++++++++++++------- docs/mcss-Doxyfile | 12 +- docs/mcss-conf.py | 12 +- examples/a_wild_card/ReadMe.md | 2 + examples/b_address_change/ReadMe.md | 2 + examples/c_check_all_addresses/ReadMe.md | 2 + examples/d_simple_logger/ReadMe.md | 2 + examples/e_continuous_measurement/ReadMe.md | 2 + examples/f_basic_data_request/ReadMe.md | 2 + examples/g_terminal_window/ReadMe.md | 2 + examples/k_concurrent_logger/ReadMe.md | 2 + examples/l_verify_crc/ReadMe.md | 2 + 12 files changed, 634 insertions(+), 306 deletions(-) diff --git a/docs/Doxyfile b/docs/Doxyfile index 7caadfc..bdf8275 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -1,4 +1,4 @@ -# Doxyfile 1.8.19 +# Doxyfile 1.11.0 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -12,6 +12,16 @@ # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). +# +# Note: +# +# Use doxygen to compare the used configuration file with the template +# configuration file: +# doxygen -x [configFile] +# Use doxygen to compare the used configuration file with the template +# configuration file without replacing the environment variables or CMake type +# replacement variables: +# doxygen -x_noenv [configFile] #--------------------------------------------------------------------------- # Project related configuration options @@ -51,25 +61,43 @@ PROJECT_BRIEF = "An Arduino library for SDI-12 communication without re # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. -PROJECT_LOGO = SDI-12Text.png +PROJECT_LOGO = gp-desktop-logo.png + +# With the PROJECT_ICON tag one can specify an icon that is included in the tabs +# when the HTML document is shown. Doxygen will copy the logo to the output +# directory. + +PROJECT_ICON = enviroDIY_Favicon.png # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. -OUTPUT_DIRECTORY = ../../Arduino-SDI-12Doxygen +OUTPUT_DIRECTORY = ../../Arduino-SDI-12_Doxygen -# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- -# directories (in 2 levels) under the output directory of each output format and -# will distribute the generated files over these directories. Enabling this +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096 +# sub-directories (in 2 levels) under the output directory of each output format +# and will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes -# performance problems for the file system. +# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to +# control the number of sub-directories. # The default value is: NO. CREATE_SUBDIRS = NO +# Controls the number of sub-directories that will be created when +# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every +# level increment doubles the number of directories, resulting in 4096 +# directories at level 8 which is the default and also the maximum value. The +# sub-directories are organized in 2 levels, the first level always has a fixed +# number of 16 directories. +# Minimum value: 0, maximum value: 8, default value: 8. +# This tag requires that the tag CREATE_SUBDIRS is set to YES. + +CREATE_SUBDIRS_LEVEL = 8 + # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode @@ -81,26 +109,18 @@ ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. -# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, -# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), -# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, -# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), -# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, -# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, -# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, -# Ukrainian and Vietnamese. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian, +# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English +# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek, +# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with +# English messages), Korean, Korean-en (Korean with English messages), Latvian, +# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, +# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, +# Swedish, Turkish, Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English -# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all generated output in the proper direction. -# Possible values are: None, LTR, RTL and Context. -# The default value is: None. - -OUTPUT_TEXT_DIRECTION = None - # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. @@ -170,7 +190,7 @@ FULL_PATH_NAMES = YES # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. -STRIP_FROM_PATH = ../src +STRIP_FROM_PATH = .. # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which @@ -225,7 +245,15 @@ QT_AUTOBRIEF = NO # not recognized any more. # The default value is: NO. -MULTILINE_CPP_IS_BRIEF = NO +MULTILINE_CPP_IS_BRIEF = YES + +# By default Python docstrings are displayed as preformatted text and doxygen's +# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the +# doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as doxygen documentation. +# The default value is: YES. + +PYTHON_DOCSTRING = YES # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. @@ -250,20 +278,20 @@ TAB_SIZE = 4 # the documentation. An alias has the form: # name=value # For example adding -# "sideeffect=@par Side Effects:\n" +# "sideeffect=@par Side Effects:^^" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading -# "Side Effects:". You can put \n's in the value part of an alias to insert -# newlines (in the resulting output). You can put ^^ in the value part of an -# alias to insert a newline as if a physical newline was in the original file. -# When you need a literal { or } or , in the value part of an alias you have to -# escape them by means of a backslash (\), this can lead to conflicts with the -# commands \{ and \} for these it is advised to use the version @{ and @} or use -# a double escape (\\{ and \\}) - -ALIASES = "license=@par License:\n" \ - "environment=@par Development Environment:\n" \ - "platform=@par Hardware Platform:\n" \ +# "Side Effects:". Note that you cannot put \n's in the value part of an alias +# to insert newlines (in the resulting output). You can put ^^ in the value part +# of an alias to insert a newline as if a physical newline was in the original +# file. When you need a literal { or } or , in the value part of an alias you +# have to escape them by means of a backslash (\), this can lead to conflicts +# with the commands \{ and \} for these it is advised to use the version @{ and +# @} or use a double escape (\\{ and \\}) + +ALIASES = "license=@par License^^" \ + "environment=@par Development Environment^^" \ + "platform=@par Hardware Platform^^" \ "m_div{1}=@xmlonly@endxmlonly" \ "m_enddiv=@xmlonly@endxmlonly" \ "m_span{1}=@xmlonly@endxmlonly" \ @@ -275,7 +303,8 @@ ALIASES = "license=@par License:\n" \ "m_keyword{3}=@xmlonly@endxmlonly" \ "m_enum_values_as_keywords=@xmlonly@endxmlonly" \ "m_since{2}=@since @m_class{m-label m-success m-flat} @ref changelog-\1-\2 \"since v\1.\2\"" \ - "m_deprecated_since{2}=@since deprecated in v\1.\2 @deprecated" + "m_deprecated_since{3}=@since deprecated in v\1.\2.\3 @deprecated" \ + "m_innerpage{1}=@xmlonly @endxmlonly" # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For @@ -318,8 +347,8 @@ OPTIMIZE_OUTPUT_SLICE = NO # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, JavaScript, -# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL, -# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice, +# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: # FortranFree, unknown formatted Fortran: Fortran. In the later case the parser # tries to guess whether the code is fixed or free formatted code, this is the # default for Fortran type files). For instance to make doxygen treat .inc files @@ -329,7 +358,10 @@ OPTIMIZE_OUTPUT_SLICE = NO # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise -# the files are not read by doxygen. +# the files are not read by doxygen. When specifying no_extension you should add +# * to the FILE_PATTERNS. +# +# Note see also the list of default file extension mappings. EXTENSION_MAPPING = ino=C++ @@ -347,11 +379,22 @@ MARKDOWN_SUPPORT = YES # to that level are automatically included in the table of contents, even if # they do not have an id attribute. # Note: This feature currently applies only to Markdown headings. -# Minimum value: 0, maximum value: 99, default value: 5. +# Minimum value: 0, maximum value: 99, default value: 6. # This tag requires that the tag MARKDOWN_SUPPORT is set to YES. TOC_INCLUDE_HEADINGS = 10 +# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to +# generate identifiers for the Markdown headings. Note: Every identifier is +# unique. +# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a +# sequence number starting at 0 and GITHUB use the lower case version of title +# with any whitespace replaced by '-' and punctuation characters removed. +# The default value is: DOXYGEN. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +MARKDOWN_ID_STYLE = GITHUB + # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or @@ -364,8 +407,8 @@ AUTOLINK_SUPPORT = YES # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); -# versus func(std::string) {}). This also make the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. +# versus func(std::string) {}). This also makes the inheritance and +# collaboration diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = NO @@ -377,9 +420,9 @@ BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: -# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen -# will parse them like normal C++ but will assume all classes use public instead -# of private inheritance when no explicit protection keyword is present. +# https://www.riverbankcomputing.com/software) sources only. Doxygen will parse +# them like normal C++ but will assume all classes use public instead of private +# inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO @@ -463,19 +506,27 @@ TYPEDEF_HIDES_STRUCT = NO LOOKUP_CACHE_SIZE = 0 -# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use +# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use # during processing. When set to 0 doxygen will based this on the number of # cores available in the system. You can set it explicitly to a value larger # than 0 to get more control over the balance between CPU load and processing # speed. At this moment only the input processing can be done using multiple # threads. Since this is still an experimental feature the default is set to 1, -# which efficively disables parallel processing. Please report any issues you +# which effectively disables parallel processing. Please report any issues you # encounter. Generating dot graphs in parallel is controlled by the # DOT_NUM_THREADS setting. # Minimum value: 0, maximum value: 32, default value: 1. NUM_PROC_THREADS = 1 +# If the TIMESTAMP tag is set different from NO then each generated page will +# contain the date or date and time when the page was generated. Setting this to +# NO can help when comparing the output of multiple runs. +# Possible values are: YES, NO, DATETIME and DATE. +# The default value is: NO. + +TIMESTAMP = YES + #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- @@ -488,13 +539,13 @@ NUM_PROC_THREADS = 1 # normally produced when WARNINGS is set to YES. # The default value is: NO. -EXTRACT_ALL = YES +EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. # The default value is: NO. -EXTRACT_PRIVATE = YES +EXTRACT_PRIVATE = NO # If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual # methods of a class will be included in the documentation. @@ -539,6 +590,13 @@ EXTRACT_LOCAL_METHODS = NO EXTRACT_ANON_NSPACES = NO +# If this flag is set to YES, the name of an unnamed parameter in a declaration +# will be determined by the corresponding definition. By default unnamed +# parameters remain unnamed in the output. +# The default value is: YES. + +RESOLVE_UNNAMED_PARAMS = YES + # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation @@ -550,7 +608,8 @@ HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO, these classes will be included in the various overviews. This option -# has no effect if EXTRACT_ALL is enabled. +# will also hide undocumented C++ concepts if enabled. This option has no effect +# if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO @@ -576,12 +635,20 @@ HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO -# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file -# names in lower-case letters. If set to YES, upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# (including Cygwin) and Mac users are advised to set this option to NO. -# The default value is: system dependent. +# With the correct setting of option CASE_SENSE_NAMES doxygen will better be +# able to match the capabilities of the underlying filesystem. In case the +# filesystem is case sensitive (i.e. it supports files in the same directory +# whose names only differ in casing), the option must be set to YES to properly +# deal with such files in case they appear in the input. For filesystems that +# are not case sensitive the option should be set to NO to properly deal with +# output files written for symbols that only differ in casing, such as for two +# classes, one named CLASS and the other named Class, and to also support +# references to files without having to specify the exact matching casing. On +# Windows (including Cygwin) and MacOS, users should typically set this option +# to NO, whereas on Linux or other Unix flavors it should typically be set to +# YES. +# Possible values are: SYSTEM, NO and YES. +# The default value is: SYSTEM. CASE_SENSE_NAMES = NO @@ -599,6 +666,12 @@ HIDE_SCOPE_NAMES = NO HIDE_COMPOUND_REFERENCE= NO +# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class +# will show which file needs to be included to use the class. +# The default value is: YES. + +SHOW_HEADERFILE = YES + # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. @@ -666,7 +739,7 @@ SORT_GROUP_NAMES = NO # list. # The default value is: NO. -SORT_BY_SCOPE_NAME = NO +SORT_BY_SCOPE_NAME = YES # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between @@ -756,7 +829,8 @@ FILE_VERSION_FILTER = # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml -# will be used as the name of the layout file. +# will be used as the name of the layout file. See also section "Changing the +# layout of pages" for information. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE @@ -802,27 +876,53 @@ WARNINGS = YES WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some parameters -# in a documented function, or documenting parameters that don't exist or using -# markup commands wrongly. +# potential errors in the documentation, such as documenting some parameters in +# a documented function twice, or documenting parameters that don't exist or +# using markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES +# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete +# function parameter documentation. If set to NO, doxygen will accept that some +# parameters have no documentation without warning. +# The default value is: YES. + +WARN_IF_INCOMPLETE_DOC = YES + # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return -# value. If set to NO, doxygen will only warn about wrong or incomplete -# parameter documentation, but not about the absence of documentation. If -# EXTRACT_ALL is set to YES then this flag will automatically be disabled. +# value. If set to NO, doxygen will only warn about wrong parameter +# documentation, but not about the absence of documentation. If EXTRACT_ALL is +# set to YES then this flag will automatically be disabled. See also +# WARN_IF_INCOMPLETE_DOC # The default value is: NO. WARN_NO_PARAMDOC = YES +# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about +# undocumented enumeration values. If set to NO, doxygen will accept +# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: NO. + +WARN_IF_UNDOC_ENUM_VAL = YES + # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when -# a warning is encountered. +# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS +# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the doxygen process doxygen will return with a non-zero status. +# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves +# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not +# write the warning messages in between other messages but write them at the end +# of a run, in case a WARN_LOGFILE is defined the warning messages will be +# besides being in the defined file also be shown at the end of a run, unless +# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case +# the behavior will remain as with the setting FAIL_ON_WARNINGS. +# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT. # The default value is: NO. -WARN_AS_ERROR = NO +WARN_AS_ERROR = FAIL_ON_WARNINGS_PRINT # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which @@ -830,15 +930,29 @@ WARN_AS_ERROR = NO # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) +# See also: WARN_LINE_FORMAT # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" +# In the $text part of the WARN_FORMAT command it is possible that a reference +# to a more specific place is given. To make it easier to jump to this place +# (outside of doxygen) the user can define a custom "cut" / "paste" string. +# Example: +# WARN_LINE_FORMAT = "'vi $file +$line'" +# See also: WARN_FORMAT +# The default value is: at line $line of file $file. + +WARN_LINE_FORMAT = "at line $line of file $file" + # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard -# error (stderr). +# error (stderr). In case the file specified cannot be opened for writing the +# warning and error messages are written to standard error. When as file - is +# specified the warning and error messages are written to standard output +# (stdout). -WARN_LOGFILE = doxygenOutput.log +WARN_LOGFILE = output_doxygen.log #--------------------------------------------------------------------------- # Configuration options related to the input files @@ -857,12 +971,23 @@ INPUT = ../README.md \ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: https://www.gnu.org/software/libiconv/) for the list of -# possible encodings. +# documentation (see: +# https://www.gnu.org/software/libiconv/) for the list of possible encodings. +# See also: INPUT_FILE_ENCODING # The default value is: UTF-8. INPUT_ENCODING = UTF-8 +# This tag can be used to specify the character encoding of the source files +# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify +# character encoding on a per file pattern basis. Doxygen will compare the file +# name with each pattern and apply the encoding instead of the default +# INPUT_ENCODING) if there is a match. The character encodings are a list of the +# form: pattern=encoding (like *.php=ISO-8859-1). +# See also: INPUT_ENCODING for further information on supported encodings. + +INPUT_FILE_ENCODING = + # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. @@ -871,13 +996,15 @@ INPUT_ENCODING = UTF-8 # need to set EXTENSION_MAPPING for the extension otherwise the files are not # read by doxygen. # -# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, -# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, -# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, -# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), -# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen -# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, -# *.vhdl, *.ucf, *.qsf and *.ice. +# Note the list of default checked file patterns might differ from the list of +# default file extension mappings. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm, +# *.cpp, *.cppm, *.ccm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, +# *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d, +# *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to +# be provided as doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. FILE_PATTERNS = *.c \ *.cc \ @@ -889,15 +1016,14 @@ FILE_PATTERNS = *.c \ *.hxx \ *.hpp \ *.h++ \ + *.tpp \ *.inc \ *.m \ *.markdown \ *.md \ *.mm \ *.dox \ - *.doc \ - *.txt \ - *.tpp + *.doc # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. @@ -934,10 +1060,7 @@ EXCLUDE_PATTERNS = # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories use the pattern */test/* +# ANamespace::AClass, ANamespace::*Test EXCLUDE_SYMBOLS = @@ -945,7 +1068,8 @@ EXCLUDE_SYMBOLS = # that contain example code fragments that are included (see the \include # command). -EXAMPLE_PATH = ../examples +EXAMPLE_PATH = ../examples \ + ../extras # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and @@ -988,6 +1112,11 @@ IMAGE_PATH = # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. # +# Note that doxygen will use the data processed and written to standard output +# for further processing, therefore nothing else, like debug statements or used +# commands (so in case of a Windows batch file always use @echo OFF), should be +# written to standard output. +# # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. @@ -1029,6 +1158,15 @@ FILTER_SOURCE_PATTERNS = USE_MDFILE_AS_MAINPAGE = README.md +# The Fortran standard specifies that for fixed formatted Fortran code all +# characters from position 72 are to be considered as comment. A common +# extension is to allow longer lines before the automatic comment starts. The +# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can +# be processed before the automatic comment starts. +# Minimum value: 7, maximum value: 10000, default value: 72. + +FORTRAN_COMMENT_AFTER = 72 + #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- @@ -1043,7 +1181,8 @@ USE_MDFILE_AS_MAINPAGE = README.md SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body of functions, -# classes and enums directly into the documentation. +# multi-line macros, enums or list initialized variables directly into the +# documentation. # The default value is: NO. INLINE_SOURCES = NO @@ -1116,16 +1255,24 @@ USE_HTAGS = NO VERBATIM_HEADERS = YES # If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the -# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the -# cost of reduced performance. This can be particularly helpful with template -# rich C++ code for which doxygen's built-in parser lacks the necessary type -# information. +# clang parser (see: +# http://clang.llvm.org/) for more accurate parsing at the cost of reduced +# performance. This can be particularly helpful with template rich C++ code for +# which doxygen's built-in parser lacks the necessary type information. # Note: The availability of this option depends on whether or not doxygen was # generated with the -Duse_libclang=ON option for CMake. # The default value is: NO. CLANG_ASSISTED_PARSING = NO +# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS +# tag is set to YES then doxygen will add the directory of each input to the +# include path. +# The default value is: YES. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_ADD_INC_PATHS = YES + # If clang assisted parsing is enabled you can provide the compiler with command # line options that you would normally use when invoking the compiler. Note that # the include paths will already be set by doxygen for the files and directories @@ -1139,7 +1286,7 @@ CLANG_OPTIONS = # file is the compilation database (see: # http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the # options used when the source files were built. This is equivalent to -# specifying the "-p" option to a clang tool, such as clang-check. These options +# specifying the -p option to a clang tool, such as clang-check. These options # will then be passed to the parser. Any options specified with CLANG_OPTIONS # will be added as well. # Note: The availability of this option depends on whether or not doxygen was @@ -1158,17 +1305,11 @@ CLANG_DATABASE_PATH = ALPHABETICAL_INDEX = NO -# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in -# which the alphabetical index list will be split. -# Minimum value: 1, maximum value: 20, default value: 5. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all classes will -# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag -# can be used to specify a prefix (or a list of prefixes) that should be ignored -# while generating the index headers. +# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes) +# that should be ignored while generating the index headers. The IGNORE_PREFIX +# tag works for classes, function and member names. The entity will be placed in +# the alphabetical list under the first letter of the entity name that remains +# after removing the prefix. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = @@ -1247,7 +1388,12 @@ HTML_STYLESHEET = # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the -# list). For an example see the documentation. +# list). +# Note: Since the styling of scrollbars can currently not be overruled in +# Webkit/Chromium, the styling will be left out of the default doxygen.css if +# one or more extra stylesheets have been specified. So if scrollbar +# customization is desired it has to be added explicitly. For an example see the +# documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = @@ -1262,9 +1408,22 @@ HTML_EXTRA_STYLESHEET = HTML_EXTRA_FILES = +# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output +# should be rendered with a dark or light theme. +# Possible values are: LIGHT always generates light mode output, DARK always +# generates dark mode output, AUTO_LIGHT automatically sets the mode according +# to the user preference, uses light mode if no preference is set (the default), +# AUTO_DARK automatically sets the mode according to the user preference, uses +# dark mode if no preference is set and TOGGLE allows a user to switch between +# light and dark mode via a button. +# The default value is: AUTO_LIGHT. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE = AUTO_LIGHT + # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to -# this color. Hue is specified as an angle on a colorwheel, see +# this color. Hue is specified as an angle on a color-wheel, see # https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. @@ -1274,7 +1433,7 @@ HTML_EXTRA_FILES = HTML_COLORSTYLE_HUE = 198 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors -# in the HTML output. For a value of 0 the output will use grayscales only. A +# in the HTML output. For a value of 0 the output will use gray-scales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1292,15 +1451,6 @@ HTML_COLORSTYLE_SAT = 83 HTML_COLORSTYLE_GAMMA = 88 -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting this -# to YES can help to show when doxygen was last run and thus if the -# documentation is up to date. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_TIMESTAMP = YES - # If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML # documentation will contain a main index with vertical navigation menus that # are dynamically created via JavaScript. If disabled, the navigation index will @@ -1320,6 +1470,33 @@ HTML_DYNAMIC_MENUS = YES HTML_DYNAMIC_SECTIONS = YES +# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be +# dynamically folded and expanded in the generated HTML source code. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_CODE_FOLDING = YES + +# If the HTML_COPY_CLIPBOARD tag is set to YES then doxygen will show an icon in +# the top right corner of code and text fragments that allows the user to copy +# its content to the clipboard. Note this only works if supported by the browser +# and the web page is served via a secure context (see: +# https://www.w3.org/TR/secure-contexts/), i.e. using the https: or file: +# protocol. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COPY_CLIPBOARD = YES + +# Doxygen stores a couple of settings persistently in the browser (via e.g. +# cookies). By default these settings apply to all HTML pages generated by +# doxygen across all projects. The HTML_PROJECT_COOKIE tag can be used to store +# the settings under a project specific key, such that the user preferences will +# be stored separately. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_PROJECT_COOKIE = + # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to @@ -1335,10 +1512,11 @@ HTML_INDEX_NUM_ENTRIES = 500 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: https://developer.apple.com/xcode/), introduced with OSX -# 10.5 (Leopard). To create a documentation set, doxygen will generate a -# Makefile in the HTML output directory. Running make will produce the docset in -# that directory and running make install will install the docset in +# environment (see: +# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To +# create a documentation set, doxygen will generate a Makefile in the HTML +# output directory. Running make will produce the docset in that directory and +# running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy # genXcode/_index.html for more information. @@ -1355,6 +1533,13 @@ GENERATE_DOCSET = NO DOCSET_FEEDNAME = "Doxygen generated docs" +# This tag determines the URL of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDURL = + # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. @@ -1380,8 +1565,12 @@ DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on -# Windows. +# on Windows. In the beginning of 2021 Microsoft took the original page, with +# a.o. the download links, offline the HTML help workshop was already many years +# in maintenance mode). You can download the HTML help workshop from the web +# archives at Installation executable (see: +# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo +# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML @@ -1438,6 +1627,16 @@ BINARY_TOC = NO TOC_EXPAND = NO +# The SITEMAP_URL tag is used to specify the full URL of the place where the +# generated documentation will be placed on the server by the user during the +# deployment of the documentation. The generated sitemap is called sitemap.xml +# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL +# is specified no sitemap is generated. For information about the sitemap +# protocol see https://www.sitemaps.org +# This tag requires that the tag GENERATE_HTML is set to YES. + +SITEMAP_URL = + # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help @@ -1456,7 +1655,8 @@ QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace -# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). +# (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1464,8 +1664,8 @@ QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual- -# folders). +# Folders (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1473,16 +1673,16 @@ QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom -# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- -# filters). +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- -# filters). +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = @@ -1494,9 +1694,9 @@ QHP_CUST_FILTER_ATTRS = QHP_SECT_FILTER_ATTRS = -# The QHG_LOCATION tag can be used to specify the location of Qt's -# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the -# generated .qhp file. +# The QHG_LOCATION tag can be used to specify the location (absolute path +# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to +# run qhelpgenerator on the generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = @@ -1539,16 +1739,28 @@ DISABLE_INDEX = NO # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can -# further fine-tune the look of the index. As an example, the default style -# sheet generated by doxygen has an example that shows how to put an image at -# the root of the tree instead of the PROJECT_NAME. Since the tree basically has -# the same information as the tab index, you could consider setting -# DISABLE_INDEX to YES when enabling this option. +# further fine tune the look of the index (see "Fine-tuning the output"). As an +# example, the default style sheet generated by doxygen has an example that +# shows how to put an image at the root of the tree instead of the PROJECT_NAME. +# Since the tree basically has the same information as the tab index, you could +# consider setting DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = YES +# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the +# FULL_SIDEBAR option determines if the side bar is limited to only the treeview +# area (value NO) or if it should extend to the full height of the window (value +# YES). Setting this to YES gives a layout similar to +# https://docs.readthedocs.io with more room for contents, but less room for the +# project logo, title, and description. If either GENERATE_TREEVIEW or +# DISABLE_INDEX is set to NO, this option has no effect. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FULL_SIDEBAR = NO + # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # @@ -1573,6 +1785,13 @@ TREEVIEW_WIDTH = 200 EXT_LINKS_IN_WINDOW = NO +# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email +# addresses. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +OBFUSCATE_EMAILS = YES + # If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg # tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see # https://inkscape.org) to generate formulas as SVG images instead of PNGs for @@ -1593,17 +1812,6 @@ HTML_FORMULA_FORMAT = png FORMULA_FONTSIZE = 10 -# Use the FORMULA_TRANSPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are not -# supported properly for IE 6.0, but are supported on all modern browsers. -# -# Note that when changing this option you need to delete any form_*.png files in -# the HTML output directory before the changes have effect. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. - -FORMULA_TRANSPARENT = YES - # The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands # to create new LaTeX commands to be used in formulas as building blocks. See # the section "Including formulas" for details. @@ -1621,11 +1829,29 @@ FORMULA_MACROFILE = USE_MATHJAX = NO +# With MATHJAX_VERSION it is possible to specify the MathJax version to be used. +# Note that the different versions of MathJax have different requirements with +# regards to the different settings, so it is possible that also other MathJax +# settings have to be changed when switching between the different MathJax +# versions. +# Possible values are: MathJax_2 and MathJax_3. +# The default value is: MathJax_2. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_VERSION = MathJax_2 + # When MathJax is enabled you can set the default output format to be used for -# the MathJax output. See the MathJax site (see: -# http://docs.mathjax.org/en/latest/output.html) for more details. +# the MathJax output. For more details about the output format see MathJax +# version 2 (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3 +# (see: +# http://docs.mathjax.org/en/latest/web/components/output.html). # Possible values are: HTML-CSS (which is slower, but has the best -# compatibility), NativeMML (i.e. MathML) and SVG. +# compatibility. This is the name for Mathjax version 2, for MathJax version 3 +# this will be translated into chtml), NativeMML (i.e. MathML. Only supported +# for MathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This +# is the name for Mathjax version 3, for MathJax version 2 this will be +# translated into HTML-CSS) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. @@ -1638,22 +1864,29 @@ MATHJAX_FORMAT = HTML-CSS # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of -# MathJax from https://www.mathjax.org before deployment. -# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2. +# MathJax from https://www.mathjax.org before deployment. The default value is: +# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2 +# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3 # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@2 # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example +# for MathJax version 2 (see +# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions): # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# For example for MathJax version 3 (see +# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): +# MATHJAX_EXTENSIONS = ams # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site -# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. @@ -1700,7 +1933,8 @@ SERVER_BASED_SEARCH = NO # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: https://xapian.org/). +# Xapian (see: +# https://xapian.org/). # # See the section "External Indexing and Searching" for details. # The default value is: NO. @@ -1713,8 +1947,9 @@ EXTERNAL_SEARCH = NO # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: https://xapian.org/). See the section "External Indexing and -# Searching" for details. +# Xapian (see: +# https://xapian.org/). See the section "External Indexing and Searching" for +# details. # This tag requires that the tag SEARCHENGINE is set to YES. SEARCHENGINE_URL = @@ -1823,29 +2058,31 @@ PAPER_TYPE = a4 EXTRA_PACKAGES = -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the -# generated LaTeX document. The header should contain everything until the first -# chapter. If it is left blank doxygen will generate a standard header. See -# section "Doxygen usage" for information on how to let doxygen write the -# default header to a separate file. +# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for +# the generated LaTeX document. The header should contain everything until the +# first chapter. If it is left blank doxygen will generate a standard header. It +# is highly recommended to start with a default header using +# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty +# and then modify the file new_header.tex. See also section "Doxygen usage" for +# information on how to generate the default header that doxygen normally uses. # -# Note: Only use a user-defined header if you know what you are doing! The -# following commands have a special meaning inside the header: $title, -# $datetime, $date, $doxygenversion, $projectname, $projectnumber, -# $projectbrief, $projectlogo. Doxygen will replace $title with the empty -# string, for the replacement values of the other commands the user is referred -# to HTML_HEADER. +# Note: Only use a user-defined header if you know what you are doing! +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. The following +# commands have a special meaning inside the header (and footer): For a +# description of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_HEADER = -# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the -# generated LaTeX document. The footer should contain everything after the last -# chapter. If it is left blank doxygen will generate a standard footer. See +# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for +# the generated LaTeX document. The footer should contain everything after the +# last chapter. If it is left blank doxygen will generate a standard footer. See # LATEX_HEADER for more information on how to generate a default footer and what -# special commands can be used inside the footer. -# -# Note: Only use a user-defined footer if you know what you are doing! +# special commands can be used inside the footer. See also section "Doxygen +# usage" for information on how to generate the default footer that doxygen +# normally uses. Note: Only use a user-defined footer if you know what you are +# doing! # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_FOOTER = @@ -1888,10 +2125,16 @@ PDF_HYPERLINKS = YES USE_PDFLATEX = YES -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode -# command to the generated LaTeX files. This will instruct LaTeX to keep running -# if errors occur, instead of asking the user for help. This option is also used -# when generating formulas in HTML. +# The LATEX_BATCHMODE tag signals the behavior of LaTeX in case of an error. +# Possible values are: NO same as ERROR_STOP, YES same as BATCH, BATCH In batch +# mode nothing is printed on the terminal, errors are scrolled as if is +# hit at every error; missing files that TeX tries to input or request from +# keyboard input (\read on a not open input stream) cause the job to abort, +# NON_STOP In nonstop mode the diagnostic message will appear on the terminal, +# but there is no possibility of user interaction just like in batch mode, +# SCROLL In scroll mode, TeX will stop only for missing files to input or if +# keyboard input is necessary and ERROR_STOP In errorstop mode, TeX will stop at +# each error, asking for user intervention. # The default value is: NO. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -1904,16 +2147,6 @@ LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO -# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source -# code with syntax highlighting in the LaTeX output. -# -# Note that which sources are shown also depends on other settings such as -# SOURCE_BROWSER. -# The default value is: NO. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_SOURCE_CODE = NO - # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. See # https://en.wikipedia.org/wiki/BibTeX and \cite for more info. @@ -1922,14 +2155,6 @@ LATEX_SOURCE_CODE = NO LATEX_BIB_STYLE = plain -# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated -# page will contain the date and time when the page was generated. Setting this -# to NO can help when comparing the output of multiple runs. -# The default value is: NO. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_TIMESTAMP = NO - # The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute) # path from which the emoji images will be read. If a relative path is entered, # it will be relative to the LATEX_OUTPUT directory. If left blank the @@ -1994,15 +2219,13 @@ RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = -# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code -# with syntax highlighting in the RTF output. -# -# Note that which sources are shown also depends on other settings such as -# SOURCE_BROWSER. -# The default value is: NO. +# The RTF_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the RTF_OUTPUT output directory. +# Note that the files will be copied as-is; there are no commands or markers +# available. # This tag requires that the tag GENERATE_RTF is set to YES. -RTF_SOURCE_CODE = NO +RTF_EXTRA_FILES = #--------------------------------------------------------------------------- # Configuration options related to the man page output @@ -2080,7 +2303,7 @@ XML_PROGRAMLISTING = NO # The default value is: NO. # This tag requires that the tag GENERATE_XML is set to YES. -XML_NS_MEMB_FILE_SCOPE = NO +XML_NS_MEMB_FILE_SCOPE = YES #--------------------------------------------------------------------------- # Configuration options related to the DOCBOOK output @@ -2100,21 +2323,12 @@ GENERATE_DOCBOOK = NO DOCBOOK_OUTPUT = docbook -# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the -# program listings (including syntax highlighting and cross-referencing -# information) to the DOCBOOK output. Note that enabling this will significantly -# increase the size of the DOCBOOK output. -# The default value is: NO. -# This tag requires that the tag GENERATE_DOCBOOK is set to YES. - -DOCBOOK_PROGRAMLISTING = NO - #--------------------------------------------------------------------------- # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an -# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures +# AutoGen Definitions (see https://autogen.sourceforge.net/) file that captures # the structure of the code including all documentation. Note that this feature # is still experimental and incomplete at the moment. # The default value is: NO. @@ -2125,6 +2339,28 @@ GENERATE_AUTOGEN_DEF = NO # Configuration options related to Sqlite3 output #--------------------------------------------------------------------------- +# If the GENERATE_SQLITE3 tag is set to YES doxygen will generate a Sqlite3 +# database with symbols found by doxygen stored in tables. +# The default value is: NO. + +GENERATE_SQLITE3 = YES + +# The SQLITE3_OUTPUT tag is used to specify where the Sqlite3 database will be +# put. If a relative path is entered the value of OUTPUT_DIRECTORY will be put +# in front of it. +# The default directory is: sqlite3. +# This tag requires that the tag GENERATE_SQLITE3 is set to YES. + +SQLITE3_OUTPUT = sqlite3 + +# The SQLITE3_RECREATE_DB tag is set to YES, the existing doxygen_sqlite3.db +# database file will be recreated with each doxygen run. If set to NO, doxygen +# will warn if a database file is already found and not modify it. +# The default value is: YES. +# This tag requires that the tag GENERATE_SQLITE3 is set to YES. + +SQLITE3_RECREATE_DB = YES + #--------------------------------------------------------------------------- # Configuration options related to the Perl module output #--------------------------------------------------------------------------- @@ -2188,7 +2424,7 @@ MACRO_EXPANSION = YES # The default value is: NO. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -EXPAND_ONLY_PREDEF = YES +EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES, the include files in the # INCLUDE_PATH will be searched if a #include is found. @@ -2199,10 +2435,11 @@ SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by the -# preprocessor. +# preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of +# RECURSIVE has no effect here. # This tag requires that the tag SEARCH_INCLUDES is set to YES. -INCLUDE_PATH = +INCLUDE_PATH = ../src # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the @@ -2221,7 +2458,8 @@ INCLUDE_FILE_PATTERNS = *.c \ *.hpp \ *.h++ \ *.md \ - *.markdown + *.markdown \ + *.tpp # The PREDEFINED tag can be used to specify one or more macro names that are # defined before the preprocessor is started (similar to the -D option of e.g. @@ -2231,13 +2469,15 @@ INCLUDE_FILE_PATTERNS = *.c \ # recursively expanded use the := operator instead of the = operator. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -PREDEFINED = __attribute__((__progmem__))= \ +PREDEFINED = DOXYGEN=1 \ + __attribute__((__progmem__))= \ PLATFORMIO=40304 \ ARDUINO_AVR_ENVIRODIY_MAYFLY \ F_CPU=8000000L \ ARDUINO_ARCH_AVR \ ARDUINO=10808 \ __AVR_ATmega1284P__ \ + SERIAL_PORT_USBVIRTUAL=0\ SDI12_EXTERNAL_PCINT \ ESP32 @@ -2285,15 +2525,15 @@ TAGFILES = GENERATE_TAGFILE = -# If the ALLEXTERNALS tag is set to YES, all external class will be listed in -# the class index. If set to NO, only the inherited external classes will be -# listed. +# If the ALLEXTERNALS tag is set to YES, all external classes and namespaces +# will be listed in the class and namespace index. If set to NO, only the +# inherited external classes will be listed. # The default value is: NO. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed -# in the modules index. If set to NO, only the current project's groups will be +# in the topic index. If set to NO, only the current project's groups will be # listed. # The default value is: YES. @@ -2307,39 +2547,23 @@ EXTERNAL_GROUPS = YES EXTERNAL_PAGES = YES #--------------------------------------------------------------------------- -# Configuration options related to the dot tool +# Configuration options related to diagram generator tools #--------------------------------------------------------------------------- -# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram -# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to -# NO turns the diagrams off. Note that this option also works with HAVE_DOT -# disabled, but it is recommended to install and use dot, since it yields more -# powerful graphs. -# The default value is: YES. - -CLASS_DIAGRAMS = YES - -# You can include diagrams made with dia in doxygen documentation. Doxygen will -# then run dia to produce the diagram and insert it in the documentation. The -# DIA_PATH tag allows you to specify the directory where the dia binary resides. -# If left empty dia is assumed to be found in the default search path. - -DIA_PATH = - # If set to YES the inheritance and collaboration graphs will hide inheritance # and usage relations if the target is undocumented or is not a class. # The default value is: YES. -HIDE_UNDOC_RELATIONS = YES +HIDE_UNDOC_RELATIONS = NO # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz (see: -# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent +# https://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent # Bell Labs. The other options in this section have no effect if this option is # set to NO # The default value is: NO. -HAVE_DOT = YES +HAVE_DOT = NO # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed # to run in parallel. When set to 0 doxygen will base this on the number of @@ -2351,53 +2575,81 @@ HAVE_DOT = YES DOT_NUM_THREADS = 0 -# When you want a differently looking font in the dot files that doxygen -# generates you can specify the font name using DOT_FONTNAME. You need to make -# sure dot is able to find the font, which can be done by putting it in a -# standard location or by setting the DOTFONTPATH environment variable or by -# setting DOT_FONTPATH to the directory containing the font. -# The default value is: Helvetica. +# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of +# subgraphs. When you want a differently looking font in the dot files that +# doxygen generates you can specify fontname, fontcolor and fontsize attributes. +# For details please see Node, +# Edge and Graph Attributes specification You need to make sure dot is able +# to find the font, which can be done by putting it in a standard location or by +# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. Default graphviz fontsize is 14. +# The default value is: fontname=Helvetica,fontsize=10. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10" + +# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can +# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. Complete documentation about +# arrows shapes. +# The default value is: labelfontname=Helvetica,labelfontsize=10. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_FONTNAME = Helvetica +DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10" -# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of -# dot graphs. -# Minimum value: 4, maximum value: 24, default value: 10. +# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes +# around nodes set 'shape=plain' or 'shape=plaintext' Shapes specification +# The default value is: shape=box,height=0.2,width=0.4. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_FONTSIZE = 10 +DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4" -# By default doxygen will tell dot to use the default font as specified with -# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set -# the path where dot can find it using this tag. +# You can set the path where dot can find font specified with fontname in +# DOT_COMMON_ATTR and others dot attributes. # This tag requires that the tag HAVE_DOT is set to YES. DOT_FONTPATH = -# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for -# each documented class showing the direct and indirect inheritance relations. -# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO. +# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then doxygen will +# generate a graph for each documented class showing the direct and indirect +# inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and +# HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case +# the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the +# CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used. +# If the CLASS_GRAPH tag is set to TEXT the direct and indirect inheritance +# relations will be shown as texts / links. Explicit enabling an inheritance +# graph or choosing a different representation for an inheritance graph of a +# specific class, can be accomplished by means of the command \inheritancegraph. +# Disabling an inheritance graph can be accomplished by means of the command +# \hideinheritancegraph. +# Possible values are: NO, YES, TEXT, GRAPH and BUILTIN. # The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. -CLASS_GRAPH = YES +CLASS_GRAPH = NO # If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a # graph for each documented class showing the direct and indirect implementation # dependencies (inheritance, containment, and class references variables) of the -# class with other documented classes. +# class with other documented classes. Explicit enabling a collaboration graph, +# when COLLABORATION_GRAPH is set to NO, can be accomplished by means of the +# command \collaborationgraph. Disabling a collaboration graph can be +# accomplished by means of the command \hidecollaborationgraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. -COLLABORATION_GRAPH = YES +COLLABORATION_GRAPH = NO # If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for -# groups, showing the direct groups dependencies. +# groups, showing the direct groups dependencies. Explicit enabling a group +# dependency graph, when GROUP_GRAPHS is set to NO, can be accomplished by means +# of the command \groupgraph. Disabling a directory graph can be accomplished by +# means of the command \hidegroupgraph. See also the chapter Grouping in the +# manual. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. -GROUP_GRAPHS = YES +GROUP_GRAPHS = NO # If the UML_LOOK tag is set to YES, doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling @@ -2416,35 +2668,62 @@ UML_LOOK = NO # but if the number exceeds 15, the total amount of fields shown is limited to # 10. # Minimum value: 0, maximum value: 100, default value: 10. -# This tag requires that the tag HAVE_DOT is set to YES. +# This tag requires that the tag UML_LOOK is set to YES. UML_LIMIT_NUM_FIELDS = 10 +# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and +# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS +# tag is set to YES, doxygen will add type and arguments for attributes and +# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen +# will not generate fields with class member information in the UML graphs. The +# class diagrams will look similar to the default class diagrams but using UML +# notation for the relationships. +# Possible values are: NO, YES and NONE. +# The default value is: NO. +# This tag requires that the tag UML_LOOK is set to YES. + +DOT_UML_DETAILS = NO + +# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters +# to display on a single line. If the actual line length exceeds this threshold +# significantly it will be wrapped across multiple lines. Some heuristics are +# applied to avoid ugly line breaks. +# Minimum value: 0, maximum value: 1000, default value: 17. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_WRAP_THRESHOLD = 17 + # If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and # collaboration graphs will show the relations between templates and their # instances. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. -TEMPLATE_RELATIONS = YES +TEMPLATE_RELATIONS = NO # If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to # YES then doxygen will generate a graph for each documented file showing the # direct and indirect include dependencies of the file with other documented -# files. +# files. Explicit enabling an include graph, when INCLUDE_GRAPH is is set to NO, +# can be accomplished by means of the command \includegraph. Disabling an +# include graph can be accomplished by means of the command \hideincludegraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. -INCLUDE_GRAPH = YES +INCLUDE_GRAPH = NO # If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are # set to YES then doxygen will generate a graph for each documented file showing # the direct and indirect include dependencies of the file with other documented -# files. +# files. Explicit enabling an included by graph, when INCLUDED_BY_GRAPH is set +# to NO, can be accomplished by means of the command \includedbygraph. Disabling +# an included by graph can be accomplished by means of the command +# \hideincludedbygraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. -INCLUDED_BY_GRAPH = YES +INCLUDED_BY_GRAPH = NO # If the CALL_GRAPH tag is set to YES then doxygen will generate a call # dependency graph for every global function or class method. @@ -2475,21 +2754,31 @@ CALLER_GRAPH = NO # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. -GRAPHICAL_HIERARCHY = YES +GRAPHICAL_HIERARCHY = NO # If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the # dependencies a directory has on other directories in a graphical way. The # dependency relations are determined by the #include relations between the -# files in the directories. +# files in the directories. Explicit enabling a directory graph, when +# DIRECTORY_GRAPH is set to NO, can be accomplished by means of the command +# \directorygraph. Disabling a directory graph can be accomplished by means of +# the command \hidedirectorygraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. -DIRECTORY_GRAPH = YES +DIRECTORY_GRAPH = NO + +# The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels +# of child directories generated in directory dependency graphs by dot. +# Minimum value: 1, maximum value: 25, default value: 1. +# This tag requires that the tag DIRECTORY_GRAPH is set to YES. + +DIR_GRAPH_MAX_DEPTH = 5 # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. For an explanation of the image formats see the section # output formats in the documentation of the dot tool (Graphviz (see: -# http://www.graphviz.org/)). +# https://www.graphviz.org/)). # Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order # to make the SVG files visible in IE 9+ (other browsers do not have this # requirement). @@ -2499,7 +2788,7 @@ DIRECTORY_GRAPH = YES # The default value is: png. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_IMAGE_FORMAT = png +DOT_IMAGE_FORMAT = svg # If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to # enable generation of interactive SVG images that allow zooming and panning. @@ -2526,11 +2815,12 @@ DOT_PATH = DOTFILE_DIRS = -# The MSCFILE_DIRS tag can be used to specify one or more directories that -# contain msc files that are included in the documentation (see the \mscfile -# command). +# You can include diagrams made with dia in doxygen documentation. Doxygen will +# then run dia to produce the diagram and insert it in the documentation. The +# DIA_PATH tag allows you to specify the directory where the dia binary resides. +# If left empty dia is assumed to be found in the default search path. -MSCFILE_DIRS = +DIA_PATH = # The DIAFILE_DIRS tag can be used to specify one or more directories that # contain dia files that are included in the documentation (see the \diafile @@ -2539,10 +2829,10 @@ MSCFILE_DIRS = DIAFILE_DIRS = # When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the -# path where java can find the plantuml.jar file. If left blank, it is assumed -# PlantUML is not used or called during a preprocessing step. Doxygen will -# generate a warning when it encounters a \startuml command in this case and -# will not generate output for the diagram. +# path where java can find the plantuml.jar file or to the filename of jar file +# to be used. If left blank, it is assumed PlantUML is not used or called during +# a preprocessing step. Doxygen will generate a warning when it encounters a +# \startuml command in this case and will not generate output for the diagram. PLANTUML_JAR_PATH = @@ -2559,7 +2849,7 @@ PLANTUML_INCLUDE_PATH = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes # that will be shown in the graph. If the number of nodes in a graph becomes # larger than this value, doxygen will truncate the graph, which is visualized -# by representing a node as a red box. Note that doxygen if the number of direct +# by representing a node as a red box. Note that if the number of direct # children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that # the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. @@ -2580,18 +2870,6 @@ DOT_GRAPH_MAX_NODES = 500 MAX_DOT_GRAPH_DEPTH = 0 -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, because dot on Windows does not seem -# to support this out of the box. -# -# Warning: Depending on the platform used, enabling this option may lead to -# badly anti-aliased labels on the edges of a graph (i.e. they become hard to -# read). -# The default value is: NO. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_TRANSPARENT = NO - # Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) support @@ -2604,14 +2882,34 @@ DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page # explaining the meaning of the various boxes and arrows in the dot generated # graphs. +# Note: This tag requires that UML_LOOK isn't set, i.e. the doxygen internal +# graphical representation for inheritance and collaboration diagrams is used. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. GENERATE_LEGEND = YES -# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot +# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate # files that are used to generate the various graphs. +# +# Note: This setting is not only used for dot files but also for msc temporary +# files. # The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. DOT_CLEANUP = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. If the MSCGEN_TOOL tag is left empty (the default), then doxygen will +# use a built-in version of mscgen tool to produce the charts. Alternatively, +# the MSCGEN_TOOL tag can also specify the name an external tool. For instance, +# specifying prog as the value, doxygen will call the tool as prog -T +# -o . The external tool should support +# output file formats "png", "eps", "svg", and "ismap". + +MSCGEN_TOOL = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the \mscfile +# command). + +MSCFILE_DIRS = diff --git a/docs/mcss-Doxyfile b/docs/mcss-Doxyfile index 107316a..2a08286 100644 --- a/docs/mcss-Doxyfile +++ b/docs/mcss-Doxyfile @@ -2,9 +2,9 @@ GENERATE_HTML = NO GENERATE_XML = YES XML_PROGRAMLISTING = NO -HTML_OUTPUT = ../Arduino-SDI-12Doxygen/m.css -SHOW_INCLUDE_FILES = YES -WARN_LOGFILE = mcssDoxygenOutput.log +HTML_OUTPUT = ../Arduino-SDI-12_Doxygen/m.css +SHOW_INCLUDE_FILES = YES +WARN_LOGFILE = output_mcss.log PROJECT_REPOSITORY = https://github.com/EnviroDIY/Arduino-SDI-12 ALIASES += \ "m_div{1}=@xmlonly@endxmlonly" \ @@ -18,4 +18,8 @@ ALIASES += \ "m_keyword{3}=@xmlonly@endxmlonly" \ "m_enum_values_as_keywords=@xmlonly@endxmlonly" \ "m_since{2}=@since @m_class{m-label m-success m-flat} @ref changelog-\1-\2 \"since v\1.\2\"" \ - "m_deprecated_since{2}=@since deprecated in v\1.\2 @deprecated" \ No newline at end of file + "m_deprecated_since{3}=@since deprecated in v\1.\2.\3 @deprecated" + +HAVE_DOT = NO +DOT_FONTNAME = Source Sans Pro +DOT_FONTSIZE = 16 diff --git a/docs/mcss-conf.py b/docs/mcss-conf.py index df2343a..596a03e 100644 --- a/docs/mcss-conf.py +++ b/docs/mcss-conf.py @@ -1,7 +1,6 @@ DOXYFILE = "mcss-Doxyfile" THEME_COLOR = "#cb4b16" -# FAVICON = "https://3qzcxr28gq9vutx8scdn91zq-wpengine.netdna-ssl.com/wp-content/uploads/2016/05/cropped-EnviroDIY_LogoMaster_TrueSquare_V5_TwoTree_Trans_notext-192x192.png" -FAVICON = "SDI-12Text-Cropped.png" +FAVICON = "enviroDIY_Favicon.png" LINKS_NAVBAR1 = [ ( "Functions", @@ -52,3 +51,12 @@ STYLESHEETS = [ "css/m-EnviroDIY+documentation.compiled.css", ] +EXTRA_FILES = [ + "gp-desktop-logo.png", + "gp-mobile-logo.png", + "gp-scrolling-logo.png", + "clipboard.js", +] +DESKTOP_LOGO = "gp-desktop-logo.png" +MOBILE_LOGO = "gp-mobile-logo.png" +SCROLLING_LOGO = "gp-scrolling-logo.png" diff --git a/examples/a_wild_card/ReadMe.md b/examples/a_wild_card/ReadMe.md index f8f87ca..5798349 100644 --- a/examples/a_wild_card/ReadMe.md +++ b/examples/a_wild_card/ReadMe.md @@ -6,6 +6,8 @@ It requests information about a single attached sensor, including its address an [//]: # ( @section a_wild_card_pio PlatformIO Configuration ) +[//]: # ( @example{lineno} a_wild_card.ino @m_examplenavigation{examples_page,} @m_footernavigation ) + [//]: # ( @include{lineno} a_wild_card/platformio.ini ) [//]: # ( @section a_wild_card_code The Complete Example ) diff --git a/examples/b_address_change/ReadMe.md b/examples/b_address_change/ReadMe.md index ce1dbda..cc462c6 100644 --- a/examples/b_address_change/ReadMe.md +++ b/examples/b_address_change/ReadMe.md @@ -19,6 +19,8 @@ If you are using a Meter Group Hydros 21 CTD sensor, change the channel to 1 in [//]: # ( @section b_address_change_pio PlatformIO Configuration ) +[//]: # ( @example{lineno} b_address_change.ino @m_examplenavigation{examples_page,} @m_footernavigation ) + [//]: # ( @include{lineno} b_address_change/platformio.ini ) [//]: # ( @section b_address_change_code The Complete Example ) diff --git a/examples/c_check_all_addresses/ReadMe.md b/examples/c_check_all_addresses/ReadMe.md index ed0247a..137b416 100644 --- a/examples/c_check_all_addresses/ReadMe.md +++ b/examples/c_check_all_addresses/ReadMe.md @@ -11,6 +11,8 @@ To address a sensor, please see Example B: b_address_change.ino [//]: # ( @section c_check_all_addresses_pio PlatformIO Configuration ) +[//]: # ( @example{lineno} c_check_all_addresses.ino @m_examplenavigation{examples_page,} @m_footernavigation ) + [//]: # ( @include{lineno} c_check_all_addresses/platformio.ini ) [//]: # ( @section c_check_all_addresses_code The Complete Example ) diff --git a/examples/d_simple_logger/ReadMe.md b/examples/d_simple_logger/ReadMe.md index 5606c43..5b6bc9f 100644 --- a/examples/d_simple_logger/ReadMe.md +++ b/examples/d_simple_logger/ReadMe.md @@ -13,6 +13,8 @@ To address a sensor, please see Example B: b_address_change.ino [//]: # ( @section d_simple_logger_pio PlatformIO Configuration ) +[//]: # ( @example{lineno} d_simple_logger.ino @m_examplenavigation{examples_page,} @m_footernavigation ) + [//]: # ( @include{lineno} d_simple_logger/platformio.ini ) [//]: # ( @section d_simple_logger_code The Complete Example ) diff --git a/examples/e_continuous_measurement/ReadMe.md b/examples/e_continuous_measurement/ReadMe.md index 43653d2..df5fb0f 100644 --- a/examples/e_continuous_measurement/ReadMe.md +++ b/examples/e_continuous_measurement/ReadMe.md @@ -13,6 +13,8 @@ To address a sensor, please see Example B: b_address_change.ino [//]: # ( @section e_continuous_measurement_pio PlatformIO Configuration ) +[//]: # ( @example{lineno} e_continuous_measurement.ino @m_examplenavigation{examples_page,} @m_footernavigation ) + [//]: # ( @include{lineno} e_continuous_measurement/platformio.ini ) [//]: # ( @section e_continuous_measurement_code The Complete Example ) diff --git a/examples/f_basic_data_request/ReadMe.md b/examples/f_basic_data_request/ReadMe.md index b8572a1..03983a4 100644 --- a/examples/f_basic_data_request/ReadMe.md +++ b/examples/f_basic_data_request/ReadMe.md @@ -7,6 +7,8 @@ This is a very basic (stripped down) example where the user initiates a measurem [//]: # ( @section f_basic_data_request_pio PlatformIO Configuration ) +[//]: # ( @example{lineno} f_basic_data_request.ino @m_examplenavigation{examples_page,} @m_footernavigation ) + [//]: # ( @include{lineno} f_basic_data_request/platformio.ini ) [//]: # ( @section f_basic_data_request_code The Complete Example ) diff --git a/examples/g_terminal_window/ReadMe.md b/examples/g_terminal_window/ReadMe.md index c222be6..5ff6a53 100644 --- a/examples/g_terminal_window/ReadMe.md +++ b/examples/g_terminal_window/ReadMe.md @@ -7,6 +7,8 @@ It's purpose is to allow a user to interact with an SDI-12 sensor directly, issu [//]: # ( @section g_terminal_window_pio PlatformIO Configuration ) +[//]: # ( @example{lineno} g_terminal_window.ino @m_examplenavigation{examples_page,} @m_footernavigation ) + [//]: # ( @include{lineno} g_terminal_window/platformio.ini ) [//]: # ( @section g_terminal_window_code The Complete Example ) diff --git a/examples/k_concurrent_logger/ReadMe.md b/examples/k_concurrent_logger/ReadMe.md index 3025222..638fe7f 100644 --- a/examples/k_concurrent_logger/ReadMe.md +++ b/examples/k_concurrent_logger/ReadMe.md @@ -7,6 +7,8 @@ This can be much faster than waiting for each sensor when you have multiple sens [//]: # ( @section k_concurrent_logger_pio PlatformIO Configuration ) +[//]: # ( @example{lineno} k_concurrent_logger.ino @m_examplenavigation{examples_page,} @m_footernavigation ) + [//]: # ( @include{lineno} k_concurrent_logger/platformio.ini ) [//]: # ( @section k_concurrent_logger_code The Complete Example ) diff --git a/examples/l_verify_crc/ReadMe.md b/examples/l_verify_crc/ReadMe.md index 6411dc4..921b06f 100644 --- a/examples/l_verify_crc/ReadMe.md +++ b/examples/l_verify_crc/ReadMe.md @@ -7,6 +7,8 @@ This is a very basic (stripped down) example where the user initiates a measurem [//]: # ( @sectionl_verify_crc_pio PlatformIO Configuration ) +[//]: # ( @example{lineno} l_verify_crc.ino @m_examplenavigation{examples_page,} @m_footernavigation ) + [//]: # ( @include{lineno} l_verify_crc/platformio.ini ) [//]: # ( @section l_verify_crc_code The Complete Example ) From e656c3d119cc42f9feb9b944e6974df2827be683 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Tue, 25 Jun 2024 17:52:28 -0400 Subject: [PATCH 38/96] Rename wake delay Signed-off-by: Sara Damiano --- examples/d_simple_logger/d_simple_logger.ino | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/d_simple_logger/d_simple_logger.ino b/examples/d_simple_logger/d_simple_logger.ino index a6ac0d1..5ac6691 100644 --- a/examples/d_simple_logger/d_simple_logger.ino +++ b/examples/d_simple_logger/d_simple_logger.ino @@ -28,7 +28,7 @@ uint32_t serialBaud = 115200; /*!< The baud rate for the output serial port */ int8_t dataPin = 7; /*!< The pin of the SDI-12 data bus */ int8_t powerPin = 22; /*!< The sensor power pin (or -1 if not switching power) */ -uint32_t wakeDelay = 0; /*!< Extra time needed for the sensor to wake (0-100ms) */ +uint32_t wake_delay = 0; /*!< Extra time needed for the sensor to wake (0-100ms) */ int8_t firstAddress = 0; /* The first address in the address space to check (0='0') */ int8_t lastAddress = 62; /* The last address in the address space to check (62='z') */ @@ -81,7 +81,7 @@ void printInfo(char i) { String command = ""; command += (char)i; command += "I!"; - mySDI12.sendCommand(command, wakeDelay); + mySDI12.sendCommand(command, wake_delay); delay(100); String sdiResponse = mySDI12.readStringUntil('\n'); @@ -112,7 +112,7 @@ bool getResults(char i, int resultsExpected) { command += "D"; command += cmd_number; command += "!"; // SDI-12 command to get data [address][D][dataOption][!] - mySDI12.sendCommand(command, wakeDelay); + mySDI12.sendCommand(command, wake_delay); uint32_t start = millis(); while (mySDI12.available() < 3 && (millis() - start) < 1500) {} @@ -149,7 +149,7 @@ bool takeMeasurement(char i, String meas_type = "") { command += "M"; command += meas_type; command += "!"; // SDI-12 measurement command format [address]['M'][!] - mySDI12.sendCommand(command, wakeDelay); + mySDI12.sendCommand(command, wake_delay); delay(100); Serial.print(command); @@ -202,7 +202,7 @@ boolean checkActive(char i) { myCommand += "!"; for (int j = 0; j < 3; j++) { // goes through three rapid contact attempts - mySDI12.sendCommand(myCommand, wakeDelay); + mySDI12.sendCommand(myCommand, wake_delay); delay(100); if (mySDI12.available()) { // If we here anything, assume we have an active sensor mySDI12.clearBuffer(); From 8e1a308c297721f8045b71ed299f17cbabbd8c68 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Tue, 25 Jun 2024 17:58:07 -0400 Subject: [PATCH 39/96] Fix licence/copyright doxy tags Signed-off-by: Sara Damiano --- examples/a_wild_card/a_wild_card.ino | 2 +- .../b_address_change/b_address_change.ino | 2 +- .../c_check_all_addresses.ino | 2 +- examples/d_simple_logger/d_simple_logger.ino | 2 +- .../e_continuous_measurement.ino | 2 +- .../f_basic_data_request.ino | 5 ++-- .../g_terminal_window/g_terminal_window.ino | 2 +- .../h_SDI-12_slave_implementation.ino | 2 +- .../i_SDI-12_interface/i_SDI-12_interface.ino | 2 +- examples/j_external_pcint_library/ReadMe.md | 2 ++ .../j_external_pcint_library.ino | 2 +- .../k_concurrent_logger.ino | 2 +- examples/l_verify_crc/l_verify_crc.ino | 2 +- src/SDI12.cpp | 4 ++-- src/SDI12.h | 4 ++-- src/SDI12_boards.cpp | 4 ++-- src/SDI12_boards.h | 4 ++-- tools/SDI12_spy/SDI12_spy.ino | 2 +- tools/TestCommands/TestCommands.ino | 24 +++---------------- tools/TestWarmUp/TestWarmUp.ino | 2 +- 20 files changed, 28 insertions(+), 45 deletions(-) diff --git a/examples/a_wild_card/a_wild_card.ino b/examples/a_wild_card/a_wild_card.ino index 613c97f..4d42339 100644 --- a/examples/a_wild_card/a_wild_card.ino +++ b/examples/a_wild_card/a_wild_card.ino @@ -2,7 +2,7 @@ /** * @file a_wild_card.ino * @copyright Stroud Water Research Center - * This example is published under the BSD-3 license. + * @license This example is published under the BSD-3 license. * @author Kevin M.Smith * @date August 2013 * diff --git a/examples/b_address_change/b_address_change.ino b/examples/b_address_change/b_address_change.ino index a358112..6245b02 100644 --- a/examples/b_address_change/b_address_change.ino +++ b/examples/b_address_change/b_address_change.ino @@ -1,7 +1,7 @@ /** * @file b_address_change.ino * @copyright Stroud Water Research Center - * This example is published under the BSD-3 license. + * @license This example is published under the BSD-3 license. * @author Kevin M.Smith * @date August 2013 * diff --git a/examples/c_check_all_addresses/c_check_all_addresses.ino b/examples/c_check_all_addresses/c_check_all_addresses.ino index 78a73e6..d306fac 100644 --- a/examples/c_check_all_addresses/c_check_all_addresses.ino +++ b/examples/c_check_all_addresses/c_check_all_addresses.ino @@ -1,7 +1,7 @@ /** * @file c_check_all_addresses.ino * @copyright Stroud Water Research Center - * This example is published under the BSD-3 license. + * @license This example is published under the BSD-3 license. * @author Kevin M.Smith * @date August 2013 * diff --git a/examples/d_simple_logger/d_simple_logger.ino b/examples/d_simple_logger/d_simple_logger.ino index 5ac6691..79d9e19 100644 --- a/examples/d_simple_logger/d_simple_logger.ino +++ b/examples/d_simple_logger/d_simple_logger.ino @@ -1,7 +1,7 @@ /** * @file d_simple_logger.ino * @copyright Stroud Water Research Center - * This example is published under the BSD-3 license. + * @license This example is published under the BSD-3 license. * @author Kevin M.Smith * @date August 2013 * diff --git a/examples/e_continuous_measurement/e_continuous_measurement.ino b/examples/e_continuous_measurement/e_continuous_measurement.ino index d6e0ffb..19a61b5 100644 --- a/examples/e_continuous_measurement/e_continuous_measurement.ino +++ b/examples/e_continuous_measurement/e_continuous_measurement.ino @@ -1,7 +1,7 @@ /** * @file d_simple_logger.ino * @copyright Stroud Water Research Center - * This example is published under the BSD-3 license. + * @license This example is published under the BSD-3 license. * @author Kevin M.Smith * @date August 2013 * diff --git a/examples/f_basic_data_request/f_basic_data_request.ino b/examples/f_basic_data_request/f_basic_data_request.ino index 86ed163..2574c93 100644 --- a/examples/f_basic_data_request/f_basic_data_request.ino +++ b/examples/f_basic_data_request/f_basic_data_request.ino @@ -1,8 +1,7 @@ /** * @file f_basic_data_request.ino - * @copyright (c) 2013-2020 Stroud Water Research Center (SWRC) - * and the EnviroDIY Development Team - * This example is published under the BSD-3 license. + * @copyright Stroud Water Research Center + * @license This example is published under the BSD-3 license. * @author Ruben Kertesz or @rinnamon on twitter * @date 2/10/2016 * diff --git a/examples/g_terminal_window/g_terminal_window.ino b/examples/g_terminal_window/g_terminal_window.ino index a18f279..a51a59d 100644 --- a/examples/g_terminal_window/g_terminal_window.ino +++ b/examples/g_terminal_window/g_terminal_window.ino @@ -1,7 +1,7 @@ /** * @file g_terminal_window.ino * @copyright Stroud Water Research Center - * This example is published under the BSD-3 license. + * @license This example is published under the BSD-3 license. * @author Kevin M.Smith * @date August 2013 * @author Ruben Kertesz or @rinnamon on twitter diff --git a/examples/h_SDI-12_slave_implementation/h_SDI-12_slave_implementation.ino b/examples/h_SDI-12_slave_implementation/h_SDI-12_slave_implementation.ino index 1cd2b3b..eafeffd 100644 --- a/examples/h_SDI-12_slave_implementation/h_SDI-12_slave_implementation.ino +++ b/examples/h_SDI-12_slave_implementation/h_SDI-12_slave_implementation.ino @@ -1,7 +1,7 @@ /** * @file h_SDI-12_slave_implementation.ino * @copyright Stroud Water Research Center - * This example is published under the BSD-3 license. + * @license This example is published under the BSD-3 license. * @date 2016 * @author D. Wasielewski * diff --git a/examples/i_SDI-12_interface/i_SDI-12_interface.ino b/examples/i_SDI-12_interface/i_SDI-12_interface.ino index 54b3a7a..8006469 100644 --- a/examples/i_SDI-12_interface/i_SDI-12_interface.ino +++ b/examples/i_SDI-12_interface/i_SDI-12_interface.ino @@ -1,7 +1,7 @@ /** * @file h_SDI-12_slave_implementation.ino * @copyright Stroud Water Research Center - * This example is published under the BSD-3 license. + * @license This example is published under the BSD-3 license. * @date 2016 * @author D. Wasielewski * diff --git a/examples/j_external_pcint_library/ReadMe.md b/examples/j_external_pcint_library/ReadMe.md index 9d6daf1..4d51ac4 100644 --- a/examples/j_external_pcint_library/ReadMe.md +++ b/examples/j_external_pcint_library/ReadMe.md @@ -7,6 +7,8 @@ To use this example, you must remove the comment braces around `#define SDI12_EX [//]: # ( @section j_external_pcint_library_pio PlatformIO Configuration ) +[//]: # ( @example{lineno} j_external_pcint_library.ino @m_examplenavigation{examples_page,} @m_footernavigation ) + [//]: # ( @include{lineno} j_external_pcint_library/platformio.ini ) [//]: # ( @section j_external_pcint_library_code The Complete Example ) diff --git a/examples/j_external_pcint_library/j_external_pcint_library.ino b/examples/j_external_pcint_library/j_external_pcint_library.ino index 8e02f3b..baf26e1 100644 --- a/examples/j_external_pcint_library/j_external_pcint_library.ino +++ b/examples/j_external_pcint_library/j_external_pcint_library.ino @@ -1,7 +1,7 @@ /** * @file j_external_pcint_library.ino * @copyright Stroud Water Research Center - * This example is published under the BSD-3 license. + * @license This example is published under the BSD-3 license. * @author Kevin M.Smith * * @brief Example J: Using External Interrupts diff --git a/examples/k_concurrent_logger/k_concurrent_logger.ino b/examples/k_concurrent_logger/k_concurrent_logger.ino index 7929b0d..e5ab327 100644 --- a/examples/k_concurrent_logger/k_concurrent_logger.ino +++ b/examples/k_concurrent_logger/k_concurrent_logger.ino @@ -1,7 +1,7 @@ /** * @file k_concurrent_logger.ino * @copyright Stroud Water Research Center - * This example is published under the BSD-3 license. + * @license This example is published under the BSD-3 license. * @author Sara Geleskie Damiano * * @brief Example K: Concurrent Measurements diff --git a/examples/l_verify_crc/l_verify_crc.ino b/examples/l_verify_crc/l_verify_crc.ino index 15d1459..b8bb99c 100644 --- a/examples/l_verify_crc/l_verify_crc.ino +++ b/examples/l_verify_crc/l_verify_crc.ino @@ -1,7 +1,7 @@ /** * @file f_basic_data_request.ino * @copyright Stroud Water Research Center - * This example is published under the BSD-3 license. + * @license This example is published under the BSD-3 license. * @author Ruben Kertesz or @rinnamon on twitter * @date 2/10/2016 * diff --git a/src/SDI12.cpp b/src/SDI12.cpp index 21ecb1e..f42eae8 100644 --- a/src/SDI12.cpp +++ b/src/SDI12.cpp @@ -1,7 +1,7 @@ /** * @file SDI12.cpp - * @copyright (c) 2013-2020 Stroud Water Research Center (SWRC) - * and the EnviroDIY Development Team + * @copyright Stroud Water Research Center + * @license This library is published under the BSD-3 license. * @date August 2013 * @author Kevin M.Smith * diff --git a/src/SDI12.h b/src/SDI12.h index a376031..af1098b 100644 --- a/src/SDI12.h +++ b/src/SDI12.h @@ -1,7 +1,7 @@ /** * @file SDI12.h - * @copyright (c) 2013-2020 Stroud Water Research Center (SWRC) - * and the EnviroDIY Development Team + * @copyright Stroud Water Research Center + * @license This library is published under the BSD-3 license. * @date August 2013 * @author Kevin M.Smith * diff --git a/src/SDI12_boards.cpp b/src/SDI12_boards.cpp index 3a21879..db3c575 100644 --- a/src/SDI12_boards.cpp +++ b/src/SDI12_boards.cpp @@ -1,7 +1,7 @@ /** * @file SDI12_boards.cpp - * @copyright (c) 2013-2020 Stroud Water Research Center (SWRC) - * and the EnviroDIY Development Team + * @copyright Stroud Water Research Center + * @license This library is published under the BSD-3 license. * @author Sara Geleskie Damiano (sdamiano@stroudcenter.org) * * @brief This file implements the setting and unsetting of the proper prescalers for diff --git a/src/SDI12_boards.h b/src/SDI12_boards.h index fc2842d..0bfbe70 100644 --- a/src/SDI12_boards.h +++ b/src/SDI12_boards.h @@ -1,7 +1,7 @@ /** * @file SDI12_boards.h - * @copyright (c) 2013-2020 Stroud Water Research Center (SWRC) - * and the EnviroDIY Development Team + * @copyright Stroud Water Research Center + * @license This library is published under the BSD-3 license. * @author Sara Geleskie Damiano (sdamiano@stroudcenter.org) * * @brief This file defines the timing units needed for various Arduino boards. diff --git a/tools/SDI12_spy/SDI12_spy.ino b/tools/SDI12_spy/SDI12_spy.ino index b02540f..ab6da5f 100644 --- a/tools/SDI12_spy/SDI12_spy.ino +++ b/tools/SDI12_spy/SDI12_spy.ino @@ -1,7 +1,7 @@ /** * @file h_SDI-12_slave_implementation.ino * @copyright Stroud Water Research Center - * This example is published under the BSD-3 license. + * @license This example is published under the BSD-3 license. * @date 2016 * @author D. Wasielewski * diff --git a/tools/TestCommands/TestCommands.ino b/tools/TestCommands/TestCommands.ino index 8db31ab..85041d7 100644 --- a/tools/TestCommands/TestCommands.ino +++ b/tools/TestCommands/TestCommands.ino @@ -1,26 +1,8 @@ /** - * @file d_simple_logger.ino + * @file TestCommands.ino * @copyright Stroud Water Research Center - * This example is published under the BSD-3 license. - * @author Kevin M.Smith - * @date August 2013 - * - * @brief Example D: Check all Addresses for Active Sensors and Log Data - * - * This is a simple demonstration of the SDI-12 library for Arduino. - * - * It discovers the address of all sensors active on a single bus and takes measurements - * from them. - * - * Every SDI-12 device is different in the time it takes to take a - * measurement, and the amount of data it returns. This sketch will not serve every - * sensor type, but it will likely be helpful in getting you started. - * - * Each sensor should have a unique address already - if not, multiple sensors may - * respond simultaenously to the same request and the output will not be readable by the - * Arduino. - * - * To address a sensor, please see Example B: b_address_change.ino + * @license This example is published under the BSD-3 license. + * @author Sara Damiano */ #include diff --git a/tools/TestWarmUp/TestWarmUp.ino b/tools/TestWarmUp/TestWarmUp.ino index e0a8f89..b1427c5 100644 --- a/tools/TestWarmUp/TestWarmUp.ino +++ b/tools/TestWarmUp/TestWarmUp.ino @@ -1,7 +1,7 @@ /** * @file TestWarmUp.ino * @copyright Stroud Water Research Center - * This example is published under the BSD-3 license. + * @license This example is published under the BSD-3 license. * @author Sara Damiano * @date March 2021 */ From 28117468ebaaa105dd33d8bc0fb3c6a66bd44880 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Tue, 25 Jun 2024 17:58:44 -0400 Subject: [PATCH 40/96] Update test programs Signed-off-by: Sara Damiano --- tools/TestCommands/TestCommands.ino | 408 +++++++++++------ tools/TestSensorTiming/TestSensorTiming.ino | 472 ++++++++++++++++++++ tools/TestWarmUp/TestWarmUp.ino | 111 +++-- tools/printCommands/printCommands.ino | 399 ----------------- 4 files changed, 788 insertions(+), 602 deletions(-) create mode 100644 tools/TestSensorTiming/TestSensorTiming.ino delete mode 100644 tools/printCommands/printCommands.ino diff --git a/tools/TestCommands/TestCommands.ino b/tools/TestCommands/TestCommands.ino index 85041d7..0ed4b5b 100644 --- a/tools/TestCommands/TestCommands.ino +++ b/tools/TestCommands/TestCommands.ino @@ -11,7 +11,7 @@ uint32_t serialBaud = 115200; /*!< The baud rate for the output serial port */ uint8_t dataPin = 7; /*!< The pin of the SDI-12 data bus */ int8_t powerPin = 22; /*!< The sensor power pin (or -1 if not switching power) */ -uint32_t wakeDelay = 0; /*!< Extra time needed for the sensor to wake (0-100ms) */ +uint32_t wake_delay = 0; /*!< Extra time needed for the sensor to wake (0-100ms) */ int8_t firstAddress = 0; /* The first address in the address space to check (0='0') */ int8_t lastAddress = 62; /* The last address in the address space to check (62='z') */ int8_t commandsToTest = @@ -44,6 +44,12 @@ String prev_result[62]; String this_result[62]; uint8_t numSensors = 0; +struct startMeasurementResult { // Structure declaration + String returned_address; + uint8_t meas_time_s; + int numberResults; +}; + /** * @brief converts allowable address characters ('0'-'9', 'a'-'z', 'A'-'Z') to a * decimal number between 0 and 61 (inclusive) to cover the 62 possible @@ -83,12 +89,11 @@ void printInfo(char i, bool printCommands = true) { String command = ""; command += (char)i; command += "I!"; - mySDI12.sendCommand(command, wakeDelay); + mySDI12.sendCommand(command, wake_delay); if (printCommands) { Serial.print(">>>"); Serial.println(command); } - delay(100); String sdiResponse = mySDI12.readStringUntil('\n'); sdiResponse.trim(); @@ -113,102 +118,207 @@ void printInfo(char i, bool printCommands = true) { Serial.println(); } -bool getResults(char addr, int resultsExpected, bool printCommands = true) { +bool getResults(char address, int resultsExpected, bool verify_crc = false, + bool printCommands = true) { uint8_t resultsReceived = 0; uint8_t cmd_number = 0; - String str_result = ""; + // The maximum number of characters that can be returned in the part of the + // response to a D command is either 35 or 75. If the D command is issued to + // retrieve data in response to a concurrent measurement command, or in response to + // a high-volume ASCII measurement command, the maximum is 75. The maximum is also + // 75 in response to a continuous measurement command. Otherwise, the maximum is 35. + int max_sdi_response = 76; + // max chars in a unsigned 64 bit number + int max_sdi_digits = 21; + + String compiled_response = ""; + while (resultsReceived < resultsExpected && cmd_number <= 9) { - // while (resultsReceived < resultsExpected && cmd_number <= 1) { String command = ""; - // in this example we will only take the 'DO' measurement - command = ""; - command += addr; + command += address; command += "D"; command += cmd_number; command += "!"; // SDI-12 command to get data [address][D][dataOption][!] - mySDI12.sendCommand(command, wakeDelay); + mySDI12.sendCommand(command, wake_delay); + + uint32_t start = millis(); if (printCommands) { Serial.print(">>>"); Serial.println(command); } + char resp_buffer[max_sdi_response] = {'\0'}; - uint32_t start = millis(); - while (mySDI12.available() < 3 && (millis() - start) < 150) {} + // read bytes into the char array until we get to a new line (\r\n) + size_t bytes_read = mySDI12.readBytesUntil('\n', resp_buffer, max_sdi_response); + // Serial.print(bytes_read); + // Serial.println(" characters"); + + size_t data_bytes_read = bytes_read - 1; // subtract one for the /r before the /n + String sdiResponse = String(resp_buffer); + compiled_response += sdiResponse; + sdiResponse.trim(); if (printCommands) { Serial.print("<<<"); - Serial.write(mySDI12.read()); // ignore the repeated SDI12 address - } else { - mySDI12.read(); + Serial.println(sdiResponse); + // Serial.println(sdiResponse.length()); + // Serial.print("<<<"); + // Serial.println(resp_buffer); + // Serial.println(strnlen(resp_buffer, max_sdi_response)); } - - bool gotResults = false; + // read and clear anything else from the buffer + int extra_chars = 0; while (mySDI12.available()) { - // First peek to see if the next character in the buffer is a number - char c = mySDI12.peek(); - // if there's a number, a decimal, or a negative sign next in the - // buffer, start reading it as a float. + Serial.write(mySDI12.read()); + extra_chars++; + } + if (extra_chars > 0) { + Serial.print(extra_chars); + Serial.println(" additional characters received."); + } + mySDI12.clearBuffer(); + + // check the address, return if it's incorrect + char returned_address = resp_buffer[0]; + if (returned_address != address) { + if (printCommands) { + Serial.println("Wrong address returned!"); + Serial.print("Expected "); + Serial.print(String(address)); + Serial.print(" Got "); + Serial.println(String(returned_address)); + Serial.println(String(resp_buffer)); + } + return false; + } + + // check the crc, return if it's incorrect + if (verify_crc) { + bool crcMatch = mySDI12.verifyCRC(sdiResponse); + data_bytes_read = data_bytes_read - 3; + if (crcMatch) { + if (printCommands) { Serial.println("CRC valid"); } + } else { + if (printCommands) { Serial.println("CRC check failed!"); } + return false; + } + } + + bool gotResults = false; + char float_buffer[max_sdi_digits] = {'\0'}; + char* dec_pl = float_buffer; + uint8_t fb_pos = 0; // start at start of buffer + bool finished_last_number = false; + // iterate through the char array and to check results + // NOTE: start at 1 since we already looked at the address! + for (size_t i = 1; i < data_bytes_read; i++) { + // Get the character at position + char c = resp_buffer[i]; + // Serial.print(i); + // Serial.print(" of "); + // Serial.print(data_bytes_read); + // Serial.print(" '"); + // Serial.print(c); + // Serial.println("'"); + // if we didn't get something number-esque or we're at the end of the buffer, + // assume the last number finished and parse it + //(c != '-' && (c < '0' || c > '9') && c != '.') if (c == '-' || (c >= '0' && c <= '9') || c == '.') { - // Read the float without skipping any in-valid characters. - // We don't want to skip anything because we want to be able to - // debug and see exactly which characters the sensor sent over - // if they weren't numbers. - // Reading the numbers as a float will remove them from the - // buffer. - float result = mySDI12.parseFloat(SKIP_NONE); - // add result to print out string - str_result += String(result, 8); - // also print results if we're printing commands - if (printCommands) { Serial.print(String(result, 8)); } + // if there's a number, a decimal, or a negative sign next in the + // buffer, add it to the float buffer. + float_buffer[fb_pos] = c; + fb_pos++; + float_buffer[fb_pos] = '\0'; // null terminate the buffer + finished_last_number = false; + // Serial.print("Added to float buffer, currently: '"); + // Serial.print(float_buffer); + // Serial.println("'"); + } else { + // Serial.println("Non Numeric"); + finished_last_number = true; + } + // if we've gotten to the end of a number or the end of the buffer, parse the + // character + if ((finished_last_number || i == data_bytes_read - 1) && + strnlen(float_buffer, max_sdi_digits) > 0) { + float result = atof(float_buffer); + if (printCommands) { + Serial.print("Result "); + Serial.print(resultsReceived); + Serial.print(", Raw value: "); + Serial.print(float_buffer); + dec_pl = strchr(float_buffer, '.'); + // Serial.print(", Len after decimal: "); + // Serial.print(strnlen(dec_pl, max_sdi_digits)); + Serial.print(", Parsed value: "); + Serial.println(String(result, strnlen(dec_pl, max_sdi_digits) - 1)); + } // add how many results we have if (result != -9999) { gotResults = true; resultsReceived++; } - } else if (c >= 0 && c != '\r' && c != '\n') { - str_result += String(c); - if (printCommands) { - Serial.write(mySDI12.read()); - } else { - mySDI12.read(); + // check for a failure error code at the end + if (resultsReceived == 5 && result != 0.0) { + gotResults = false; + resultsReceived = 0; + if (printCommands) { + Serial.print("Got a failure code of "); + Serial.println(String(result, strnlen(dec_pl, max_sdi_digits) - 1)); + } } - } else { - mySDI12.read(); + + // empty the buffer + float_buffer[0] = '\0'; + fb_pos = 0; } - delay(10); // 1 character ~ 7.5ms - } - if (printCommands) { - Serial.print("\nTotal Results Received: "); - Serial.print(resultsReceived); - Serial.print(", Remaining: "); - Serial.println(resultsExpected - resultsReceived); } + if (!gotResults) { if (printCommands) { Serial.println((" No results received, will not continue requests!")); } break; } // don't do another loop if we got nothing + + if (printCommands) { + Serial.print("Total Results Received: "); + Serial.print(resultsReceived); + Serial.print(", Remaining: "); + Serial.println(resultsExpected - resultsReceived); + } + cmd_number++; } + mySDI12.clearBuffer(); + if (printCommands) { + Serial.print("After "); + Serial.print(cmd_number); + Serial.print(" data commands got "); + Serial.print(resultsReceived); + Serial.print(" results of the expected "); + Serial.print(resultsExpected); + Serial.print(" expected. This is a "); + Serial.println(resultsReceived == resultsExpected ? "success." : "failure."); + } + bool success = resultsReceived == resultsExpected; - if (success) { this_result[charToDec(addr)] = str_result; } + if (success) { this_result[charToDec(address)] = compiled_response; } return success; } -bool getContinuousResults(char i, int resultsExpected, bool printCommands = true) { +bool getContinuousResults(char address, int resultsExpected, + bool printCommands = true) { uint8_t resultsReceived = 0; uint8_t cmd_number = 0; while (resultsReceived < resultsExpected && cmd_number <= 9) { String command = ""; - // in this example we will only take the 'DO' measurement - command = ""; - command += i; + command += address; command += "R"; command += cmd_number; command += "!"; // SDI-12 command to get data [address][D][dataOption][!] - mySDI12.sendCommand(command, wakeDelay); + mySDI12.sendCommand(command, wake_delay); if (printCommands) { Serial.print(">>>"); Serial.println(command); @@ -248,19 +358,26 @@ bool getContinuousResults(char i, int resultsExpected, bool printCommands = true return resultsReceived == resultsExpected; } -int startConcurrentMeasurement(char i, String meas_type = "", - bool printCommands = true) { +startMeasurementResult startMeasurement(char address, bool is_concurrent = false, + bool request_crc = false, String meas_type = "", + bool printCommands = true) { + // Create the return struct + startMeasurementResult return_result; + return_result.returned_address = ""; + return_result.meas_time_s = 0; + return_result.numberResults = 0; + String command = ""; - command += i; - command += "C"; - command += meas_type; - command += "!"; // SDI-12 concurrent measurement command format [address]['C'][!] - mySDI12.sendCommand(command, wakeDelay); + command += address; // All commands start with the address + command += is_concurrent ? "C" : "M"; // C for concurrent, M for standard + command += request_crc ? "C" : ""; // add an additional C to request a CRC + command += meas_type; // Measurement type, "" or 0-9 + command += "!"; // All commands end with "!" + mySDI12.sendCommand(command, wake_delay); if (printCommands) { Serial.print(">>>"); Serial.println(command); } - delay(5); // wait for acknowlegement with format [address][ttt (3 char, seconds)][number of // measurments available, 0-9] @@ -272,8 +389,25 @@ int startConcurrentMeasurement(char i, String meas_type = "", } mySDI12.clearBuffer(); + // check the address, return if it's incorrect + String returned_address = sdiResponse.substring(0, 1); + char ret_addr_array[2]; + returned_address.toCharArray(ret_addr_array, sizeof(ret_addr_array)); + return_result.returned_address = ret_addr_array[0]; + if (returned_address != String(address)) { + if (printCommands) { + Serial.println("Wrong address returned!"); + Serial.print("Expected "); + Serial.print(String(address)); + Serial.print(" Got "); + Serial.println(returned_address); + } + return return_result; + } + // find out how long we have to wait (in seconds). - uint8_t meas_time_s = sdiResponse.substring(1, 4).toInt(); + uint8_t meas_time_s = sdiResponse.substring(1, 4).toInt(); + return_result.meas_time_s = meas_time_s; if (printCommands) { Serial.print("expected measurement time: "); Serial.print(meas_time_s); @@ -281,93 +415,73 @@ int startConcurrentMeasurement(char i, String meas_type = "", } // Set up the number of results to expect - int numResults = sdiResponse.substring(4).toInt(); + int numResults = sdiResponse.substring(4).toInt(); + return_result.numberResults = numResults; if (printCommands) { Serial.print("Number Results: "); Serial.println(numResults); } - uint8_t sensorNum = charToDec(i); // e.g. convert '0' to 0, 'a' to 10, 'Z' to 61. - meas_time_ms[sensorNum] = ((uint32_t)(meas_time_s)) * 1000; - millisStarted[sensorNum] = millis(); - if (meas_time_ms == 0) { - millisReady[sensorNum] = millis(); - } else { - // give an extra second - millisReady[sensorNum] = millis() + meas_time_ms[sensorNum] + 1000; - } - returnedResults[sensorNum] = numResults; - - return numResults; + return return_result; } -bool takeMeasurement(char i, String meas_type = "", bool printCommands = true) { - String command = ""; - command += i; - command += "M"; - command += meas_type; - command += "!"; // SDI-12 measurement command format [address]['M'][!] - mySDI12.sendCommand(command, wakeDelay); - if (printCommands) { - Serial.print(">>>"); - Serial.println(command); +uint32_t takeMeasurement(char address, bool request_crc = false, String meas_type = "", + bool printCommands = true) { + startMeasurementResult startResult = startMeasurement(address, false, request_crc, + meas_type, printCommands); + if (startResult.numberResults == 0) { return -1; } + + uint32_t timerStart = millis(); + uint32_t measTime = -1; + // wait up to 1 second longer than the specified return time + while ((millis() - timerStart) < ((uint32_t)startResult.meas_time_s + 1) * 1000) { + if (mySDI12.available()) { + break; + } // sensor can interrupt us to let us know it is done early } - delay(100); - - // wait for acknowlegement with format [address][ttt (3 char, seconds)][number of - // measurments available, 0-9] - String sdiResponse = mySDI12.readStringUntil('\n'); - sdiResponse.trim(); + measTime = millis() - timerStart; + String interrupt_response = mySDI12.readStringUntil('\n'); if (printCommands) { Serial.print("<<<"); - Serial.println(sdiResponse); + Serial.println(interrupt_response); + Serial.print("Completed after "); + Serial.print(measTime); + Serial.println(" ms"); } - // find out how long we have to wait (in seconds). - uint32_t meas_time_s = sdiResponse.substring(1, 4).toInt(); - if (printCommands) { - Serial.print("Sensor Reported Measurement Time: "); - Serial.print(meas_time_s); - Serial.print(" s, "); + // if we got results, return the measurement time, else -1 + if (getResults(address, startResult.numberResults, request_crc, printCommands)) { + return measTime; } - // Set up the number of results to expect - int numResults = sdiResponse.substring(4).toInt(); - if (printCommands) { - Serial.print("Number Results: "); - Serial.println(numResults); - } - // if (numResults==0){return false;} - if (numResults == 0) { return ""; } - - unsigned long timerStart = millis(); - while ((millis() - timerStart) < (meas_time_s + 1) * 1000) { - if (mySDI12.available()) // sensor can interrupt us to let us know it is done early - { - unsigned long measTime = millis() - timerStart; - if (printCommands) { - Serial.print("<<<"); - Serial.println(mySDI12.readStringUntil('\n')); - // mySDI12.clearBuffer(); - } - Serial.print("Completed after "); - Serial.print(measTime); - Serial.println(" ms"); - break; - } + return -1; +} + +int startConcurrentMeasurement(char address, bool request_crc = false, + String meas_type = "", bool printCommands = true) { + startMeasurementResult startResult = startMeasurement(address, true, request_crc, + meas_type, printCommands); + + uint8_t sensorNum = + charToDec(address); // e.g. convert '0' to 0, 'a' to 10, 'Z' to 61. + meas_time_ms[sensorNum] = ((uint32_t)(startResult.meas_time_s)) * 1000; + millisStarted[sensorNum] = millis(); + if (startResult.meas_time_s == 0) { + millisReady[sensorNum] = millis(); + } else { + // give an extra second + millisReady[sensorNum] = millis() + meas_time_ms[sensorNum] + 1000; } - // Wait for anything else and clear it out - delay(30); - mySDI12.clearBuffer(); + returnedResults[sensorNum] = startResult.numberResults; - return getResults(i, numResults, printCommands); + return startResult.numberResults; } // this checks for activity at a particular address // expects a char, '0'-'9', 'a'-'z', or 'A'-'Z' -boolean checkActive(char i, int8_t numPings = 3, bool printCommands = false) { +bool checkActive(char address, int8_t numPings = 3, bool printCommands = true) { String command = ""; - command += (char)i; // sends basic 'acknowledge' command [address][!] + command += (char)address; // sends basic 'acknowledge' command [address][!] command += "!"; for (int j = 0; j < numPings; j++) { // goes through three rapid contact attempts @@ -375,20 +489,22 @@ boolean checkActive(char i, int8_t numPings = 3, bool printCommands = false) { Serial.print(">>>"); Serial.println(command); } - mySDI12.sendCommand(command, wakeDelay); - delay(100); - if (mySDI12.available()) { // If we here anything, assume we have an active sensor - if (printCommands) { - Serial.print("<<<"); - while (mySDI12.available()) { - Serial.write(mySDI12.read()); - delay(10); - } - } else { - mySDI12.clearBuffer(); - } - return true; + mySDI12.sendCommand(command, wake_delay); + + // the sensor should just return its address + String sdiResponse = mySDI12.readStringUntil('\n'); + sdiResponse.trim(); + if (printCommands) { + Serial.print("<<<"); + Serial.println(sdiResponse); } + mySDI12.clearBuffer(); + + // check the address, return false if it's incorrect + String returned_address = sdiResponse.substring(0, 1); + char ret_addr_array[2]; + returned_address.toCharArray(ret_addr_array, sizeof(ret_addr_array)); + if (returned_address == String(address)) { return true; } } mySDI12.clearBuffer(); return false; @@ -427,11 +543,10 @@ void setup() { // Power the sensors; if (powerPin >= 0) { - Serial.println("Powering up sensors, wait..."); + Serial.println("Powering up sensors, wait 30s..."); pinMode(powerPin, OUTPUT); digitalWrite(powerPin, HIGH); - // delay(10000L); - delay(125); + delay(30000L); } // Quickly Scan the Address Space @@ -505,7 +620,7 @@ void loop() { Serial.print("M"); Serial.print(commands[a]); Serial.println('!'); - takeMeasurement(addr, commands[a], true); + takeMeasurement(addr, true, commands[a], true); } // getContinuousResults(addr, 3); Serial.println(); @@ -531,7 +646,7 @@ void loop() { Serial.print("C"); Serial.print(commands[a]); Serial.println('!'); - startConcurrentMeasurement(addr, commands[a], true); + startConcurrentMeasurement(addr, true, commands[a], true); if (meas_time_ms[i] < min_wait) { min_wait = meas_time_ms[i]; } if (meas_time_ms[i] > max_wait) { max_wait = meas_time_ms[i]; } } else { @@ -584,5 +699,4 @@ void loop() { Serial.println("-------------------------------------------------------------------" "------------"); - // delay(1000); // wait ten seconds between measurement attempts. } diff --git a/tools/TestSensorTiming/TestSensorTiming.ino b/tools/TestSensorTiming/TestSensorTiming.ino new file mode 100644 index 0000000..c15803f --- /dev/null +++ b/tools/TestSensorTiming/TestSensorTiming.ino @@ -0,0 +1,472 @@ +/** + * @file TestWarmUp.ino + * @copyright Stroud Water Research Center + * @license This example is published under the BSD-3 license. + * @author Sara Damiano + * @date March 2024 + */ + +#include + +/* connection information */ +uint32_t serialBaud = 115200; /*!< The baud rate for the output serial port */ +int8_t dataPin = 7; /*!< The pin of the SDI-12 data bus */ +char sensorAddress = '0'; /*!< The address of the SDI-12 sensor */ +int8_t powerPin = 22; /*!< The sensor power pin (or -1 if not switching power) */ +uint32_t wake_delay = 100; /*!< Extra time needed for the sensor to wake (0-100ms) */ + +/** Define the SDI-12 bus */ +SDI12 mySDI12(dataPin); + +/** Define some testing specs */ +bool testPowerOff = true; +int32_t min_power_delay = 4900L; /*!< The min time to test wake after power on. */ +int32_t max_power_delay = 180000L; /*!< The max time to test wake after power on. */ +int32_t increment_power_delay = 100L; /*!< The time to lengthen waits between reps. */ +int32_t power_off_time = 60000L; /*!< The time to power off between tests. */ +/** NOTE: set the power off time to be something similar to what you will be using the + * the real world! Some sensors take longer to warm up if they've been off for a while. + */ + +/** set some initial values */ +int32_t power_delay = min_power_delay; + +int32_t total_meas_time = 0; +int32_t total_meas_made = 0; +int32_t max_meas_time = 0; + +struct startMeasurementResult { // Structure declaration + String returned_address; + uint8_t meas_time_s; + int numberResults; +}; + +bool getResults(char address, int resultsExpected, bool verify_crc = false, + bool printCommands = true) { + uint8_t resultsReceived = 0; + uint8_t cmd_number = 0; + // The maximum number of characters that can be returned in the part of the + // response to a D command is either 35 or 75. If the D command is issued to + // retrieve data in response to a concurrent measurement command, or in response to + // a high-volume ASCII measurement command, the maximum is 75. The maximum is also + // 75 in response to a continuous measurement command. Otherwise, the maximum is 35. + int max_sdi_response = 76; + // max chars in a unsigned 64 bit number + int max_sdi_digits = 21; + + while (resultsReceived < resultsExpected && cmd_number <= 9) { + String command = ""; + command += address; + command += "D"; + command += cmd_number; + command += "!"; // SDI-12 command to get data [address][D][dataOption][!] + mySDI12.sendCommand(command, wake_delay); + + uint32_t start = millis(); + if (printCommands) { + Serial.print(">>>"); + Serial.println(command); + } + char resp_buffer[max_sdi_response] = {'\0'}; + + // read bytes into the char array until we get to a new line (\r\n) + size_t bytes_read = mySDI12.readBytesUntil('\n', resp_buffer, max_sdi_response); + // Serial.print(bytes_read); + // Serial.println(" characters"); + + size_t data_bytes_read = bytes_read - 1; // subtract one for the /r before the /n + String sdiResponse = String(resp_buffer); + sdiResponse.trim(); + if (printCommands) { + Serial.print("<<<"); + Serial.println(sdiResponse); + // Serial.println(sdiResponse.length()); + // Serial.print("<<<"); + // Serial.println(resp_buffer); + // Serial.println(strnlen(resp_buffer, max_sdi_response)); + } + // read and clear anything else from the buffer + int extra_chars = 0; + while (mySDI12.available()) { + Serial.write(mySDI12.read()); + extra_chars++; + } + if (extra_chars > 0) { + Serial.print(extra_chars); + Serial.println(" additional characters received."); + } + mySDI12.clearBuffer(); + + // check the address, return if it's incorrect + char returned_address = resp_buffer[0]; + if (returned_address != address) { + if (printCommands) { + Serial.println("Wrong address returned!"); + Serial.print("Expected "); + Serial.print(String(address)); + Serial.print(" Got "); + Serial.println(String(returned_address)); + Serial.println(String(resp_buffer)); + } + return false; + } + + // check the crc, return if it's incorrect + if (verify_crc) { + bool crcMatch = mySDI12.verifyCRC(sdiResponse); + data_bytes_read = data_bytes_read - 3; + if (crcMatch) { + if (printCommands) { Serial.println("CRC valid"); } + } else { + if (printCommands) { Serial.println("CRC check failed!"); } + return false; + } + } + + bool gotResults = false; + char float_buffer[max_sdi_digits] = {'\0'}; + char* dec_pl = float_buffer; + uint8_t fb_pos = 0; // start at start of buffer + bool finished_last_number = false; + // iterate through the char array and to check results + // NOTE: start at 1 since we already looked at the address! + for (size_t i = 1; i < data_bytes_read; i++) { + // Get the character at position + char c = resp_buffer[i]; + // Serial.print(i); + // Serial.print(" of "); + // Serial.print(data_bytes_read); + // Serial.print(" '"); + // Serial.print(c); + // Serial.println("'"); + // if we didn't get something number-esque or we're at the end of the buffer, + // assume the last number finished and parse it + //(c != '-' && (c < '0' || c > '9') && c != '.') + if (c == '-' || (c >= '0' && c <= '9') || c == '.') { + // if there's a number, a decimal, or a negative sign next in the + // buffer, add it to the float buffer. + float_buffer[fb_pos] = c; + fb_pos++; + float_buffer[fb_pos] = '\0'; // null terminate the buffer + finished_last_number = false; + // Serial.print("Added to float buffer, currently: '"); + // Serial.print(float_buffer); + // Serial.println("'"); + } else { + // Serial.println("Non Numeric"); + finished_last_number = true; + } + // if we've gotten to the end of a number or the end of the buffer, parse the + // character + if ((finished_last_number || i == data_bytes_read - 1) && + strnlen(float_buffer, max_sdi_digits) > 0) { + float result = atof(float_buffer); + if (printCommands) { + Serial.print("Result "); + Serial.print(resultsReceived); + Serial.print(", Raw value: "); + Serial.print(float_buffer); + dec_pl = strchr(float_buffer, '.'); + // Serial.print(", Len after decimal: "); + // Serial.print(strnlen(dec_pl, max_sdi_digits)); + Serial.print(", Parsed value: "); + Serial.println(String(result, strnlen(dec_pl, max_sdi_digits) - 1)); + } + // add how many results we have + if (result != -9999) { + gotResults = true; + resultsReceived++; + } + // check for a failure error code at the end + if (resultsReceived == 5 && result != 0.0) { + gotResults = false; + resultsReceived = 0; + if (printCommands) { + Serial.print("Got a failure code of "); + Serial.println(String(result, strnlen(dec_pl, max_sdi_digits) - 1)); + } + } + + // empty the buffer + float_buffer[0] = '\0'; + fb_pos = 0; + } + } + + if (!gotResults) { + if (printCommands) { + Serial.println((" No results received, will not continue requests!")); + } + break; + } // don't do another loop if we got nothing + + if (printCommands) { + Serial.print("Total Results Received: "); + Serial.print(resultsReceived); + Serial.print(", Remaining: "); + Serial.println(resultsExpected - resultsReceived); + } + + cmd_number++; + } + + mySDI12.clearBuffer(); + + if (printCommands) { + Serial.print("After "); + Serial.print(cmd_number); + Serial.print(" data commands got "); + Serial.print(resultsReceived); + Serial.print(" results of the expected "); + Serial.print(resultsExpected); + Serial.print(" expected. This is a "); + Serial.println(resultsReceived == resultsExpected ? "success." : "failure."); + } + + bool success = resultsReceived == resultsExpected; + return success; +} + +startMeasurementResult startMeasurement(char address, bool is_concurrent = false, + bool request_crc = false, String meas_type = "", + bool printCommands = true) { + // Create the return struct + startMeasurementResult return_result; + return_result.returned_address = ""; + return_result.meas_time_s = 0; + return_result.numberResults = 0; + + String command = ""; + command += address; // All commands start with the address + command += is_concurrent ? "C" : "M"; // C for concurrent, M for standard + command += request_crc ? "C" : ""; // add an additional C to request a CRC + command += meas_type; // Measurement type, "" or 0-9 + command += "!"; // All commands end with "!" + mySDI12.sendCommand(command, wake_delay); + if (printCommands) { + Serial.print(">>>"); + Serial.println(command); + } + + // wait for acknowlegement with format [address][ttt (3 char, seconds)][number of + // measurments available, 0-9] + String sdiResponse = mySDI12.readStringUntil('\n'); + sdiResponse.trim(); + if (printCommands) { + Serial.print("<<<"); + Serial.println(sdiResponse); + } + mySDI12.clearBuffer(); + + // check the address, return if it's incorrect + String returned_address = sdiResponse.substring(0, 1); + char ret_addr_array[2]; + returned_address.toCharArray(ret_addr_array, sizeof(ret_addr_array)); + return_result.returned_address = ret_addr_array[0]; + if (returned_address != String(address)) { + if (printCommands) { + Serial.println("Wrong address returned!"); + Serial.print("Expected "); + Serial.print(String(address)); + Serial.print(" Got "); + Serial.println(returned_address); + } + return return_result; + } + + // find out how long we have to wait (in seconds). + uint8_t meas_time_s = sdiResponse.substring(1, 4).toInt(); + return_result.meas_time_s = meas_time_s; + if (printCommands) { + Serial.print("expected measurement time: "); + Serial.print(meas_time_s); + Serial.print(" s, "); + } + + // Set up the number of results to expect + int numResults = sdiResponse.substring(4).toInt(); + return_result.numberResults = numResults; + if (printCommands) { + Serial.print("Number Results: "); + Serial.println(numResults); + } + + return return_result; +} + +uint32_t takeMeasurement(char address, bool request_crc = false, String meas_type = "", + bool printCommands = true) { + startMeasurementResult startResult = startMeasurement(address, false, request_crc, + meas_type, printCommands); + if (startResult.numberResults == 0) { return -1; } + + uint32_t timerStart = millis(); + uint32_t measTime = -1; + // wait up to 1 second longer than the specified return time + while ((millis() - timerStart) < ((uint32_t)startResult.meas_time_s + 1) * 1000) { + if (mySDI12.available()) { + break; + } // sensor can interrupt us to let us know it is done early + } + measTime = millis() - timerStart; + String interrupt_response = mySDI12.readStringUntil('\n'); + if (printCommands) { + Serial.print("<<<"); + Serial.println(interrupt_response); + Serial.print("Completed after "); + Serial.print(measTime); + Serial.println(" ms"); + } + + // if we got results, return the measurement time, else -1 + if (getResults(address, startResult.numberResults, request_crc, printCommands)) { + return measTime; + } + + return -1; +} + +// this checks for activity at a particular address +// expects a char, '0'-'9', 'a'-'z', or 'A'-'Z' +bool checkActive(char address, int8_t numPings = 3, bool printCommands = true) { + String command = ""; + command += (char)address; // sends basic 'acknowledge' command [address][!] + command += "!"; + + for (int j = 0; j < numPings; j++) { // goes through three rapid contact attempts + if (printCommands) { + Serial.print(">>>"); + Serial.println(command); + } + mySDI12.sendCommand(command, wake_delay); + + // the sensor should just return its address + String sdiResponse = mySDI12.readStringUntil('\n'); + sdiResponse.trim(); + if (printCommands) { + Serial.print("<<<"); + Serial.println(sdiResponse); + } + mySDI12.clearBuffer(); + + // check the address, return false if it's incorrect + String returned_address = sdiResponse.substring(0, 1); + char ret_addr_array[2]; + returned_address.toCharArray(ret_addr_array, sizeof(ret_addr_array)); + if (returned_address == String(address)) { return true; } + } + mySDI12.clearBuffer(); + return false; +} + +void setup() { + Serial.begin(serialBaud); + while (!Serial) + ; + + Serial.println("Opening SDI-12 bus..."); + mySDI12.begin(); + delay(500); // allow things to settle + + Serial.println("Timeout value: "); + Serial.println(mySDI12.TIMEOUT); + + // Power the sensors; + if (powerPin >= 0 && !testPowerOff) { + Serial.println("Powering up sensors, wait..."); + pinMode(powerPin, OUTPUT); + digitalWrite(powerPin, HIGH); + delay(30000L); + } +} + +void loop() { + bool got_good_results = true; + int checks_at_time = 0; + while (got_good_results && checks_at_time < 25) { + Serial.print("Repeat attempt "); + Serial.print(checks_at_time); + Serial.print(" with warm-up time of "); + Serial.println(power_delay); + + // Power down the sensors; + if (powerPin >= 0 && testPowerOff) { + Serial.print("Powering down sensors, wait "); + Serial.print(power_off_time); + Serial.println(" ms"); + pinMode(powerPin, OUTPUT); + digitalWrite(powerPin, LOW); + delay(power_off_time); + } + + // Power up the sensors; + if (powerPin >= 0 && testPowerOff) { + Serial.print("Powering up sensors, wait "); + Serial.print(power_delay); + Serial.println(" ms"); + pinMode(powerPin, OUTPUT); + digitalWrite(powerPin, HIGH); + delay(power_delay); + mySDI12.clearBuffer(); + } + + // checkActive(sensorAddress, true); + + uint32_t this_meas_time = takeMeasurement(sensorAddress, true, "", true); + bool this_result_good = this_meas_time != (uint32_t)-1; + + if (this_result_good) { + total_meas_time += this_meas_time; + total_meas_made++; + if (this_meas_time > max_meas_time) { max_meas_time = this_meas_time; } + Serial.print("Warm-up Time: "); + Serial.print(power_delay); + Serial.print(", This measurement time: "); + Serial.print(this_meas_time); + Serial.print(", Mean measurement time: "); + Serial.print(total_meas_time / total_meas_made); + Serial.print(", Max measurement time: "); + Serial.println(max_meas_time); + } + + got_good_results &= this_result_good; + checks_at_time++; + + if (!got_good_results) { + Serial.print("Time check failed after "); + Serial.print(checks_at_time); + Serial.println(" reps"); + total_meas_time = 0; + total_meas_made = 0; + max_meas_time = 0; + } + } + + // if we got a good result 25 at this warm-up, keep testing how long the + // measurements take + while (got_good_results) { + uint32_t this_meas_time = takeMeasurement(sensorAddress, true, "", false); + if (this_meas_time != (uint32_t)-1) { + total_meas_time += this_meas_time; + total_meas_made++; + if (this_meas_time > max_meas_time) { max_meas_time = this_meas_time; } + Serial.print("Warm-up Time: "); + Serial.print(power_delay); + Serial.print(", This measurement time: "); + Serial.print(this_meas_time); + Serial.print(", Mean measurement time: "); + Serial.print(total_meas_time / total_meas_made); + Serial.print(", Max measurement time: "); + Serial.println(max_meas_time); + } + } + + + Serial.println("-------------------------------------------------------------------" + "------------"); + if (power_delay > max_power_delay) { + power_delay = min_power_delay; + } else { + power_delay = power_delay + increment_power_delay; + } +} diff --git a/tools/TestWarmUp/TestWarmUp.ino b/tools/TestWarmUp/TestWarmUp.ino index b1427c5..b31314d 100644 --- a/tools/TestWarmUp/TestWarmUp.ino +++ b/tools/TestWarmUp/TestWarmUp.ino @@ -11,13 +11,13 @@ /* connection information */ uint32_t serialBaud = 115200; /*!< The baud rate for the output serial port */ int8_t dataPin = 7; /*!< The pin of the SDI-12 data bus */ -char sensorAddress = '1'; /*!< The address of the SDI-12 sensor */ +char sensorAddress = '0'; /*!< The address of the SDI-12 sensor */ int8_t powerPin = 22; /*!< The sensor power pin (or -1 if not switching power) */ /** Define the SDI-12 bus */ SDI12 mySDI12(dataPin); int32_t min_wake_delay = 0; /*!< The min time to test wake after a line break. */ -int32_t increment_wake = 100; /*!< The time to lengthen waits between reps. */ +int32_t increment_wake = 5; /*!< The time to lengthen waits between reps. */ int32_t max_wake_delay = 100; /*!< The max time to test wake (should be <=100). */ int32_t min_power_delay = 100L; /*!< The min time to test wake after power on. */ int32_t increment_power = 100; /*!< The time to lengthen waits between reps. */ @@ -71,9 +71,9 @@ bool printInfo(char i, bool printCommands = true) { // this checks for activity at a particular address // expects a char, '0'-'9', 'a'-'z', or 'A'-'Z' -boolean checkActive(char i, int8_t numPings = 3, bool printCommands = false) { +bool checkActive(char address, int8_t numPings = 3, bool printCommands = false) { String command = ""; - command += (char)i; // sends basic 'acknowledge' command [address][!] + command += (char)address; // sends basic 'acknowledge' command [address][!] command += "!"; for (int j = 0; j < numPings; j++) { // goes through three rapid contact attempts @@ -82,19 +82,21 @@ boolean checkActive(char i, int8_t numPings = 3, bool printCommands = false) { Serial.println(command); } mySDI12.sendCommand(command, wake_delay); - delay(100); - if (mySDI12.available()) { // If we here anything, assume we have an active sensor - if (printCommands) { - Serial.print("<<<"); - while (mySDI12.available()) { - Serial.write(mySDI12.read()); - delay(10); - } - } else { - mySDI12.clearBuffer(); - } - return true; + + // the sensor should just return its address + String sdiResponse = mySDI12.readStringUntil('\n'); + sdiResponse.trim(); + if (printCommands) { + Serial.print("<<<"); + Serial.println(sdiResponse); } + mySDI12.clearBuffer(); + + // check the address, return false if it's incorrect + String returned_address = sdiResponse.substring(0, 1); + char ret_addr_array[2]; + returned_address.toCharArray(ret_addr_array, sizeof(ret_addr_array)); + if (returned_address == String(address)) { return true; } } mySDI12.clearBuffer(); return false; @@ -114,52 +116,49 @@ void setup() { } void loop() { - // Power the sensors; - if (powerPin >= 0) { - Serial.println("Powering down sensors..."); - pinMode(powerPin, OUTPUT); - digitalWrite(powerPin, LOW); - delay(10000L); - } + while (wake_delay <= max_wake_delay) { + // Power the sensors; + if (powerPin >= 0) { + Serial.println("Powering down sensors..."); + pinMode(powerPin, OUTPUT); + digitalWrite(powerPin, LOW); + delay(300000L); + } - // Power the sensors; - if (powerPin >= 0) { - Serial.println("Powering up sensors..."); - pinMode(powerPin, OUTPUT); - digitalWrite(powerPin, HIGH); - delay(power_delay); - } + // Power the sensors; + if (powerPin >= 0) { + Serial.println("Powering up sensors..."); + pinMode(powerPin, OUTPUT); + digitalWrite(powerPin, HIGH); + delay(power_delay); + } - if (checkActive(sensorAddress, 5, true)) { - Serial.print("Got some response after "); - Serial.print(power_delay); - Serial.print("ms after power with "); - Serial.print(wake_delay); - Serial.println("ms with wake delay"); - if (printInfo(sensorAddress, true)) { - // if we got sensor info, stop - Serial.println("Looks good. Stopping."); - while (1) - ; + if (checkActive(sensorAddress, 1, true)) { + Serial.print("Got some response after "); + Serial.print(power_delay); + Serial.print("ms after power with "); + Serial.print(wake_delay); + Serial.println("ms with wake delay"); + if (printInfo(sensorAddress, true)) { + // if we got sensor info, stop + Serial.println("Looks good. Stopping."); + while (1) + ; + } else { + Serial.println("Sensor info not valid!"); + } } else { - Serial.println("Sensor info not valid!"); + Serial.print("No response after "); + Serial.print(power_delay); + Serial.print("ms after power with "); + Serial.print(wake_delay); + Serial.println("ms with wake delay"); } - } else { - Serial.print("No response after "); - Serial.print(power_delay); - Serial.print("ms after power with "); - Serial.print(wake_delay); - Serial.println("ms with wake delay"); + Serial.println("-------------------------------------------------------------------" + "------------"); } - Serial.println("-------------------------------------------------------------------" - "------------"); + power_delay = power_delay + increment_power; if (power_delay > max_power_delay) { - power_delay = min_power_delay; - wake_delay = wake_delay + increment_wake; - } else { - power_delay = power_delay + increment_power; - } - if (wake_delay > max_wake_delay) { Serial.println("FINISHED!!"); while (1) {} } diff --git a/tools/printCommands/printCommands.ino b/tools/printCommands/printCommands.ino deleted file mode 100644 index f07c5f1..0000000 --- a/tools/printCommands/printCommands.ino +++ /dev/null @@ -1,399 +0,0 @@ -/** - * @file d_simple_logger.ino - * @copyright Stroud Water Research Center - * This example is published under the BSD-3 license. - * @author Kevin M.Smith - * @date August 2013 - * - * @brief Example D: Check all Addresses for Active Sensors and Log Data - * - * This is a simple demonstration of the SDI-12 library for Arduino. - * - * It discovers the address of all sensors active on a single bus and takes measurements - * from them. - * - * Every SDI-12 device is different in the time it takes to take a - * measurement, and the amount of data it returns. This sketch will not serve every - * sensor type, but it will likely be helpful in getting you started. - * - * Each sensor should have a unique address already - if not, multiple sensors may - * respond simultaenously to the same request and the output will not be readable by the - * Arduino. - * - * To address a sensor, please see Example B: b_address_change.ino - */ - -#include - -/* connection information */ -uint32_t serialBaud = 115200; /*!< The baud rate for the output serial port */ -uint8_t dataPin = 7; /*!< The pin of the SDI-12 data bus */ -uint8_t powerPin = 22; /*!< The sensor power pin (or -1 if not switching power) */ -uint32_t wakeDelay = 0; /*!< Extra time needed for the sensor to wake (0-100ms) */ -int8_t firstAddress = 0; /* The first address in the address space to check (0='0') */ -int8_t lastAddress = 62; /* The last address in the address space to check (62='z') */ -int8_t commandsToTest = - 1; /*!< The number of measurement commands to test, between 1 and 11. */ - -/** Define the SDI-12 bus */ -SDI12 mySDI12(dataPin); - -/// variable that alternates output type back and forth between parsed and raw -boolean flip = 0; - -String commands[] = {"", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}; - -// keeps track of active addresses -bool isActive[62]; - -// keeps track of the wait time for each active addresses -uint32_t meas_time_ms[62]; - -// keeps track of the time each sensor was started -uint32_t millisStarted[62]; - -// keeps track of the time each sensor will be ready -uint32_t millisReady[62]; - -// keeps track of the number of results expected -uint8_t returnedResults[62]; - -String prev_result[62]; -String this_result[62]; -uint8_t numSensors = 0; - -/** - * @brief converts allowable address characters ('0'-'9', 'a'-'z', 'A'-'Z') to a - * decimal number between 0 and 61 (inclusive) to cover the 62 possible - * addresses. - */ -byte charToDec(char i) { - if ((i >= '0') && (i <= '9')) return i - '0'; - if ((i >= 'a') && (i <= 'z')) return i - 'a' + 10; - if ((i >= 'A') && (i <= 'Z')) - return i - 'A' + 36; - else - return i; -} - -/** - * @brief maps a decimal number between 0 and 61 (inclusive) to allowable - * address characters '0'-'9', 'a'-'z', 'A'-'Z', - * - * THIS METHOD IS UNUSED IN THIS EXAMPLE, BUT IT MAY BE HELPFUL. - */ -char decToChar(byte i) { - if (i < 10) return i + '0'; - if ((i >= 10) && (i < 36)) return i + 'a' - 10; - if ((i >= 36) && (i <= 62)) - return i + 'A' - 36; - else - return i; -} - -/** - * @brief gets identification information from a sensor, and prints it to the serial - * port - * - * @param i a character between '0'-'9', 'a'-'z', or 'A'-'Z'. - */ -void printInfo(char i) { - String command = ""; - command += (char)i; - command += "I!"; - mySDI12.sendCommand(command, wakeDelay); - Serial.print("\n>>>"); - Serial.println(command); - delay(15); - - String sdiResponse = mySDI12.readStringUntil('\n'); - // sdiResponse.trim(); - // allccccccccmmmmmmvvvxxx...xx - Serial.print("\n<<<"); - Serial.println(sdiResponse); -} - -bool getResults(char addr, int resultsExpected) { - uint8_t resultsReceived = 0; - uint8_t cmd_number = 0; - String str_result = ""; - while (resultsReceived < resultsExpected && cmd_number <= 9) { - // while (resultsReceived < resultsExpected && cmd_number <= 1) { - String command = ""; - // in this example we will only take the 'DO' measurement - command = ""; - command += addr; - command += "D"; - command += cmd_number; - command += "!"; // SDI-12 command to get data [address][D][dataOption][!] - mySDI12.sendCommand(command, wakeDelay); - Serial.print("\n>>>"); - Serial.println(command); - - uint32_t start = millis(); - while (mySDI12.available() < 3 && (millis() - start) < 150) {} - Serial.print("\n<<<"); - Serial.write(mySDI12.read()); // ignore the repeated SDI12 address - - uint8_t char_rcvd = 0; - while (mySDI12.available()) { - char_rcvd++; - char c = mySDI12.peek(); - if (c == '+') { - Serial.write(mySDI12.read()); - resultsReceived++; - } else if (c >= 0 && c != '\r' && c != '\n') { - Serial.write(mySDI12.read()); - } else { - mySDI12.read(); - Serial.println(); - } - delay(10); // 1 character ~ 7.5ms - } - if (char_rcvd <= 2) { - break; - } // don't do another loop if didn't get more than the address - cmd_number++; - } - mySDI12.clearBuffer(); - - bool success = resultsReceived == resultsExpected; - if (success) { this_result[charToDec(addr)] = str_result; } - return success; -} - -bool getContinuousResults(char i, int resultsExpected) { - uint8_t resultsReceived = 0; - uint8_t cmd_number = 0; - while (resultsReceived < resultsExpected && cmd_number <= 9) { - String command = ""; - // in this example we will only take the 'DO' measurement - command = ""; - command += i; - command += "R"; - command += cmd_number; - command += "!"; // SDI-12 command to get data [address][D][dataOption][!] - mySDI12.sendCommand(command, wakeDelay); - Serial.print("\n>>>"); - Serial.println(command); - - uint32_t start = millis(); - while (mySDI12.available() < 3 && (millis() - start) < 1500) {} - Serial.print("\n<<<"); - Serial.write(mySDI12.read()); // ignore the repeated SDI12 address - - while (mySDI12.available()) { - char c = mySDI12.peek(); - if (c == '-' || (c >= '0' && c <= '9') || c == '.') { - float result = mySDI12.parseFloat(SKIP_NONE); - Serial.print(String(result, 10)); - if (result != -9999) { resultsReceived++; } - } else if (c >= 0 && c != '\r' && c != '\n') { - Serial.write(mySDI12.read()); - } else { - // mySDI12.read(); - Serial.println(); - } - delay(10); // 1 character ~ 7.5ms - } - if (!resultsReceived) { break; } // don't do another loop if we got nothing - cmd_number++; - } - mySDI12.clearBuffer(); - - return resultsReceived == resultsExpected; -} - -int startConcurrentMeasurement(char i, String meas_type = "") { - String command = ""; - command += i; - command += "C"; - command += meas_type; - command += "!"; // SDI-12 concurrent measurement command format [address]['C'][!] - mySDI12.sendCommand(command, wakeDelay); - Serial.print("\n>>>"); - Serial.println(command); - delay(5); - - // wait for acknowlegement with format [address][ttt (3 char, seconds)][number of - // measurments available, 0-9] - String sdiResponse = mySDI12.readStringUntil('\n'); - Serial.print("\n<<<"); - Serial.println(sdiResponse); - mySDI12.clearBuffer(); - - // find out how long we have to wait (in seconds). - uint8_t meas_time_s = sdiResponse.substring(1, 4).toInt(); - - // Set up the number of results to expect - int numResults = sdiResponse.substring(4).toInt(); - - uint8_t sensorNum = charToDec(i); // e.g. convert '0' to 0, 'a' to 10, 'Z' to 61. - meas_time_ms[sensorNum] = ((uint32_t)(meas_time_s)) * 1000; - millisStarted[sensorNum] = millis(); - if (meas_time_ms == 0) { - millisReady[sensorNum] = millis(); - } else { - // give an extra second - millisReady[sensorNum] = millis() + meas_time_ms[sensorNum] + 1000; - } - returnedResults[sensorNum] = numResults; - - return numResults; -} - -bool takeMeasurement(char i, String meas_type = "") { - String command = ""; - command += i; - command += "M"; - command += meas_type; - command += "!"; // SDI-12 measurement command format [address]['M'][!] - mySDI12.sendCommand(command, wakeDelay); - Serial.print("\n>>>"); - Serial.println(command); - delay(15); - - // wait for acknowlegement with format [address][ttt (3 char, seconds)][number of - // measurments available, 0-9] - String sdiResponse = mySDI12.readStringUntil('\n'); - Serial.print("\n<<<"); - Serial.println(sdiResponse); - - // find out how long we have to wait (in seconds). - uint32_t meas_time_s = sdiResponse.substring(1, 4).toInt(); - - // Set up the number of results to expect - int numResults = sdiResponse.substring(4).toInt(); - // if (numResults==0){return false;} - if (numResults == 0) { return ""; } - - unsigned long timerStart = millis(); - while ((millis() - timerStart) < (meas_time_s + 1) * 1000) { - if (mySDI12.available()) // sensor can interrupt us to let us know it is done early - { - unsigned long measTime = millis() - timerStart; - Serial.print("\n<<<"); - Serial.println(mySDI12.readStringUntil('\n')); - break; - } - } - // Wait for anything else and clear it out - delay(30); - mySDI12.clearBuffer(); - - return getResults(i, numResults); -} - -// this checks for activity at a particular address -// expects a char, '0'-'9', 'a'-'z', or 'A'-'Z' -boolean checkActive(char i, int8_t numPings = 3) { - String command = ""; - command += (char)i; // sends basic 'acknowledge' command [address][!] - command += "!"; - - for (int j = 0; j < numPings; j++) { // goes through three rapid contact attempts - Serial.print("\n>>>"); - Serial.println(command); - mySDI12.sendCommand(command, wakeDelay); - delay(15); - if (mySDI12.available()) { // If we here anything, assume we have an active sensor - Serial.print("\n<<<"); - uint8_t i = 0; - while (mySDI12.available() && i < 60) { - Serial.write(mySDI12.read()); - delay(5); - i++; - } - return true; - } - } - mySDI12.clearBuffer(); - return false; -} - -void setup() { - Serial.begin(serialBaud); - while (!Serial) - ; - - Serial.println("Opening SDI-12 bus..."); - mySDI12.begin(); - delay(500); // allow things to settle - - Serial.println("Timeout value: "); - Serial.println(mySDI12.TIMEOUT); - - for (int8_t i = firstAddress; i < lastAddress; i++) { - isActive[i] = false; - meas_time_ms[i] = 0; - millisStarted[i] = 0; - millisReady[i] = 0; - returnedResults[i] = 0; - prev_result[i] = ""; - this_result[i] = ""; - } - - // Power the sensors; - if (powerPin >= 0) { - Serial.println("Powering down sensors..."); - pinMode(powerPin, OUTPUT); - digitalWrite(powerPin, LOW); - // delay(2500L); - delay(250L); - } - - // Power the sensors; - if (powerPin >= 0) { - Serial.println("Powering up sensors, wait..."); - pinMode(powerPin, OUTPUT); - digitalWrite(powerPin, HIGH); - // delay(10000L); - delay(125); - } - - // Quickly Scan the Address Space - Serial.println("Scanning all addresses, please wait..."); - - for (int8_t i = firstAddress; i <= lastAddress; i++) { - char addr = decToChar(i); - if (checkActive(addr, 5)) { - numSensors++; - isActive[i] = 1; - printInfo(addr); - } else { - } - } - Serial.print("Total number of sensors found: "); - Serial.println(numSensors); - - if (numSensors == 0) { - Serial.println( - "No sensors found, please check connections and restart the Arduino."); - while (true) { delay(10); } // do nothing forever - } - - Serial.println(); - Serial.println("-------------------------------------------------------------------" - "------------"); - - delay(1000); -} - -void loop() { - uint32_t start = millis(); - // measure one at a time - for (int8_t i = firstAddress; i < lastAddress; i++) { - char addr = decToChar(i); - if (isActive[i]) { - printInfo(addr); - for (uint8_t a = 0; a < commandsToTest; a++) { - takeMeasurement(addr, commands[a]); - } - Serial.println(); - } - } - - Serial.println("-------------------------------------------------------------------" - "------------"); - delay(1000); // wait ten seconds -} From f7630a93815ae41d7e1bdd1b6db15002bcda33ac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Jul 2024 20:39:46 +0000 Subject: [PATCH 41/96] ci: bump actions/setup-node from 4.0.2 to 4.0.3 Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4.0.2 to 4.0.3. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/v4.0.2...v4.0.3) --- updated-dependencies: - dependency-name: actions/setup-node dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/verify_library_json.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/verify_library_json.yaml b/.github/workflows/verify_library_json.yaml index 2684751..41dafc1 100644 --- a/.github/workflows/verify_library_json.yaml +++ b/.github/workflows/verify_library_json.yaml @@ -12,7 +12,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup Node.js - uses: actions/setup-node@v4.0.2 + uses: actions/setup-node@v4.0.3 - name: Cache Node.js modules uses: actions/cache@v4.0.2 From 43f157b8988d336342b04d9b90e78aa53a21afbb Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Mon, 29 Jul 2024 15:16:54 -0400 Subject: [PATCH 42/96] Markdown updates Signed-off-by: Sara Damiano --- ChangeLog.md | 21 +- README.md | 87 ++-- docs/CreatingACharacter.md | 88 ++-- docs/Doxyfile | 12 +- docs/OverviewOfInterrupts.md | 22 +- docs/documentExamples.py | 4 +- docs/footer.html | 29 ++ docs/header.html | 81 ++++ docs/mermaidDiagrams.md | 453 ++++++++++++++++++ examples/ReadMe.md | 9 +- examples/a_wild_card/ReadMe.md | 3 +- examples/b_address_change/ReadMe.md | 3 +- examples/c_check_all_addresses/ReadMe.md | 3 +- examples/d_simple_logger/ReadMe.md | 3 +- examples/e_continuous_measurement/ReadMe.md | 3 +- examples/f_basic_data_request/ReadMe.md | 3 +- examples/g_terminal_window/ReadMe.md | 3 +- .../h_SDI-12_slave_implementation/ReadMe.md | 3 +- examples/i_SDI-12_interface/ReadMe.md | 3 +- examples/j_external_pcint_library/ReadMe.md | 3 +- examples/k_concurrent_logger/ReadMe.md | 3 +- examples/l_verify_crc/ReadMe.md | 5 +- 22 files changed, 706 insertions(+), 138 deletions(-) create mode 100644 docs/footer.html create mode 100644 docs/header.html create mode 100644 docs/mermaidDiagrams.md diff --git a/ChangeLog.md b/ChangeLog.md index b49c8b3..3ddb745 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,4 +1,5 @@ # Changelog + All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), @@ -6,10 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 *** - ## [Unreleased] ### Changed + - Added python version to GitHub actions (for PlatformIO) ### Added @@ -23,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## v2.1.4 (2021-05-05) [Revert wake delay to 0ms](https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v2.1.4) ### Possibly breaking changes + - Reverted the default wake delay to 0ms. - In 92055d377b26fa862c43d1429de1ccbef054af01 this was bumped up to 10ms, which caused problems for several people. - The delay can now also be set using the build flag `-D SDI12_WAKE_DELAY=#` @@ -30,12 +32,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## v2.1.3 (2021-03-24) [Migrate to GitHub Actions](https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v2.1.3) ### Improvements + - Migrate from Travis to GitHub actions ## v2.1.1 (2020-08-20) [Patches for ATTiny](https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v2.1.1) ### Bug Fixes -- fixes for the timer and pre-scaler for the ATTiny, courtesy of @gabbas1 + +- fixes for the timer and pre-scaler for the ATTiny, courtesy of \@gabbas1 ## v2.1.0 (2020-07-10) [Library Rename and ESP support](https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v2.1.0) @@ -44,24 +48,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 **To comply with requirements for inclusion in the Arduino IDE, the word Arduino has been removed from the name of this library!** The repository name is unchanged. ### New Features + - Adds support for Espressif ESP8266 and ESP32 -- Add option of adding a delay before sending a command to allow the sensor to wake. Take advantage of this by calling the function ```sendCommand(command, extraWakeTime)```. This may resolve issues with some Campbell sensors that would not previous communicate with this library. See https://www.envirodiy.org/topic/campbell-scientific-cs-215-sdi-12-communication-issues-w-mayfly/#post-14103 -- Adds Doxygen (Javadoc) style comments to **ALL** members of the library. The generated documentation is available at https://envirodiy.github.io/Arduino-SDI-12/. +- Add option of adding a delay before sending a command to allow the sensor to wake. Take advantage of this by calling the function `sendCommand(command, extraWakeTime)`. This may resolve issues with some Campbell sensors that would not previous communicate with this library. See +- Adds Doxygen (Javadoc) style comments to **ALL** members of the library. The generated documentation is available at . ## v1.3.6 (2019-08-29) [Fixed extra compiler warnings](https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v1.3.6) ### Bug Fixes + - A very minor update to fix compiler warnings found when using -Wextra in addition to -Wall. ## v1.3.5 (2019-07-01) [Removed SAMD Tone Conflict](https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v1.3.5) ### Improvements + - SAMD boards will no longer have a conflict with the Tone functions in the Arduino core. AVR boards will still conflict. If you need to use Tone and SDI-12 together for some reason on an AVR boards, you must use the "delayBase" branch. - Examples were also updated and given platformio.ini files. ## v1.3.4 (2019-10-29) [Timer class](https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v1.3.4) ### Improvements + - Made the timer changes into a compiled class. Maintaining interrupt control for SAMD processors as there are no interrupt vectors to be in conflict. Because the pin mode changes from input to output and back, allowing another library to control interrupts doesn't work. @@ -69,26 +77,31 @@ Maintaining interrupt control for SAMD processors as there are no interrupt vect ## v1.3.3 (2018-05-11) [Unset prescalers](https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v1.3.3) ### Improvements + - Now unsetting timer prescalers and setting the isActive pointer to NULL in both the end and the destructor functions. - Also some clean-up of the examples. ## v1.3.1 (2018-04-06) [Added processor timer for greater stability](https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v1.3.1) ### New Features + - Changed the incoming data ISR to use a processor timer, this makes the reception more stable, especially when the ISR is controlled by an external library. This also creates some conflicts with other libraries that use Timer2. ### Improvements + - Made changes to the write functions to use the timer to reduce the amount of time that all system interrupts are off. - Forcing all SDI-12 objects to use the same buffer to reduce ram usage. ## v1.1.0 (2018-03-15) [Better integration inside other libraries](https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v1.1.0) ### Improvements + - Added notes and an empty constructor/populated begin method to allow this library to be more easily called inside of other libraries. ## v1.0.6 (2018-03-09) [Fixed timeout values](https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v1.0.6) ### Bug Fixes + - Fixes the time-out values for the ParseInt and ParseFloat to be -9999. This was the intended behavior all along, but at some point those functions changed in the stream library and the identically named functions within SDI-12 intended to "hide" the stream functions ceased to be called. ## v1.0.1 (2017-05-16) [Initial Release](https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v1.0.1) diff --git a/README.md b/README.md index 724aba5..b45a9ca 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,7 @@ -[//]: # ( @mainpage SDI-12 for Arduino) -# SDI-12 for Arduino +# SDI-12 for Arduino -[//]: # ( @section mainpage_intro Introduction ) -## Introduction +## Introduction This is an Arduino library for SDI-12 communication with a wide variety of environmental sensors. It provides a general software solution, without requiring any additional hardware, to implement the SDI-12 communication protocol between an Arduino-based data logger and SDI-12-enabled sensors. @@ -13,20 +11,22 @@ It provides a general software solution, without requiring any additional hardwa This work is motivated by the [EnviroDIY community](http://envirodiy.org/) vision to create an open source hardware and software stack to deliver near real time environmental data from wireless sensor networks, such as the Arduino-compatible [EnviroDIY™ Mayfly Data Logger](http://envirodiy.org/mayfly/). -[//]: # ( Start GitHub Only ) +[//]: # ( @cond GitHub ) + ## Documentation -Extensive documentation on the SDI-12 functions and classes is available here: https://envirodiy.github.io/Arduino-SDI-12/index.html +Extensive documentation on the SDI-12 functions and classes is available here: + +[//]: # ( @endcond ) -[//]: # ( End GitHub Only ) +### Renaming Notice -[//]: # ( @subsection mainpage_rename Renaming Notice ) -### Renaming Notice **As of version 2.0.0 this library was renamed from "Arduino-SDI-12" to simply "SDI-12" to comply with requirements for inclusion in the Arduino.cc's IDE and Library Manager.** [//]: # ( @tableofcontents ) -[//]: # ( Start GitHub Only ) +[//]: # ( @cond GitHub ) + - [SDI-12 for Arduino](#sdi-12-for-arduino) - [Introduction](#introduction) - [Documentation](#documentation) @@ -35,30 +35,28 @@ Extensive documentation on the SDI-12 functions and classes is available here: - [Origins and Inherited Limitations](#origins-and-inherited-limitations) - [Compatibility Considerations](#compatibility-considerations) - [Variants and Branches](#variants-and-branches) - - [EnviroDIY_SDI12](#envirodiy_sdi12) - - [EnviroDIY_SDI12_PCINT3](#envirodiy_sdi12_pcint3) - - [EnviroDIY_SDI12_ExtInts](#envirodiy_sdi12_extints) + - [EnviroDIY\_SDI12](#envirodiy_sdi12) + - [EnviroDIY\_SDI12\_PCINT3](#envirodiy_sdi12_pcint3) + - [EnviroDIY\_SDI12\_ExtInts](#envirodiy_sdi12_extints) - [Contribute](#contribute) - [License](#license) - [Credits](#credits) -[//]: # ( End GitHub Only ) +[//]: # ( @endcond ) -[//]: # ( @section mainpage_getting_started Getting Started ) -## Getting Started +## Getting Started Learn more, below, about this library's: -* [Origins and Inherited Limitations](https://github.com/EnviroDIY/Arduino-SDI-12#origins-and-inherited-limitations); -* [Compatibility Considerations](https://github.com/EnviroDIY/Arduino-SDI-12#compatibility-considerations); -* [Variants and Branches](https://github.com/EnviroDIY/Arduino-SDI-12#variants-and-branches) we created to overcome some limitations. -Try running our [Example sketches](https://github.com/EnviroDIY/Arduino-SDI-12/tree/master/examples) with your Arduino board and SDI-12 sensor. +- [Origins and Inherited Limitations](https://github.com/EnviroDIY/Arduino-SDI-12#origins-and-inherited-limitations); +- [Compatibility Considerations](https://github.com/EnviroDIY/Arduino-SDI-12#compatibility-considerations); +- [Variants and Branches](https://github.com/EnviroDIY/Arduino-SDI-12#variants-and-branches) we created to overcome some limitations. -Full details on the library functionality can be found on github pages: https://envirodiy.github.io/Arduino-SDI-12/ +Try running our [Example sketches](https://github.com/EnviroDIY/Arduino-SDI-12/tree/master/examples) with your Arduino board and SDI-12 sensor. +Full details on the library functionality can be found on github pages: -[//]: # ( @section mainpage_origins Origins and Inherited Limitations ) -## Origins and Inherited Limitations +## Origins and Inherited Limitations This library was developed from the [SoftwareSerial](https://github.com/arduino/Arduino/tree/master/hardware/arduino/avr/libraries/SoftwareSerial) library that is a built-in [standard Arduino library](https://www.arduino.cc/en/Reference/Libraries). It was further modified to use a timer to improve read stability and decrease the amount of time universal interrupts are disabled using logic from [NeoSWSerial](https://github.com/SlashDevin/NeoSWSerial). @@ -78,8 +76,7 @@ There will be no obvious compile error, but because SDI-12 and the tone library All 8MHz AVR boards will also have unresolvable prescaler conflicts with [NeoSWSerial](https://github.com/SlashDevin/NeoSWSerial). The pre-scaler values needed for the SDI-12 functionality are set in the begin() function and reset to their original values in the end() function. -[//]: # ( @section mainpage_compatibility Compatibility Considerations ) -## Compatibility Considerations +## Compatibility Considerations This library has been tested with an Arduino Uno (AtMega328p), EnviroDIY Mayfly (AtMega1284p), Adafruit Feather 32u4 (AtMega32u4, identical to Arduino Leonardo), an Adafruit Feather M0 (SAMD21G18, identical to Arduino Zero), the ESP8266, and the ESP32. It should also work on an Arduino Mega (AtMega2560), Gemma/AtTiny board, and most other AVR processors running on the Arduino framework. @@ -97,63 +94,68 @@ Not all Arduino boards have the same pin capabilities. The known compatibile pins for common variants are shown below: **AtMega328p / Arduino Uno:** + - Any pin **AtMega1284p / EnviroDIY Mayfly** + - Any pin **ATmega2560 / Arduino Mega or Mega 2560:** + - 0, 11, 12, 13, 14, 15, 50, 51, 52, 53, A8 (62), A9 (63), A10 (64), A11 (65), A12 (66), A13 (67), A14 (68), A15 (69) **AtMega32u4 / Arduino Leonardo or Adafruit Feather:** + - 8, 9, 10, 11, 14 (MISO), 15 (SCK), 16 (MOSI) **SAMD21G18 / Arduino Zero:** + - Any pin (except 4 on the zero) **ESP8266:** + - Any GPIO, except GPIO16 **ESP32:** + - Any GPIO Note that not all of these pins are available with our [Variants and Branches](https://github.com/EnviroDIY/Arduino-SDI-12#variants-and-branches), below. +## Variants and Branches -[//]: # ( @section mainpage_variants Variants and Branches ) -## Variants and Branches As we've described, the default "master" branch of this library will conflict with SoftwareSerial and any other library that monopolizes all pin change interrupt vectors for all AVR boards. To allow simultaneous use of SDI-12 and SoftwareSerial, we have created additional variants of these libraries that we maintain as separate branches of this repository. For background information, my be helpful to read our [Overview of Interrupts](https://github.com/EnviroDIY/Arduino-SDI-12/wiki/2b.-Overview-of-Interrupts) wiki page or this [Arduino Pin Change Interrupts article](https://thewanderingengineer.com/2014/08/11/arduino-pin-change-interrupts/). -[//]: # ( @subsection mainpage_master EnviroDIY_SDI12 ) -#### EnviroDIY_SDI12 +#### EnviroDIY_SDI12 + EnviroDIY_SDI12 is the default master branch of this repository. It controls and monopolizes all pin change interrupt vectors, and can therefore have conflicts with any variant of SoftwareSerial and other libraries that use interrupts. -[//]: # ( @subsection mainpage_pcint3 EnviroDIY_SDI12_PCINT3 ) -#### EnviroDIY_SDI12_PCINT3 +#### EnviroDIY_SDI12_PCINT3 + EnviroDIY_SDI12_PCINT3 is in the Mayfly branch of this repository, and was historically was called "SDI12_mod". It's been cropped to only control interrupt vector 3, or PCINT3 (D), which on the Mayfly (or Sodaq Mbili) corresponds to Pins D0-D7. It is designed to be compatible with [EnviroDIY_SoftwareSerial_PCINT12](https://github.com/EnviroDIY/SoftwareSerial_PCINT12) library (which controls interrupt vectors PCINT1 (B) & PCINT2 (C) / Mayfly pins D08-D15 & D16-D23) and [EnviroDIY PcInt PCINT0](https://github.com/EnviroDIY/PcInt_PCINT0) (which controls interrupt vectors PCINT0 (A) / Mayfly pins D24-D31/A0-A7). Note that different AtMega1284p boards have a different mapping from the physical PIN numbers to the listed digital PIN numbers that are printed on the board. One of the most helpful lists of pins and interrupts vectors is in the the [Pin/Port Bestiary wiki page for the Enable Interrupt library](https://github.com/GreyGnome/EnableInterrupt/wiki/Usage#PIN__PORT_BESTIARY). -[//]: # ( @subsection mainpage_extints EnviroDIY_SDI12_ExtInts ) -#### EnviroDIY_SDI12_ExtInts +#### EnviroDIY_SDI12_ExtInts + EnviroDIY_SDI12_ExtInts is the ExtInt branch of this repository. It doesn't control any of the interrupts, but instead relies on an external interrupt management library (like [EnableInterrupt](https://github.com/GreyGnome/EnableInterrupt)) to assign the SDI-12 receive data function to the right pin. This is the least stable because there's some extra delay because the external library is involved, but the use of timers in the SDI-12 library greatly increases it's stability. It's also the easiest to get working in combination with any other pin change interrupt based library. It can be paired with the [EnviroDIY_SoftwareSerial_ExtInts](https://github.com/EnviroDIY/SoftwareSerial_ExternalInts) libraries (which is, by the way, extremely unstable). -If you would like to use a different pin change interrupt library, uncomment the line ```#define SDI12_EXTERNAL_PCINT``` in SDI12.h and recompile the library. +If you would like to use a different pin change interrupt library, uncomment the line `#define SDI12_EXTERNAL_PCINT` in SDI12.h and recompile the library. Then, in your own code call `SDI12::handleInterrupt()` as the interrupt for the SDI12 pin using the other interrupt library. Example j shows doing this in GreyGnome's [EnableInterrupt](https://github.com/GreyGnome/EnableInterrupt) library. +## Contribute -[//]: # ( @section mainpage_contribute Contribute ) -## Contribute Open an [issue](https://github.com/EnviroDIY/Arduino-SDI-12/issues) to suggest and discuss potential changes/additions. For power contributors: @@ -164,17 +166,16 @@ For power contributors: 4. Push to the branch: `git push origin my-new-feature` 5. Submit a pull request :D +## License -[//]: # ( @section mainpage_license License ) -## License The SDI12 library code is released under the GNU Lesser Public License (LGPL 2.1) -- See [LICENSE-examples.md](https://github.com/EnviroDIY/Arduino-SDI-12/blob/master/LICENSE) file for details. Example Arduino sketches are released under the BSD 3-Clause License -- See [LICENSE-examples.md](https://github.com/EnviroDIY/Arduino-SDI-12/blob/master/LICENSE.md) file for details. Documentation is licensed as [Creative Commons Attribution-ShareAlike 4.0](https://creativecommons.org/licenses/by-sa/4.0/) (CC-BY-SA) copyright. -[//]: # ( @section mainpage_credits Credits ) -## Credits +## Credits + [EnviroDIY](http://envirodiy.org/)™ is presented by the Stroud Water Research Center, with contributions from a community of enthusiasts sharing do-it-yourself ideas for environmental science and monitoring. [Kevin M. Smith](https://github.com/Kevin-M-Smith) was the primary developer of the SDI-12 library, with input from [S. Hicks](https://github.com/s-hicks2) and [Anthony Aufdenkampe](https://github.com/aufdenkampe). @@ -183,6 +184,6 @@ Documentation is licensed as [Creative Commons Attribution-ShareAlike 4.0](https This project has benefited from the support from the following funders: -* National Science Foundation, awards [EAR-0724971](http://www.nsf.gov/awardsearch/showAward?AWD_ID=0724971), [EAR-1331856](http://www.nsf.gov/awardsearch/showAward?AWD_ID=1331856), [ACI-1339834](http://www.nsf.gov/awardsearch/showAward?AWD_ID=1339834) -* William Penn Foundation, grant 158-15 -* Stroud Water Research Center endowment +- National Science Foundation, awards [EAR-0724971](http://www.nsf.gov/awardsearch/showAward?AWD_ID=0724971), [EAR-1331856](http://www.nsf.gov/awardsearch/showAward?AWD_ID=1331856), [ACI-1339834](http://www.nsf.gov/awardsearch/showAward?AWD_ID=1339834) +- William Penn Foundation, grant 158-15 +- Stroud Water Research Center endowment diff --git a/docs/CreatingACharacter.md b/docs/CreatingACharacter.md index 8eeb6cb..2e3a76d 100644 --- a/docs/CreatingACharacter.md +++ b/docs/CreatingACharacter.md @@ -1,16 +1,16 @@ -[//]: # ( @page rx_page Creating a Character - Stepping through the Rx ISR ) -# Creating a Character - Stepping through the Rx ISR +# Creating a Character - Stepping through the Rx ISR [//]: # ( @tableofcontents ) -[//]: # ( Start GitHub Only ) +[//]: # ( @cond GitHub ) + - [Creating a Character - Stepping through the Rx ISR](#creating-a-character---stepping-through-the-rx-isr) - [How a Character Looks in SDI-12](#how-a-character-looks-in-sdi-12) - [Static Variables we Need](#static-variables-we-need) - [Following the Mask](#following-the-mask) - [Waiting for a Start Bit](#waiting-for-a-start-bit) - [The Start of a Character](#the-start-of-a-character) - - [The Interrupt Fires!](#the-interrupt-fires) + - [The Interrupt Fires](#the-interrupt-fires) - [Bit by Bit](#bit-by-bit) - [A LOW/1 Bit](#a-low1-bit) - [A HIGH/0 Bit](#a-high0-bit) @@ -18,7 +18,7 @@ - [A Finished Character](#a-finished-character) - [The Full Interrupt Function](#the-full-interrupt-function) -[//]: # ( End GitHub Only ) +[//]: # ( @endcond ) Here we'll walk step-by-step through how the SDI-12 library (and NeoSWSerial) create a character from the ISR. Unlike SoftwareSerial which listens for a start bit and then halts all program and other ISR execution until the end of the character, this library grabs the time of the interrupt, does some quick math, and lets the processor move on. @@ -28,10 +28,10 @@ For a person, that 8.33ms is trivial, but for even a "slow" 8MHz processor, that So, let's look at what's happening. -[//]: # ( @section rx_specs How a Character Looks in SDI-12 ) -## How a Character Looks in SDI-12 +## How a Character Looks in SDI-12 First we need to keep in mind the specifications of SDI-12: + - We use *inverse logic* that means a "1" bit is at LOW level and a "0" bit is HIGH level. - characters are sent as 10 bits - 1 start bit, which is always a 0/HIGH @@ -39,10 +39,10 @@ First we need to keep in mind the specifications of SDI-12: - 1 parity bit - 1 stop bit, which is always 1/LOW -[//]: # ( @section rx_vars Static Variables we Need ) -## Static Variables we Need +## Static Variables we Need And lets remind ourselves of the static variables we're using to store states: + - `prevBitTCNT` stores the time of the previous RX transition in micros - `rxState` tracks how many bits are accounted for on an incoming character. - if 0: indicates that we got a start bit @@ -52,11 +52,9 @@ And lets remind ourselves of the static variables we're using to store states: - The mask has a single bit set, in the place of the active bit based on the rxState - `rxValue` is the value of the character being built -[//]: # ( @section rx_mask Following the Mask ) -## Following the Mask +## Following the Mask -[//]: # ( @subsection rx_mask_wait Waiting for a Start Bit ) -### Waiting for a Start Bit +### Waiting for a Start Bit The `rxState`, `rxMask`, and `rxValue` all work together to form a character. When we're waiting for a start bit `rxValue` is empty, `rxMask` has only the bottom bit set, and `rxState` is set to WAITING-FOR-START-BIT: @@ -68,9 +66,7 @@ When we're waiting for a start bit `rxValue` is empty, `rxMask` has only the bot rxState: | 1 1 1 1 1 1 1 1 ``` - -[//]: # ( @subsection rx_mask_start The Start of a Character ) -### The Start of a Character +### The Start of a Character After we get a start bit, the `startChar()` function creates a blank slate for the new character, so our values are: @@ -81,27 +77,36 @@ After we get a start bit, the `startChar()` function creates a blank slate for t rxState: | 0 0 0 0 0 0 0 0 ``` - -[//]: # ( @subsection rx_mask_fire The Interrupt Fires! ) -### The Interrupt Fires! +### The Interrupt Fires When an interrupts is received, we use capture the time if the interrupt in `thisBitTCNT`. Then we subtract `prevBitTCNT` from `thisBitTCNT` and use the `bitTimes()` function to calculate how many bit-times have passed between this interrupt and the previous one. (There's also a fudge factor in this calculation we call the [rxWindowWidth](https://github.com/SlashDevin/NeoSWSerial/pull/13#issuecomment-315463522).) - -[//]: # ( @subsection rx_mask_bit Bit by Bit ) -### Bit by Bit +### Bit by Bit For **each bit time that passed**, we apply the `rxMask` to the `rxValue`. + - Keep in mind multiple bit times can pass between interrupts - this happens any time there are two (or more) high or low bits in a row. - We also leave time for the (high) start and (low) stop bit, but do anything with the `rxState`, `rxMask`, or `rxValue` for those bits. - -[//]: # ( @subsubsection rx_mask_low A LOW/1 Bit ) -#### A LOW/1 Bit +#### A LOW/1 Bit - if the data bit received is LOW (1) we do an `|=` (bitwise OR) between the `rxMask` and the `rxValue` +[//]: # ( @dot ) + +digraph xor { + graph[rankdir=BT]; + node[shape=record]; + + rxValue[label="{ {rxValue|0|0|0|0|0|0|0|1} }"]; + maskState[label=" {rxMask|rxState}|{0|0}|{0|0}|{0|0}|{0|0}|{0|0}|{0|0}|{0|0}|{0|0}"]; + + maskState:rxMask0 -> rxValue:rxValue0[label="bit-wise or (|=) puts the one \nfrom the rxMask into the rxValue"]; +} + +[//]: # ( @enddot ) + ``` rxValue: | 0 0 0 0 0 0 0 1 @@ -110,12 +115,24 @@ For **each bit time that passed**, we apply the `rxMask` to the `rxValue`. rxState: | 0 0 0 0 0 0 0 0 the rxValue ``` - -[//]: # ( @subsubsection rx_mask_high A HIGH/0 Bit ) -#### A HIGH/0 Bit +#### A HIGH/0 Bit - if the data bit received is HIGH (0) we do nothing + +[//]: # ( @dot ) + +digraph xor { + graph[rankdir=BT]; + node[shape=record]; + + rxValue[label="{ {rxValue|0|0|0|0|0|0|0|1} }"]; + maskState[label=" {rxMask|rxState}|{0|0}|{0|0}|{0|0}|{0|0}|{0|0}|{0|0}|{0|0}|{0|0}"]; + + maskState:rxMask0 -> rxValue:rxValue0[label="nothing happens",arrowhead="obox"]; +} + +[//]: # ( @enddot ) ``` rxValue: | 0 0 0 0 0 0 0 0 -------------|---------------------------------x- nothing happens @@ -123,9 +140,7 @@ For **each bit time that passed**, we apply the `rxMask` to the `rxValue`. rxState: | 0 0 0 0 0 0 0 0 ``` - -[//]: # ( @subsubsection rx_mask_shift Shifting Up ) -#### Shifting Up +#### Shifting Up - *After* applying the mask, we push everything over one bit to the left. The top bit falls off. @@ -142,14 +157,13 @@ The top bit falls off. | falls off the top | | added to the bottom ``` - -[//]: # ( @subsection rx_mask_fin A Finished Character ) -### A Finished Character +### A Finished Character After 8 bit times have passed, we should have a fully formed character with 8 bits of data (7 of the character + 1 parity). The `rxMask` will have the one in the top bit. And the rxState will be filled - which just happens to be the value of `WAITING-FOR-START-BIT` for the next character. + ``` rxValue: | ? ? ? ? ? ? ? ? -------------|----------------------------------- @@ -157,9 +171,7 @@ And the rxState will be filled - which just happens to be the value of `WAITING- rxState: | 1 1 1 1 1 1 1 1 ``` - -[//]: # ( @section rx_fxn The Full Interrupt Function ) -## The Full Interrupt Function +## The Full Interrupt Function Understanding how the masking creates the character, you should now be able to follow the full interrupt function below. @@ -288,4 +300,4 @@ void SDI12::charToBuffer(uint8-t c) { -rxBufferTail = (-rxBufferTail + 1) % SDI12-BUFFER-SIZE; } } -``` \ No newline at end of file +``` diff --git a/docs/Doxyfile b/docs/Doxyfile index bdf8275..d299d8a 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -964,9 +964,7 @@ WARN_LOGFILE = output_doxygen.log # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = ../README.md \ - ../src \ - ../docs +INPUT = .. # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -1156,7 +1154,7 @@ FILTER_SOURCE_PATTERNS = # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. -USE_MDFILE_AS_MAINPAGE = README.md +USE_MDFILE_AS_MAINPAGE = # The Fortran standard specifies that for fixed formatted Fortran code all # characters from position 72 are to be considered as comment. A common @@ -1356,7 +1354,7 @@ HTML_FILE_EXTENSION = .html # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_HEADER = +HTML_HEADER = header.html # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard @@ -1366,7 +1364,7 @@ HTML_HEADER = # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_FOOTER = +HTML_FOOTER = footer.html # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of @@ -2563,7 +2561,7 @@ HIDE_UNDOC_RELATIONS = NO # set to NO # The default value is: NO. -HAVE_DOT = NO +HAVE_DOT = YES # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed # to run in parallel. When set to 0 doxygen will base this on the number of diff --git a/docs/OverviewOfInterrupts.md b/docs/OverviewOfInterrupts.md index c6c3edb..28370af 100644 --- a/docs/OverviewOfInterrupts.md +++ b/docs/OverviewOfInterrupts.md @@ -1,9 +1,8 @@ -[//]: # ( @page interrupts_page Overview of Interrupts ) -# Overview of Interrupts +# Overview of Interrupts [//]: # ( @tableofcontents ) -[//]: # ( Start GitHub Only ) +[//]: # ( @cond GitHub ) - [Overview of Interrupts](#overview-of-interrupts) - [What is an Interrupt?](#what-is-an-interrupt) - [Directly Controlling Interrupts on an AVR Board](#directly-controlling-interrupts-on-an-avr-board) @@ -11,10 +10,9 @@ - [Enabling an Interrupt](#enabling-an-interrupt) - [Disabling an Interrupt](#disabling-an-interrupt) -[//]: # ( End GitHub Only ) +[//]: # ( @endcond ) -[//]: # ( @section interrupts_what What is an Interrupt? ) -## What is an Interrupt? +## What is an Interrupt? An interrupt is a signal that causes the microcontroller to halt execution of the program, and perform a subroutine known as an interrupt handler or Interrupt Service Routine (ISR). After the ISR, program execution continues where it left off. This allows the microcontroller to efficiently handle a time-sensitive function such as receiving a burst of data on one of its pins, by not forcing the microcontroller to wait for the data. @@ -27,11 +25,9 @@ Obviously, we don't want the processor to be halting operation every time any pi For Atmel SAMD or Espressif processors the processor has dedicated control registers for each pin and the Arduino core provides us with a handy "attachInterrupt" function to use to tie our ISR to that pin. For AVR processors, like the Arduino Uno or the EnviroDIY Mayfly, we have to use a get a bit fancier to control the interrupts. -[//]: # ( @section interrupts_avr Directly Controlling Interrupts on an AVR Board ) -## Directly Controlling Interrupts on an AVR Board +## Directly Controlling Interrupts on an AVR Board -[//]: # ( @subsection interrupts_vocab Some Vocabulary ) -### Some Vocabulary: +### Some Vocabulary: **Registers**: small 1-byte (8-bit) stores of memory directly accessible by processor PCMSK0, PCMSK1, PCMSK2, PCMSK3 @@ -54,8 +50,7 @@ On an Arduino Uno: **interrupts()**: a function to globally enable interrupts (of all types) - interrupts will only occur if the requisite registers are set (e.g. PCMSK and PCICR). -[//]: # ( @subsection interrupts_enable Enabling an Interrupt ) -### Enabling an Interrupt +### Enabling an Interrupt Initially, no interrupts are enabled, so PCMSK0 looks like: `{00000000}`. If we were to use pin 9 as the data pin, we would set the bit in the pin 9 position to 1, like so: `{00000010}`. @@ -138,8 +133,7 @@ So now: By using a bitmask and bitwise operation, we have successfully enabled pin 9 without effecting the state of pin 13. -[//]: # ( @subsection interrupts_disable Disabling an Interrupt ) -### Disabling an Interrupt +### Disabling an Interrupt When the we would like to put the SDI-12 object in the DISABLED state, (e.g. the destructor is called), we need to make sure the bit corresponding to the data pin is unset. diff --git a/docs/documentExamples.py b/docs/documentExamples.py index ddce971..5882ae4 100644 --- a/docs/documentExamples.py +++ b/docs/documentExamples.py @@ -110,10 +110,10 @@ # I'm using these comments to fence off content that is only intended for # github mardown rendering - if "[//]: # ( Start GitHub Only )" in line: + if "[//]: # ( @cond GitHub )" in line: print_me = False - if "[//]: # ( End GitHub Only )" in line: + if "[//]: # ( @endcond )" in line: print_me = True i += 1 diff --git a/docs/footer.html b/docs/footer.html new file mode 100644 index 0000000..bbadb8a --- /dev/null +++ b/docs/footer.html @@ -0,0 +1,29 @@ + + + + + + + + + + + + diff --git a/docs/header.html b/docs/header.html new file mode 100644 index 0000000..659e88d --- /dev/null +++ b/docs/header.html @@ -0,0 +1,81 @@ + + + + + + + + +$projectname: $title +$title + + + + + + + + + + + + + + +$treeview +$search +$mathjax +$darkmode + +$extrastylesheet + + + + + +
+ + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
$projectname $projectnumber +
+
$projectbrief
+
+
$projectbrief
+
$searchbox
$searchbox
+
+ + diff --git a/docs/mermaidDiagrams.md b/docs/mermaidDiagrams.md new file mode 100644 index 0000000..3650ee8 --- /dev/null +++ b/docs/mermaidDiagrams.md @@ -0,0 +1,453 @@ + + +```mermaid +block-beta +columns 5 + rxValue + block:rxValueBits:3 + rxValue7["0"] rxValue6["0"] rxValue5["0"] rxValue4["0"] rxValue3["0"] rxValue2["0"] rxValue1["0"] rxValue0["1"] + end + space:6 + rxMask + block:rxMaskBits:3 + rxMask7["0"] rxMask6["0"] rxMask5["0"] rxMask4["0"] rxMask3["0"] rxMask2["0"] rxMask1["0"] rxMask0["1"] + end + space:1 + rxState + block:rxStateBits:3 + rxState7["0"] rxState6["0"] rxState5["0"] rxState4["0"] rxState3["0"] rxState2["0"] rxState1["0"] rxState0["0"] + end + space:1 + rxMask0-- "bit-wise or (|=) puts the one \nfrom the rxMask into the rxValue" -->rxValue0 + +classDef labels stroke:None,fill:None +class rxValue,rxMask,rxState labels + +classDef bits stroke-width:0.5,fill:None +class rxValue7,rxValue6,rxValue5,rxValue4,rxValue3,rxValue2,rxValue1,rxValue0 bits +class rxMask7,rxMask6,rxMask5,rxMask4,rxMask3,rxMask2,rxMask1,rxMask0 bits +class rxState7,rxState6,rxState5,rxState4,rxState3,rxState2,rxState1,rxState0 bits +``` + +```mermaid +block-beta + block:labels:2 + columns 1 + space + rxValue + space + rxMask + rxState + end + block:bit7 + columns 1 + bitLabel7["Bit\n7"] + rxValue7["0"] + space + rxMask7["0"] + rxState7["0"] + end + block:bit6 + columns 1 + bitLabel6["Bit\n6"] + rxValue6["0"] + space + rxMask6["0"] + rxState6["0"] + end + block:bit5 + columns 1 + bitLabel5["Bit\n5"] + rxValue5["0"] + space + rxMask5["0"] + rxState5["0"] + end + block:bit4 + columns 1 + bitLabel4["Bit\n4"] + rxValue4["0"] + space + rxMask4["0"] + rxState4["0"] + end + block:bit3 + columns 1 + bitLabel3["Bit\n3"] + rxValue3["0"] + space + rxMask3["0"] + rxState3["0"] + end + block:bit2 + columns 1 + bitLabel2["Bit\n2"] + rxValue2["0"] + space + rxMask2["0"] + rxState2["0"] + end + block:bit1 + columns 1 + bitLabel1["Bit\n1"] + rxValue1["0"] + space + rxMask1["0"] + rxState1["0"] + end + block:bit0 + columns 1 + bitLabel0["Bit\n0"] + rxValue0["0"] + space + rxMask0["1"] + rxState0["0"] + end + rxMask0-- "bit-wise or (|=) puts the one \nfrom the rxMask into the rxValue" -->rxValue0 + +classDef outerLabels stroke:None,fill:None +class labels,actionTop,actionBottom outerLabels +classDef innerLabels stroke:None,fill:None +class rxValue,rxMask,rxState innerLabels +classDef innerActions stroke:None,fill:None +class rxValueActionTop,rxValueActionBottom,rxMaskActionTop,rxMaskActionBottom,rxStateActionTop,rxStateActionBottom innerActions + +classDef bitLabels stroke-width:0.5,fill:None +class bitLabel7,bitLabel6,bitLabel5,bitLabel4,bitLabel3,bitLabel2,bitLabel1,bitLabel0 bitLabels + +classDef bits stroke-width:0.5,fill:None +class rxValue7,rxValue6,rxValue5,rxValue4,rxValue3,rxValue2,rxValue1,rxValue0 bits +class rxMask7,rxMask6,rxMask5,rxMask4,rxMask3,rxMask2,rxMask1,rxMask0 bits +class rxState7,rxState6,rxState5,rxState4,rxState3,rxState2,rxState1,rxState0 bits +``` + + + +```mermaid +block-beta +columns 5 + rxValue + block:rxValueBits:3 + rxValue7["0"] rxValue6["0"] rxValue5["0"] rxValue4["0"] rxValue3["0"] rxValue2["0"] rxValue1["0"] rxValue0["1"] + end + space:6 + rxMask + block:rxMaskBits:3 + rxMask7["0"] rxMask6["0"] rxMask5["0"] rxMask4["0"] rxMask3["0"] rxMask2["0"] rxMask1["0"] rxMask0["1"] + end + space:1 + rxState + block:rxStateBits:3 + rxState7["0"] rxState6["0"] rxState5["0"] rxState4["0"] rxState3["0"] rxState2["0"] rxState1["0"] rxState0["0"] + end + space:1 + rxMask0-- "nothing happens" --xrxValue0 + +classDef labels stroke:None,fill:None +class rxValue,rxMask,rxState labels + +classDef bits stroke-width:0.5,fill:None +class rxValue7,rxValue6,rxValue5,rxValue4,rxValue3,rxValue2,rxValue1,rxValue0 bits +class rxMask7,rxMask6,rxMask5,rxMask4,rxMask3,rxMask2,rxMask1,rxMask0 bits +class rxState7,rxState6,rxState5,rxState4,rxState3,rxState2,rxState1,rxState0 bits +``` + +```mermaid +block-beta + block:labels:2 + columns 1 + space + rxValue + space + rxMask + rxState + end + block:bit7 + columns 1 + bitLabel7["Bit\n7"] + rxValue7["0"] + space + rxMask7["0"] + rxState7["0"] + end + block:bit6 + columns 1 + bitLabel6["Bit\n6"] + rxValue6["0"] + space + rxMask6["0"] + rxState6["0"] + end + block:bit5 + columns 1 + bitLabel5["Bit\n5"] + rxValue5["0"] + space + rxMask5["0"] + rxState5["0"] + end + block:bit4 + columns 1 + bitLabel4["Bit\n4"] + rxValue4["0"] + space + rxMask4["0"] + rxState4["0"] + end + block:bit3 + columns 1 + bitLabel3["Bit\n3"] + rxValue3["0"] + space + rxMask3["0"] + rxState3["0"] + end + block:bit2 + columns 1 + bitLabel2["Bit\n2"] + rxValue2["0"] + space + rxMask2["0"] + rxState2["0"] + end + block:bit1 + columns 1 + bitLabel1["Bit\n1"] + rxValue1["0"] + space + rxMask1["0"] + rxState1["0"] + end + block:bit0 + columns 1 + bitLabel0["Bit\n0"] + rxValue0["0"] + space + rxMask0["1"] + rxState0["0"] + end + rxMask0-- "nothing happens" --xrxValue0 + +classDef outerLabels stroke:None,fill:None +class labels,actionTop,actionBottom outerLabels +classDef innerLabels stroke:None,fill:None +class rxValue,rxMask,rxState innerLabels +classDef innerActions stroke:None,fill:None +class rxValueActionTop,rxValueActionBottom,rxMaskActionTop,rxMaskActionBottom,rxStateActionTop,rxStateActionBottom innerActions + +classDef bitLabels stroke-width:0.5,fill:None +class bitLabel7,bitLabel6,bitLabel5,bitLabel4,bitLabel3,bitLabel2,bitLabel1,bitLabel0 bitLabels + +classDef bits stroke-width:0.5,fill:None +class rxValue7,rxValue6,rxValue5,rxValue4,rxValue3,rxValue2,rxValue1,rxValue0 bits +class rxMask7,rxMask6,rxMask5,rxMask4,rxMask3,rxMask2,rxMask1,rxMask0 bits +class rxState7,rxState6,rxState5,rxState4,rxState3,rxState2,rxState1,rxState0 bits +``` + + +```mermaid +block-beta + block:labels:2 + columns 1 + space + rxValue + space + rxMask + rxState + end + block:actionTop:2 + columns 1 + space + rxValueActionTop["falls off the top"] + space + rxMaskActionTop["falls off the top"] + rxStateActionTop["falls off the top"] + end + space + block:bit7 + columns 1 + bitLabel7["Bit\n7"] + rxValue7["0"] + space + rxMask7["0"] + rxState7["0"] + end + block:bit6 + columns 1 + bitLabel6["Bit\n6"] + rxValue6["0"] + space + rxMask6["0"] + rxState6["0"] + end + block:bit5 + columns 1 + bitLabel5["Bit\n5"] + rxValue5["0"] + space + rxMask5["0"] + rxState5["0"] + end + block:bit4 + columns 1 + bitLabel4["Bit\n4"] + rxValue4["0"] + space + rxMask4["0"] + rxState4["0"] + end + block:bit3 + columns 1 + bitLabel3["Bit\n3"] + rxValue3["0"] + space + rxMask3["0"] + rxState3["0"] + end + block:bit2 + columns 1 + bitLabel2["Bit\n2"] + rxValue2["0"] + space + rxMask2["0"] + rxState2["0"] + end + block:bit1 + columns 1 + bitLabel1["Bit\n1"] + rxValue1["?"] + space + rxMask1["1"] + rxState1["0"] + end + block:bit0 + columns 1 + bitLabel0["Bit\n0"] + rxValue0["0"] + space + rxMask0["0"] + rxState0["1"] + end + space + block:actionBottom:2 + columns 1 + space + rxValueActionBottom["add a zero"] + space + rxMaskActionBottom["add a zero"] + rxStateActionBottom["add a one"] + end + rxValue7-->rxValueActionTop + rxMask7-->rxMaskActionTop + rxState7-->rxStateActionTop + rxValueActionBottom-->rxValue0 + rxMaskActionBottom-->rxMask0 + rxStateActionBottom-->rxState0 + +classDef outerLabels stroke:None,fill:None +class labels,actionTop,actionBottom outerLabels +classDef innerLabels stroke:None,fill:None +class rxValue,rxMask,rxState innerLabels +classDef innerActions stroke:None,fill:None +class rxValueActionTop,rxValueActionBottom,rxMaskActionTop,rxMaskActionBottom,rxStateActionTop,rxStateActionBottom innerActions + +classDef bitLabels stroke-width:0.5,fill:None +class bitLabel7,bitLabel6,bitLabel5,bitLabel4,bitLabel3,bitLabel2,bitLabel1,bitLabel0 bitLabels + +classDef bits stroke-width:0.5,fill:None +class rxValue7,rxValue6,rxValue5,rxValue4,rxValue3,rxValue2,rxValue1,rxValue0 bits +class rxMask7,rxMask6,rxMask5,rxMask4,rxMask3,rxMask2,rxMask1,rxMask0 bits +class rxState7,rxState6,rxState5,rxState4,rxState3,rxState2,rxState1,rxState0 bits +``` + + +```mermaid +block-beta + block:labels:2 + columns 1 + space + rxValue + space + rxMask + rxState + end + block:bit7 + columns 1 + bitLabel7["Bit\n7"] + rxValue7["?"] + space + rxMask7["1"] + rxState7["1"] + end + block:bit6 + columns 1 + bitLabel6["Bit\n6"] + rxValue6["?"] + space + rxMask6["0"] + rxState6["1"] + end + block:bit5 + columns 1 + bitLabel5["Bit\n5"] + rxValue5["?"] + space + rxMask5["0"] + rxState5["1"] + end + block:bit4 + columns 1 + bitLabel4["Bit\n4"] + rxValue4["?"] + space + rxMask4["0"] + rxState4["1"] + end + block:bit3 + columns 1 + bitLabel3["Bit\n3"] + rxValue3["?"] + space + rxMask3["0"] + rxState3["1"] + end + block:bit2 + columns 1 + bitLabel2["Bit\n2"] + rxValue2["?"] + space + rxMask2["0"] + rxState2["1"] + end + block:bit1 + columns 1 + bitLabel1["Bit\n1"] + rxValue1["?"] + space + rxMask1["0"] + rxState1["1"] + end + block:bit0 + columns 1 + bitLabel0["Bit\n0"] + rxValue0["?"] + space + rxMask0["0"] + rxState0["1"] + end + +classDef outerLabels stroke:None,fill:None +class labels,actionTop,actionBottom outerLabels +classDef innerLabels stroke:None,fill:None +class rxValue,rxMask,rxState innerLabels +classDef innerActions stroke:None,fill:None +class rxValueActionTop,rxValueActionBottom,rxMaskActionTop,rxMaskActionBottom,rxStateActionTop,rxStateActionBottom innerActions + +classDef bitLabels stroke-width:0.5,fill:None +class bitLabel7,bitLabel6,bitLabel5,bitLabel4,bitLabel3,bitLabel2,bitLabel1,bitLabel0 bitLabels + +classDef bits stroke-width:0.5,fill:None +class rxValue7,rxValue6,rxValue5,rxValue4,rxValue3,rxValue2,rxValue1,rxValue0 bits +class rxMask7,rxMask6,rxMask5,rxMask4,rxMask3,rxMask2,rxMask1,rxMask0 bits +class rxState7,rxState6,rxState5,rxState4,rxState3,rxState2,rxState1,rxState0 bits +``` diff --git a/examples/ReadMe.md b/examples/ReadMe.md index b6acefc..d9ebdfb 100644 --- a/examples/ReadMe.md +++ b/examples/ReadMe.md @@ -1,9 +1,8 @@ -[//]: # ( @page examples_page Examples ) -# Examples using the SDI-12 Library +# Examples using the SDI-12 Library [//]: # ( @brief Examples using the SDI-12 Library ) -[//]: # ( Start GitHub Only ) +[//]: # ( @cond GitHub ) - [Example A](@ref a_wild_card.ino): - Gets sensor information from a single attached sensor and prints it to the serial port - [GitHub](https://github.com/EnviroDIY/Arduino-SDI-12/tree/master/examples/a_wild_card) @@ -38,7 +37,7 @@ - Shows how to request concurrent measurements - [GitHub](https://github.com/EnviroDIY/Arduino-SDI-12/tree/master/examples/k_concurrent_logger) -[//]: # ( End GitHub Only ) +[//]: # ( @endcond ) - [Example A](@ref a_wild_card.ino): - Gets sensor information from a single attached sensor and prints it to the serial port @@ -72,4 +71,4 @@ - [GitHub](https://github.com/EnviroDIY/Arduino-SDI-12/tree/master/examples/j_external_pcint_library) - [Example K](@ref k_concurrent_logger.ino): - Shows how to request concurrent measurements - - [GitHub](https://github.com/EnviroDIY/Arduino-SDI-12/tree/master/examples/k_concurrent_logger) \ No newline at end of file + - [GitHub](https://github.com/EnviroDIY/Arduino-SDI-12/tree/master/examples/k_concurrent_logger) diff --git a/examples/a_wild_card/ReadMe.md b/examples/a_wild_card/ReadMe.md index 5798349..09535a2 100644 --- a/examples/a_wild_card/ReadMe.md +++ b/examples/a_wild_card/ReadMe.md @@ -1,5 +1,4 @@ -[//]: # ( @page example_a_page Example A: Using the Wildcard - Getting Single Sensor Information ) -# Example A: Using the Wildcard - Getting Single Sensor Information +# Example A: Using the Wildcard - Getting Single Sensor Information This is a simple demonstration of the SDI-12 library for Arduino. It requests information about a single attached sensor, including its address and manufacturer info, and prints it to the serial port diff --git a/examples/b_address_change/ReadMe.md b/examples/b_address_change/ReadMe.md index cc462c6..f7c864f 100644 --- a/examples/b_address_change/ReadMe.md +++ b/examples/b_address_change/ReadMe.md @@ -1,5 +1,4 @@ -[//]: # ( @page example_b_page Example B: Changing the Address of your SDI-12 Sensor ) -# Example B: Changing the Address of your SDI-12 Sensor +# Example B: Changing the Address of your SDI-12 Sensor Communication with an SDI-12 sensor depends on its 1-character alphanumeric address (1-9, A-Z, a-z). A sensor can also be programmed with an address of 0, but that address cannot always be used to get measurements from the sensor. This sketch enables you to find and change the address of your sensor. diff --git a/examples/c_check_all_addresses/ReadMe.md b/examples/c_check_all_addresses/ReadMe.md index 137b416..55ac981 100644 --- a/examples/c_check_all_addresses/ReadMe.md +++ b/examples/c_check_all_addresses/ReadMe.md @@ -1,5 +1,4 @@ -[//]: # ( @page example_c_page Example C: Check all Addresses for Active Sensors and Print Status ) -# Example C: Check all Addresses for Active Sensors and Print Status +# Example C: Check all Addresses for Active Sensors and Print Status This is a simple demonstration of the SDI-12 library for Arduino. diff --git a/examples/d_simple_logger/ReadMe.md b/examples/d_simple_logger/ReadMe.md index 5b6bc9f..485915c 100644 --- a/examples/d_simple_logger/ReadMe.md +++ b/examples/d_simple_logger/ReadMe.md @@ -1,5 +1,4 @@ -[//]: # ( @page example_d_page Example D: Check all Addresses for Active Sensors and Log Data ) -## Example D: Check all Addresses for Active Sensors and Log Data +## Example D: Check all Addresses for Active Sensors and Log Data This is a simple demonstration of the SDI-12 library for Arduino. diff --git a/examples/e_continuous_measurement/ReadMe.md b/examples/e_continuous_measurement/ReadMe.md index df5fb0f..0f8f00b 100644 --- a/examples/e_continuous_measurement/ReadMe.md +++ b/examples/e_continuous_measurement/ReadMe.md @@ -1,5 +1,4 @@ -[//]: # ( @page example_e_page Example E: Check all Addresses for Active Sensors and Start Continuous Measurements ) -## Example E: Check all Addresses for Active Sensors and Start Continuous Measurements +## Example E: Check all Addresses for Active Sensors and Start Continuous Measurements This is a simple demonstration of the SDI-12 library for Arduino. diff --git a/examples/f_basic_data_request/ReadMe.md b/examples/f_basic_data_request/ReadMe.md index 03983a4..4387063 100644 --- a/examples/f_basic_data_request/ReadMe.md +++ b/examples/f_basic_data_request/ReadMe.md @@ -1,5 +1,4 @@ -[//]: # ( @page example_f_page Example F: Basic Data Request to a Single Sensor ) -# Example F: Basic Data Request to a Single Sensor +# Example F: Basic Data Request to a Single Sensor This is a simple demonstration of the SDI-12 library for Arduino. diff --git a/examples/g_terminal_window/ReadMe.md b/examples/g_terminal_window/ReadMe.md index 5ff6a53..666adb3 100644 --- a/examples/g_terminal_window/ReadMe.md +++ b/examples/g_terminal_window/ReadMe.md @@ -1,5 +1,4 @@ -[//]: # ( @page example_g_page Example G: Using the Arduino as a Command Terminal for SDI-12 Sensors ) -# Example G: Using the Arduino as a Command Terminal for SDI-12 Sensors +# Example G: Using the Arduino as a Command Terminal for SDI-12 Sensors This is a simple demonstration of the SDI-12 library for Arduino. diff --git a/examples/h_SDI-12_slave_implementation/ReadMe.md b/examples/h_SDI-12_slave_implementation/ReadMe.md index 950d64d..7a5e33a 100644 --- a/examples/h_SDI-12_slave_implementation/ReadMe.md +++ b/examples/h_SDI-12_slave_implementation/ReadMe.md @@ -1,5 +1,4 @@ -[//]: # ( @page example_h_page Example H: Using SDI-12 in Slave Mode ) -# Example H: Using SDI-12 in Slave Mode +# Example H: Using SDI-12 in Slave Mode Example sketch demonstrating how to implement an Arduino as a slave on an SDI-12 bus. This may be used, for example, as a middleman between an I2C sensor and an SDI-12 datalogger. diff --git a/examples/i_SDI-12_interface/ReadMe.md b/examples/i_SDI-12_interface/ReadMe.md index 9b7dd5a..0926120 100644 --- a/examples/i_SDI-12_interface/ReadMe.md +++ b/examples/i_SDI-12_interface/ReadMe.md @@ -1,5 +1,4 @@ -[//]: # ( @page example_i_page Example I: SDI-12 PC Interface ) -# Example I: SDI-12 PC Interface +# Example I: SDI-12 PC Interface Code for an Arduino-based USB dongle translates serial comm from PC to SDI-12 (electrical and timing) 1. Allows user to communicate to SDI-12 devices from a serial terminal emulator (e.g. PuTTY). diff --git a/examples/j_external_pcint_library/ReadMe.md b/examples/j_external_pcint_library/ReadMe.md index 4d51ac4..33ed303 100644 --- a/examples/j_external_pcint_library/ReadMe.md +++ b/examples/j_external_pcint_library/ReadMe.md @@ -1,5 +1,4 @@ -[//]: # ( @page example_j_page Example J: Using External Interrupts ) -# Example J: Using External Interrupts +# Example J: Using External Interrupts This is identical to example D, except that instead of using internal definitions of pin change interrupt vectors, it depends on another library to define them for it. diff --git a/examples/k_concurrent_logger/ReadMe.md b/examples/k_concurrent_logger/ReadMe.md index 638fe7f..095d0a9 100644 --- a/examples/k_concurrent_logger/ReadMe.md +++ b/examples/k_concurrent_logger/ReadMe.md @@ -1,5 +1,4 @@ -[//]: # ( @page example_k_page Example K: Concurrent Measurements ) -# Example K: Concurrent Measurements +# Example K: Concurrent Measurements This is very similar to example D - finding all attached sensors and logging data from them. Unlike example D, however, which waits for each sensor to complete a measurement, this asks all sensors to take measurements concurrently and then waits until each is finished to query for results. diff --git a/examples/l_verify_crc/ReadMe.md b/examples/l_verify_crc/ReadMe.md index 921b06f..07f1b41 100644 --- a/examples/l_verify_crc/ReadMe.md +++ b/examples/l_verify_crc/ReadMe.md @@ -1,11 +1,10 @@ -[//]: # ( @page example_l_page Example L: Verifying CRC Values ) -# Example L: Verifying CRC Values +# Example L: Verifying CRC Values This is a simple demonstration of the SDI-12 library for Arduino. This is a very basic (stripped down) example where the user initiates a measurement with a CRC check and receives and verifies the CRC response -[//]: # ( @sectionl_verify_crc_pio PlatformIO Configuration ) +[//]: # ( @section l_verify_crc_pio PlatformIO Configuration ) [//]: # ( @example{lineno} l_verify_crc.ino @m_examplenavigation{examples_page,} @m_footernavigation ) From 2b87e6de8b4a092e79e578045638d96ebeedd0ec Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Mon, 29 Jul 2024 15:17:04 -0400 Subject: [PATCH 43/96] CRC docs Signed-off-by: Sara Damiano --- src/SDI12.cpp | 6 +++++- src/SDI12.h | 12 ++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/SDI12.cpp b/src/SDI12.cpp index f42eae8..04aa766 100644 --- a/src/SDI12.cpp +++ b/src/SDI12.cpp @@ -566,6 +566,9 @@ void SDI12::sendResponse(FlashString resp, bool addCRC) { setState(SDI12_LISTENING); // return to listening state } +/** + * @brief The polynomial to match the CRC with; set in the SDI-12 specifications + */ #define POLY 0xa001 uint16_t SDI12::calculateCRC(String& resp) { @@ -577,7 +580,8 @@ uint16_t SDI12::calculateCRC(String& resp) { for (int j = 0; j < 8; j++) { // count = 1 to 8 if (crc & 0x0001) { // if the least significant bit of the CRC is one crc >>= 1; // right shift the CRC one bit - crc ^= POLY; // set CRC equal to the exclusive OR of POLY and itself + crc ^= + POLY; // set CRC equal to the exclusive OR of the match polynomial and itself } else { crc >>= 1; // right shift the CRC one bit } diff --git a/src/SDI12.h b/src/SDI12.h index af1098b..0dc065a 100644 --- a/src/SDI12.h +++ b/src/SDI12.h @@ -929,6 +929,14 @@ class SDI12 : public Stream { /** * @brief Converts a numeric 16-bit CRC to an ASCII String. * + * From the SDI-12 Specifications: + * + * The 16 bit CRC is encoded as three ASCII characters + * using the following algorithm: + * 1st character = 0x40 OR (CRC shifted right 12 bits) + * 2nd character = 0x40 OR ((CRC shifted right 6 bits) AND 0x3F) + * 3rd character = 0x40 OR (CRC AND 0x3F) + * * @param crc The 16-bit CRC * @return *String* An ASCII string for the CRC */ @@ -955,9 +963,9 @@ class SDI12 : public Stream { * SDI-12 device itself, not as a recorder for another SDI-12 device. */ void sendResponse(String& resp, bool addCRC = false); - /// @copydoc SDI12::sendResponse(String& resp) + /// @copydoc SDI12::sendResponse(String& resp, bool addCRC) void sendResponse(const char* resp, bool addCRC = false); - /// @copydoc SDI12::sendResponse(String& resp) + /// @copydoc SDI12::sendResponse(String& resp, bool addCRC) void sendResponse(FlashString resp, bool addCRC = false); ///@} From 907ead3f4aa6f9ac982360bfb52ee45e6bcfc222 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Mon, 29 Jul 2024 15:27:01 -0400 Subject: [PATCH 44/96] Update gitignore Signed-off-by: Sara Damiano --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index d230dfa..d764eb6 100644 --- a/.gitignore +++ b/.gitignore @@ -163,3 +163,6 @@ output_*.log sync.ffs_db docs/Doxyfile.bak SDI12 Repo Token.txt +boards/ +lib/ +variants/ From 98d150e07404355f907eead0ad0130f5f4852e19 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Tue, 30 Jul 2024 16:42:40 -0400 Subject: [PATCH 45/96] Add docs on clocks Signed-off-by: Sara Damiano --- docs/SAMD51PeripheralClocks.dox | 84 ++++++++++ docs/SupportedBoardsAndTimers.md | 256 +++++++++++++++++++++++++++++++ 2 files changed, 340 insertions(+) create mode 100644 docs/SAMD51PeripheralClocks.dox create mode 100644 docs/SupportedBoardsAndTimers.md diff --git a/docs/SAMD51PeripheralClocks.dox b/docs/SAMD51PeripheralClocks.dox new file mode 100644 index 0000000..a566cd2 --- /dev/null +++ b/docs/SAMD51PeripheralClocks.dox @@ -0,0 +1,84 @@ + // -- DPLL (Digital Phase Locked Loop) + // OSCCTRL_GCLK_ID_DFLL48 0 // Index of Generic Clock for DFLL48 + // OSCCTRL_GCLK_ID_FDPLL0 1 // Index of Generic Clock for DPLL0 + // OSCCTRL_GCLK_ID_FDPLL1 2 // Index of Generic Clock for DPLL1 + // OSCCTRL_GCLK_ID_FDPLL032K 3 // Index of Generic Clock for DPLL0 32K + // OSCCTRL_GCLK_ID_FDPLL132K 3 // Index of Generic Clock for DPLL1 32K + // -- EIC (External Interrupt Control) + // EIC_GCLK_ID 4 + // -- FREQM (Frequency Meter) + // FREQM_GCLK_ID_MSR 5 + // 6 seems to be unused, though it's USB on the SAMD21 + // -- SERCOM's 0-1 + // SERCOM0_GCLK_ID_CORE 7 + // SERCOM0_GCLK_ID_SLOW 3 + // SERCOM1_GCLK_ID_CORE 8 + // SERCOM1_GCLK_ID_SLOW 3 + // Timer Control 0-1 + // TC0_GCLK_ID 9 + // TC1_GCLK_ID 9 + // -- USB + // USB_GCLK_ID 10 + // -- EVSYS (Event System) + // EVSYS_GCLK_ID_0 11 + // EVSYS_GCLK_ID_1 12 + // EVSYS_GCLK_ID_2 13 + // EVSYS_GCLK_ID_3 14 + // EVSYS_GCLK_ID_4 15 + // EVSYS_GCLK_ID_5 16 + // EVSYS_GCLK_ID_6 17 + // EVSYS_GCLK_ID_7 18 + // EVSYS_GCLK_ID_8 19 + // EVSYS_GCLK_ID_9 20 + // EVSYS_GCLK_ID_10 21 + // EVSYS_GCLK_ID_11 22 + // -- SERCOM's 2-3 + // SERCOM2_GCLK_ID_CORE 23 + // SERCOM2_GCLK_ID_SLOW 3 + // SERCOM3_GCLK_ID_CORE 24 + // SERCOM3_GCLK_ID_SLOW 3 + // -- TCC (Timer/Counter for Control) 0-1; TC (Timer/Counter) 2-3 + // TCC0_GCLK_ID 25 + // TCC1_GCLK_ID 25 + // TC2_GCLK_ID 26 + // TC3_GCLK_ID 26 + // -- CAN (Control Area Network) 0-1 + // CAN0_GCLK_ID 27 + // CAN1_GCLK_ID 28 // NOTE: Incorrectly set to 36 in UF2 bootloader?? + // -- TCC (Timer/Counter for Control) 2-3; TC (Timer/Counter) 4-5 + // TCC2_GCLK_ID 29 + // TCC3_GCLK_ID 29 + // TC4_GCLK_ID 30 + // TC5_GCLK_ID 30 + // -- PDEC (Position Decoder) + // PDEC_GCLK_ID 31 + // -- AC (Analog Input Comparitor) + // AC_GCLK_ID 32 + // -- CCL (Configurable Custom Logic) + // CCL_GCLK_ID 33 + // -- SERCOM's 4-7 + // SERCOM4_GCLK_ID_CORE 34 + // SERCOM4_GCLK_ID_SLOW 3 + // SERCOM5_GCLK_ID_CORE 35 + // SERCOM5_GCLK_ID_SLOW 3 + // SERCOM6_GCLK_ID_CORE 36 + // SERCOM6_GCLK_ID_SLOW 3 + // SERCOM7_GCLK_ID_CORE 37 + // SERCOM7_GCLK_ID_SLOW 3 + // -- TCC (Timer/Counter for Control) 4; TC (Timer/Counter) 6-7 + // TCC4_GCLK_ID 38 + // TC6_GCLK_ID 39 + // TC7_GCLK_ID 39 + // -- ADC (Analog to Digital Converter) + // ADC0_GCLK_ID 40 + // ADC1_GCLK_ID 41 + // -- DAC (Digital to Analog Converter) + // DAC_GCLK_ID 42 + // -- I2S (Sound) + // I2S_GCLK_ID_0 43 + // I2S_GCLK_ID_1 44 + // -- SDHC (SD Card) + // SDHC0_GCLK_ID 45 + // SDHC0_GCLK_ID_SLOW 3 + // SDHC1_GCLK_ID 46 + // SDHC1_GCLK_ID_SLOW 3 diff --git a/docs/SupportedBoardsAndTimers.md b/docs/SupportedBoardsAndTimers.md new file mode 100644 index 0000000..3fdd2ea --- /dev/null +++ b/docs/SupportedBoardsAndTimers.md @@ -0,0 +1,256 @@ +# Bits and Ticks + +This library listens for pin level changes and then use a timer to calculate how many databits have been sent since the last change and to convert that to a character. +The speed of the timer is dependent on the speed of the processor and "dividers" and "prescalers" used to slow the effective clock. +Unfortunately, the "ticks" of the processor clock aren't perfectly aligned with the times of the level changes from the SDI-12 device. +With the clocks not perfectly aligned, we can't know exactly the time that a bit started or ended, just the time of the closest tick. +This means we need to do some averaging and "fudging" to align the two. + +@See @page rx_page for more information on how a character is created. + +SDI-12 Communicates at 1200 baud (bits/s) and sends each character using 10 bits (7E1). + - 1 bit = 0.83333 ms + - 1 character = 8.33333 ms + +When setting up our timers, the goal is to be able to have as many ticks as possible for each bit. +The more ticks we have, the better job we can do with the needed averaging and fudging. +But, we also need to make sure that the clock timer doesn't roll over before the end of the 8.33ms required for each character. +When acting as a recording device, it would be even better if the timer could last until the end of a 112.5ms retry timer before rolling over. + +Using a 16 bit counter, the counter rolls after 65535 ticks. + - Going for the maximum retry time, 112.5ms / 65536 ticks = 1.71661376953125 µsec/tick (582.54222 kHz) = 485.25767 ticks/bit + - This is *plenty* of ticks per bit! With a 16-bit timer, there's no reason to not use this whole period. + - For an 8MHz board, this is a 14x pre-scaler + - For a 12MHz board, this is a 21x pre-scaler + - For a 16MHz board, this is a 27x pre-scaler + - For a 20MHz board, this is a 35x pre-scaler + - Prescalers generally work in powers of 2, so 1024/512/256/128/64/8/4/2 + - Because we have to catch the full character, we need to round UP the prescaler number to give us more ticks than required. + - For a 8, 12, 16, or 20 Mhz board, this means a 64x pre-scaler + - For the faster SAMD-boards, we have more options with divisors and prescalers + - Going for the minimum 8.33ms per character, 8.33ms / 65536 ticks = 0.127105712890625 µsec/tick (7.86747 MHz) = 6553.6 ticks/bit + - This is overkill. + +If we only have an 8 bit timer, the counter rolls after 256 ticks. + - Going for the maximum retry time, 112.5ms / 256 ticks = 1.89554 ticks/bit + - 0.439453125 msec/tick, 2.275 kHz + - This is no where near enough bits / tick for accuracy, so it is not possible to keep a timer running for the 112.5 ms retry with a 8-bit timer. + - Going for the minimum 8.33ms per character, 8.33ms / 256 ticks = 25.6 ticks/bit + - 0.0325390625 msec/tick, 30.73229 kHz + - For an 8MHz board, this is a 260x pre-scaler + - For a 12MHz board, this is a 390x pre-scaler + - For a 16MHz board, this is a 520x pre-scaler + - For a 20MHz board, this is a 651x pre-scaler + - Prescalers generally work in powers of 2, so 1024/512/256/128/64/8/4/2 + - Because we have to catch the full character, we need to round UP the prescaler number to give us more ticks than required. + - For a 8MHz or 12MHz board, this means a 512x pre-scaler + - For a 16MHz or 20MHz board, this is a 1024x pre-scaler + +# Supported Processors and Timers + +## AVR Boards + +### Available Processor Speeds and Timers + +#### ATmega164A/PA/324A/PA/644A/PA/1284/P: +- Up to 20MIPS throughput at 20MHz + - Most Arduino boards are run at 16 or 8 MHz with a few at 12 MHz +- Two 8-bit Timer/Counters with Separate Prescalers and Compare Modes + - Timers 0 and 2 + - Prescalers available at 8/64/256/1024 on Timer 0 + - Prescalers available at 8/32/64/128/256/1024 on Timer 2 +- One/two 16-bit Timer/Counter with Separate Prescaler, Compare Mode, and Capture Mode + - Timers 1 and 3 + - Prescalers available at 8/64/256/1024 on Timers 1 and 3 + - Timer 3 is only available on the 1284p + +#### ATmega640/V-1280/V-1281/V-2560/V-2561/V: +- Up to 16 MIPS Throughput at 16MHz +– Two 8-bit Timer/Counters with Separate Prescaler and Compare Mode + - Timers 0 and 2 + - Prescalers available at 8/64/256/1024 on Timer 0 + - Prescalers available at 8/32/64/128/256/1024 on Timer 2 +– Four 16-bit Timer/Counters with Separate Prescaler, Compare- and Capture Mode + - Timers 1, 3, 4, and 5 + - Prescalers available at 8/64/256/1024 on Timer 1, 3, 4, and 5 + +#### ATmega16U4/ATmega32U4: +– Up to 16 MIPS Throughput at 16MHz +– One 8-bit Timer/Counter with Separate Prescaler and Compare Mode + - Timer 0 + - Prescalers available at 8/64/256/1024 on Timer 0 +– Two 16-bit Timer/Counter with Separate Prescaler, Compare- and Capture Mode + - Timers 1 and 3 + - Prescalers available at 8/64/256/1024 on Timer 1 and 3 +– One 10-bit High-Speed Timer/Counter with PLL (64MHz) and Compare Mode + - Timer 4 + - Prescalers available at 2/4/8/16/32/64/128/256/512/1024/2048/8192/169384 on Timer 4 +- There is no Timer 2 on the 16U4 or the 32U4 + +#### ATtiny25/V / ATtiny45/V / ATtiny85/V: +- Up to 20MIPS throughput at 20MHz + - Most Arduino boards are run at 16 or 8 MHz with a few at 12 MHz +– One 8-bit Timer/Counter with Prescaler and Two PWM Channels + - Timer 0 + - Prescalers available at 8/64/256/1024 +– One 8-bit High Speed Timer/Counter with Separate Prescaler + - Timer 1 + - Prescalers available at 64/128/256/512/1024/2048/4096/8192/16384 + +### Commonly Used Timers +- Timer 0 + - [The primary clock (millis)](https://github.com/arduino/ArduinoCore-avr/blob/master/cores/arduino/wiring.c) + - Fast hardware PWM +- Timer 1 + - Phase-correct hardware PWM + - [AltSoftSerial](https://github.com/PaulStoffregen/AltSoftSerial/) + - Primary for [Servo](https://github.com/arduino-libraries/Servo) +- Timer 2 + - Primary for [Tone](https://github.com/arduino/ArduinoCore-avr/blob/master/cores/arduino/Tone.cpp) (except on the 16U4/32U4) + - Phase-correct hardware PWM + - [NeoSWSerial](https://github.com/SlashDevin/NeoSWSerial/) +- Timer 3 + - Primary for [Tone](https://github.com/arduino/ArduinoCore-avr/blob/master/cores/arduino/Tone.cpp) (only on the 16U4/32U4) + - Optional for [Tone](https://github.com/arduino/ArduinoCore-avr/blob/master/cores/arduino/Tone.cpp) on some boards + - Optional for [Servo](https://github.com/arduino-libraries/Servo) on some boards +- Timer 4 + - Optional for [Tone](https://github.com/arduino/ArduinoCore-avr/blob/master/cores/arduino/Tone.cpp) on some boards + - Optional for [Servo](https://github.com/arduino-libraries/Servo) on some boards +- Timer 5 + - Optional for [Tone](https://github.com/arduino/ArduinoCore-avr/blob/master/cores/arduino/Tone.cpp) on some boards + - Optional for [Servo](https://github.com/arduino-libraries/Servo) on some boards + +#### Selected Timers for SDI-12 + +For simplicity, we use Timer/Counter 2 on most AVR boards. + +On the ATTiny boards, we use Timer/Counter 1 + +On the AtMega16U4 and AtMega32U4, we use Timer/Counter 4 as an 8-bit timer. + +## SAMD Boards + +### SAMD21 + +#### Available Processor Speeds and Timers + +The Generic Clock controller GCLK provides nine Generic Clock Generators that can provide a wide range of clock frequencies. +Generators can be set to use different external and internal oscillators as source. +The clock of each Generator can be divided. +The outputs from the Generators are used as sources for the Generic Clock Multiplexers, which provide the Generic Clock (GCLK_PERIPHERAL) to the peripheral modules, as shown in Generic Clock Controller +Block Diagram. + +Features +- Provides Generic Clocks +- Wide frequency range +- Clock source for the generator can be changed on the fly + +The TC consists of a counter, a prescaler, compare/capture channels and control logic. +The counter can be set to count events, or it can be configured to count clock pulses. +The counter, together with the compare/capture channels, can be configured to timestamp input events, allowing capture of frequency and pulse width. +It can also perform waveform generation, such as frequency generation and pulse-width modulation (PWM). + +Features +- Selectable configuration + – Up to five 16-bit Timer/Counters (TC) including one low-power TC, each configurable as: + - 8-bit TC with two compare/capture channels + - 16-bit TC with two compare/capture channels + - 32-bit TC with two compare/capture channels, by using two TCs +- Waveform generation + – Frequency generation + – Single-slope pulse-width modulation +- Input capture + – Event capture + – Frequency capture + – Pulse-width capture +- One input event +- Interrupts/output events on: + – Counter overflow/underflow + – Compare match or capture +- Internal prescaler +- Can be used with DMA and to trigger DMA transactions + +#### Commonly Used Timers + +The Adafruit Arduino core uses: +- 0 as GENERIC_CLOCK_GENERATOR_MAIN (the main clock) + +The Adafruit Arduino core uses: +- TC5 for Tone +- TC4 for Servo + +### SAMD51/SAME51 + +#### Available Processor Speeds and Timers + +Depending on the application, peripherals may require specific clock frequencies to operate correctly. +The Generic Clock controller (GCLK) features 12 Generic Clock Generators [11:0] that can provide a wide range of clock frequencies. + +Generators can be set to use different external and internal oscillators as source. +The clock of each Generator can be divided. +The outputs from the Generators are used as sources for the Peripheral Channels, which provide the Generic Clock (GCLK_PERIPH) to the peripheral modules, as shown in Figure 14-2. +The number of Peripheral Clocks depends on how many peripherals the device has. + +NOTE: The Generator 0 is always the direct source of the GCLK_MAIN signal. + +Features +- Provides a device-defined, configurable number of Peripheral Channel clocks +- Wide frequency range + - Various clock sources + - Embedded dividers + +There are up to eight TC peripheral instances. + +Each TC consists of a counter, a prescaler, compare/capture channels and control logic. +The counter can be set to count events, or clock pulses. +The counter, together with the compare/capture channels, can be configured to timestamp input events or IO pin edges, allowing for capturing of frequency and/or pulse width. + +A TC can also perform waveform generation, such as frequency generation and pulse-width modulation. + +Features + - Selectable configuration + - 8-, 16- or 32-bit TC operation, with compare/capture channels + - 2 compare/capture channels (CC) with: + - Double buffered timer period setting (in 8-bit mode only) + - Double buffered compare channel + - Waveform generation + - Frequency generation + - Single-slope pulse-width modulation + - Input capture + - Event / IO pin edge capture + - Frequency capture + - Pulse-width capture + - Time-stamp capture + - Minimum and maximum capture + - One input event + - Interrupts/output events on: + - Counter overflow/underflow + - Compare match or capture + - Internal prescaler + - DMA support + +#### Commonly Used Timers + +The Adafruit Arduino core uses: +- 0 as GENERIC_CLOCK_GENERATOR_MAIN (the main clock, sourced from MAIN_CLOCK_SOURCE = GCLK_GENCTRL_SRC_DPLL0) +- 1 as GENERIC_CLOCK_GENERATOR_48M (48MHz clock for USB and 'stuff', sourced from GCLK_GENCTRL_SRC_DPLL0) +- 2 as GENERIC_CLOCK_GENERATOR_100M (100MHz clock for other peripherals, sourced from GCLK_GENCTRL_SRC_DPLL1) +- 3 as GENERIC_CLOCK_GENERATOR_XOSC32K (32kHz oscillator, sourced from 32kHz external oscillator GCLK_GENCTRL_SRC_XOSC32K) +- 4 as GENERIC_CLOCK_GENERATOR_12M (12MHz clock for DAC, sourced from GCLK_GENCTRL_SRC_DPLL0) +- 5 as GENERIC_CLOCK_GENERATOR_1M (??, sourced from CLK_GENCTRL_SRC_DPLL0) + +For SDI-12, we'll use Timer Control 2 + +The Adafruit Arduino core uses: +- TC0 for Tone +- TC1 for Servo + +## Other Boards + +For sufficiently fast boards, instead of using a dedicated processor timer, we can use the built-in `micros()` function as the timer. +Both the ESP8266 and ESP32 are fast enough that this works. + +- The ESP8266 runs at either 80 or 160 MHz +- The ESP32 runs at 160 or 240 MHz. + +All of the other processors using the Arduino core also have the micros function, but the rest are not fast enough to waste the processor cycles to use the micros function and must manually configure the processor timer and use the faster assembly macros to read that processor timer directly. From 7a0abd8210efdaffbc5d4a5f07ddcc9209c94fc1 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Tue, 30 Jul 2024 16:43:27 -0400 Subject: [PATCH 46/96] Re-write and condense timers Signed-off-by: Sara Damiano --- src/SDI12.cpp | 24 +- src/SDI12.h | 81 +----- src/SDI12_boards.cpp | 223 +++++++++++++-- src/SDI12_boards.h | 630 +++++++++++++++++++++---------------------- 4 files changed, 535 insertions(+), 423 deletions(-) diff --git a/src/SDI12.cpp b/src/SDI12.cpp index 04aa766..b484ac1 100644 --- a/src/SDI12.cpp +++ b/src/SDI12.cpp @@ -66,11 +66,7 @@ const uint16_t SDI12::lineBreak_micros = (uint16_t)12300; const uint16_t SDI12::marking_micros = (uint16_t)8500; // the width of a single bit in "ticks" of the cpu clock. -const uint8_t SDI12::txBitWidth = TICKS_PER_BIT; -// A fudge factor to make things work -const uint8_t SDI12::rxWindowWidth = RX_WINDOW_FUDGE; -// The number of bits per tick, shifted by 2^10. -const uint8_t SDI12::bitsPerTick_Q10 = BITS_PER_TICK_Q10; +const sdi12timer_t SDI12::txBitWidth = TICKS_PER_BIT; // A mask waiting for a start bit; 0b11111111 const uint8_t SDI12::WAITING_FOR_START_BIT = 0xFF; @@ -79,14 +75,6 @@ uint8_t SDI12::rxState = WAITING_FOR_START_BIT; // 0: got start bit; >0: bits uint8_t SDI12::rxMask; // bit mask for building received character uint8_t SDI12::rxValue; // character being built -uint16_t SDI12::mul8x8to16(uint8_t x, uint8_t y) { - return x * y; -} - -uint16_t SDI12::bitTimes(uint8_t dt) { - return mul8x8to16(dt + rxWindowWidth, bitsPerTick_Q10) >> 10; -} - /* ================ Buffer Setup ====================================================*/ uint8_t SDI12::_rxBuffer[SDI12_BUFFER_SIZE]; // The Rx buffer volatile uint8_t SDI12::_rxBufferTail = 0; // index of buff tail @@ -446,7 +434,7 @@ void SDI12::writeChar(uint8_t outChar) { // Hold the line for the rest of the start bit duration - while ((uint8_t)(READTIME - t0) < txBitWidth) {} + while ((sdi12timer_t)(READTIME - t0) < txBitWidth) {} t0 = READTIME; // advance start time // repeat for all data bits until the last bit different from marking @@ -458,7 +446,7 @@ void SDI12::writeChar(uint8_t outChar) { digitalWrite(_dataPin, HIGH); // set the pin state to HIGH for 0's } // Hold the line for this bit duration - while ((uint8_t)(READTIME - t0) < txBitWidth) {} + while ((sdi12timer_t)(READTIME - t0) < txBitWidth) {} t0 = READTIME; // start time outChar = outChar >> 1; // shift character to expose the following bit @@ -470,8 +458,8 @@ void SDI12::writeChar(uint8_t outChar) { interrupts(); // Re-enable universal interrupts as soon as critical timing is past // Hold the line low until the end of the 10th bit - uint8_t bitTimeRemaining = txBitWidth * (10 - lastHighBit); - while ((uint8_t)(READTIME - t0) < bitTimeRemaining) {} + sdi12timer_t bitTimeRemaining = txBitWidth * (10 - lastHighBit); + while ((sdi12timer_t)(READTIME - t0) < bitTimeRemaining) {} } // The typical write functionality for a stream object @@ -698,7 +686,7 @@ void ESPFAMILY_USE_INSTRUCTION_RAM SDI12::receiveISR() { // data, parity, or stop bit. // Check how many bit times have passed since the last change - uint16_t rxBits = bitTimes((uint8_t)(thisBitTCNT - prevBitTCNT)); + uint16_t rxBits = SDI12Timer::bitTimes((uint8_t)(thisBitTCNT - prevBitTCNT)); // Calculate how many *data+parity* bits should be left in the current character // - Each character has a total of 10 bits, 1 start bit, 7 data bits, 1 parity // bit, and 1 stop bit diff --git a/src/SDI12.h b/src/SDI12.h index 0dc065a..bd21d5a 100644 --- a/src/SDI12.h +++ b/src/SDI12.h @@ -181,25 +181,7 @@ enum LookaheadMode { /** Only tabs, spaces, line feeds & carriage returns are skipped.*/ SKIP_WHITESPACE }; - -/** - * @brief The function or macro used to read the clock timer value. - * - * @note The ESP32 and ESP8266 are fast enough processors that they can take the - * time to read the core 'micros()' function still complete the other processing needed - * on the serial bits. All of the other processors using the Arduino core also have the - * micros function, but the rest are not fast enough to waste the processor cycles to - * use the micros function and must use the faster assembly macros to read the - * processor timer directly. - */ - -#define READTIME sdi12timer.SDI12TimerRead() -#else -/** - * @brief The function or macro used to read the clock timer value. - */ -#define READTIME TCNTX -#endif // defined(ESP32) || defined(ESP8266) +#endif /** * @brief The main class for SDI 12 instances @@ -240,15 +222,7 @@ class SDI12 : public Stream { /** * @brief the width of a single bit in "ticks" of the cpu clock. */ - static const uint8_t txBitWidth; - /** - * @brief A fudge factor to make things work - */ - static const uint8_t rxWindowWidth; - /** - * @brief The number of bits per tick, shifted by 2^10. - */ - static const uint8_t bitsPerTick_Q10; + static const sdi12timer_t txBitWidth; /** * @brief A mask for the #rxState while waiting for a start bit; 0b11111111 */ @@ -276,35 +250,6 @@ class SDI12 : public Stream { * @brief the value of the character being built */ static uint8_t rxValue; - - /** - * @brief static method for getting a 16-bit value from the multiplication of 2 8-bit - * values - * - * @param x The first 8 bit integer - * @param y The second 8 bit integer - * @return @m_span{m-type} uint16_t @m_endspan The result of the multiplication, as a - * 16 bit integer. - */ - static uint16_t mul8x8to16(uint8_t x, uint8_t y); - - /** - * @brief static method for calculating the number of bit-times that have elapsed - * given an 8-bit counter/timer timestamp. - * - * @param dt The current value of the 8-bit timer - * @return @m_span{m-type} uint16_t @m_endspan The number of bit times that have - * passed at 1200 baud. - * - * Adds a rxWindowWidth fudge factor to the time difference to get the number of - * ticks, and then multiplies the fudged ticks by the number of bits per tick. Uses - * the number of bits per tick shifted up by 2^10 and then shifts the result down by - * the same amount to compensate for the fact that the number of bits per tick is a - * decimal the timestamp is only an 8-bit integer. - * - * @see https://github.com/SlashDevin/NeoSWSerial/pull/13#issuecomment-315463522 - */ - static uint16_t bitTimes(uint8_t dt); /**@}*/ @@ -380,7 +325,7 @@ class SDI12 : public Stream { /** * @brief Return the number of bytes available in the Rx buffer * - * @return @m_span{m-type} int @m_endspan The number of characters in the buffer + * @return The number of characters in the buffer * * available() is a public function that returns the number of characters available in * the Rx buffer. @@ -424,7 +369,7 @@ class SDI12 : public Stream { /** * @brief Reveal next byte in the Rx buffer without consuming it. * - * @return @m_span{m-type} int @m_endspan The next byte in the character buffer. + * @return The next byte in the character buffer. * * peek() is a public function that allows the user to look at the character that is * at the head of the buffer. Unlike read() it does not consume the character (i.e. @@ -442,7 +387,7 @@ class SDI12 : public Stream { /** * @brief Return next byte in the Rx buffer, consuming it * - * @return @m_span{m-type} int @m_endspan The next byte in the character buffer. + * @return The next byte in the character buffer. * * read() returns the character at the current head in the buffer after incrementing * the index of the buffer head. This action 'consumes' the character, meaning it can @@ -468,7 +413,7 @@ class SDI12 : public Stream { * @param lookahead the mode to use to look ahead in the * stream, default is LookaheadMode::SKIP_ALL * @param ignore a character to ignore in the stream, default is '\\x01' - * @return @m_span{m-type} long @m_endspan The first valid integer in the stream + * @return The first valid integer in the stream * * @note This function _hides_ the Stream class function to allow a custom value to be * returned on timeout. It cannot overwrite the Stream function because it is not @@ -489,7 +434,7 @@ class SDI12 : public Stream { * @param lookahead the mode to use to look ahead in the * stream, default is LookaheadMode::SKIP_ALL * @param ignore a character to ignore in the stream, default is '\\x01' - * @return @m_span{m-type} long @m_endspan The first valid float in the stream + * @return The first valid float in the stream * * @note This function _hides_ the Stream class function to allow a custom value to be * returned on timeout. It cannot overwrite the Stream function because it is not @@ -506,7 +451,7 @@ class SDI12 : public Stream { * stream * @param detectDecimal True to accept a decimal point ('.') as part of a * number - * @return @m_span{m-type} int @m_endspan The next numeric digit in the stream + * @return The next numeric digit in the stream */ int peekNextDigit(LookaheadMode lookahead, bool detectDecimal); /**@}*/ @@ -615,7 +560,7 @@ class SDI12 : public Stream { /** * @brief Get the data pin for the current SDI-12 instance * - * @return @m_span{m-type} int8_t @m_endspan the data pin number + * @return the data pin number */ int8_t getDataPin(); /** @@ -662,7 +607,7 @@ class SDI12 : public Stream { /** * @brief Set this instance as the active SDI-12 instance * - * @return @m_span{m-type} bool @m_endspan True indicates that the current SDI-12 + * @return True indicates that the current SDI-12 * instance was not formerly the active one and now is. False indicates that the * current SDI-12 instance *is already the active one* and the state was not changed. * @@ -678,7 +623,7 @@ class SDI12 : public Stream { /** * @brief Check if this instance is active * - * @return @m_span{m-type} bool @m_endspan True indicates that the curren SDI-12 + * @return True indicates that the curren SDI-12 * instace is the active one. * * isActive() is a method for checking if the object is the active object. Returns @@ -766,7 +711,7 @@ class SDI12 : public Stream { * @brief Calculate the parity value for a character using even parity. * * @param v **uint8_t (char)** the character to calculate the parity of - * @return @m_span{m-type} uint8_t @m_endspan the input character with the 8th bit set + * @return the input character with the 8th bit set * to the even parity value for that character * * Sets up parity and interrupts for different processor types - that is, imports the @@ -886,7 +831,7 @@ class SDI12 : public Stream { * @brief Write out a byte on the SDI-12 line * * @param byte The character to write - * @return @m_span{m-type} size_t @m_endspan The number of characters written + * @return The number of characters written * * Sets the state to transmitting, writes a character, and then sets the state back to * listening. This function must be implemented as part of the Arduino Stream diff --git a/src/SDI12_boards.cpp b/src/SDI12_boards.cpp index db3c575..b33226b 100644 --- a/src/SDI12_boards.cpp +++ b/src/SDI12_boards.cpp @@ -18,6 +18,28 @@ sensors. This library provides a general software solution, without requiring SDI12Timer::SDI12Timer() {} +uint16_t SDI12Timer::mul8x8to16(uint8_t x, uint8_t y) { + return x * y; +} + +// Using an 8-bit timer, we need to do fanciness to get proper 16 bit results +#if TIMER_INT_SIZE == 8 +sdi12timer_t SDI12Timer::bitTimes(sdi12timer_t dt) { + // multiply the time delta in ticks by the bits per tick + return mul8x8to16(dt + RX_WINDOW_FUDGE, BITS_PER_TICK_Q10) >> 10; +} + +// But nothing fancy for bigger timers +#elif TIMER_INT_SIZE == 16 || TIMER_INT_SIZE == 32 +sdi12timer_t SDI12Timer::bitTimes(sdi12timer_t dt) { + // divide the number of ticks by the ticks per bit + return (dt + RX_WINDOW_FUDGE) / TICKS_PER_BIT; +} +#else +#error "Board timer is incorrectly configured!" +#endif + + // Most 'standard' AVR boards // #if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) || \ @@ -213,8 +235,28 @@ void SDI12Timer::resetSDI12TimerPrescale(void) { // Arduino Zero other SAMD21 boards // -#elif defined(ARDUINO_SAMD_ZERO) || defined(ARDUINO_ARCH_SAMD) || \ - defined(__SAMD21G18A__) || defined(__SAMD21J18A__) || defined(__SAMD21E18A__) +#elif defined(ARDUINO_SAMD_ZERO) || defined(__SAMD21G18A__) || \ + defined(__SAMD21J18A__) || defined(__SAMD21E18A__) + +#define SDI12_TC TC3 +#define SDI12_TC_IRQn TC3_IRQn +#define SDI12_Handler TC3_Handler + +#define WAIT_TC16_REGS_SYNC(x) \ + while (x->COUNT16.STATUS.bit.SYNCBUSY) \ + ; + +static inline void resetTC(Tc* TCx) { + // Disable TCx + TCx->COUNT16.CTRLA.reg &= ~TC_CTRLA_ENABLE; + WAIT_TC16_REGS_SYNC(TCx) + + // Reset TCx + TCx->COUNT16.CTRLA.reg = TC_CTRLA_SWRST; + WAIT_TC16_REGS_SYNC(TCx) + while (TCx->COUNT16.CTRLA.bit.SWRST) + ; +} void SDI12Timer::configSDI12TimerPrescale(void) { // Select generic clock generator 4 (Arduino core uses 0, 1, and 3. RTCZero uses 2) @@ -222,8 +264,9 @@ void SDI12Timer::configSDI12TimerPrescale(void) { // I would use a higher clock number, but some of the cores don't include them for // some reason REG_GCLK_GENDIV = GCLK_GENDIV_ID(4) | // Select Generic Clock Generator 4 - GCLK_GENDIV_DIV(3); // Divide the clock source by divisor 3 + GCLK_GENDIV_DIV(5); // Divide the clock source by divisor 5 while (GCLK->STATUS.bit.SYNCBUSY) {} // Wait for synchronization + // NOTE: Could write the above as GCLK->GENDIV.reg instead of REG_GCLK_GENDIV // Write the generic clock generator 4 configuration @@ -232,37 +275,36 @@ void SDI12Timer::configSDI12TimerPrescale(void) { GCLK_GENCTRL_IDC | // Set the duty cycle to 50/50 HIGH/LOW GCLK_GENCTRL_GENEN) & // Enable the generic clock clontrol ~GCLK_GENCTRL_RUNSTDBY & // Do NOT run in stand by - ~GCLK_GENCTRL_DIVSEL; // Divide clock source by GENDIV.DIV: 48MHz/3=16MHz - // ^^ & ~ for DIVSEL because not not divided + ~GCLK_GENCTRL_DIVSEL; // Divide clock source by GENDIV.DIV: 48MHz/5=9.6MHz + // ^^ & ~ for DIVSEL to set DIVSEL to 0 while (GCLK->STATUS.bit.SYNCBUSY) {} // Wait for synchronization + // NOTE: Could write the above as GCLK->GENCTRL.reg instead of REG_GCLK_GENCTRL // Feed GCLK4 to TC3 (also feeds to TCC2, the two must have the same source) // TC3 (and TCC2) seem to be free, so I'm using them - // TC4 is used by Tone, TC5 is tied to the same clock as TC4 + // TC4 is used by Tone and Servo, TC5 is tied to the same clock as TC4 // TC6 and TC7 are not available on all boards REG_GCLK_CLKCTRL = GCLK_CLKCTRL_GEN_GCLK4 | // Select Generic Clock Generator 4 GCLK_CLKCTRL_CLKEN | // Enable the generic clock generator GCLK_CLKCTRL_ID_TCC2_TC3; // Feed the Generic Clock Generator 4 to TCC2 and TC3 + // NOTE: Could write the above as GCLK->CLKCTRL.reg instead of REG_GCLK_CLKCTRL + while (GCLK->STATUS.bit.SYNCBUSY) {} // Wait for synchronization + ; REG_TC3_CTRLA |= - TC_CTRLA_PRESCALER_DIV1024 | // Set prescaler to 1024, 16MHz/1024 = 15.625kHz - TC_CTRLA_WAVEGEN_NFRQ | // Put the timer TC3 into normal frequency (NFRQ) mode - TC_CTRLA_MODE_COUNT8 | // Put the timer TC3 into 8-bit mode - TC_CTRLA_ENABLE; // Enable TC3 + TC_CTRLA_PRESCALER_DIV16 | // Set prescaler to 16, 9.6MHz/16 = 600kHz + TC_CTRLA_WAVEGEN_NFRQ | // Put the timer TC3 into normal frequency (NFRQ) mode + TC_CTRLA_MODE_COUNT16 | // Put the timer TC3 into 16-bit mode + TC_CTRLA_ENABLE; // Enable TC3 + + while (TC3->COUNT16.STATUS.bit.SYNCBUSY) {} // Wait for synchronization } // NOT resetting the SAMD timer settings void SDI12Timer::resetSDI12TimerPrescale(void) { - // Disable TCx - TC3->COUNT16.CTRLA.reg &= ~TC_CTRLA_ENABLE; - while (TC3->COUNT16.STATUS.bit.SYNCBUSY) {} - - // Reset TCx - TC3->COUNT16.CTRLA.reg = TC_CTRLA_SWRST; - while (TC3->COUNT16.STATUS.bit.SYNCBUSY) {} - while (TC3->COUNT16.CTRLA.bit.SWRST) {} + resetTC(SDI12_TC); // Disable generic clock generator REG_GCLK_GENCTRL = GCLK_GENCTRL_ID(4) & // Select GCLK4 @@ -270,6 +312,147 @@ void SDI12Timer::resetSDI12TimerPrescale(void) { while (GCLK->STATUS.bit.SYNCBUSY) {} // Wait for synchronization } +// SAMD51 and SAME51 boards +#elif defined(__SAMD51__) || defined(__SAME51__) + + +#define SDI12_TC TC2 +#define SDI12_TC_IRQn TC2_IRQn +#define SDI12_TC_GCLK_ID TC2_GCLK_ID +#define SDI12_Handler TC2_Handler + +#define WAIT_TC16_REGS_SYNC(x) \ + while (x->COUNT16.SYNCBUSY.bit.ENABLE) \ + ; + +static inline void resetTC(Tc* TCx) { + // Disable TCx + TCx->COUNT16.CTRLA.reg &= ~TC_CTRLA_ENABLE; + WAIT_TC16_REGS_SYNC(TCx) + + // Reset TCx + TCx->COUNT16.CTRLA.reg = TC_CTRLA_SWRST; + WAIT_TC16_REGS_SYNC(TCx) + while (TCx->COUNT16.CTRLA.bit.SWRST) + ; +} + +void SDI12Timer::configSDI12TimerPrescale(void) { + // Select generic clock generator 5 + // The Adafruit Arduino core uses: + // - 0 as GENERIC_CLOCK_GENERATOR_MAIN + // - 1 as GENERIC_CLOCK_GENERATOR_48M + // - 2 as GENERIC_CLOCK_GENERATOR_100M + // - 3 as GENERIC_CLOCK_GENERATOR_XOSC32K + // - 4 as GENERIC_CLOCK_GENERATOR_12M + + // Select Generic Clock instance + // The Adafruit Arduino core uses: + // TC0 for Tone + // TC1 for Servo + + // Set up the control register for the clock generator + GCLK->GENCTRL[GENERIC_CLOCK_GENERATOR_SDI12].reg = + // ^^ select the control register for generic clock 5 + (GCLK_GENCTRL_DIV( + 25) & // Bits 31:16 – DIV[15:0] Division Factor + // ^^ These bits represent a division value for the corresponding + // Generator. The actual division factor is dependent on the state of + // DIVSEL. The number of relevant DIV bits for each Generator can be seen + // in this table. Written bits outside of the specified range will be + // ignored. See datasheet table 14-6 + // Bits 15:14 are reserved + ~GCLK_GENCTRL_RUNSTDBY & // Bit 13 – RUNSTDBY Run in Standby + // ^^ This bit is used to keep the Generator running in Standby as long as it is + // configured to output to a dedicated GCLK_IOn pin. If GENCTRLn.OE is zero, this + // bit has no effect and the generator will only be running if a peripheral + // requires the clock. + // For SDI-12, we do *not* run in standby + ~GCLK_GENCTRL_DIVSEL & // Bit 12 – DIVSEL Divide Selection + // ^^ This bit determines how the division factor of the clock source of the + // Generator will be calculated from DIV. If the clock source should not be + // divided, DIVSEL must be 0 and the GENCTRLn.DIV value must be either 0 or 1. + // For SDI-12, we set this to 0 to divide by the value in the Division Factor bits + // (ie, 512) + ~GCLK_GENCTRL_OE) | // Bit 11 – OE Output Enable + // ^^ This bit is used to output the Generator clock output to the corresponding pin + // (GCLK_IO[7..0]), as long as GCLK_IOn is not defined as the Generator source in + // the GENCTRLn.SRC bit field. + // For SDI-12, we don't need to enable output + // GCLK_GENCTRL_OOV | // Bit 10 – OOV Output Off Value + // ^^ This bit is used to control the clock output value on pin (GCLK_IO[7..0]) when + // the Generator is turned off or the OE bit is zero, as long as GCLK_IOn is not + // defined as the Generator source in the GENCTRLn.SRC bit field. + // For SDI-12, we don't need to enable output or have an output value + (GCLK_GENCTRL_IDC | // Bit 9 = Improve Duty Cycle + // ^^ This bit is used to improve the duty cycle of the + // Generator output to 50/50 for odd division factors. For + // SDI-12, set the generator output clock duty cycle to 50/50 + GCLK_GENCTRL_GENEN | // Bit 8 Generator Enable + // ^^ This bit is used to enable and disable the Generator. + // Enable the generator! + // Bits 7:4 are reserved + GCLK_GENCTRL_SRC( + GCLK_GENCTRL_SRC_OSCULP32K) // Bits 3:0 Generator Clock Source Selection + // ^^ These bits select the Generator clock source, as shown in this table. (See + // datasheet table 14-3) + // MAIN_CLOCK_SOURCE = GCLK_GENCTRL_SRC_DPLL0 = 120 MHz primary clock + // GCLK_GENCTRL_SRC_OSCULP32K = 32 kHz Ultra Low Power Internal Oscillator + // (OSCULP32K) + // GCLK_GENCTRL_SRC_XOSC32K = 32 kHz External Crystal Oscillator (XOSC32K) + // For SDI-12, let's use the internal oscillator, just in case the board is + // crystalless (without an external oscillator) + ); + + while (GCLK->SYNCBUSY.reg & GENERIC_CLOCK_GENERATOR_SDI12_SYNC) { + /* Wait for synchronization */ + } + + // Enable peripheral control of the Timer Controller from the generic clock generator + GCLK->PCHCTRL[SDI12_TC_GCLK_ID].reg = + // Bit 7 – WRTLOCK Write Lock + // ^^ After this bit is set to '1', further writes to the PCHCTRLm register will be + // discarded. The control register of the corresponding Generator n (GENCTRLn), as + // assigned in PCHCTRLm.GEN, will also be locked. It can only be unlocked by a Power + // Reset. + // For SDI-12, ignore this bit + GCLK_PCHCTRL_CHEN | + // Bit 6 – CHEN Channel Enable + // ^^ This bit is used to enable and disable a Peripheral Channel. + // We're enabling the channel + // Bits 5:4 are reserved + GENERIC_CLOCK_GENERATOR_SDI12 + // Bits 3:0 – GEN[3:0] Generator Selection + // ^^ This bit field selects the Generator to be used as the source of a peripheral + // clock, as shown in the table 14-7 of the datasheet + // For SDI-12, we select generic clock generator 6 + ; + + // configure the control register for the timer control + SDI12_TC->COUNT16.CTRLA.reg = ~TC_CTRLA_CAPTEN0 & // Disable capture for channel 0 + ~TC_CTRLA_CAPTEN1 & // Disable capture for channel 1 + ~TC_CTRLA_RUNSTDBY & // Disable run on standby + (TC_CTRLA_PRESCALER_DIV16 | // Set prescaler to 16 + TCC_CTRLA_PRESCSYNC_GCLK | // Reload or reset the counter on next generic clock + TC_CTRLA_MODE_COUNT16 | // Put the timer TC3 into 16-bit mode + TC_CTRLA_ENABLE // Enable TC3 + ); + WAIT_TC16_REGS_SYNC(SDI12_TC) +} + +// NOT resetting the SAMD timer settings +void SDI12Timer::resetSDI12TimerPrescale(void) { + resetTC(SDI12_TC); + + // Disable generic clock generator + GCLK->GENCTRL[GENERIC_CLOCK_GENERATOR_SDI12].reg = + ~GCLK_GENCTRL_GENEN; // Disable the generic clock control + + while (GCLK->SYNCBUSY.reg & GENERIC_CLOCK_GENERATOR_SDI12_SYNC) { + /* Wait for synchronization */ + } +} + // Espressif ESP32/ESP8266 boards // #elif defined(ESP32) || defined(ESP8266) @@ -279,9 +462,7 @@ void SDI12Timer::configSDI12TimerPrescale(void) {} void SDI12Timer::resetSDI12TimerPrescale(void) {} sdi12timer_t ESPFAMILY_USE_INSTRUCTION_RAM SDI12Timer::SDI12TimerRead(void) { - // Its a one microsecond clock but we want 64uS ticks so divide by 64 i.e. right shift - // 6 - return ((sdi12timer_t)(micros() >> 6)); + return ((sdi12timer_t)(micros())); } // Unknown board diff --git a/src/SDI12_boards.h b/src/SDI12_boards.h index 0bfbe70..7b9d2b6 100644 --- a/src/SDI12_boards.h +++ b/src/SDI12_boards.h @@ -21,48 +21,12 @@ sensors. This library provides a general software solution, without requiring #if defined(ESP32) || defined(ESP8266) // On espressif boards (ESP8266 and ESP32), the ISR must be stored in IRAM #define ESPFAMILY_USE_INSTRUCTION_RAM IRAM_ATTR -/** The interger type (size) of the timer return value */ -typedef uint32_t sdi12timer_t; - #else #define ESPFAMILY_USE_INSTRUCTION_RAM -/** The interger type (size) of the timer return value */ -typedef uint8_t sdi12timer_t; #endif -/** - * @brief The class used to define the processor timer for the SDI-12 serial emulation. - */ -class SDI12Timer { - public: - /** - * @brief Construct a new SDI12Timer - */ - SDI12Timer(); - /** - * @brief Set the processor timer prescaler such that the 10 bits of an SDI-12 - * character are divided into the rollover time of the timer. - * - * @note The ESP32 and ESP8266 are fast enough processors that they can take the - * time to read the core 'micros()' function still complete the other processing - * needed on the serial bits. All of the other processors using the Arduino core also - * have the micros function, but the rest are not fast enough to waste the processor - * cycles to use the micros function and must manually configure the processor timer - * and use the faster assembly macros to read that processor timer directly. - */ - void configSDI12TimerPrescale(void); - /** - * @brief Reset the processor timer prescaler to whatever it was prior to being - * adjusted for this library. - * - * @note The prescaler is *NOT* set back to initial values for SAMD boards! On those - * processors, generic clock generator 4 will remain configured for SDI-12 until it is - * reset outside of this library. - */ - void resetSDI12TimerPrescale(void); // Most 'standard' AVR boards -// #if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) || \ defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || \ defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__) || \ @@ -70,295 +34,304 @@ class SDI12Timer { /** * @brief A string description of the timer to use + */ +#define TIMER_IN_USE_STR "Timer2" + +/** + * @brief The interger type of the timer. * - * Timer/Counter2 (TC2) is a general purpose, single channel, 8-bit Timer/Counter - * module. - * - * Features - * - Single Channel Counter - * - Clear Timer on Compare Match (Auto Reload) - * - Glitch-free, Phase Correct Pulse Width Modulator (PWM) - * - Frequency Generator - * - 10-bit Clock Prescaler - * - Overflow and Compare Match Interrupt Sources (TOV2, OCF2A, and OCF2B) - * - Allows Clocking from External 32kHz Watch Crystal Independent of the I/O Clock + * Timer 2 is an 8-bit timer */ -#define TIMER_IN_USE_STR "TCNT2" +#define TIMER_INT_TYPE uint8_t +#define TIMER_INT_SIZE 8 + /** - * @brief The c macro name for the assembly timer to use + * @brief The function or macro used to read the clock timer value. * - * The register used to access the timer/counter value is TCNT2 + * The c macro name for the register used to access the timer/counter 2 value is TCNT2 */ -#define TCNTX TCNT2 // Using Timer 2 +#define READTIME TCNT2 #if F_CPU == 16000000L /** * @brief A string description of the prescaler in use. */ -#define PRESCALE_IN_USE_STR "1024" +#define PRESCALE_IN_USE_STR "16MHz/1024=15.625kHz" /** - * @brief The number of "ticks" of the timer that occur within the timing of one bit at - * the SDI-12 baud rate of 1200 bits/second. + * @brief The number of clock ticks per second, after accounting for the prescaler. * * 16MHz / 1024 prescaler = 15624 'ticks'/sec = 64 µs / 'tick' - * (1 sec/1200 bits) * (1 tick/64 µs) = 13.0208 ticks/bit - * - * The 8-bit timer rolls over after 256 ticks, 19.66085 bits, or 16.38505 ms - * (256 ticks/roll-over) * (1 bit/13.0208 ticks) = 19.66085 bits - * (256 ticks/roll-over) * (1 sec/15624 ticks) = 16.38505 milliseconds */ -#define TICKS_PER_BIT 13 +#define TICKS_PER_SECOND 15624 + +#elif F_CPU == 12000000L /** - * @brief The number of "ticks" of the timer per SDI-12 bit, shifted by 2^10. - * - * 1/(13.0208 ticks/bit) * 2^10 = 78.6432 + * @brief A string description of the prescaler in use. */ -#define BITS_PER_TICK_Q10 79 +#define PRESCALE_IN_USE_STR "12MHz/1024=11.7kHz" /** - * @brief A "fudge factor" to get the Rx to work well. It mostly works to ensure that - * uneven tick increments get rounded up. + * @brief The number of clock ticks per second, after accounting for the prescaler. * - * @see https://github.com/SlashDevin/NeoSWSerial/pull/13 + * 12MHz / 1024 prescaler = 11719 'ticks'/sec = 85 µs / 'tick' */ -#define RX_WINDOW_FUDGE 2 +#define TICKS_PER_SECOND 11719 -#elif F_CPU == 12000000L +#elif F_CPU == 8000000L /** * @brief A string description of the prescaler in use. */ -#define PRESCALE_IN_USE_STR "1024" +#define PRESCALE_IN_USE_STR "8MHz/256=31.25kHz" /** - * @brief The number of "ticks" of the timer that occur within the timing of one bit at - * the SDI-12 baud rate of 1200 bits/second. + * @brief The number of clock ticks per second, after accounting for the prescaler. * - * 12MHz / 1024 prescaler = 11719 'ticks'/sec = 85 µs / 'tick' - * (1 sec/1200 bits) * (1 tick/85 µs) = 9.765625 ticks/bit - * - * The 8-bit timer rolls over after 256 ticks, 26.2144 bits, or 21.84487 ms - * (256 ticks/roll-over) * (1 bit/9.765625 ticks) = 26.2144 bits - * (256 ticks/roll-over) * (1 sec/11719 ticks) = 21.84487 milliseconds + * 8MHz / 256 prescaler = 31250 'ticks'/sec = 32 µs / 'tick' */ -#define TICKS_PER_BIT 10 +#define TICKS_PER_SECOND 31250 +#endif + + +// ATtiny boards (ie, adafruit trinket) +#elif defined(__AVR_ATtiny25__) | defined(__AVR_ATtiny45__) | defined(__AVR_ATtiny85__) + /** - * @brief The number of "ticks" of the timer per SDI-12 bit, shifted by 2^10. - * - * 1/(9.765625 ticks/bit) * 2^10 = 104.8576 + * @brief A string description of the timer to use */ -#define BITS_PER_TICK_Q10 105 +#define TIMER_IN_USE_STR "Timer1" /** - * @brief A "fudge factor" to get the Rx to work well. It mostly works to ensure that - * uneven tick increments get rounded up. + * @brief The interger type of the timer. * - * @see https://github.com/SlashDevin/NeoSWSerial/pull/13 + * Timer 1 is an 8-bit timer */ -#define RX_WINDOW_FUDGE 2 - -#elif F_CPU == 8000000L +#define TIMER_INT_TYPE uint8_t /** - * @brief A string description of the prescaler in use. + * @brief The number of clock ticks per second, after accounting for the prescaler. */ -#define PRESCALE_IN_USE_STR "256" +#define TIMER_INT_SIZE 8 + /** - * @brief The number of "ticks" of the timer that occur within the timing of one bit at - * the SDI-12 baud rate of 1200 bits/second. - * - * 8MHz / 256 prescaler = 31250 'ticks'/sec = 32 µs / 'tick' - * (1 sec/1200 bits) * (1 tick/32 µs) = 26.04166667 ticks/bit + * @brief The function or macro used to read the clock timer value. * - * The 8-bit timer rolls over after 256 ticks, 9.8304 bits, or 8.192 ms - * (256 ticks/roll-over) * (1 bit/26.04166667 ticks) = 9.8304 bits - * (256 ticks/roll-over) * (1 sec/31250 ticks) = 8.192 milliseconds - * @note The timer will roll-over with each character! + * The c macro name for the register used to access the timer/counter 1 value is TCNT1 */ -#define TICKS_PER_BIT 26 +#define READTIME TCNT2 + +#if F_CPU == 16000000L /** - * @brief The number of "ticks" of the timer per SDI-12 bit, shifted by 2^10. - * - * 1/(26.04166667 ticks/bit) * 2^10 = 39.3216 + * @brief A string description of the prescaler in use. */ -#define BITS_PER_TICK_Q10 39 +#define PRESCALE_IN_USE_STR "16MHz/1024=15.625kHz" /** - * @brief A "fudge factor" to get the Rx to work well. It mostly works to ensure that - * uneven tick increments get rounded up. + * @brief The number of clock ticks per second, after accounting for the prescaler. * - * @see https://github.com/SlashDevin/NeoSWSerial/pull/13 + * 16MHz / 1024 prescaler = 15625 'ticks'/sec = 15.625 kHz = 64 µs / 'tick' */ -#define RX_WINDOW_FUDGE 10 +#define TICKS_PER_SECOND 15625 - // #define PRESCALE_IN_USE_STR "1024" - // #define TICKS_PER_BIT 6 - // // 8MHz / 1024 prescaler = 31250 'ticks'/sec = 128 µs / 'tick' - // // (1 sec/1200 bits) * (1 tick/128 µs) = 6.5104166667 ticks/bit - // #define BITS_PER_TICK_Q10 157 - // // 1/(6.5104166667 ticks/bit) * 2^10 = 157.2864 - // #define RX_WINDOW_FUDGE 5 +#elif F_CPU == 8000000L +#define PRESCALE_IN_USE_STR "8MHz/512=15.625kHz" +/** + * @brief The number of clock ticks per second, after accounting for the prescaler. + * + * 8MHz / 512 prescaler = 15625 'ticks'/sec = 15.625 kHz = 64 µs / 'tick' + */ +#define TICKS_PER_SECOND 15625 #endif -// ATtiny boards (ie, adafruit trinket) +// Arduino Leonardo & Yun and other 32U4 boards // -#elif defined(__AVR_ATtiny25__) | defined(__AVR_ATtiny45__) | defined(__AVR_ATtiny85__) +#elif defined(ARDUINO_AVR_YUN) || defined(ARDUINO_AVR_LEONARDO) || \ + defined(__AVR_ATmega32U4__) /** * @brief A string description of the timer to use + */ +#define TIMER_IN_USE_STR "Timer4" + +/** + * @brief The interger type of the timer. * - * The Timer/Counter1 features a high resolution and a high accuracy usage with the - * lower prescaling opportunities. It can also support two accurate, high speed, 8-bit - * pulse width modulators using clock speeds up to 64MHz (or 32MHz in low speedmode). + * Timer 4 is an 10-bit timer, but we're only using the lower 8 bits */ -#define TIMER_IN_USE_STR "TCNT1" +#define TIMER_INT_TYPE uint8_t +#define TIMER_INT_SIZE 8 /** - * @brief The c macro name for the assembly timer to use + * @brief The function or macro used to read the clock timer value. + * + * The c macro name for the register used to access the timer/counter 4 value is TCNT4. * - * The register used to access the timer/counter value is TCNT1. + * @note We only utilize the low byte register, effectively using the 10-bit timer as an + * 8-bit timer. */ -#define TCNTX TCNT1 // Using Timer 1 +#define READTIME TCNT4 #if F_CPU == 16000000L /** * @brief A string description of the prescaler in use. */ -#define PRESCALE_IN_USE_STR "1024" +#define PRESCALE_IN_USE_STR "16MHz/1024=15.625kHz" /** - * @brief The number of "ticks" of the timer that occur within the timing of one bit at - * the SDI-12 baud rate of 1200 bits/second. + * @brief The number of clock ticks per second, after accounting for the prescaler. * * 16MHz / 1024 prescaler = 15624 'ticks'/sec = 64 µs / 'tick' - * (1 sec/1200 bits) * (1 tick/64 µs) = 13.0208 ticks/bit - * - * The 8-bit timer rolls over after 256 ticks, 19.66 bits, or 16.38505 ms - * (256 ticks/roll-over) * (1 bit/13.0208 ticks) = 19.66 bits - * (256 ticks/roll-over) * (1 sec/15624 ticks) = 16.38505 milliseconds */ -#define TICKS_PER_BIT 13 +#define TICKS_PER_SECOND 15625 + +#elif F_CPU == 8000000L /** - * @brief The number of "ticks" of the timer per SDI-12 bit, shifted by 2^10. - * - * 1/(13.0208 ticks/bit) * 2^10 = 78.6432 + * @brief A string description of the prescaler in use. */ -#define BITS_PER_TICK_Q10 79 +#define PRESCALE_IN_USE_STR "8MHz/512=15.625kHz" /** - * @brief A "fudge factor" to get the Rx to work well. It mostly works to ensure that - * uneven tick increments get rounded up. + * @brief The number of clock ticks per second, after accounting for the prescaler. * - * @see https://github.com/SlashDevin/NeoSWSerial/pull/13 + * 8MHz / 512 prescaler = 15624 'ticks'/sec = 64 µs / 'tick' */ -#define RX_WINDOW_FUDGE 2 +#define TICKS_PER_SECOND 15625 + +#endif + + +// Arduino Zero other SAMD21 boards +#elif defined(ARDUINO_SAMD_ZERO) || defined(__SAMD21G18A__) || \ + defined(__SAMD21J18A__) || defined(__SAMD21E18A__) -#elif F_CPU == 8000000L -#define PRESCALE_IN_USE_STR "512" /** - * @brief The number of "ticks" of the timer that occur within the timing of one bit at - * the SDI-12 baud rate of 1200 bits/second. + * @brief A string description of the timer to use * - * 8MHz / 512 prescaler = 15624 'ticks'/sec = 64 µs / 'tick' - * (1 sec/1200 bits) * (1 tick/64 µs) = 13.0208 ticks/bit + * For SDI-12, we'll use generic clock generator 4 * - * The 8-bit timer rolls over after 256 ticks, 19.66 bits, or 16.38505 ms - * (256 ticks/roll-over) * (1 bit/13.0208 ticks) = 19.66 bits - * (256 ticks/roll-over) * (1 sec/15624 ticks) = 16.38505 milliseconds + * The Adafruit Arduino core uses: + * - 0 as GENERIC_CLOCK_GENERATOR_MAIN (the main clock) + * + * For SDI-12, we'll use Timer Control 3 + * The Adafruit Arduino core uses: + * - TC5 for Tone + * - TC4 for Servo */ -#define TICKS_PER_BIT 13 +#define TIMER_IN_USE_STR "GCLK4-TC3" + /** - * @brief The number of "ticks" of the timer per SDI-12 bit, shifted by 2^10. + * @brief The interger type of the timer. * - * 1/(13.0208 ticks/bit) * 2^10 = 78.6432 + * We're using the timer in 16-bit mode */ -#define BITS_PER_TICK_Q10 79 +#define TIMER_INT_TYPE uint16_t +#define TIMER_INT_SIZE 16 + /** - * @brief A "fudge factor" to get the Rx to work well. It mostly works to ensure that - * uneven tick increments get rounded up. + * @brief The function or macro used to read the clock timer value. * - * @see https://github.com/SlashDevin/NeoSWSerial/pull/13 + * This signifies the register of timer/counter 3, the 16-bit count, the count value */ -#define RX_WINDOW_FUDGE 5 +#define READTIME REG_TC3_COUNT16_COUNT -#endif +/** + * @brief A string description of the prescaler in use. + */ +#define PRESCALE_IN_USE_STR "48MHz/5/16=600kHz" +/** + * @brief The number of clock ticks per second, after accounting for the prescaler. + * + * Start with 48MHz "main" clock source (GCLK_GENCTRL_SRC_DFLL48M) + * 48MHz / 5x clock source divider (GCLK_GENDIV_DIV(5)) = 9.6MHz + * 9.6MHz / 16x prescaler (TC_CTRLA_PRESCALER_DIV16) = 600kHz - 600,000 'ticks'/sec + */ +#define TICKS_PER_SECOND 600000 -// Arduino Leonardo & Yun and other 32U4 boards -// -#elif defined(ARDUINO_AVR_YUN) || defined(ARDUINO_AVR_LEONARDO) || \ - defined(__AVR_ATmega32U4__) +// SAMD51 and SAME51 boards +#elif defined(__SAMD51__) || defined(__SAME51__) /** * @brief A string description of the timer to use * - * Timer/Counter4 is a general purpose high speed Timer/Counter module, with three - * independent Output Compare Units, and with enhanced PWM support. + * For SDI-12, we'll use generic clock generator 6 + * For SDI-12, we'll use Timer Control 2 + */ +#define TIMER_IN_USE_STR "GCLK6-TC2" + +/** + * @brief The interger type of the timer. * - * Features - * - Up to 10-Bit Accuracy - * - Three Independent Output Compare Units - * - Clear Timer on Compare Match (Auto Reload) - * - Glitch Free, Phase and Frequency Correct Pulse Width Modulator (PWM) - * - Enhanced PWM mode: one optional additional accuracy bit without effect on output - * frequency - * - Variable PWM Period - * - Independent Dead Time Generators for each PWM channels - * - Synchronous update of PWM registers - * - Five Independent Interrupt Sources (TOV4, OCF4A, OCF4B, OCF4D, FPF4) - * - High Speed Asynchronous and Synchronous Clocking Modes - * - Separate Prescaler Unit + * We're using the timer in 16-bit mode + */ +#define TIMER_INT_TYPE uint16_t +#define TIMER_INT_SIZE 16 + +/** + * The clock generator number to use */ -#define TIMER_IN_USE_STR "TCNT4" +#define GENERIC_CLOCK_GENERATOR_SDI12 (6u) /** - * @brief The c macro name for the assembly timer to use + * @brief The bit to check for synchornization + */ +#define GENERIC_CLOCK_GENERATOR_SDI12_SYNC GCLK_SYNCBUSY_GENCTRL6 + +/** + * @brief The function or macro used to read the clock timer value. * - * The register used to access the low byte timer/counter value is TCNT4. - * @note We only utilize the low byte register, effectively using the 10-bit timer as an - * 8-bit timer. + * This signifies the register of timer/counter 2, the 16-bit count, the count value */ -#define TCNTX TCNT4 // Using Timer 4 +#define READTIME REG_TC2_COUNT16_COUNT -#if F_CPU == 16000000L /** * @brief A string description of the prescaler in use. */ -#define PRESCALE_IN_USE_STR "1024" +#define PRESCALE_IN_USE_STR "120MHz/25/8=600kHz" /** - * @brief The number of "ticks" of the timer that occur within the timing of one bit at - * the SDI-12 baud rate of 1200 bits/second. - * - * 16MHz / 1024 prescaler = 15624 'ticks'/sec = 64 µs / 'tick' - * (1 sec/1200 bits) * (1 tick/64 µs) = 13.0208 ticks/bit + * @brief The number of clock ticks per second, after accounting for the prescaler. * - * The first 8-bits of the 10-bit timer roll over after 256 ticks, 19.66 bits, - * or 16.38505 ms - * (256 ticks/roll-over) * (1 bit/13.0208 ticks) = 19.66 bits - * (256 ticks/roll-over) * (1 sec/15624 ticks) = 16.38505 milliseconds + * Start with 120MHz "main" clock source (MAIN_CLOCK_SOURCE = GCLK_GENCTRL_SRC_DPLL0) + * 120MHz / 25x clock source divider (GCLK_GENDIV_DIV(25)) = 4.8MHz + * 4.8MHz / 8x prescaler (TC_CTRLA_PRESCALER_DIV16) = 600kHz - 600,000 'ticks'/sec + * = 1.66667 µs / 'tick' (1 sec/1200 bits) * (1 tick/1.66667 µs) = 500 ticks/bit */ -#define TICKS_PER_BIT 13 +#define TICKS_PER_SECOND 600000 + +// Espressif ESP32/ESP8266 boards +// +#elif defined(ESP32) || defined(ESP8266) + /** - * @brief The number of "ticks" of the timer per SDI-12 bit, shifted by 2^10. - * - * 1/(13.0208 ticks/bit) * 2^10 = 78.6432 + * @brief A string description of the timer to use */ -#define BITS_PER_TICK_Q10 79 +#define TIMER_IN_USE_STR "micros" + /** - * @brief A "fudge factor" to get the Rx to work well. It mostly works to ensure that - * uneven tick increments get rounded up. + * @brief The interger type of the timer. * - * @see https://github.com/SlashDevin/NeoSWSerial/pull/13 + * Since we're using `micros()`, this is 32 bit */ -#define RX_WINDOW_FUDGE 2 +#define TIMER_INT_TYPE uint32_t +#define TIMER_INT_SIZE 32 + +#define TICKS_PER_SECOND 1000000 -#elif F_CPU == 8000000L /** - * @brief A string description of the prescaler in use. + * @brief The function or macro used to read the clock timer value. + * + * This signifies the register of timer/counter 2, the 16-bit count, the count value */ -#define PRESCALE_IN_USE_STR "512" +#define READTIME SDI12TimerRead + +// Unknown board +#else +#error "Please define your board timer and prescaler!" +#endif + + +#if TICKS_PER_SECOND == 15624 && TIMER_INT_SIZE == 8 /** - * @brief The number of "ticks" of the timer that occur within the timing of one bit - * at the SDI-12 baud rate of 1200 bits/second. + * @brief The number of "ticks" of the timer that occur within the timing of one bit at + * the SDI-12 baud rate of 1200 bits/second. * - * 8MHz / 512 prescaler = 15624 'ticks'/sec = 64 µs / 'tick' + * 15624 'ticks'/sec = 64 µs / 'tick' * (1 sec/1200 bits) * (1 tick/64 µs) = 13.0208 ticks/bit * - * The first 8-bits of the 10-bit timer roll over after 256 ticks, 19.66 bits, - * or 16.38505 ms - * (256 ticks/roll-over) * (1 bit/13.0208 ticks) = 19.66 bits + * The 8-bit timer rolls over after 256 ticks, 19.66085 bits, or 16.38505 ms + * (256 ticks/roll-over) * (1 bit/13.0208 ticks) = 19.66085 bits * (256 ticks/roll-over) * (1 sec/15624 ticks) = 16.38505 milliseconds */ #define TICKS_PER_BIT 13 @@ -369,153 +342,178 @@ class SDI12Timer { */ #define BITS_PER_TICK_Q10 79 /** - * @brief A "fudge factor" to get the Rx to work well. It mostly works to ensure that + * @brief A "fudge factor" to get the Rx to work well. It mostly works to ensure that * uneven tick increments get rounded up. * * @see https://github.com/SlashDevin/NeoSWSerial/pull/13 */ -#define RX_WINDOW_FUDGE 5 - -#endif - - -// Arduino Zero other SAMD21 boards -// -#elif defined(ARDUINO_SAMD_ZERO) || defined(ARDUINO_ARCH_SAMD) || \ - defined(__SAMD21G18A__) || defined(__SAMD21J18A__) || defined(__SAMD21E18A__) +#define RX_WINDOW_FUDGE 2 +#elif TICKS_PER_SECOND == 11719 && TIMER_INT_SIZE == 8 /** - * @brief A string description of the timer to use + * @brief The number of "ticks" of the timer that occur within the timing of one bit at + * the SDI-12 baud rate of 1200 bits/second. + * + * 11719 'ticks'/sec = 85 µs / 'tick' + * (1 sec/1200 bits) * (1 tick/85 µs) = 9.765625 ticks/bit * - * The Generic Clock controller GCLK provides nine Generic Clock Generators that can - * provide a wide range of clock frequencies. - * - * Generators can be set to use different external and internal oscillators as source. - * The clock of each Generator can be divided. The outputs from the Generators are used - * as sources for the Generic Clock Multiplexers, which provide the Generic Clock - * (GCLK_PERIPHERAL) to the peripheral modules, as shown in Generic Clock Controller - * Block Diagram. - * - * Features - * - Provides Generic Clocks - * - Wide frequency range - * - Clock source for the generator can be changed on the fly - * - * The TC consists of a counter, a prescaler, compare/capture channels and control - * logic. The counter can be set to count events, or it can be configured to count clock - * pulses. The counter, together with the compare/capture channels, can be configured to - * timestamp input events, allowing capture of frequency and pulse width. It can also - * perform waveform generation, such as frequency generation and pulse-width modulation - * (PWM). - * - * Features - * - Selectable configuration - * – Up to five 16-bit Timer/Counters (TC) including one low-power TC, each - * configurable as: - * - 8-bit TC with two compare/capture channels - * - 16-bit TC with two compare/capture channels - * - 32-bit TC with two compare/capture channels, by using two TCs - * - Waveform generation - * – Frequency generation - * – Single-slope pulse-width modulation - * - Input capture - * – Event capture - * – Frequency capture - * – Pulse-width capture - * - One input event - * - Interrupts/output events on: - * – Counter overflow/underflow - * – Compare match or capture - * - Internal prescaler - * - Can be used with DMA and to trigger DMA transactions + * The 8-bit timer rolls over after 256 ticks, 26.2144 bits, or 21.84487 ms + * (256 ticks/roll-over) * (1 bit/9.765625 ticks) = 26.2144 bits + * (256 ticks/roll-over) * (1 sec/11719 ticks) = 21.84487 milliseconds */ -#define TIMER_IN_USE_STR "GCLK4-TC3" +#define TICKS_PER_BIT 10 /** - * @brief The c macro name for the assembly timer to use + * @brief The number of "ticks" of the timer per SDI-12 bit, shifted by 2^10. * - * This signifies the register of timer/counter 3, the 8-bit count, the count value + * 1/(9.765625 ticks/bit) * 2^10 = 104.8576 */ -#define TCNTX REG_TC3_COUNT8_COUNT // Using Timer 3 with generic clock 4 - +#define BITS_PER_TICK_Q10 105 /** - * @brief A string description of the prescaler in use. + * @brief A "fudge factor" to get the Rx to work well. It mostly works to ensure that + * uneven tick increments get rounded up. + * + * @see https://github.com/SlashDevin/NeoSWSerial/pull/13 */ -#define PRESCALE_IN_USE_STR "3x1024" +#define RX_WINDOW_FUDGE 2 + + +#elif TICKS_PER_SECOND == 31250 && TIMER_INT_SIZE == 8 /** * @brief The number of "ticks" of the timer that occur within the timing of one bit at * the SDI-12 baud rate of 1200 bits/second. * - * 48MHz / 3 pre-prescaler = 16MHz - * 16MHz / 1024 prescaler = 15624 'ticks'/sec = 64 µs / 'tick' - * (1 sec/1200 bits) * (1 tick/64 µs) = 13.0208 ticks/bit + * 31250 'ticks'/sec = 32 µs / 'tick' + * (1 sec/1200 bits) * (1 tick/32 µs) = 26.04166667 ticks/bit * - * The 8-bit count rolls over after 256 ticks, 19.66 bits, or 16.38505 ms - * (256 ticks/roll-over) * (1 bit/13.0208 ticks) = 19.66 bits - * (256 ticks/roll-over) * (1 sec/15624 ticks) = 16.38505 milliseconds + * The 8-bit timer rolls over after 256 ticks, 9.8304 bits, or 8.192 ms + * (256 ticks/roll-over) * (1 bit/26.04166667 ticks) = 9.8304 bits + * (256 ticks/roll-over) * (1 sec/31250 ticks) = 8.192 milliseconds + * @note The timer will roll-over with each character! */ -#define TICKS_PER_BIT 13 +#define TICKS_PER_BIT 26 /** * @brief The number of "ticks" of the timer per SDI-12 bit, shifted by 2^10. * - * 1/(13.0208 ticks/bit) * 2^10 = 78.6432 + * 1/(26.04166667 ticks/bit) * 2^10 = 39.3216 */ -#define BITS_PER_TICK_Q10 79 +#define BITS_PER_TICK_Q10 39 /** - * @brief A "fudge factor" to get the Rx to work well. It mostly works to ensure that + * @brief A "fudge factor" to get the Rx to work well. It mostly works to ensure that * uneven tick increments get rounded up. * * @see https://github.com/SlashDevin/NeoSWSerial/pull/13 */ -#define RX_WINDOW_FUDGE 2 +#define RX_WINDOW_FUDGE 10 -// Espressif ESP32/ESP8266 boards -// -#elif defined(ESP32) || defined(ESP8266) - /** - * @brief Read the processor micros and right shift 6 bits (ie, divide by 64) to get a - * 64µs tick. - * - * @note The ESP32 and ESP8266 are fast enough processors that they can take the time - * to read the core 'micros()' function still complete the other processing needed on - * the serial bits. All of the other processors using the Arduino core also have the - * micros function, but the rest are not fast enough to waste the processor cycles to - * use the micros function and must use the faster assembly macros to read the - * processor timer directly. - * - * @return **sdi12timer_t** The current processor micros - */ - sdi12timer_t SDI12TimerRead(void); +#elif TICKS_PER_SECOND == 600000 && TIMER_INT_SIZE == 16 /** - * @brief The number of "ticks" of the timer that occur within the timing of one bit - * at the SDI-12 baud rate of 1200 bits/second. + * @brief The number of "ticks" of the timer that occur within the timing of one bit at + * the SDI-12 baud rate of 1200 bits/second. * - * 48MHz / 3 pre-prescaler = 16MHz - * 16MHz / 1024 prescaler = 15624 'ticks'/sec = 64 µs / 'tick' - * (1 sec/1200 bits) * (1 tick/64 µs) = 13.0208 ticks/bit + * 600000 'ticks'/sec = 1.66667 µs / 'tick' + * (1 sec/1200 bits) * (1 tick/1.66667 µs) = 500 ticks/bit * - * The 8-bit count rolls over after 256 ticks, 19.66 bits, or 16.38505 ms - * (256 ticks/roll-over) * (1 bit/13.0208 ticks) = 19.66 bits - * (256 ticks/roll-over) * (1 sec/15624 ticks) = 16.38505 microseconds + * The 16-bit timer rolls over after 65536 ticks, 131.072 bits, or 109.22667 ms + * (65536 ticks/roll-over) * (1 bit/500 ticks) = 131.0724 bits + * (65536 ticks/roll-over) * (1 sec/600000 ticks) = 109.22667 milliseconds */ -#define TICKS_PER_BIT 13 +#define TICKS_PER_BIT 500 /** - * @brief The number of "ticks" of the timer per SDI-12 bit, shifted by 2^10. + * @brief A "fudge factor" to get the Rx to work well. It mostly works to ensure that + * uneven tick increments get rounded up. * - * 1/(13.0208 ticks/bit) * 2^10 = 78.6432 + * @see https://github.com/SlashDevin/NeoSWSerial/pull/13 */ -#define BITS_PER_TICK_Q10 79 +#define RX_WINDOW_FUDGE 2 + +#elif TICKS_PER_SECOND == 1000000 && TTIMER_INT_SIZE == 32 /** - * @brief A "fudge factor" to get the Rx to work well. It mostly works to ensure that + * @brief The number of "ticks" of the timer that occur within the timing of one bit at + * the SDI-12 baud rate of 1200 bits/second. + * + * Using `micros()` 1 "tick" is 1 µsec + * (1 sec/1200 bits) * (1 tick/1 µs) * (1000000 µsec/sec)= 833.33333 ticks/bit + * + * The 32-bit timer rolls over after 4294967296 ticks, or 4294.9673 seconds + */ +#define TICKS_PER_BIT 834 +/** + * @brief A "fudge factor" to get the Rx to work well. It mostly works to ensure that * uneven tick increments get rounded up. * * @see https://github.com/SlashDevin/NeoSWSerial/pull/13 */ #define RX_WINDOW_FUDGE 2 -// Unknown board #else -#error "Please define your board timer and pins" +#error "Board timer is incorrectly configured!" +#endif + + +/** The interger type (size) of the timer return value */ +typedef TIMER_INT_TYPE sdi12timer_t; + +/** + * @brief The class used to define the processor timer for the SDI-12 serial emulation. + */ +class SDI12Timer { + public: + /** + * @brief Construct a new SDI12Timer + */ + SDI12Timer(); + + /** + * @brief static method for getting a 16-bit value from the multiplication of 2 8-bit + * values + * + * @param x The first 8 bit integer + * @param y The second 8 bit integer + * @return The result of the multiplication, as a 16 bit integer. + */ + static uint16_t mul8x8to16(uint8_t x, uint8_t y); + + /** + * @brief static method for calculating the number of bit-times that have elapsed + * between interrupts. + * + * @param dt The current value of the timer + * @return The number of bit times that have passed at 1200 baud. + * + * Adds a rxWindowWidth fudge factor to the time difference to get the number of + * ticks, and then multiplies the fudged ticks by the number of bits per tick. Uses + * the number of bits per tick shifted up by 2^10 and then shifts the result down by + * the same amount to compensate for the fact that the number of bits per tick is a + * decimal the timestamp is only an 8-bit integer. + * + * @see https://github.com/SlashDevin/NeoSWSerial/pull/13#issuecomment-315463522 + */ + static sdi12timer_t bitTimes(sdi12timer_t dt); + + /** + * @brief Set the processor timer prescaler such that the 10 bits of an SDI-12 + * character are divided into the rollover time of the timer. + */ + void configSDI12TimerPrescale(void); + /** + * @brief Reset the processor timer prescaler to whatever it was prior to being + * adjusted for this library. + * + * @note The prescaler is *NOT* set back to initial values for SAMD boards! On those + * processors. + */ + void resetSDI12TimerPrescale(void); + +// if we're using the `micros()` function +#if TICKS_PER_SECOND == 1000000 && TTIMER_INT_SIZE == 32 + /** + * @brief Read the processor micros and right shift 6 bits (ie, divide by 64) to get a + * 64µs tick. + * + * @return **sdi12timer_t** The current processor micros + */ + sdi12timer_t SDI12TimerRead(void); #endif }; From ff16d40caa56f47847e5a257f9a250337961f678 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Tue, 30 Jul 2024 17:12:26 -0400 Subject: [PATCH 47/96] Fix 15624 to 15625, some typos Signed-off-by: Sara Damiano --- src/SDI12_boards.cpp | 6 +++--- src/SDI12_boards.h | 29 ++++++++++++++--------------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/SDI12_boards.cpp b/src/SDI12_boards.cpp index b33226b..9da1d58 100644 --- a/src/SDI12_boards.cpp +++ b/src/SDI12_boards.cpp @@ -453,9 +453,9 @@ void SDI12Timer::resetSDI12TimerPrescale(void) { } } -// Espressif ESP32/ESP8266 boards -// -#elif defined(ESP32) || defined(ESP8266) +// Espressif ESP32/ESP8266 boards or other boards faster than 48MHz +// WARNING: I haven't tested the minimum speed that this will work at! +#elif defined(ESP32) || defined(ESP8266) || F_CPU >= 48000000L void SDI12Timer::configSDI12TimerPrescale(void) {} diff --git a/src/SDI12_boards.h b/src/SDI12_boards.h index 7b9d2b6..4e3432c 100644 --- a/src/SDI12_boards.h +++ b/src/SDI12_boards.h @@ -60,9 +60,9 @@ sensors. This library provides a general software solution, without requiring /** * @brief The number of clock ticks per second, after accounting for the prescaler. * - * 16MHz / 1024 prescaler = 15624 'ticks'/sec = 64 µs / 'tick' + * 16MHz / 1024 prescaler = 15625 'ticks'/sec = 64 µs / 'tick' */ -#define TICKS_PER_SECOND 15624 +#define TICKS_PER_SECOND 15625 #elif F_CPU == 12000000L /** @@ -72,7 +72,7 @@ sensors. This library provides a general software solution, without requiring /** * @brief The number of clock ticks per second, after accounting for the prescaler. * - * 12MHz / 1024 prescaler = 11719 'ticks'/sec = 85 µs / 'tick' + * 12MHz / 1024 prescaler = 11719 'ticks'/sec = 85.33 µs / 'tick' */ #define TICKS_PER_SECOND 11719 @@ -140,7 +140,6 @@ sensors. This library provides a general software solution, without requiring // Arduino Leonardo & Yun and other 32U4 boards -// #elif defined(ARDUINO_AVR_YUN) || defined(ARDUINO_AVR_LEONARDO) || \ defined(__AVR_ATmega32U4__) @@ -174,7 +173,7 @@ sensors. This library provides a general software solution, without requiring /** * @brief The number of clock ticks per second, after accounting for the prescaler. * - * 16MHz / 1024 prescaler = 15624 'ticks'/sec = 64 µs / 'tick' + * 16MHz / 1024 prescaler = 15625 'ticks'/sec = 64 µs / 'tick' */ #define TICKS_PER_SECOND 15625 @@ -186,7 +185,7 @@ sensors. This library provides a general software solution, without requiring /** * @brief The number of clock ticks per second, after accounting for the prescaler. * - * 8MHz / 512 prescaler = 15624 'ticks'/sec = 64 µs / 'tick' + * 8MHz / 512 prescaler = 15625 'ticks'/sec = 64 µs / 'tick' */ #define TICKS_PER_SECOND 15625 @@ -290,9 +289,9 @@ sensors. This library provides a general software solution, without requiring */ #define TICKS_PER_SECOND 600000 -// Espressif ESP32/ESP8266 boards -// -#elif defined(ESP32) || defined(ESP8266) +// Espressif ESP32/ESP8266 boards or any boards faster than 48MHz not mentioned above +// WARNING: I haven't tested the minimum speed that this will work at! +#elif defined(ESP32) || defined(ESP8266) || F_CPU >= 48000000L /** * @brief A string description of the timer to use @@ -314,7 +313,7 @@ sensors. This library provides a general software solution, without requiring * * This signifies the register of timer/counter 2, the 16-bit count, the count value */ -#define READTIME SDI12TimerRead +#define READTIME sdi12timer.SDI12TimerRead() // Unknown board #else @@ -322,17 +321,17 @@ sensors. This library provides a general software solution, without requiring #endif -#if TICKS_PER_SECOND == 15624 && TIMER_INT_SIZE == 8 +#if TICKS_PER_SECOND == 15625 && TIMER_INT_SIZE == 8 /** * @brief The number of "ticks" of the timer that occur within the timing of one bit at * the SDI-12 baud rate of 1200 bits/second. * - * 15624 'ticks'/sec = 64 µs / 'tick' + * 15625 'ticks'/sec = 64 µs / 'tick' * (1 sec/1200 bits) * (1 tick/64 µs) = 13.0208 ticks/bit * * The 8-bit timer rolls over after 256 ticks, 19.66085 bits, or 16.38505 ms * (256 ticks/roll-over) * (1 bit/13.0208 ticks) = 19.66085 bits - * (256 ticks/roll-over) * (1 sec/15624 ticks) = 16.38505 milliseconds + * (256 ticks/roll-over) * (1 sec/15625 ticks) = 16.38505 milliseconds */ #define TICKS_PER_BIT 13 /** @@ -427,7 +426,7 @@ sensors. This library provides a general software solution, without requiring */ #define RX_WINDOW_FUDGE 2 -#elif TICKS_PER_SECOND == 1000000 && TTIMER_INT_SIZE == 32 +#elif TICKS_PER_SECOND == 1000000 && TIMER_INT_SIZE == 32 /** * @brief The number of "ticks" of the timer that occur within the timing of one bit at * the SDI-12 baud rate of 1200 bits/second. @@ -506,7 +505,7 @@ class SDI12Timer { void resetSDI12TimerPrescale(void); // if we're using the `micros()` function -#if TICKS_PER_SECOND == 1000000 && TTIMER_INT_SIZE == 32 +#if TICKS_PER_SECOND == 1000000 && TIMER_INT_SIZE == 32 /** * @brief Read the processor micros and right shift 6 bits (ie, divide by 64) to get a * 64µs tick. From e35412fd744a7aea233301223fc936d35e8d05a8 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Thu, 1 Aug 2024 15:14:05 -0400 Subject: [PATCH 48/96] doc changes Signed-off-by: Sara Damiano --- docs/SupportedBoardsAndTimers.md | 86 ++++++++++++++++++----------- src/SDI12.cpp | 36 ++++++++++-- src/SDI12_boards.cpp | 13 ++++- src/SDI12_boards.h | 40 +++++++++++--- tools/TestCommands/TestCommands.ino | 2 +- 5 files changed, 129 insertions(+), 48 deletions(-) diff --git a/docs/SupportedBoardsAndTimers.md b/docs/SupportedBoardsAndTimers.md index 3fdd2ea..04b4879 100644 --- a/docs/SupportedBoardsAndTimers.md +++ b/docs/SupportedBoardsAndTimers.md @@ -3,48 +3,72 @@ This library listens for pin level changes and then use a timer to calculate how many databits have been sent since the last change and to convert that to a character. The speed of the timer is dependent on the speed of the processor and "dividers" and "prescalers" used to slow the effective clock. Unfortunately, the "ticks" of the processor clock aren't perfectly aligned with the times of the level changes from the SDI-12 device. -With the clocks not perfectly aligned, we can't know exactly the time that a bit started or ended, just the time of the closest tick. +With the clocks not perfectly aligned, we can't know exactly the time that a bit started or ended, just the time of the last readable tick. This means we need to do some averaging and "fudging" to align the two. @See @page rx_page for more information on how a character is created. SDI-12 Communicates at 1200 baud (bits/s) and sends each character using 10 bits (7E1). - - 1 bit = 0.83333 ms - - 1 character = 8.33333 ms + +- Character Times: + - 1 bit = 0.83333 ms (?? tolerance ??) + - 1 character = 8.33333 ms + - maximum marking between character stop (LOW) and next character start (HIGH) = 1.66ms (**no tolerance**) + - This is equivalent to 2 bits! + +- Break and Marking Times + - maximum sensor wake time (between a break and start bit) = 100ms ± 0.4ms + - minimum marking (LOW) return-to-sleep time = 100ms ± 0.4ms + - recorder break (HIGH) between commands = >12ms ± 0.4ms (ie, >12.5ms) + - maximum recorder marking (LOW) before a start bit (HIGH) = >8.33ms ± 0.4ms (ie, >8.73ms) + - maximum time before relinquishing line control after stop bit = 7.5ms ± 0.4ms + - maximum marking (LOW) before a new waking break (HIGH) must be issued = 87ms ± 0.4ms + - A break is also required when switching between sensors + +- Retry Times + - There are two retry "loops" - an "inner" loop of retries without breaks between and an "outer" loop of retries with breaks in between + - Inner Retries (*without* breaks between) + - Response window before a retry = 16.67ms - 87ms (< 87ms = time before a break is required) + - A minimum of 3 "inner" retries are required. + - At least one of the "inner" retries must start >100ms after the falling edge (end) of the break that started the innter retry loop. + - Outer Retries (*with* breaks between) + - Outer retries are used after >112.5ms of inner retries have been attempted + - A minimum of 3 "outer" retries are required. When setting up our timers, the goal is to be able to have as many ticks as possible for each bit. The more ticks we have, the better job we can do with the needed averaging and fudging. +With <10 ticks/bit, we probably won't be accurate enough to be functional. But, we also need to make sure that the clock timer doesn't roll over before the end of the 8.33ms required for each character. When acting as a recording device, it would be even better if the timer could last until the end of a 112.5ms retry timer before rolling over. +There is no benefit to the timer lasting longer than 112.5ms before rolling over. + +Each timer has finite options for pre-scaling, often in powers of 2. +To catch all the bits we need, when selecting the prescaler, we must round **UP** to the next closest available prescaler number (round **DOWN** the Hz) to give us more ticks than required. + +Using a 16 bit counter, the counter rolls after 65536 ticks. -Using a 16 bit counter, the counter rolls after 65535 ticks. - - Going for the maximum retry time, 112.5ms / 65536 ticks = 1.71661376953125 µsec/tick (582.54222 kHz) = 485.25767 ticks/bit - - This is *plenty* of ticks per bit! With a 16-bit timer, there's no reason to not use this whole period. - - For an 8MHz board, this is a 14x pre-scaler - - For a 12MHz board, this is a 21x pre-scaler - - For a 16MHz board, this is a 27x pre-scaler - - For a 20MHz board, this is a 35x pre-scaler - - Prescalers generally work in powers of 2, so 1024/512/256/128/64/8/4/2 - - Because we have to catch the full character, we need to round UP the prescaler number to give us more ticks than required. - - For a 8, 12, 16, or 20 Mhz board, this means a 64x pre-scaler - - For the faster SAMD-boards, we have more options with divisors and prescalers - - Going for the minimum 8.33ms per character, 8.33ms / 65536 ticks = 0.127105712890625 µsec/tick (7.86747 MHz) = 6553.6 ticks/bit - - This is overkill. +- Going for the maximum retry time, 112.5ms / 65536 ticks + - 1.71661376953125 µsec/tick, 582.54222 kHz + - 485.25767 ticks/bit + - This is *plenty* of ticks per bit! With a 16-bit timer, there's no reason to not use this whole period. +- Going for the minimum 8.33ms per character, 8.33ms / 65536 ticks + - 0.127105712890625 µsec/tick, 7.86747 MHz + - 6553.6 ticks/bit + - This is overkill. + +- Conclusion: With a 16-bit timer, select the smallest prescaler possible that keeps the speed *below* 582 kHz If we only have an 8 bit timer, the counter rolls after 256 ticks. - - Going for the maximum retry time, 112.5ms / 256 ticks = 1.89554 ticks/bit - - 0.439453125 msec/tick, 2.275 kHz - - This is no where near enough bits / tick for accuracy, so it is not possible to keep a timer running for the 112.5 ms retry with a 8-bit timer. - - Going for the minimum 8.33ms per character, 8.33ms / 256 ticks = 25.6 ticks/bit - - 0.0325390625 msec/tick, 30.73229 kHz - - For an 8MHz board, this is a 260x pre-scaler - - For a 12MHz board, this is a 390x pre-scaler - - For a 16MHz board, this is a 520x pre-scaler - - For a 20MHz board, this is a 651x pre-scaler - - Prescalers generally work in powers of 2, so 1024/512/256/128/64/8/4/2 - - Because we have to catch the full character, we need to round UP the prescaler number to give us more ticks than required. - - For a 8MHz or 12MHz board, this means a 512x pre-scaler - - For a 16MHz or 20MHz board, this is a 1024x pre-scaler + +- Going for the maximum retry time, 112.5ms / 256 ticks + - 0.439453125 msec/tick, 2.275 kHz + - 1.89554 ticks/bit + - This is no where near enough bits / tick for accuracy, so it is not possible to keep a timer running for the 112.5 ms retry with a 8-bit timer. +- Going for the minimum 8.33ms per character, 8.33ms / 256 ticks = 25.6 ticks/bit + - 0.0325390625 msec/tick, 30.73229 kHz + - 25.6 ticks/bit + +- Conclusion: With a 8-bit timer, select the smallest prescaler possible that keeps the speed *below* 30 kHz # Supported Processors and Timers @@ -242,8 +266,8 @@ The Adafruit Arduino core uses: For SDI-12, we'll use Timer Control 2 The Adafruit Arduino core uses: -- TC0 for Tone -- TC1 for Servo +- TC0 for Tone (though any other timer may be used, if another pin is selected) +- TC1 for Servo (though any other timer may be used, if another pin is selected) ## Other Boards diff --git a/src/SDI12.cpp b/src/SDI12.cpp index b484ac1..6697bcf 100644 --- a/src/SDI12.cpp +++ b/src/SDI12.cpp @@ -252,7 +252,6 @@ void SDI12::end() { setState(SDI12_DISABLED); _activeObject = nullptr; // Set the timer prescalers back to original values - // NOTE: This does NOT reset SAMD board pre-scalers! sdi12timer.resetSDI12TimerPrescale(); } @@ -389,7 +388,8 @@ void SDI12::forceListen() { } /* ================ Waking Up and Talking To Sensors ================================*/ -// this function wakes up the entire sensor bus +// this function wakes up the entire sensor bus by sending a 12ms break followed by 8.33 +// ms of marking void SDI12::wakeSensors(int8_t extraWakeTime) { setState(SDI12_TRANSMITTING); // Universal interrupts can be on while the break and marking happen because @@ -407,7 +407,27 @@ void SDI12::writeChar(uint8_t outChar) { uint8_t currentTxBitNum = 0; // first bit is start bit uint8_t bitValue = 1; // start bit is HIGH (inverse parity...) - noInterrupts(); // _ALL_ interrupts disabled so timing can't be shifted + // The tolerance on all SDI-12 commands is 0.40ms = 400µs. But... that's for between + // commands, and we don't know how accurate all sensors are, so we probably don't want + // to be off by more than 1/10 of that between bits. + + // Let's assume an interrupt routine can take up 1000 clock cycles. I don't know if + // that's reasonable, but per + // https://forum.arduino.cc/t/how-many-clock-cycles-does-digitalread-write-take/467153 + // a single digitalWrite function takes up 50 clock cycles so 20x that seems like a + // safe buffer. Our own SDI-12 receive ISR takes up roughly 617 clock cycles on a + // Mayfly. [Calculated using a modified version of + // https://github.com/SRGDamia1/avrcycles.] For the a 1000 clock cycle interrupt + // to not shift timing by more than 400µs the clock must have more than 40,000,000 + // cycles in one second (40MHz). For any board slower than 40MHz, we'll, disable _ALL_ + // interrupts during sending so timing can't be shifted. For faster boards, we + // can probably safely leave interrupts on. Disabling interrupts can screw up build-in + // functions like micros(), millis() and any real-time clocks, so we don't want to + // disable them if we don't really have to. + +#if F_CPU < 40000000UL + noInterrupts(); // _ALL_ interrupts disabled +#endif sdi12timer_t t0 = READTIME; // start time @@ -417,12 +437,18 @@ void SDI12::writeChar(uint8_t outChar) { // this gives us 833µs to calculate parity and position of last high bit currentTxBitNum++; + // Calculate parity, while writing the start bit + // This takes about 24 clock cycles on an AVR board (at 8MHz, that's 3µsec) + uint8_t parityBit = parity_even_bit(outChar); // Calculate the parity bit outChar |= (parityBit << 7); // Add parity bit to the outgoing character // Calculate the position of the last bit that is a 0/HIGH (ie, HIGH, not marking) // That bit will be the last time-critical bit. All bits after that can be // sent with interrupts enabled. + // This calculation should also finish while writing the start bit + // This takes at least 10+13 clock cycles, and up to 10+(13*9)= 127 clock cycles (at + // 8MHz, that's 15.875µsec) uint8_t lastHighBit = 9; // The position of the last bit that is a 0 (ie, HIGH, not marking) @@ -433,6 +459,8 @@ void SDI12::writeChar(uint8_t outChar) { } // Hold the line for the rest of the start bit duration + // We've used up roughly 150 clock cycles messing with parity, but a bit is 833µs, so + // we've got time. while ((sdi12timer_t)(READTIME - t0) < txBitWidth) {} t0 = READTIME; // advance start time @@ -447,7 +475,7 @@ void SDI12::writeChar(uint8_t outChar) { } // Hold the line for this bit duration while ((sdi12timer_t)(READTIME - t0) < txBitWidth) {} - t0 = READTIME; // start time + t0 = READTIME; // advance start time outChar = outChar >> 1; // shift character to expose the following bit } diff --git a/src/SDI12_boards.cpp b/src/SDI12_boards.cpp index 9da1d58..7719375 100644 --- a/src/SDI12_boards.cpp +++ b/src/SDI12_boards.cpp @@ -41,7 +41,6 @@ sdi12timer_t SDI12Timer::bitTimes(sdi12timer_t dt) { // Most 'standard' AVR boards -// #if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) || \ defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || \ defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__) || \ @@ -155,7 +154,6 @@ void SDI12Timer::resetSDI12TimerPrescale(void) { // Arduino Leonardo & Yun and other 32U4 boards -// #elif defined(ARDUINO_AVR_YUN) || defined(ARDUINO_AVR_LEONARDO) || \ defined(__AVR_ATmega32U4__) @@ -234,7 +232,6 @@ void SDI12Timer::resetSDI12TimerPrescale(void) { // Arduino Zero other SAMD21 boards -// #elif defined(ARDUINO_SAMD_ZERO) || defined(__SAMD21G18A__) || \ defined(__SAMD21J18A__) || defined(__SAMD21E18A__) @@ -367,29 +364,39 @@ void SDI12Timer::configSDI12TimerPrescale(void) { // configured to output to a dedicated GCLK_IOn pin. If GENCTRLn.OE is zero, this // bit has no effect and the generator will only be running if a peripheral // requires the clock. + // (0x1U << 13) = ~0b00000000000000000010000000000000 = + // 0b11111111111111111101111111111111 // For SDI-12, we do *not* run in standby ~GCLK_GENCTRL_DIVSEL & // Bit 12 – DIVSEL Divide Selection // ^^ This bit determines how the division factor of the clock source of the // Generator will be calculated from DIV. If the clock source should not be // divided, DIVSEL must be 0 and the GENCTRLn.DIV value must be either 0 or 1. + // (0x1U << 12) = ~0b00000000000000000001000000000000 + // 0b11111111111111111110111111111111 // For SDI-12, we set this to 0 to divide by the value in the Division Factor bits // (ie, 512) ~GCLK_GENCTRL_OE) | // Bit 11 – OE Output Enable // ^^ This bit is used to output the Generator clock output to the corresponding pin // (GCLK_IO[7..0]), as long as GCLK_IOn is not defined as the Generator source in // the GENCTRLn.SRC bit field. + // (0x1U << 11) = ~0b00000000000000000000100000000000 + // 0b11111111111111111111011111111111 // For SDI-12, we don't need to enable output // GCLK_GENCTRL_OOV | // Bit 10 – OOV Output Off Value // ^^ This bit is used to control the clock output value on pin (GCLK_IO[7..0]) when // the Generator is turned off or the OE bit is zero, as long as GCLK_IOn is not // defined as the Generator source in the GENCTRLn.SRC bit field. + // (0x1U << 10) = ~0b00000000000000000000010000000000 + // 0b11111111111111111111101111111111 // For SDI-12, we don't need to enable output or have an output value (GCLK_GENCTRL_IDC | // Bit 9 = Improve Duty Cycle // ^^ This bit is used to improve the duty cycle of the // Generator output to 50/50 for odd division factors. For // SDI-12, set the generator output clock duty cycle to 50/50 + // (0x1U << 9) = 0b00000000000000000000001000000000 GCLK_GENCTRL_GENEN | // Bit 8 Generator Enable // ^^ This bit is used to enable and disable the Generator. + // (0x1U << 8) = ~0b00000000000000000000000100000000 // Enable the generator! // Bits 7:4 are reserved GCLK_GENCTRL_SRC( diff --git a/src/SDI12_boards.h b/src/SDI12_boards.h index 4e3432c..5bc9b4d 100644 --- a/src/SDI12_boards.h +++ b/src/SDI12_boards.h @@ -223,6 +223,7 @@ sensors. This library provides a general software solution, without requiring * @brief The function or macro used to read the clock timer value. * * This signifies the register of timer/counter 3, the 16-bit count, the count value + * This is equivalent to TC3->COUNT16.COUNT.reg */ #define READTIME REG_TC3_COUNT16_COUNT @@ -247,7 +248,18 @@ sensors. This library provides a general software solution, without requiring * @brief A string description of the timer to use * * For SDI-12, we'll use generic clock generator 6 + * The Adafruit Arduino core uses: + * - 0 as GENERIC_CLOCK_GENERATOR_MAIN + * - 1 as GENERIC_CLOCK_GENERATOR_48M + * - 2 as GENERIC_CLOCK_GENERATOR_100M + * - 3 as GENERIC_CLOCK_GENERATOR_XOSC32K + * - 4 as GENERIC_CLOCK_GENERATOR_12M + * * For SDI-12, we'll use Timer Control 2 + * The Adafruit Arduino core uses: + * - TC0 as primary for Tone (any other timer could be used, depending on the pin) + * - TC1 for Servo (any other timer could be used, depending on the pin) + */ #define TIMER_IN_USE_STR "GCLK6-TC2" @@ -271,7 +283,9 @@ sensors. This library provides a general software solution, without requiring /** * @brief The function or macro used to read the clock timer value. * - * This signifies the register of timer/counter 2, the 16-bit count, the count value + * For the SAMD51, reading the timer is a multi-step process of first writing a read + * sync bit, waiting, and then reading the register. Because of the steps, we need a + * function. */ #define READTIME REG_TC2_COUNT16_COUNT @@ -290,7 +304,20 @@ sensors. This library provides a general software solution, without requiring #define TICKS_PER_SECOND 600000 // Espressif ESP32/ESP8266 boards or any boards faster than 48MHz not mentioned above -// WARNING: I haven't tested the minimum speed that this will work at! + +// From calculations using https://github.com/SRGDamia1/avrcycle, the micros() function +// takes 60 (!!) clock cycles. We're going to blindly assume that the micros() function +// takes up about the same number of clock cycles for all Arduino boards. This is +// probably a huge assumption, but go with it. If we're going to use micros() for +// timing, lets set a minimum usable CPU speed of the micros() function being accurate +// to 1µs. That means we need to get 60 ticks/1µs or 60MHz. Ehh.. Maybe we'll be +// generous and try it down to 48MHz. +// TODO: Test 48MHz + +// I know from testing, that we *cannot* use micros on a board 8MHz AVR board, but that +// it does work on a 80MHz Espressif8266. + +// WARNING: I haven't actullay tested the minimum speed that this will work at! #elif defined(ESP32) || defined(ESP8266) || F_CPU >= 48000000L /** @@ -503,17 +530,12 @@ class SDI12Timer { * processors. */ void resetSDI12TimerPrescale(void); - -// if we're using the `micros()` function -#if TICKS_PER_SECOND == 1000000 && TIMER_INT_SIZE == 32 /** - * @brief Read the processor micros and right shift 6 bits (ie, divide by 64) to get a - * 64µs tick. + * @brief A function to read the timer value, where a multi-step function is needed. * - * @return **sdi12timer_t** The current processor micros + * @return **sdi12timer_t** The current timer value */ sdi12timer_t SDI12TimerRead(void); -#endif }; #endif // SRC_SDI12_BOARDS_H_ diff --git a/tools/TestCommands/TestCommands.ino b/tools/TestCommands/TestCommands.ino index 0ed4b5b..d6cf424 100644 --- a/tools/TestCommands/TestCommands.ino +++ b/tools/TestCommands/TestCommands.ino @@ -657,7 +657,7 @@ void loop() { } min_wait = 800; // min_wait = max(10, min_wait / 2); - max_wait = max(1000L, max_wait + 1000L); + max_wait = max((uint32_t)1000, max_wait + (uint32_t)1000); Serial.print("minimum expected wait for all sensors: "); Serial.println(min_wait); Serial.print("maximum expected wait for all sensors: "); From 4160c9ed6c0b7ef24a0fcf0db9691ea8904c4dc4 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Thu, 1 Aug 2024 15:17:28 -0400 Subject: [PATCH 49/96] SAMD Prescaler changes, read fix Signed-off-by: Sara Damiano --- src/SDI12.cpp | 2 +- src/SDI12_boards.cpp | 298 ++++++++++++++++++++++++++++++------------- src/SDI12_boards.h | 52 +++----- 3 files changed, 228 insertions(+), 124 deletions(-) diff --git a/src/SDI12.cpp b/src/SDI12.cpp index 6697bcf..0325efa 100644 --- a/src/SDI12.cpp +++ b/src/SDI12.cpp @@ -714,7 +714,7 @@ void ESPFAMILY_USE_INSTRUCTION_RAM SDI12::receiveISR() { // data, parity, or stop bit. // Check how many bit times have passed since the last change - uint16_t rxBits = SDI12Timer::bitTimes((uint8_t)(thisBitTCNT - prevBitTCNT)); + uint16_t rxBits = SDI12Timer::bitTimes((sdi12timer_t)(thisBitTCNT - prevBitTCNT)); // Calculate how many *data+parity* bits should be left in the current character // - Each character has a total of 10 bits, 1 start bit, 7 data bits, 1 parity // bit, and 1 stop bit diff --git a/src/SDI12_boards.cpp b/src/SDI12_boards.cpp index 7719375..4859807 100644 --- a/src/SDI12_boards.cpp +++ b/src/SDI12_boards.cpp @@ -33,7 +33,7 @@ sdi12timer_t SDI12Timer::bitTimes(sdi12timer_t dt) { #elif TIMER_INT_SIZE == 16 || TIMER_INT_SIZE == 32 sdi12timer_t SDI12Timer::bitTimes(sdi12timer_t dt) { // divide the number of ticks by the ticks per bit - return (dt + RX_WINDOW_FUDGE) / TICKS_PER_BIT; + return (sdi12timer_t)((dt + (sdi12timer_t)RX_WINDOW_FUDGE) / (sdi12timer_t)TICKS_PER_BIT); } #else #error "Board timer is incorrectly configured!" @@ -236,37 +236,61 @@ void SDI12Timer::resetSDI12TimerPrescale(void) { defined(__SAMD21J18A__) || defined(__SAMD21E18A__) #define SDI12_TC TC3 -#define SDI12_TC_IRQn TC3_IRQn -#define SDI12_Handler TC3_Handler - -#define WAIT_TC16_REGS_SYNC(x) \ - while (x->COUNT16.STATUS.bit.SYNCBUSY) \ - ; +/// Fully reset the TC to factory settings and disable it static inline void resetTC(Tc* TCx) { // Disable TCx TCx->COUNT16.CTRLA.reg &= ~TC_CTRLA_ENABLE; - WAIT_TC16_REGS_SYNC(TCx) + while (TCx->COUNT16.STATUS.bit.SYNCBUSY) + ; // Reset TCx TCx->COUNT16.CTRLA.reg = TC_CTRLA_SWRST; - WAIT_TC16_REGS_SYNC(TCx) + while (TCx->COUNT16.STATUS.bit.SYNCBUSY) + ; while (TCx->COUNT16.CTRLA.bit.SWRST) ; } +/** + * @brief The value of generic clock generator divider register prior to being set for + * SDI-12. + * + * This is an 32-bit register located at 0x40000C00 + 0x8 + */ +static uint32_t preSDI12_REG_GCLK_GENDIV; +/** + * @brief The value of generic clock _generator_ control register prior to being set for + * SDI-12. + * + * This is an 32-bit register located at 0x40000C00 + 0x4 + */ +static uint32_t preSDI12_REG_GCLK_GENCTRL; +/** + * @brief The value of generic clock control register prior to being set for + * SDI-12. + * + * This is an 32-bit register located at 0x40000C00 + 0x2 + */ +static uint8_t preSDI12_REG_GCLK_CLKCTRL; + void SDI12Timer::configSDI12TimerPrescale(void) { - // Select generic clock generator 4 (Arduino core uses 0, 1, and 3. RTCZero uses 2) - // Many examples use clock generator 4.. consider yourself warned! - // I would use a higher clock number, but some of the cores don't include them for - // some reason + // read control register values prior to changes + preSDI12_REG_GCLK_GENDIV = REG_GCLK_GENDIV; + preSDI12_REG_GCLK_GENCTRL = REG_GCLK_GENCTRL; + preSDI12_REG_GCLK_CLKCTRL = REG_GCLK_CLKCTRL; + + // Set up the generic clock generator divisor register + // NOTE: Could write the below as GCLK->GENDIV.reg instead of REG_GCLK_GENDIV REG_GCLK_GENDIV = GCLK_GENDIV_ID(4) | // Select Generic Clock Generator 4 - GCLK_GENDIV_DIV(5); // Divide the clock source by divisor 5 - while (GCLK->STATUS.bit.SYNCBUSY) {} // Wait for synchronization - // NOTE: Could write the above as GCLK->GENDIV.reg instead of REG_GCLK_GENDIV + GCLK_GENDIV_DIV(6); // Divide the clock source by divisor 6 + while (GCLK->STATUS.bit.SYNCBUSY) + ; + ; // Wait for synchronization - // Write the generic clock generator 4 configuration + // Set up the generic clock generator control register + // NOTE: Could write the below as GCLK->GENCTRL.reg instead ofREG_GCLK_GENCTRL REG_GCLK_GENCTRL = (GCLK_GENCTRL_ID(4) | // Select GCLK4 GCLK_GENCTRL_SRC_DFLL48M | // Select the 48MHz clock source GCLK_GENCTRL_IDC | // Set the duty cycle to 50/50 HIGH/LOW @@ -274,90 +298,124 @@ void SDI12Timer::configSDI12TimerPrescale(void) { ~GCLK_GENCTRL_RUNSTDBY & // Do NOT run in stand by ~GCLK_GENCTRL_DIVSEL; // Divide clock source by GENDIV.DIV: 48MHz/5=9.6MHz // ^^ & ~ for DIVSEL to set DIVSEL to 0 - while (GCLK->STATUS.bit.SYNCBUSY) {} // Wait for synchronization - // NOTE: Could write the above as GCLK->GENCTRL.reg instead of REG_GCLK_GENCTRL - - // Feed GCLK4 to TC3 (also feeds to TCC2, the two must have the same source) - // TC3 (and TCC2) seem to be free, so I'm using them - // TC4 is used by Tone and Servo, TC5 is tied to the same clock as TC4 - // TC6 and TC7 are not available on all boards - REG_GCLK_CLKCTRL = GCLK_CLKCTRL_GEN_GCLK4 | // Select Generic Clock Generator 4 + while (GCLK->STATUS.bit.SYNCBUSY) + ; + ; // Wait for synchronization + + // Set up the generic clock control register + // NOTE: Could write the below as GCLK->CLKCTRL.reg instead of REG_GCLK_CLKCTRL + // Feed GCLK4 to TC3 (also feeds to TCC2, the two must have the same source). + // TC3 (and TCC2) seem to be free, so I'm using them. + // TC4 is used by Tone and Servo, TC5 is tied to the same clock as TC4. + // TC6 and TC7 are not available on all boards. + REG_GCLK_CLKCTRL = GCLK_CLKCTRL_GEN_GCLK4 | // Select Generic Clock Generator + // 4 GCLK_CLKCTRL_CLKEN | // Enable the generic clock generator GCLK_CLKCTRL_ID_TCC2_TC3; // Feed the Generic Clock Generator 4 to TCC2 and TC3 - // NOTE: Could write the above as GCLK->CLKCTRL.reg instead of REG_GCLK_CLKCTRL - while (GCLK->STATUS.bit.SYNCBUSY) {} // Wait for synchronization - ; + while (GCLK->STATUS.bit.SYNCBUSY) + ; + ; // Wait for synchronization + // fully software reset and disable the TC before we start messing with it + resetTC(SDI12_TC); + + // Set up the control register for Timer Controller 3 REG_TC3_CTRLA |= TC_CTRLA_PRESCALER_DIV16 | // Set prescaler to 16, 9.6MHz/16 = 600kHz TC_CTRLA_WAVEGEN_NFRQ | // Put the timer TC3 into normal frequency (NFRQ) mode TC_CTRLA_MODE_COUNT16 | // Put the timer TC3 into 16-bit mode TC_CTRLA_ENABLE; // Enable TC3 - - - while (TC3->COUNT16.STATUS.bit.SYNCBUSY) {} // Wait for synchronization + while (SDI12_TC->COUNT16.STATUS.bit.SYNCBUSY) + ; // Wait for synchronization } -// NOT resetting the SAMD timer settings void SDI12Timer::resetSDI12TimerPrescale(void) { + // reset the generic clock generator divisor register + REG_GCLK_GENDIV = preSDI12_REG_GCLK_GENDIV; + while (GCLK->STATUS.bit.SYNCBUSY) + ; // Wait for synchronization + + // reset the generic clock generator control register + REG_GCLK_GENCTRL = preSDI12_REG_GCLK_GENCTRL; + while (GCLK->STATUS.bit.SYNCBUSY) + ; // Wait for synchronization + + // reset the generic clock control register + REG_GCLK_CLKCTRL = preSDI12_REG_GCLK_CLKCTRL; + while (GCLK->STATUS.bit.SYNCBUSY) + ; // Wait for synchronization + + // fully software reset the control register for Timer Controller 3 and then disable + // it resetTC(SDI12_TC); - - // Disable generic clock generator - REG_GCLK_GENCTRL = GCLK_GENCTRL_ID(4) & // Select GCLK4 - ~GCLK_GENCTRL_GENEN; // Disable the generic clock control - while (GCLK->STATUS.bit.SYNCBUSY) {} // Wait for synchronization } // SAMD51 and SAME51 boards #elif defined(__SAMD51__) || defined(__SAME51__) - +/// The clock generator number to use +#define GENERIC_CLOCK_GENERATOR_SDI12 (6u) +/// The bit to check for synchronization +#define GCLK_SYNCBUSY_SDI12 GCLK_SYNCBUSY_GENCTRL6 +/// The timer controller to use #define SDI12_TC TC2 -#define SDI12_TC_IRQn TC2_IRQn +// The peripheral index within the generic clock for the selected timer controller #define SDI12_TC_GCLK_ID TC2_GCLK_ID -#define SDI12_Handler TC2_Handler - -#define WAIT_TC16_REGS_SYNC(x) \ - while (x->COUNT16.SYNCBUSY.bit.ENABLE) \ - ; +/// Fully reset the TC to factory settings and disable it static inline void resetTC(Tc* TCx) { // Disable TCx - TCx->COUNT16.CTRLA.reg &= ~TC_CTRLA_ENABLE; - WAIT_TC16_REGS_SYNC(TCx) + TCx->COUNT16.CTRLA.reg &= ~TC_CTRLA_ENABLE; // unset enable bit + while (TCx->COUNT16.SYNCBUSY.bit.ENABLE) + ; // wait for enable sync busy bit to clear + + // Reset TCx with SWRST (Software Reset) bit + // - Writing a '0' to this bit has no effect. + // - Writing a '1' to this bit resets all registers in the TC, except DBGCTRL, to + // their initial state, and the TC will be disabled. + // - Writing a '1' to CTRLA.SWRST will always take precedence; all other writes in the + // same write-operation will be discarded. - // Reset TCx TCx->COUNT16.CTRLA.reg = TC_CTRLA_SWRST; - WAIT_TC16_REGS_SYNC(TCx) - while (TCx->COUNT16.CTRLA.bit.SWRST) - ; + while (TCx->COUNT16.SYNCBUSY.bit.SWRST) + ; // wait for software reset busy bit to clear } +/** + * @brief The value of generic clock _generator_ control register prior to being set for + * SDI-12. + * + * This is an 32-bit register offset from the GCLK register by 0x20 + n*0x04 [n=0..11], + * where n is the generic clock number. + */ +static uint32_t preSDI12_REG_GCLK_GENCTRL; +/** + * @brief The value of generic clock peripheral control channel register prior to being + * set for SDI-12. + * + * This is an 32-bit register offset from the GCLK register by 0x80 + m*0x04 [m=0..47], + * where m is the peripheral channel number. + * + * @see docs/SAMD51PeripheralClocks.dox for a list of the peripheral channel numbers. + */ +static uint32_t preSDI12_REG_GCLK_PCHCTRL; + void SDI12Timer::configSDI12TimerPrescale(void) { - // Select generic clock generator 5 - // The Adafruit Arduino core uses: - // - 0 as GENERIC_CLOCK_GENERATOR_MAIN - // - 1 as GENERIC_CLOCK_GENERATOR_48M - // - 2 as GENERIC_CLOCK_GENERATOR_100M - // - 3 as GENERIC_CLOCK_GENERATOR_XOSC32K - // - 4 as GENERIC_CLOCK_GENERATOR_12M - - // Select Generic Clock instance - // The Adafruit Arduino core uses: - // TC0 for Tone - // TC1 for Servo - - // Set up the control register for the clock generator - GCLK->GENCTRL[GENERIC_CLOCK_GENERATOR_SDI12].reg = - // ^^ select the control register for generic clock 5 + // read the values of the registers prior to making changes + preSDI12_REG_GCLK_GENCTRL = GCLK->GENCTRL[GENERIC_CLOCK_GENERATOR_SDI12].reg; + preSDI12_REG_GCLK_PCHCTRL = GCLK->PCHCTRL[SDI12_TC_GCLK_ID].reg; + + // Calculate the new value for the generic clock _generator_ control register + uint32_t postSDI12_REG_GCLK_GENCTRL = (GCLK_GENCTRL_DIV( - 25) & // Bits 31:16 – DIV[15:0] Division Factor + 15) & // Bits 31:16 – DIV[15:0] Division Factor // ^^ These bits represent a division value for the corresponding // Generator. The actual division factor is dependent on the state of // DIVSEL. The number of relevant DIV bits for each Generator can be seen // in this table. Written bits outside of the specified range will be // ignored. See datasheet table 14-6 + // ((0xFFFFU << 16) & ((15) << 16)) = 0b00000000000011110000000000000000 // Bits 15:14 are reserved ~GCLK_GENCTRL_RUNSTDBY & // Bit 13 – RUNSTDBY Run in Standby // ^^ This bit is used to keep the Generator running in Standby as long as it is @@ -400,29 +458,31 @@ void SDI12Timer::configSDI12TimerPrescale(void) { // Enable the generator! // Bits 7:4 are reserved GCLK_GENCTRL_SRC( - GCLK_GENCTRL_SRC_OSCULP32K) // Bits 3:0 Generator Clock Source Selection + GCLK_GENCTRL_SRC_DPLL0) // Bits 3:0 Generator Clock Source Selection // ^^ These bits select the Generator clock source, as shown in this table. (See // datasheet table 14-3) // MAIN_CLOCK_SOURCE = GCLK_GENCTRL_SRC_DPLL0 = 120 MHz primary clock + // (0x7U << 0) = 0b00000000000000000000000000000111 // GCLK_GENCTRL_SRC_OSCULP32K = 32 kHz Ultra Low Power Internal Oscillator // (OSCULP32K) + // (0x3U << 0) = 0b00000000000000000000000000000011 // GCLK_GENCTRL_SRC_XOSC32K = 32 kHz External Crystal Oscillator (XOSC32K) - // For SDI-12, let's use the internal oscillator, just in case the board is - // crystalless (without an external oscillator) + // (0x5U << 0) = 0b00000000000000000000000000000101 ); - while (GCLK->SYNCBUSY.reg & GENERIC_CLOCK_GENERATOR_SDI12_SYNC) { - /* Wait for synchronization */ - } + // Set the generator control register for the clock generator for the selected clock + // generator source + GCLK->GENCTRL[GENERIC_CLOCK_GENERATOR_SDI12].reg = postSDI12_REG_GCLK_GENCTRL; + while (GCLK->SYNCBUSY.reg & GCLK_SYNCBUSY_SDI12) + ; // Wait for the SDI-12 clock generator sync busy bit to clear - // Enable peripheral control of the Timer Controller from the generic clock generator - GCLK->PCHCTRL[SDI12_TC_GCLK_ID].reg = + // Calculate the new value for the generic clock peripheral control channel register. + uint8_t postSDI12_REG_GCLK_PCHCTRL = // Bit 7 – WRTLOCK Write Lock - // ^^ After this bit is set to '1', further writes to the PCHCTRLm register will be - // discarded. The control register of the corresponding Generator n (GENCTRLn), as - // assigned in PCHCTRLm.GEN, will also be locked. It can only be unlocked by a Power - // Reset. - // For SDI-12, ignore this bit + // ^^ After this bit is set to '1', further writes to the PCHCTRLm register will + // be discarded. The control register of the corresponding Generator n (GENCTRLn), + // as assigned in PCHCTRLm.GEN, will also be locked. It can only be unlocked by a + // Power Reset. For SDI-12, ignore this bit GCLK_PCHCTRL_CHEN | // Bit 6 – CHEN Channel Enable // ^^ This bit is used to enable and disable a Peripheral Channel. @@ -435,29 +495,83 @@ void SDI12Timer::configSDI12TimerPrescale(void) { // For SDI-12, we select generic clock generator 6 ; + // Set the generic clock peripheral control channel register + GCLK->PCHCTRL[SDI12_TC_GCLK_ID].reg = postSDI12_REG_GCLK_PCHCTRL; + while (!GCLK->PCHCTRL[SDI12_TC_GCLK_ID].bit.CHEN) + ; // wait to finish enabling + + // fully software reset and disable the TC before we start messing with it + resetTC(SDI12_TC); + + // Set timer counter mode as normal PWM + SDI12_TC->COUNT16.WAVE.bit.WAVEGEN = TCC_WAVE_WAVEGEN_NPWM_Val; + // NOTE: This register isn't synced, so no wait needed + + // Set the timer to count up + // >> This bit is used to change the direction of the counter. + // >> Writing a '0' to this bit has no effect + // >> Writing a '1' to this bit will clear the bit and make the counter count up. + // SRGD Note: Writing to 1 actually flips the direction from whatever it was, not + // reset it to up + if (SDI12_TC->COUNT16.CTRLBSET.bit + .DIR) { // check the current direction first (0=counting up, 1=counting down) + SDI12_TC->COUNT16.CTRLBSET.bit.DIR = 1; + while (SDI12_TC->COUNT16.SYNCBUSY.bit.CTRLB) + ; // wait the control B sync busy bit to clear + } + // configure the control register for the timer control - SDI12_TC->COUNT16.CTRLA.reg = ~TC_CTRLA_CAPTEN0 & // Disable capture for channel 0 - ~TC_CTRLA_CAPTEN1 & // Disable capture for channel 1 - ~TC_CTRLA_RUNSTDBY & // Disable run on standby - (TC_CTRLA_PRESCALER_DIV16 | // Set prescaler to 16 + uint32_t postSDI12_REG_TC_CTRLA = + ~TC_CTRLA_CAPTEN0 & // Disable capture for channel 0 + ~TC_CTRLA_CAPTEN1 & // Disable capture for channel 1 + ~TC_CTRLA_RUNSTDBY & // Disable run on standby + (TC_CTRLA_PRESCALER_DIV16 | // Set prescaler to 16 TCC_CTRLA_PRESCSYNC_GCLK | // Reload or reset the counter on next generic clock TC_CTRLA_MODE_COUNT16 | // Put the timer TC3 into 16-bit mode TC_CTRLA_ENABLE // Enable TC3 ); - WAIT_TC16_REGS_SYNC(SDI12_TC) + + SDI12_TC->COUNT16.CTRLA.reg = postSDI12_REG_TC_CTRLA; + while (SDI12_TC->COUNT16.SYNCBUSY.bit.ENABLE) + ; // wait for to finish enabling } -// NOT resetting the SAMD timer settings void SDI12Timer::resetSDI12TimerPrescale(void) { + // Reset the generator control register for the clock generator + GCLK->GENCTRL[GENERIC_CLOCK_GENERATOR_SDI12].reg = preSDI12_REG_GCLK_GENCTRL; + while (GCLK->SYNCBUSY.reg & GCLK_SYNCBUSY_SDI12) + ; // Wait for the SDI-12 clock generator sync busy bit to clear + + // Reset the generic clock peripheral control channel register + GCLK->PCHCTRL[SDI12_TC_GCLK_ID].reg = preSDI12_REG_GCLK_PCHCTRL; + while (!GCLK->PCHCTRL[SDI12_TC_GCLK_ID].bit.CHEN) + ; // wait to finish enabling ?? + + // fully software reset the control register for SDI-12 Timer Controller and then + // disable it resetTC(SDI12_TC); +} - // Disable generic clock generator - GCLK->GENCTRL[GENERIC_CLOCK_GENERATOR_SDI12].reg = - ~GCLK_GENCTRL_GENEN; // Disable the generic clock control +sdi12timer_t SDI12Timer::SDI12TimerRead(void) { + // Note from datasheet: Prior to any read access, this register must be synchronized + // by user by writing the according TC Command value to the Control B Set register + // (CTRLBSET.CMD=READSYNC) + // see: + // https://onlinedocs.microchip.com/oxy/GUID-F5813793-E016-46F5-A9E2-718D8BCED496-en-US-13/GUID-5033DFD7-EB2D-4870-AE98-D40CADB0531E.html - while (GCLK->SYNCBUSY.reg & GENERIC_CLOCK_GENERATOR_SDI12_SYNC) { - /* Wait for synchronization */ - } + // Code taken from Microchip article on how to read the tiemr value + // https://microchip.my.site.com/s/article/SAM-D5x-E5x--Reading-TC-TCC-COUNT-register + + + // write READSYNC command to the Control B Set register + SDI12_TC->COUNT16.CTRLBSET.reg = TC_CTRLBSET_CMD_READSYNC; + + // wait for the CMD bits in CTRLBSET to be cleared, meaning the CMD has been executed + while (SDI12_TC->COUNT16.CTRLBSET.reg & TC_CTRLBSET_CMD_READSYNC) + ; + + // read the COUNT register + return SDI12_TC->COUNT16.COUNT.reg; } // Espressif ESP32/ESP8266 boards or other boards faster than 48MHz diff --git a/src/SDI12_boards.h b/src/SDI12_boards.h index 5bc9b4d..26457a3 100644 --- a/src/SDI12_boards.h +++ b/src/SDI12_boards.h @@ -230,15 +230,16 @@ sensors. This library provides a general software solution, without requiring /** * @brief A string description of the prescaler in use. */ -#define PRESCALE_IN_USE_STR "48MHz/5/16=600kHz" +#define PRESCALE_IN_USE_STR "48MHz/6/16=500kHz" /** * @brief The number of clock ticks per second, after accounting for the prescaler. * * Start with 48MHz "main" clock source (GCLK_GENCTRL_SRC_DFLL48M) - * 48MHz / 5x clock source divider (GCLK_GENDIV_DIV(5)) = 9.6MHz - * 9.6MHz / 16x prescaler (TC_CTRLA_PRESCALER_DIV16) = 600kHz - 600,000 'ticks'/sec + * 48MHz / 6x clock source divider (GCLK_GENDIV_DIV(6)) = 8MHz + * 8MHz / 16x prescaler (TC_CTRLA_PRESCALER_DIV16) = 500kHz = 500,000 'ticks'/sec = 2 + * µs / 'tick' (1 sec/1200 bits) * (1 tick/2 µs) = 416.66667 ticks/bit */ -#define TICKS_PER_SECOND 600000 +#define TICKS_PER_SECOND 500000 // SAMD51 and SAME51 boards @@ -271,15 +272,6 @@ sensors. This library provides a general software solution, without requiring #define TIMER_INT_TYPE uint16_t #define TIMER_INT_SIZE 16 -/** - * The clock generator number to use - */ -#define GENERIC_CLOCK_GENERATOR_SDI12 (6u) -/** - * @brief The bit to check for synchornization - */ -#define GENERIC_CLOCK_GENERATOR_SDI12_SYNC GCLK_SYNCBUSY_GENCTRL6 - /** * @brief The function or macro used to read the clock timer value. * @@ -287,21 +279,21 @@ sensors. This library provides a general software solution, without requiring * sync bit, waiting, and then reading the register. Because of the steps, we need a * function. */ -#define READTIME REG_TC2_COUNT16_COUNT +#define READTIME sdi12timer.SDI12TimerRead() /** * @brief A string description of the prescaler in use. */ -#define PRESCALE_IN_USE_STR "120MHz/25/8=600kHz" +#define PRESCALE_IN_USE_STR "120MHz/15/16=500kHz" /** * @brief The number of clock ticks per second, after accounting for the prescaler. * * Start with 120MHz "main" clock source (MAIN_CLOCK_SOURCE = GCLK_GENCTRL_SRC_DPLL0) - * 120MHz / 25x clock source divider (GCLK_GENDIV_DIV(25)) = 4.8MHz - * 4.8MHz / 8x prescaler (TC_CTRLA_PRESCALER_DIV16) = 600kHz - 600,000 'ticks'/sec - * = 1.66667 µs / 'tick' (1 sec/1200 bits) * (1 tick/1.66667 µs) = 500 ticks/bit + * 120MHz / 15x clock source divider (GCLK_GENCTRL_DIV(15)) = 8MHz + * 8MHz / 16x prescaler (TC_CTRLA_PRESCALER_DIV16) = 500kHz = 500,000 'ticks'/sec = 2 µs + * / 'tick' (1 sec/1200 bits) * (1 tick/2 µs) = 416.66667 ticks/bit */ -#define TICKS_PER_SECOND 600000 +#define TICKS_PER_SECOND 500000 // Espressif ESP32/ESP8266 boards or any boards faster than 48MHz not mentioned above @@ -337,8 +329,6 @@ sensors. This library provides a general software solution, without requiring /** * @brief The function or macro used to read the clock timer value. - * - * This signifies the register of timer/counter 2, the 16-bit count, the count value */ #define READTIME sdi12timer.SDI12TimerRead() @@ -400,7 +390,7 @@ sensors. This library provides a general software solution, without requiring * * @see https://github.com/SlashDevin/NeoSWSerial/pull/13 */ -#define RX_WINDOW_FUDGE 2 +#define RX_WINDOW_FUDGE 1 #elif TICKS_PER_SECOND == 31250 && TIMER_INT_SIZE == 8 @@ -432,26 +422,26 @@ sensors. This library provides a general software solution, without requiring #define RX_WINDOW_FUDGE 10 -#elif TICKS_PER_SECOND == 600000 && TIMER_INT_SIZE == 16 +#elif TICKS_PER_SECOND == 500000 && TIMER_INT_SIZE == 16 /** * @brief The number of "ticks" of the timer that occur within the timing of one bit at * the SDI-12 baud rate of 1200 bits/second. * - * 600000 'ticks'/sec = 1.66667 µs / 'tick' - * (1 sec/1200 bits) * (1 tick/1.66667 µs) = 500 ticks/bit + * 500kHz = 500,000 'ticks'/sec = 2 µs / 'tick' + * (1 sec/1200 bits) * (1 tick/2 µs) = 416.66667 ticks/bit * - * The 16-bit timer rolls over after 65536 ticks, 131.072 bits, or 109.22667 ms - * (65536 ticks/roll-over) * (1 bit/500 ticks) = 131.0724 bits - * (65536 ticks/roll-over) * (1 sec/600000 ticks) = 109.22667 milliseconds + * The 16-bit timer rolls over after 65536 ticks, 157.284 bits, or 131.07 ms + * (65536 ticks/roll-over) * (1 bit/416.66667 ticks) = 157.284 bits + * (65536 ticks/roll-over) * (1 sec/500000 ticks) = 131.07 milliseconds */ -#define TICKS_PER_BIT 500 +#define TICKS_PER_BIT 417 /** * @brief A "fudge factor" to get the Rx to work well. It mostly works to ensure that * uneven tick increments get rounded up. * * @see https://github.com/SlashDevin/NeoSWSerial/pull/13 */ -#define RX_WINDOW_FUDGE 2 +#define RX_WINDOW_FUDGE 11 #elif TICKS_PER_SECOND == 1000000 && TIMER_INT_SIZE == 32 /** @@ -470,7 +460,7 @@ sensors. This library provides a general software solution, without requiring * * @see https://github.com/SlashDevin/NeoSWSerial/pull/13 */ -#define RX_WINDOW_FUDGE 2 +#define RX_WINDOW_FUDGE 1 #else #error "Board timer is incorrectly configured!" From c59aac41032e17ff68d4fc448712554fa27f9c53 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Thu, 1 Aug 2024 15:21:21 -0400 Subject: [PATCH 50/96] condense avr prescaler settings Signed-off-by: Sara Damiano --- src/SDI12_boards.cpp | 93 +++++++++----------------------------------- 1 file changed, 18 insertions(+), 75 deletions(-) diff --git a/src/SDI12_boards.cpp b/src/SDI12_boards.cpp index 4859807..be8cc57 100644 --- a/src/SDI12_boards.cpp +++ b/src/SDI12_boards.cpp @@ -55,47 +55,30 @@ static uint8_t preSDI12_TCCR2A; */ static uint8_t preSDI12_TCCR2B; -#if F_CPU == 16000000L +sdi12timer_t SDI12Timer::SDI12TimerRead(void) { + return TCNT2; +} void SDI12Timer::configSDI12TimerPrescale(void) { preSDI12_TCCR2A = TCCR2A; preSDI12_TCCR2B = TCCR2B; + +#if F_CPU == 16000000L TCCR2A = 0x00; // TCCR2A = 0x00 = "normal" operation - Normal port operation, OC2A & // OC2B disconnected TCCR2B = 0x07; // TCCR2B = 0x07 = 0b00000111 - Clock Select bits 22, 21, & 20 on - // prescaler set to CK/1024 -} - -void SDI12Timer::resetSDI12TimerPrescale(void) { - TCCR2A = preSDI12_TCCR2A; - TCCR2B = preSDI12_TCCR2B; -} - #elif F_CPU == 12000000L - -void SDI12Timer::configSDI12TimerPrescale(void) { - preSDI12_TCCR2A = TCCR2A; - preSDI12_TCCR2B = TCCR2B; TCCR2A = 0x00; // TCCR2A = 0x00 = "normal" operation - Normal port operation, OC2A & // OC2B disconnected TCCR2B = 0x07; // TCCR2B = 0x07 = 0b00000111 - Clock Select bits 22, 21, & 20 on - // prescaler set to CK/1024 -} - -void SDI12Timer::resetSDI12TimerPrescale(void) { - TCCR2A = preSDI12_TCCR2A; - TCCR2B = preSDI12_TCCR2B; -} - #elif F_CPU == 8000000L - -void SDI12Timer::configSDI12TimerPrescale(void) { - preSDI12_TCCR2A = TCCR2A; - preSDI12_TCCR2B = TCCR2B; TCCR2A = 0x00; // TCCR2A = 0x00 = "normal" operation - Normal port operation, OC2A & // OC2B disconnected TCCR2B = 0x06; // TCCR2B = 0x06 = 0b00000110 - Clock Select bits 22 & 20 on - // prescaler set to CK/256 +#endif } void SDI12Timer::resetSDI12TimerPrescale(void) { @@ -103,60 +86,38 @@ void SDI12Timer::resetSDI12TimerPrescale(void) { TCCR2B = preSDI12_TCCR2B; } -// void SDI12Timer::configSDI12TimerPrescale(void) -// { -// preSDI12_TCCR2A = TCCR2A; -// preSDI12_TCCR2B = TCCR2B; -// TCCR2A = 0x00; // TCCR2A = 0x00 = "normal" operation - Normal port operation, -// OC2A & OC2B disconnected TCCR2B = 0x07; // TCCR2B = 0x07 = 0b00000111 - Clock -// Select bits 22, 21, & 20 on - prescaler set to CK/1024 -// } -// void SDI12Timer::resetSDI12TimerPrescale(void) -// { -// TCCR2A = preSDI12_TCCR2A; -// TCCR2B = preSDI12_TCCR2B; -// } -#endif - - // ATtiny boards (ie, adafruit trinket) -// #elif defined(__AVR_ATtiny25__) | defined(__AVR_ATtiny45__) | defined(__AVR_ATtiny85__) +sdi12timer_t SDI12Timer::SDI12TimerRead(void) { + return TCNT1; +} /** * @brief The value of timer control register 1A prior to being set for SDI-12. */ static uint8_t preSDI12_TCCR1A; -#if F_CPU == 16000000L - void SDI12Timer::configSDI12TimerPrescale(void) { preSDI12_TCCR1A = TCCR1; +#if F_CPU == 16000000L TCCR1 = 0b00001011; // Set the prescaler to 1024 -} - -void SDI12Timer::resetSDI12TimerPrescale(void) { - TCCR1 = preSDI12_TCCR1A; -} - - #elif F_CPU == 8000000L - -void SDI12Timer::configSDI12TimerPrescale(void) { - preSDI12_TCCR1A = TCCR1; - TCCR1 = 0b00001010; // Set the prescaler to 512 + TCCR1 = 0b00001010; // Set the prescaler to 512 +#endif } void SDI12Timer::resetSDI12TimerPrescale(void) { TCCR1 = preSDI12_TCCR1A; } -#endif - // Arduino Leonardo & Yun and other 32U4 boards #elif defined(ARDUINO_AVR_YUN) || defined(ARDUINO_AVR_LEONARDO) || \ defined(__AVR_ATmega32U4__) +sdi12timer_t SDI12Timer::SDI12TimerRead(void) { + return TCNT4; +} + /** * @brief The value of timer control register 4A prior to being set for SDI-12. */ @@ -178,14 +139,13 @@ static uint8_t preSDI12_TCCR4D; */ static uint8_t preSDI12_TCCR4E; -#if F_CPU == 16000000L - void SDI12Timer::configSDI12TimerPrescale(void) { preSDI12_TCCR4A = TCCR4A; preSDI12_TCCR4B = TCCR4B; preSDI12_TCCR4C = TCCR4C; preSDI12_TCCR4D = TCCR4D; preSDI12_TCCR4E = TCCR4E; +#if F_CPU == 16000000L TCCR4A = 0x00; // TCCR4A = 0x00 = "normal" operation - Normal port operation, OC4A & // OC4B disconnected TCCR4B = 0x0B; // TCCR4B = 0x0B = 0b00001011 - Clock Select bits 43, 41, & 40 on - @@ -194,23 +154,7 @@ void SDI12Timer::configSDI12TimerPrescale(void) { // disconnected TCCR4D = 0x00; // TCCR4D = 0x00 = No fault protection TCCR4E = 0x00; // TCCR4E = 0x00 = No register locks or overrides -} - -void SDI12Timer::resetSDI12TimerPrescale(void) { - TCCR4A = preSDI12_TCCR4A; - TCCR4B = preSDI12_TCCR4B; - TCCR4C = preSDI12_TCCR4C; - TCCR4D = preSDI12_TCCR4D; - TCCR4E = preSDI12_TCCR4E; -} - #elif F_CPU == 8000000L -void SDI12Timer::configSDI12TimerPrescale(void) { - preSDI12_TCCR4A = TCCR4A; - preSDI12_TCCR4B = TCCR4B; - preSDI12_TCCR4C = TCCR4C; - preSDI12_TCCR4D = TCCR4D; - preSDI12_TCCR4E = TCCR4E; TCCR4A = 0x00; // TCCR4A = 0x00 = "normal" operation - Normal port operation, OC4A & // OC4B disconnected TCCR4B = 0x0A; // TCCR4B = 0x0A = 0b00001010 - Clock Select bits 43 & 41 on - @@ -219,6 +163,7 @@ void SDI12Timer::configSDI12TimerPrescale(void) { // disconnected TCCR4D = 0x00; // TCCR4D = 0x00 = No fault protection TCCR4E = 0x00; // TCCR4E = 0x00 = No register locks or overrides +#endif } void SDI12Timer::resetSDI12TimerPrescale(void) { @@ -228,8 +173,6 @@ void SDI12Timer::resetSDI12TimerPrescale(void) { TCCR4D = preSDI12_TCCR4D; TCCR4E = preSDI12_TCCR4E; } -#endif - // Arduino Zero other SAMD21 boards #elif defined(ARDUINO_SAMD_ZERO) || defined(__SAMD21G18A__) || \ From 9ca66877ae98971cf9b72c10540e4068c2ac2c13 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Thu, 1 Aug 2024 15:29:04 -0400 Subject: [PATCH 51/96] Update gitignore Signed-off-by: Sara Damiano --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index d230dfa..d764eb6 100644 --- a/.gitignore +++ b/.gitignore @@ -163,3 +163,6 @@ output_*.log sync.ffs_db docs/Doxyfile.bak SDI12 Repo Token.txt +boards/ +lib/ +variants/ From f128f88ff5735b06cc3e8e1e6e7632b89b3c1786 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Fri, 2 Aug 2024 09:53:16 -0400 Subject: [PATCH 52/96] Fix timer 1 Signed-off-by: Sara Damiano --- src/SDI12.cpp | 2 +- src/SDI12.h | 2 +- src/SDI12_boards.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/SDI12.cpp b/src/SDI12.cpp index 0325efa..fd2cf66 100644 --- a/src/SDI12.cpp +++ b/src/SDI12.cpp @@ -425,7 +425,7 @@ void SDI12::writeChar(uint8_t outChar) { // functions like micros(), millis() and any real-time clocks, so we don't want to // disable them if we don't really have to. -#if F_CPU < 40000000UL +#if F_CPU < 48000000UL noInterrupts(); // _ALL_ interrupts disabled #endif diff --git a/src/SDI12.h b/src/SDI12.h index bd21d5a..414c13a 100644 --- a/src/SDI12.h +++ b/src/SDI12.h @@ -181,7 +181,7 @@ enum LookaheadMode { /** Only tabs, spaces, line feeds & carriage returns are skipped.*/ SKIP_WHITESPACE }; -#endif +#endif // defined(ESP32) || defined(ESP8266) /** * @brief The main class for SDI 12 instances diff --git a/src/SDI12_boards.h b/src/SDI12_boards.h index 26457a3..948749a 100644 --- a/src/SDI12_boards.h +++ b/src/SDI12_boards.h @@ -113,7 +113,7 @@ sensors. This library provides a general software solution, without requiring * * The c macro name for the register used to access the timer/counter 1 value is TCNT1 */ -#define READTIME TCNT2 +#define READTIME TCNT1 // Using Timer 1 #if F_CPU == 16000000L /** From 31358eeeeac7660a9ee44b89d1ceb771b008cce0 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Fri, 2 Aug 2024 14:45:19 -0400 Subject: [PATCH 53/96] Update testing programs --- tools/TestCommands/TestCommands.ino | 103 +++++++++++--------- tools/TestSensorTiming/TestSensorTiming.ino | 75 ++++++++++++-- tools/TestWarmUp/TestWarmUp.ino | 88 +++++++++-------- 3 files changed, 173 insertions(+), 93 deletions(-) diff --git a/tools/TestCommands/TestCommands.ino b/tools/TestCommands/TestCommands.ino index d6cf424..55d757a 100644 --- a/tools/TestCommands/TestCommands.ino +++ b/tools/TestCommands/TestCommands.ino @@ -9,7 +9,7 @@ /* connection information */ uint32_t serialBaud = 115200; /*!< The baud rate for the output serial port */ -uint8_t dataPin = 7; /*!< The pin of the SDI-12 data bus */ +int8_t dataPin = 7; /*!< The pin of the SDI-12 data bus */ int8_t powerPin = 22; /*!< The sensor power pin (or -1 if not switching power) */ uint32_t wake_delay = 0; /*!< Extra time needed for the sensor to wake (0-100ms) */ int8_t firstAddress = 0; /* The first address in the address space to check (0='0') */ @@ -20,6 +20,10 @@ int8_t commandsToTest = /** Define the SDI-12 bus */ SDI12 mySDI12(dataPin); +/** Error codes, if returned */ +int8_t error_result_number = 7; +float no_error_value = 0; + /// variable that alternates output type back and forth between parsed and raw boolean flip = 0; @@ -79,45 +83,6 @@ char decToChar(byte i) { return i; } -/** - * @brief gets identification information from a sensor, and prints it to the serial - * port - * - * @param i a character between '0'-'9', 'a'-'z', or 'A'-'Z'. - */ -void printInfo(char i, bool printCommands = true) { - String command = ""; - command += (char)i; - command += "I!"; - mySDI12.sendCommand(command, wake_delay); - if (printCommands) { - Serial.print(">>>"); - Serial.println(command); - } - - String sdiResponse = mySDI12.readStringUntil('\n'); - sdiResponse.trim(); - // allccccccccmmmmmmvvvxxx...xx - if (printCommands) { - Serial.print("<<<"); - Serial.println(sdiResponse); - } - - Serial.print("Address: "); - Serial.print(sdiResponse.substring(0, 1)); // address - Serial.print(", SDI-12 Version: "); - Serial.print(sdiResponse.substring(1, 3).toFloat() / 10); // SDI-12 version number - Serial.print(", Vendor ID: "); - Serial.print(sdiResponse.substring(3, 11)); // vendor id - Serial.print(", Sensor Model: "); - Serial.print(sdiResponse.substring(11, 17)); // sensor model - Serial.print(", Sensor Version: "); - Serial.print(sdiResponse.substring(17, 20)); // sensor version - Serial.print(", Sensor ID: "); - Serial.print(sdiResponse.substring(20)); // sensor id - Serial.println(); -} - bool getResults(char address, int resultsExpected, bool verify_crc = false, bool printCommands = true) { uint8_t resultsReceived = 0; @@ -258,12 +223,15 @@ bool getResults(char address, int resultsExpected, bool verify_crc = false, resultsReceived++; } // check for a failure error code at the end - if (resultsReceived == 5 && result != 0.0) { - gotResults = false; - resultsReceived = 0; - if (printCommands) { - Serial.print("Got a failure code of "); - Serial.println(String(result, strnlen(dec_pl, max_sdi_digits) - 1)); + if (error_result_number >= 1) { + if (resultsReceived == error_result_number && result != no_error_value) { + gotResults = false; + resultsReceived = 0; + if (printCommands) { + Serial.print("Got a failure code of "); + Serial.println(String(result, strnlen(dec_pl, max_sdi_digits) - 1)); + } + return false; } } @@ -510,6 +478,49 @@ bool checkActive(char address, int8_t numPings = 3, bool printCommands = true) { return false; } +/** + * @brief gets identification information from a sensor, and prints it to the serial + * port + * + * @param i a character between '0'-'9', 'a'-'z', or 'A'-'Z'. + */ +bool printInfo(char i, bool printCommands = true) { + String command = ""; + command += (char)i; + command += "I!"; + mySDI12.sendCommand(command, wake_delay); + if (printCommands) { + Serial.print(">>>"); + Serial.println(command); + } + delay(100); + + String sdiResponse = mySDI12.readStringUntil('\n'); + sdiResponse.trim(); + // allccccccccmmmmmmvvvxxx...xx + if (printCommands) { + Serial.print("<<<"); + Serial.println(sdiResponse); + } + + Serial.print("Address: "); + Serial.print(sdiResponse.substring(0, 1)); // address + Serial.print(", SDI-12 Version: "); + Serial.print(sdiResponse.substring(1, 3).toFloat() / 10); // SDI-12 version number + Serial.print(", Vendor ID: "); + Serial.print(sdiResponse.substring(3, 11)); // vendor id + Serial.print(", Sensor Model: "); + Serial.print(sdiResponse.substring(11, 17)); // sensor model + Serial.print(", Sensor Version: "); + Serial.print(sdiResponse.substring(17, 20)); // sensor version + Serial.print(", Sensor ID: "); + Serial.print(sdiResponse.substring(20)); // sensor id + Serial.println(); + + if (sdiResponse.length() < 3) { return false; }; + return true; +} + void setup() { Serial.begin(serialBaud); while (!Serial) diff --git a/tools/TestSensorTiming/TestSensorTiming.ino b/tools/TestSensorTiming/TestSensorTiming.ino index c15803f..c515773 100644 --- a/tools/TestSensorTiming/TestSensorTiming.ino +++ b/tools/TestSensorTiming/TestSensorTiming.ino @@ -13,14 +13,19 @@ uint32_t serialBaud = 115200; /*!< The baud rate for the output serial port * int8_t dataPin = 7; /*!< The pin of the SDI-12 data bus */ char sensorAddress = '0'; /*!< The address of the SDI-12 sensor */ int8_t powerPin = 22; /*!< The sensor power pin (or -1 if not switching power) */ -uint32_t wake_delay = 100; /*!< Extra time needed for the sensor to wake (0-100ms) */ /** Define the SDI-12 bus */ SDI12 mySDI12(dataPin); /** Define some testing specs */ -bool testPowerOff = true; -int32_t min_power_delay = 4900L; /*!< The min time to test wake after power on. */ + +/** Error codes, if returned */ +int8_t error_result_number = 7; +float no_error_value = 0; + +/** Testing turning off power */ +bool testPowerOff = false; +int32_t min_power_delay = 100L; /*!< The min time to test wake after power on. */ int32_t max_power_delay = 180000L; /*!< The max time to test wake after power on. */ int32_t increment_power_delay = 100L; /*!< The time to lengthen waits between reps. */ int32_t power_off_time = 60000L; /*!< The time to power off between tests. */ @@ -28,8 +33,15 @@ int32_t power_off_time = 60000L; /*!< The time to power off between tests * the real world! Some sensors take longer to warm up if they've been off for a while. */ +/** Testing the length of the break */ +bool testBreak = true; +int32_t min_wake_delay = 0; /*!< The min time to test wake after a line break. */ +int32_t max_wake_delay = 100; /*!< The max time to test wake (should be <=100). */ +int32_t increment_wake = 5; /*!< The time to lengthen waits between reps. */ + /** set some initial values */ int32_t power_delay = min_power_delay; +int32_t wake_delay = min_wake_delay; int32_t total_meas_time = 0; int32_t total_meas_made = 0; @@ -178,12 +190,14 @@ bool getResults(char address, int resultsExpected, bool verify_crc = false, resultsReceived++; } // check for a failure error code at the end - if (resultsReceived == 5 && result != 0.0) { - gotResults = false; - resultsReceived = 0; - if (printCommands) { - Serial.print("Got a failure code of "); - Serial.println(String(result, strnlen(dec_pl, max_sdi_digits) - 1)); + if (error_result_number >= 1) { + if (resultsReceived == error_result_number && result != no_error_value) { + gotResults = false; + resultsReceived = 0; + if (printCommands) { + Serial.print("Got a failure code of "); + Serial.println(String(result, strnlen(dec_pl, max_sdi_digits) - 1)); + } } } @@ -359,6 +373,49 @@ bool checkActive(char address, int8_t numPings = 3, bool printCommands = true) { return false; } +/** + * @brief gets identification information from a sensor, and prints it to the serial + * port + * + * @param i a character between '0'-'9', 'a'-'z', or 'A'-'Z'. + */ +bool printInfo(char i, bool printCommands = true) { + String command = ""; + command += (char)i; + command += "I!"; + mySDI12.sendCommand(command, wake_delay); + if (printCommands) { + Serial.print(">>>"); + Serial.println(command); + } + delay(100); + + String sdiResponse = mySDI12.readStringUntil('\n'); + sdiResponse.trim(); + // allccccccccmmmmmmvvvxxx...xx + if (printCommands) { + Serial.print("<<<"); + Serial.println(sdiResponse); + } + + Serial.print("Address: "); + Serial.print(sdiResponse.substring(0, 1)); // address + Serial.print(", SDI-12 Version: "); + Serial.print(sdiResponse.substring(1, 3).toFloat() / 10); // SDI-12 version number + Serial.print(", Vendor ID: "); + Serial.print(sdiResponse.substring(3, 11)); // vendor id + Serial.print(", Sensor Model: "); + Serial.print(sdiResponse.substring(11, 17)); // sensor model + Serial.print(", Sensor Version: "); + Serial.print(sdiResponse.substring(17, 20)); // sensor version + Serial.print(", Sensor ID: "); + Serial.print(sdiResponse.substring(20)); // sensor id + Serial.println(); + + if (sdiResponse.length() < 3) { return false; }; + return true; +} + void setup() { Serial.begin(serialBaud); while (!Serial) diff --git a/tools/TestWarmUp/TestWarmUp.ino b/tools/TestWarmUp/TestWarmUp.ino index b31314d..b58616d 100644 --- a/tools/TestWarmUp/TestWarmUp.ino +++ b/tools/TestWarmUp/TestWarmUp.ino @@ -15,17 +15,61 @@ char sensorAddress = '0'; /*!< The address of the SDI-12 sensor */ int8_t powerPin = 22; /*!< The sensor power pin (or -1 if not switching power) */ /** Define the SDI-12 bus */ -SDI12 mySDI12(dataPin); -int32_t min_wake_delay = 0; /*!< The min time to test wake after a line break. */ -int32_t increment_wake = 5; /*!< The time to lengthen waits between reps. */ -int32_t max_wake_delay = 100; /*!< The max time to test wake (should be <=100). */ +SDI12 mySDI12(dataPin); + +/** Define some testing specs */ + +/** Error codes, if returned */ +int8_t error_result_number = 7; +float no_error_value = 0; + +/** Testing turning off power */ int32_t min_power_delay = 100L; /*!< The min time to test wake after power on. */ -int32_t increment_power = 100; /*!< The time to lengthen waits between reps. */ int32_t max_power_delay = 10000L; /*!< The max time to test wake after power on. */ +int32_t increment_power = 100; /*!< The time to lengthen waits between reps. */ +/** Testing the length of the break */ +int32_t min_wake_delay = 0; /*!< The min time to test wake after a line break. */ +int32_t max_wake_delay = 100; /*!< The max time to test wake (should be <=100). */ +int32_t increment_wake = 5; /*!< The time to lengthen waits between reps. */ + +/** set some initial values */ int32_t power_delay = min_power_delay; int32_t wake_delay = min_wake_delay; +// this checks for activity at a particular address +// expects a char, '0'-'9', 'a'-'z', or 'A'-'Z' +bool checkActive(char address, int8_t numPings = 3, bool printCommands = false) { + String command = ""; + command += (char)address; // sends basic 'acknowledge' command [address][!] + command += "!"; + + for (int j = 0; j < numPings; j++) { // goes through three rapid contact attempts + if (printCommands) { + Serial.print(">>>"); + Serial.println(command); + } + mySDI12.sendCommand(command, wake_delay); + + // the sensor should just return its address + String sdiResponse = mySDI12.readStringUntil('\n'); + sdiResponse.trim(); + if (printCommands) { + Serial.print("<<<"); + Serial.println(sdiResponse); + } + mySDI12.clearBuffer(); + + // check the address, return false if it's incorrect + String returned_address = sdiResponse.substring(0, 1); + char ret_addr_array[2]; + returned_address.toCharArray(ret_addr_array, sizeof(ret_addr_array)); + if (returned_address == String(address)) { return true; } + } + mySDI12.clearBuffer(); + return false; +} + /** * @brief gets identification information from a sensor, and prints it to the serial * port @@ -69,39 +113,6 @@ bool printInfo(char i, bool printCommands = true) { return true; } -// this checks for activity at a particular address -// expects a char, '0'-'9', 'a'-'z', or 'A'-'Z' -bool checkActive(char address, int8_t numPings = 3, bool printCommands = false) { - String command = ""; - command += (char)address; // sends basic 'acknowledge' command [address][!] - command += "!"; - - for (int j = 0; j < numPings; j++) { // goes through three rapid contact attempts - if (printCommands) { - Serial.print(">>>"); - Serial.println(command); - } - mySDI12.sendCommand(command, wake_delay); - - // the sensor should just return its address - String sdiResponse = mySDI12.readStringUntil('\n'); - sdiResponse.trim(); - if (printCommands) { - Serial.print("<<<"); - Serial.println(sdiResponse); - } - mySDI12.clearBuffer(); - - // check the address, return false if it's incorrect - String returned_address = sdiResponse.substring(0, 1); - char ret_addr_array[2]; - returned_address.toCharArray(ret_addr_array, sizeof(ret_addr_array)); - if (returned_address == String(address)) { return true; } - } - mySDI12.clearBuffer(); - return false; -} - void setup() { Serial.begin(serialBaud); while (!Serial) @@ -131,6 +142,7 @@ void loop() { pinMode(powerPin, OUTPUT); digitalWrite(powerPin, HIGH); delay(power_delay); + mySDI12.clearBuffer(); } if (checkActive(sensorAddress, 1, true)) { From 468f1c8548ea5d7067784de914c2afe30c00184d Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Fri, 2 Aug 2024 13:25:02 -0400 Subject: [PATCH 54/96] Fix types in test Signed-off-by: Sara Damiano --- tools/TestSensorTiming/TestSensorTiming.ino | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/TestSensorTiming/TestSensorTiming.ino b/tools/TestSensorTiming/TestSensorTiming.ino index c515773..ce77f01 100644 --- a/tools/TestSensorTiming/TestSensorTiming.ino +++ b/tools/TestSensorTiming/TestSensorTiming.ino @@ -45,7 +45,7 @@ int32_t wake_delay = min_wake_delay; int32_t total_meas_time = 0; int32_t total_meas_made = 0; -int32_t max_meas_time = 0; +uint32_t max_meas_time = 0; struct startMeasurementResult { // Structure declaration String returned_address; @@ -430,7 +430,7 @@ void setup() { // Power the sensors; if (powerPin >= 0 && !testPowerOff) { - Serial.println("Powering up sensors, wait..."); + Serial.println("Powering up sensors, wait 30s..."); pinMode(powerPin, OUTPUT); digitalWrite(powerPin, HIGH); delay(30000L); @@ -443,7 +443,7 @@ void loop() { while (got_good_results && checks_at_time < 25) { Serial.print("Repeat attempt "); Serial.print(checks_at_time); - Serial.print(" with warm-up time of "); + Serial.print(" with power on warm-up time of "); Serial.println(power_delay); // Power down the sensors; @@ -499,7 +499,7 @@ void loop() { } } - // if we got a good result 25 at this warm-up, keep testing how long the + // if we got a good result 25x at this warm-up, keep testing how long the // measurements take while (got_good_results) { uint32_t this_meas_time = takeMeasurement(sensorAddress, true, "", false); From 79058b6984764fa75477ef412a0ff267447f6226 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Fri, 2 Aug 2024 14:51:33 -0400 Subject: [PATCH 55/96] Fix types of bitTimes Signed-off-by: Sara Damiano --- src/SDI12.cpp | 2 +- src/SDI12_boards.cpp | 8 ++++---- src/SDI12_boards.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/SDI12.cpp b/src/SDI12.cpp index fd2cf66..31c8317 100644 --- a/src/SDI12.cpp +++ b/src/SDI12.cpp @@ -714,7 +714,7 @@ void ESPFAMILY_USE_INSTRUCTION_RAM SDI12::receiveISR() { // data, parity, or stop bit. // Check how many bit times have passed since the last change - uint16_t rxBits = SDI12Timer::bitTimes((sdi12timer_t)(thisBitTCNT - prevBitTCNT)); + uint16_t rxBits = SDI12Timer::bitTimes(thisBitTCNT - prevBitTCNT); // Calculate how many *data+parity* bits should be left in the current character // - Each character has a total of 10 bits, 1 start bit, 7 data bits, 1 parity // bit, and 1 stop bit diff --git a/src/SDI12_boards.cpp b/src/SDI12_boards.cpp index be8cc57..87503b4 100644 --- a/src/SDI12_boards.cpp +++ b/src/SDI12_boards.cpp @@ -24,16 +24,16 @@ uint16_t SDI12Timer::mul8x8to16(uint8_t x, uint8_t y) { // Using an 8-bit timer, we need to do fanciness to get proper 16 bit results #if TIMER_INT_SIZE == 8 -sdi12timer_t SDI12Timer::bitTimes(sdi12timer_t dt) { +uint16_t SDI12Timer::bitTimes(sdi12timer_t dt) { // multiply the time delta in ticks by the bits per tick return mul8x8to16(dt + RX_WINDOW_FUDGE, BITS_PER_TICK_Q10) >> 10; } // But nothing fancy for bigger timers #elif TIMER_INT_SIZE == 16 || TIMER_INT_SIZE == 32 -sdi12timer_t SDI12Timer::bitTimes(sdi12timer_t dt) { +uint16_t SDI12Timer::bitTimes(sdi12timer_t dt) { // divide the number of ticks by the ticks per bit - return (sdi12timer_t)((dt + (sdi12timer_t)RX_WINDOW_FUDGE) / (sdi12timer_t)TICKS_PER_BIT); + return (uint16_t)((dt + (sdi12timer_t)RX_WINDOW_FUDGE) / (sdi12timer_t)TICKS_PER_BIT); } #else #error "Board timer is incorrectly configured!" @@ -62,7 +62,6 @@ sdi12timer_t SDI12Timer::SDI12TimerRead(void) { void SDI12Timer::configSDI12TimerPrescale(void) { preSDI12_TCCR2A = TCCR2A; preSDI12_TCCR2B = TCCR2B; - #if F_CPU == 16000000L TCCR2A = 0x00; // TCCR2A = 0x00 = "normal" operation - Normal port operation, OC2A & // OC2B disconnected @@ -92,6 +91,7 @@ void SDI12Timer::resetSDI12TimerPrescale(void) { sdi12timer_t SDI12Timer::SDI12TimerRead(void) { return TCNT1; } + /** * @brief The value of timer control register 1A prior to being set for SDI-12. */ diff --git a/src/SDI12_boards.h b/src/SDI12_boards.h index 948749a..9a13b2c 100644 --- a/src/SDI12_boards.h +++ b/src/SDI12_boards.h @@ -505,7 +505,7 @@ class SDI12Timer { * * @see https://github.com/SlashDevin/NeoSWSerial/pull/13#issuecomment-315463522 */ - static sdi12timer_t bitTimes(sdi12timer_t dt); + static uint16_t bitTimes(sdi12timer_t dt); /** * @brief Set the processor timer prescaler such that the 10 bits of an SDI-12 From f5f5748d3dfc1c848b977992ae3162b9dfc9fdf4 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Fri, 2 Aug 2024 15:04:55 -0400 Subject: [PATCH 56/96] Restore some internal docs Signed-off-by: Sara Damiano --- src/SDI12_boards.cpp | 58 ++++++++++---------- src/SDI12_boards.h | 123 +++++++++++++++++++++++++++++++++---------- 2 files changed, 124 insertions(+), 57 deletions(-) diff --git a/src/SDI12_boards.cpp b/src/SDI12_boards.cpp index 87503b4..b1f0d58 100644 --- a/src/SDI12_boards.cpp +++ b/src/SDI12_boards.cpp @@ -145,25 +145,19 @@ void SDI12Timer::configSDI12TimerPrescale(void) { preSDI12_TCCR4C = TCCR4C; preSDI12_TCCR4D = TCCR4D; preSDI12_TCCR4E = TCCR4E; -#if F_CPU == 16000000L TCCR4A = 0x00; // TCCR4A = 0x00 = "normal" operation - Normal port operation, OC4A & // OC4B disconnected +#if F_CPU == 16000000L TCCR4B = 0x0B; // TCCR4B = 0x0B = 0b00001011 - Clock Select bits 43, 41, & 40 on - // prescaler set to CK/1024 - TCCR4C = 0x00; // TCCR4C = 0x00 = "normal" operation - Normal port operation, OC4D0 - // disconnected - TCCR4D = 0x00; // TCCR4D = 0x00 = No fault protection - TCCR4E = 0x00; // TCCR4E = 0x00 = No register locks or overrides #elif F_CPU == 8000000L - TCCR4A = 0x00; // TCCR4A = 0x00 = "normal" operation - Normal port operation, OC4A & - // OC4B disconnected TCCR4B = 0x0A; // TCCR4B = 0x0A = 0b00001010 - Clock Select bits 43 & 41 on - // prescaler set to CK/512 +#endif TCCR4C = 0x00; // TCCR4C = 0x00 = "normal" operation - Normal port operation, OC4D0 // disconnected TCCR4D = 0x00; // TCCR4D = 0x00 = No fault protection TCCR4E = 0x00; // TCCR4E = 0x00 = No register locks or overrides -#endif } void SDI12Timer::resetSDI12TimerPrescale(void) { @@ -217,6 +211,10 @@ static uint32_t preSDI12_REG_GCLK_GENCTRL; */ static uint8_t preSDI12_REG_GCLK_CLKCTRL; +sdi12timer_t SDI12Timer::SDI12TimerRead(void) { + return REG_TC3_COUNT8_COUNT; +} + void SDI12Timer::configSDI12TimerPrescale(void) { // read control register values prior to changes preSDI12_REG_GCLK_GENDIV = REG_GCLK_GENDIV; @@ -344,6 +342,28 @@ static uint32_t preSDI12_REG_GCLK_GENCTRL; */ static uint32_t preSDI12_REG_GCLK_PCHCTRL; +sdi12timer_t SDI12Timer::SDI12TimerRead(void) { + // Note from datasheet: Prior to any read access, this register must be synchronized + // by user by writing the according TC Command value to the Control B Set register + // (CTRLBSET.CMD=READSYNC) + // see: + // https://onlinedocs.microchip.com/oxy/GUID-F5813793-E016-46F5-A9E2-718D8BCED496-en-US-13/GUID-5033DFD7-EB2D-4870-AE98-D40CADB0531E.html + + // Code taken from Microchip article on how to read the tiemr value + // https://microchip.my.site.com/s/article/SAM-D5x-E5x--Reading-TC-TCC-COUNT-register + + + // write READSYNC command to the Control B Set register + SDI12_TC->COUNT16.CTRLBSET.reg = TC_CTRLBSET_CMD_READSYNC; + + // wait for the CMD bits in CTRLBSET to be cleared, meaning the CMD has been executed + while (SDI12_TC->COUNT16.CTRLBSET.reg & TC_CTRLBSET_CMD_READSYNC) + ; + + // read the COUNT register + return SDI12_TC->COUNT16.COUNT.reg; +} + void SDI12Timer::configSDI12TimerPrescale(void) { // read the values of the registers prior to making changes preSDI12_REG_GCLK_GENCTRL = GCLK->GENCTRL[GENERIC_CLOCK_GENERATOR_SDI12].reg; @@ -495,28 +515,6 @@ void SDI12Timer::resetSDI12TimerPrescale(void) { resetTC(SDI12_TC); } -sdi12timer_t SDI12Timer::SDI12TimerRead(void) { - // Note from datasheet: Prior to any read access, this register must be synchronized - // by user by writing the according TC Command value to the Control B Set register - // (CTRLBSET.CMD=READSYNC) - // see: - // https://onlinedocs.microchip.com/oxy/GUID-F5813793-E016-46F5-A9E2-718D8BCED496-en-US-13/GUID-5033DFD7-EB2D-4870-AE98-D40CADB0531E.html - - // Code taken from Microchip article on how to read the tiemr value - // https://microchip.my.site.com/s/article/SAM-D5x-E5x--Reading-TC-TCC-COUNT-register - - - // write READSYNC command to the Control B Set register - SDI12_TC->COUNT16.CTRLBSET.reg = TC_CTRLBSET_CMD_READSYNC; - - // wait for the CMD bits in CTRLBSET to be cleared, meaning the CMD has been executed - while (SDI12_TC->COUNT16.CTRLBSET.reg & TC_CTRLBSET_CMD_READSYNC) - ; - - // read the COUNT register - return SDI12_TC->COUNT16.COUNT.reg; -} - // Espressif ESP32/ESP8266 boards or other boards faster than 48MHz // WARNING: I haven't tested the minimum speed that this will work at! #elif defined(ESP32) || defined(ESP8266) || F_CPU >= 48000000L diff --git a/src/SDI12_boards.h b/src/SDI12_boards.h index 9a13b2c..0d9944c 100644 --- a/src/SDI12_boards.h +++ b/src/SDI12_boards.h @@ -23,7 +23,7 @@ sensors. This library provides a general software solution, without requiring #define ESPFAMILY_USE_INSTRUCTION_RAM IRAM_ATTR #else #define ESPFAMILY_USE_INSTRUCTION_RAM -#endif +#endif // defined(ESP32) || defined(ESP8266) // Most 'standard' AVR boards @@ -34,17 +34,27 @@ sensors. This library provides a general software solution, without requiring /** * @brief A string description of the timer to use + * + * Timer/Counter2 (TC2) is a general purpose, single channel, 8-bit Timer/Counter + * module. + * + * Features + * - Single Channel Counter + * - Clear Timer on Compare Match (Auto Reload) + * - Glitch-free, Phase Correct Pulse Width Modulator (PWM) + * - Frequency Generator + * - 10-bit Clock Prescaler + * - Overflow and Compare Match Interrupt Sources (TOV2, OCF2A, and OCF2B) + * - Allows Clocking from External 32kHz Watch Crystal Independent of the I/O Clock */ #define TIMER_IN_USE_STR "Timer2" - /** * @brief The interger type of the timer. * - * Timer 2 is an 8-bit timer + * Timer 2 on AtMega boards is an 8-bit timer */ #define TIMER_INT_TYPE uint8_t #define TIMER_INT_SIZE 8 - /** * @brief The function or macro used to read the clock timer value. * @@ -87,7 +97,8 @@ sensors. This library provides a general software solution, without requiring * 8MHz / 256 prescaler = 31250 'ticks'/sec = 32 µs / 'tick' */ #define TICKS_PER_SECOND 31250 -#endif + +#endif // F_CPU // ATtiny boards (ie, adafruit trinket) @@ -95,25 +106,25 @@ sensors. This library provides a general software solution, without requiring /** * @brief A string description of the timer to use + * + * The Timer/Counter1 features a high resolution and a high accuracy usage with the + * lower prescaling opportunities. It can also support two accurate, high speed, 8-bit + * pulse width modulators using clock speeds up to 64MHz (or 32MHz in low speedmode). */ #define TIMER_IN_USE_STR "Timer1" /** * @brief The interger type of the timer. * - * Timer 1 is an 8-bit timer + * Timer 1 on the ATTiny boards is an 8-bit timer */ #define TIMER_INT_TYPE uint8_t -/** - * @brief The number of clock ticks per second, after accounting for the prescaler. - */ #define TIMER_INT_SIZE 8 - /** * @brief The function or macro used to read the clock timer value. * * The c macro name for the register used to access the timer/counter 1 value is TCNT1 */ -#define READTIME TCNT1 // Using Timer 1 +#define READTIME TCNT1 #if F_CPU == 16000000L /** @@ -136,7 +147,7 @@ sensors. This library provides a general software solution, without requiring */ #define TICKS_PER_SECOND 15625 -#endif +#endif // F_CPU // Arduino Leonardo & Yun and other 32U4 boards @@ -145,13 +156,29 @@ sensors. This library provides a general software solution, without requiring /** * @brief A string description of the timer to use + * + * Timer/Counter4 is a general purpose high speed Timer/Counter module, with three + * independent Output Compare Units, and with enhanced PWM support. + * + * Features + * - Up to 10-Bit Accuracy + * - Three Independent Output Compare Units + * - Clear Timer on Compare Match (Auto Reload) + * - Glitch Free, Phase and Frequency Correct Pulse Width Modulator (PWM) + * - Enhanced PWM mode: one optional additional accuracy bit without effect on output + * frequency + * - Variable PWM Period + * - Independent Dead Time Generators for each PWM channels + * - Synchronous update of PWM registers + * - Five Independent Interrupt Sources (TOV4, OCF4A, OCF4B, OCF4D, FPF4) + * - High Speed Asynchronous and Synchronous Clocking Modes + * - Separate Prescaler Unit */ #define TIMER_IN_USE_STR "Timer4" - /** * @brief The interger type of the timer. * - * Timer 4 is an 10-bit timer, but we're only using the lower 8 bits + * Timer 4 on the U4 series is an 10-bit timer, but we're only using the lower 8 bits */ #define TIMER_INT_TYPE uint8_t #define TIMER_INT_SIZE 8 @@ -189,7 +216,7 @@ sensors. This library provides a general software solution, without requiring */ #define TICKS_PER_SECOND 15625 -#endif +#endif // F_CPU // Arduino Zero other SAMD21 boards @@ -208,6 +235,48 @@ sensors. This library provides a general software solution, without requiring * The Adafruit Arduino core uses: * - TC5 for Tone * - TC4 for Servo + * + * The Generic Clock controller GCLK provides nine Generic Clock Generators that can + * provide a wide range of clock frequencies. + * + * Generators can be set to use different external and internal oscillators as source. + * The clock of each Generator can be divided. The outputs from the Generators are used + * as sources for the Generic Clock Multiplexers, which provide the Generic Clock + * (GCLK_PERIPHERAL) to the peripheral modules, as shown in Generic Clock Controller + * Block Diagram. + * + * Features + * - Provides Generic Clocks + * - Wide frequency range + * - Clock source for the generator can be changed on the fly + * + * The TC consists of a counter, a prescaler, compare/capture channels and control + * logic. The counter can be set to count events, or it can be configured to count clock + * pulses. The counter, together with the compare/capture channels, can be configured to + * timestamp input events, allowing capture of frequency and pulse width. It can also + * perform waveform generation, such as frequency generation and pulse-width modulation + * (PWM). + * + * Features + * - Selectable configuration + * – Up to five 16-bit Timer/Counters (TC) including one low-power TC, each + * configurable as: + * - 8-bit TC with two compare/capture channels + * - 16-bit TC with two compare/capture channels + * - 32-bit TC with two compare/capture channels, by using two TCs + * - Waveform generation + * – Frequency generation + * – Single-slope pulse-width modulation + * - Input capture + * – Event capture + * – Frequency capture + * – Pulse-width capture + * - One input event + * - Interrupts/output events on: + * – Counter overflow/underflow + * – Compare match or capture + * - Internal prescaler + * - Can be used with DMA and to trigger DMA transactions */ #define TIMER_IN_USE_STR "GCLK4-TC3" @@ -218,11 +287,11 @@ sensors. This library provides a general software solution, without requiring */ #define TIMER_INT_TYPE uint16_t #define TIMER_INT_SIZE 16 - /** * @brief The function or macro used to read the clock timer value. * * This signifies the register of timer/counter 3, the 16-bit count, the count value + * * This is equivalent to TC3->COUNT16.COUNT.reg */ #define READTIME REG_TC3_COUNT16_COUNT @@ -296,6 +365,7 @@ sensors. This library provides a general software solution, without requiring #define TICKS_PER_SECOND 500000 // Espressif ESP32/ESP8266 boards or any boards faster than 48MHz not mentioned above +#elif defined(ESP32) || defined(ESP8266) || F_CPU >= 48000000L // From calculations using https://github.com/SRGDamia1/avrcycle, the micros() function // takes 60 (!!) clock cycles. We're going to blindly assume that the micros() function @@ -310,13 +380,11 @@ sensors. This library provides a general software solution, without requiring // it does work on a 80MHz Espressif8266. // WARNING: I haven't actullay tested the minimum speed that this will work at! -#elif defined(ESP32) || defined(ESP8266) || F_CPU >= 48000000L /** * @brief A string description of the timer to use */ -#define TIMER_IN_USE_STR "micros" - +#define TIMER_IN_USE_STR "micros()" /** * @brief The interger type of the timer. * @@ -324,13 +392,18 @@ sensors. This library provides a general software solution, without requiring */ #define TIMER_INT_TYPE uint32_t #define TIMER_INT_SIZE 32 - -#define TICKS_PER_SECOND 1000000 - /** * @brief The function or macro used to read the clock timer value. + * + * The c macro name for the register used to access the timer/counter 2 value is TCNT2 */ #define READTIME sdi12timer.SDI12TimerRead() +/** + * @brief The number of clock ticks per second, after accounting for the prescaler. + * + * Since we're using micros() each 'tick' is 1µs + */ +#define TICKS_PER_SECOND 1000000 // Unknown board #else @@ -390,8 +463,7 @@ sensors. This library provides a general software solution, without requiring * * @see https://github.com/SlashDevin/NeoSWSerial/pull/13 */ -#define RX_WINDOW_FUDGE 1 - +#define RX_WINDOW_FUDGE 2 #elif TICKS_PER_SECOND == 31250 && TIMER_INT_SIZE == 8 /** @@ -515,9 +587,6 @@ class SDI12Timer { /** * @brief Reset the processor timer prescaler to whatever it was prior to being * adjusted for this library. - * - * @note The prescaler is *NOT* set back to initial values for SAMD boards! On those - * processors. */ void resetSDI12TimerPrescale(void); /** From 9ce0561859cd625614b808b8e86edbe8f8d1f843 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Fri, 2 Aug 2024 16:16:41 -0400 Subject: [PATCH 57/96] Doc changes Signed-off-by: Sara Damiano --- src/SDI12.h | 4 ++-- src/SDI12_boards.h | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/SDI12.h b/src/SDI12.h index 414c13a..463d6de 100644 --- a/src/SDI12.h +++ b/src/SDI12.h @@ -560,7 +560,7 @@ class SDI12 : public Stream { /** * @brief Get the data pin for the current SDI-12 instance * - * @return the data pin number + * @return The data pin number */ int8_t getDataPin(); /** @@ -711,7 +711,7 @@ class SDI12 : public Stream { * @brief Calculate the parity value for a character using even parity. * * @param v **uint8_t (char)** the character to calculate the parity of - * @return the input character with the 8th bit set + * @return The input character with the 8th bit set * to the even parity value for that character * * Sets up parity and interrupts for different processor types - that is, imports the diff --git a/src/SDI12_boards.h b/src/SDI12_boards.h index 0d9944c..665a60c 100644 --- a/src/SDI12_boards.h +++ b/src/SDI12_boards.h @@ -305,8 +305,7 @@ sensors. This library provides a general software solution, without requiring * * Start with 48MHz "main" clock source (GCLK_GENCTRL_SRC_DFLL48M) * 48MHz / 6x clock source divider (GCLK_GENDIV_DIV(6)) = 8MHz - * 8MHz / 16x prescaler (TC_CTRLA_PRESCALER_DIV16) = 500kHz = 500,000 'ticks'/sec = 2 - * µs / 'tick' (1 sec/1200 bits) * (1 tick/2 µs) = 416.66667 ticks/bit + * 8MHz / 16x prescaler (TC_CTRLA_PRESCALER_DIV16) = 500kHz = 500,000 'ticks'/sec */ #define TICKS_PER_SECOND 500000 @@ -332,7 +331,6 @@ sensors. This library provides a general software solution, without requiring */ #define TIMER_IN_USE_STR "GCLK6-TC2" - /** * @brief The interger type of the timer. * From c73c93a565a078651a8cbdba5967e0d55cf65f48 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Tue, 6 Aug 2024 12:18:27 -0400 Subject: [PATCH 58/96] Move remaining defines to boards.h Signed-off-by: Sara Damiano --- src/SDI12_boards.cpp | 15 ++------------- src/SDI12_boards.h | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/SDI12_boards.cpp b/src/SDI12_boards.cpp index b1f0d58..d74071a 100644 --- a/src/SDI12_boards.cpp +++ b/src/SDI12_boards.cpp @@ -172,8 +172,6 @@ void SDI12Timer::resetSDI12TimerPrescale(void) { #elif defined(ARDUINO_SAMD_ZERO) || defined(__SAMD21G18A__) || \ defined(__SAMD21J18A__) || defined(__SAMD21E18A__) -#define SDI12_TC TC3 - /// Fully reset the TC to factory settings and disable it static inline void resetTC(Tc* TCx) { // Disable TCx @@ -295,15 +293,6 @@ void SDI12Timer::resetSDI12TimerPrescale(void) { // SAMD51 and SAME51 boards #elif defined(__SAMD51__) || defined(__SAME51__) -/// The clock generator number to use -#define GENERIC_CLOCK_GENERATOR_SDI12 (6u) -/// The bit to check for synchronization -#define GCLK_SYNCBUSY_SDI12 GCLK_SYNCBUSY_GENCTRL6 -/// The timer controller to use -#define SDI12_TC TC2 -// The peripheral index within the generic clock for the selected timer controller -#define SDI12_TC_GCLK_ID TC2_GCLK_ID - /// Fully reset the TC to factory settings and disable it static inline void resetTC(Tc* TCx) { // Disable TCx @@ -485,8 +474,8 @@ void SDI12Timer::configSDI12TimerPrescale(void) { // configure the control register for the timer control uint32_t postSDI12_REG_TC_CTRLA = - ~TC_CTRLA_CAPTEN0 & // Disable capture for channel 0 - ~TC_CTRLA_CAPTEN1 & // Disable capture for channel 1 + ~TC_CTRLA_CAPTEN0 & // Disable capture for channel 0 (use for compare) + ~TC_CTRLA_CAPTEN1 & // Disable capture for channel 1 (use for compare) ~TC_CTRLA_RUNSTDBY & // Disable run on standby (TC_CTRLA_PRESCALER_DIV16 | // Set prescaler to 16 TCC_CTRLA_PRESCSYNC_GCLK | // Reload or reset the counter on next generic clock diff --git a/src/SDI12_boards.h b/src/SDI12_boards.h index 665a60c..3d99487 100644 --- a/src/SDI12_boards.h +++ b/src/SDI12_boards.h @@ -287,6 +287,9 @@ sensors. This library provides a general software solution, without requiring */ #define TIMER_INT_TYPE uint16_t #define TIMER_INT_SIZE 16 + +#define SDI12_TC TC3 + /** * @brief The function or macro used to read the clock timer value. * @@ -339,6 +342,21 @@ sensors. This library provides a general software solution, without requiring #define TIMER_INT_TYPE uint16_t #define TIMER_INT_SIZE 16 +/// The clock generator number to use +#define GENERIC_CLOCK_GENERATOR_SDI12 (6u) +/// The bit to check for synchronization +#define GCLK_SYNCBUSY_SDI12 GCLK_SYNCBUSY_GENCTRL6 +/// The timer controller to use +#define SDI12_TC TC2 +/// The interupt request number for the SDI-12 stop bit compare interrupt +#define SDI12_TC_IRQn TC2_IRQn +/// The interupt handler for the SDI-12 stop bit compare interrupt +#define SDI12_STOP_HANDLER TC2_Handler +/// The timer capture/compare channel to use +#define SDI12_TC_CHANNEL 0 +// The peripheral index within the generic clock for the selected timer controller +#define SDI12_TC_GCLK_ID TC2_GCLK_ID + /** * @brief The function or macro used to read the clock timer value. * From 6330308a200551245d94d4718b9d91b41d7cfa2e Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Tue, 6 Aug 2024 12:19:57 -0400 Subject: [PATCH 59/96] Adjust SAMD fudge and add yield function Signed-off-by: Sara Damiano --- src/SDI12.cpp | 3 +++ src/SDI12.h | 30 ++++++++++++++++++++++++++++++ src/SDI12_boards.h | 4 ++-- 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/SDI12.cpp b/src/SDI12.cpp index 31c8317..f494d9d 100644 --- a/src/SDI12.cpp +++ b/src/SDI12.cpp @@ -84,12 +84,14 @@ volatile uint8_t SDI12::_rxBufferHead = 0; // index of buff head // reveals the number of characters available in the buffer int SDI12::available() { + SDI12_YIELD() if (_bufferOverflow) return -1; return (_rxBufferTail + SDI12_BUFFER_SIZE - _rxBufferHead) % SDI12_BUFFER_SIZE; } // reveals the next character in the buffer without consuming int SDI12::peek() { + SDI12_YIELD() if (_rxBufferHead == _rxBufferTail) return -1; // Empty buffer? If yes, -1 return _rxBuffer[_rxBufferHead]; // Otherwise, read from "head" } @@ -103,6 +105,7 @@ void SDI12::clearBuffer() { // reads in the next character from the buffer (and moves the index ahead) int SDI12::read() { + SDI12_YIELD() _bufferOverflow = false; // Reading makes room in the buffer if (_rxBufferHead == _rxBufferTail) return -1; // Empty buffer? If yes, -1 uint8_t nextChar = _rxBuffer[_rxBufferHead]; // Otherwise, grab char at head diff --git a/src/SDI12.h b/src/SDI12.h index 463d6de..d149275 100644 --- a/src/SDI12.h +++ b/src/SDI12.h @@ -162,6 +162,36 @@ typedef const __FlashStringHelper* FlashString; #define SDI12_BUFFER_SIZE 81 #endif +#ifndef SDI12_YIELD_MS +/** + * @brief The time to delay, in milliseconds, to allow the buffer to fill before + * returning the value from the buffer. + * + * This may be needed for faster processors to account for the slow baud rate of SDI-12. + * Without this, the available() function may return 0 while we're in the middle of + * reading a character. + * + * There are 8.33 ms/character, so we delay by 8ms for fast processors to allow one + * character to finish. + */ +#if F_CPU >= 48000000L +#define SDI12_YIELD_MS 8 +#else +#define SDI12_YIELD_MS 0 +#endif +#endif + +#ifndef SDI12_YIELD +/** + * @brief A delay function to allow the buffer to fill before returning the value from + * the buffer. + * + * This may be needed for faster processors to account for the slow baud rate of SDI-12. + */ +#define SDI12_YIELD() \ + { delay(SDI12_YIELD_MS); } +#endif + #if defined(ESP32) || defined(ESP8266) /** * @brief This enumeration provides the lookahead options for parseInt(), parseFloat(). diff --git a/src/SDI12_boards.h b/src/SDI12_boards.h index 3d99487..b2d137d 100644 --- a/src/SDI12_boards.h +++ b/src/SDI12_boards.h @@ -522,14 +522,14 @@ sensors. This library provides a general software solution, without requiring * (65536 ticks/roll-over) * (1 bit/416.66667 ticks) = 157.284 bits * (65536 ticks/roll-over) * (1 sec/500000 ticks) = 131.07 milliseconds */ -#define TICKS_PER_BIT 417 +#define TICKS_PER_BIT 416 /** * @brief A "fudge factor" to get the Rx to work well. It mostly works to ensure that * uneven tick increments get rounded up. * * @see https://github.com/SlashDevin/NeoSWSerial/pull/13 */ -#define RX_WINDOW_FUDGE 11 +#define RX_WINDOW_FUDGE 30 #elif TICKS_PER_SECOND == 1000000 && TIMER_INT_SIZE == 32 /** From eb12169784ec4753f7359f10479324858f4da773 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Tue, 6 Aug 2024 12:20:13 -0400 Subject: [PATCH 60/96] Fix doc typo Signed-off-by: Sara Damiano --- docs/CreatingACharacter.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/CreatingACharacter.md b/docs/CreatingACharacter.md index 2e3a76d..b24fb16 100644 --- a/docs/CreatingACharacter.md +++ b/docs/CreatingACharacter.md @@ -205,7 +205,7 @@ void SDI12::receiveISR() { // data, parity, or stop bit. // Check how many bit times have passed since the last change - uint16-t rxBits = bitTimes((uint8-t)(thisBitTCNT - prevBitTCNT)); + uint16-t rxBits = bitTimes((uint8_t)(thisBitTCNT - prevBitTCNT)); // Calculate how many *data+parity* bits should be left in the current character // - Each character has a total of 10 bits, 1 start bit, 7 data bits, 1 parity // bit, and 1 stop bit From d284004a23d5073e34915a6766c64542cc06f39e Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Tue, 6 Aug 2024 12:20:47 -0400 Subject: [PATCH 61/96] Add a tool to print all characters as 7E1 Signed-off-by: Sara Damiano --- .../print_character_parity.ino | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 tools/print_character_parity/print_character_parity.ino diff --git a/tools/print_character_parity/print_character_parity.ino b/tools/print_character_parity/print_character_parity.ino new file mode 100644 index 0000000..e83fd49 --- /dev/null +++ b/tools/print_character_parity/print_character_parity.ino @@ -0,0 +1,109 @@ +#include + +#if defined __AVR__ +#include // optimized parity bit handling +#else +// Added MJB: parity function to replace the one specific for AVR from util/parity.h +// http://graphics.stanford.edu/~seander/bithacks.html#ParityNaive +uint8_t parity_even_bit(uint8_t v) { + uint8_t parity = 0; + while (v) { + parity = !parity; + v = v & (v - 1); + } + return parity; +} +#endif + +void printBIN(uint16_t c, uint8_t padding = 8, bool reverse = false) { + Serial.print("0b"); + for (int8_t b = 0; b < padding; b++) { + if (reverse) { + Serial.print(bitRead(c, b)); + } else { + Serial.print(bitRead(c, padding - 1 - b)); + } + } +} + +void printHEX(uint8_t c) { + Serial.print("0x"); + if (c < 0x10) { Serial.print("0"); } + Serial.print(String(c, HEX)); +} + +// All characters transmitted on the SDI-12 bus must be printable ASCII characters. +// Allowable: " " (space) = 32 = 0x20 -> "~" = 126 = 0x7E +// Exceptions: +// - sensor responses end with a carriage return (0D hex, 13 decimal) and a line feed +// (0A hex, 10 decimal) character +// - 2nd and 3rd character of CRC +// - the contents of data packets returned by the High Volume Binary command +// Total number of allowable characters = 97, plus CRC/BIN unprintables +uint8_t sdi_chars[97]; + +void setup() { + Serial.begin(115200); + while (!Serial) + ; + + // add allowable characters to the array + sdi_chars[0] = '\r'; + sdi_chars[1] = '\n'; + uint8_t j = 2; + for (uint8_t i = 32; i <= 126; i++) { + sdi_chars[j] = i; + j++; + } + Serial.println( + "Character, ASCII (decimal), ASCII (hex), ASCII (binary), Parity, " + "Character with parity, Transmission Order (LSB first), Bit - Start, Bit 0, " + "Bit 1, Bit 2, Bit 3, Bit 4, Bit 5, Bit 6, Bit 7 Parity, Bit + Stop"); + + // print info about the allowable characters + for (uint8_t i = 0; i < 97; i++) { + uint8_t c = sdi_chars[i]; + Serial.print("\""); + if (c == '\r') { + Serial.print("\\r"); + } else if (c == '\n') { + Serial.print("\\n"); + } else if (c == ' ') { + Serial.print("space"); + } else if (c == '"') { + Serial.print("quote"); + } else { + Serial.write(c); + } + Serial.print("\", "); + Serial.print(String(c, DEC)); + Serial.print(", "); + printHEX(c); + Serial.print(", "); + printBIN(c); + + uint8_t parityBit = parity_even_bit(c); // Calculate the parity bit + Serial.print(", "); + Serial.print(parityBit); + uint8_t c_par = c | (parityBit << 7); // Add parity bit to the outgoing character + Serial.print(", "); + printBIN(c_par); + + // print in reverse to show transmission order (transmitted LSB first) + Serial.print(", "); + printBIN(c_par, 8, true); + // print the levels of each bit with the start and stop bits and separated columns + // SDI-12 uses inverse logic, so a 0 bit is HIGH and a 1 bit is LOW + // Start bit is HIGH (equivalent to data bit 0) + // Stop bit is LOW (equivalent to data bit 1) + Serial.print(", 1"); // HIGH start bit + for (int8_t b = 0; b < 8; b++) { + Serial.print(", "); + Serial.print(!bitRead(c_par, b)); // Inverse logic! Use ! + } + Serial.print(", 0"); // LOW stop bit + Serial.println(); + } +} + +void loop() {} From d3b9d19e9fac7e348d6f54893350a163d42e2709 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Tue, 6 Aug 2024 12:31:38 -0400 Subject: [PATCH 62/96] Add parity check Signed-off-by: Sara Damiano --- src/SDI12.cpp | 13 ++++++++++++- src/SDI12.h | 10 ++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/SDI12.cpp b/src/SDI12.cpp index f494d9d..bc3ca02 100644 --- a/src/SDI12.cpp +++ b/src/SDI12.cpp @@ -359,6 +359,9 @@ void SDI12::setState(SDI12_STATES state) { pinMode(_dataPin, INPUT); // Turn off the pull-up resistor pinMode(_dataPin, OUTPUT); // Pin mode = output setPinInterrupts(false); // Interrupts disabled on data pin +#ifdef SDI12_CHECK_PARITY; + _parityFailure = false; // reset the parity failure flag +#endif break; } case SDI12_LISTENING: @@ -790,10 +793,18 @@ void ESPFAMILY_USE_INSTRUCTION_RAM SDI12::receiveISR() { } // If this was the 8th or more bit then the character and parity are complete. + // The stop bit may still be outstanding if (rxState > 7) { +#ifdef SDI12_CHECK_PARITY; + uint8_t rxParity = bitRead(rxValue, 7); // pull out the parity bit +#endif rxValue &= 0x7F; // Throw away the parity bit (and with 0b01111111) charToBuffer(rxValue); // Put the finished character into the buffer - +#ifdef SDI12_CHECK_PARITY; + uint8_t checkParity = + parity_even_bit(rxValue); // Calculate the parity bit from character w/o parity + if (rxParity != checkParity) { _parityFailure = true; } +#endif // if this is LOW, or we haven't exceeded the number of bits in a // character (but have gotten all the data bits) then this should be a diff --git a/src/SDI12.h b/src/SDI12.h index d149275..2bddc34 100644 --- a/src/SDI12.h +++ b/src/SDI12.h @@ -138,6 +138,13 @@ typedef const __FlashStringHelper* FlashString; /// a char not found in a valid ASCII numeric field #define NO_IGNORE_CHAR '\x01' +#ifndef SDI12_IGNORE_PARITY +/** + * @brief Check the value of the parity bit on reception + */ +#define SDI12_CHECK_PARITY +#endif + #ifndef SDI12_WAKE_DELAY /** * @brief The amount of additional time in milliseconds that the sensor takes to wake @@ -599,6 +606,9 @@ class SDI12 : public Stream { * @param dataPin The data pin's digital pin number */ void setDataPin(int8_t dataPin); +#ifdef SDI12_CHECK_PARITY; + bool _parityFailure; +#endif /**@}*/ From b94f9730bface0601d4c3f83c38770949edc284c Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Tue, 6 Aug 2024 13:38:42 -0400 Subject: [PATCH 63/96] Fix parity check Signed-off-by: Sara Damiano --- src/SDI12.cpp | 8 ++++---- src/SDI12.h | 14 +++++++++++++- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/SDI12.cpp b/src/SDI12.cpp index bc3ca02..d0bd0d3 100644 --- a/src/SDI12.cpp +++ b/src/SDI12.cpp @@ -359,7 +359,7 @@ void SDI12::setState(SDI12_STATES state) { pinMode(_dataPin, INPUT); // Turn off the pull-up resistor pinMode(_dataPin, OUTPUT); // Pin mode = output setPinInterrupts(false); // Interrupts disabled on data pin -#ifdef SDI12_CHECK_PARITY; +#ifdef SDI12_CHECK_PARITY _parityFailure = false; // reset the parity failure flag #endif break; @@ -795,12 +795,12 @@ void ESPFAMILY_USE_INSTRUCTION_RAM SDI12::receiveISR() { // If this was the 8th or more bit then the character and parity are complete. // The stop bit may still be outstanding if (rxState > 7) { -#ifdef SDI12_CHECK_PARITY; +#ifdef SDI12_CHECK_PARITY uint8_t rxParity = bitRead(rxValue, 7); // pull out the parity bit #endif rxValue &= 0x7F; // Throw away the parity bit (and with 0b01111111) charToBuffer(rxValue); // Put the finished character into the buffer -#ifdef SDI12_CHECK_PARITY; +#ifdef SDI12_CHECK_PARITY uint8_t checkParity = parity_even_bit(rxValue); // Calculate the parity bit from character w/o parity if (rxParity != checkParity) { _parityFailure = true; } @@ -810,7 +810,7 @@ void ESPFAMILY_USE_INSTRUCTION_RAM SDI12::receiveISR() { // character (but have gotten all the data bits) then this should be a // stop bit and we can start looking for a new start bit. if ((pinLevel == LOW) || !nextCharStarted) { - rxState = WAITING_FOR_START_BIT; // DISABLE STOP BIT TIMER + rxState = WAITING_FOR_START_BIT; // reset the rx state, stop waiting for stop bit } else { // If we just switched to HIGH, or we've exceeded the total number of // bits in a character, then the character must have ended with 1's/LOW, diff --git a/src/SDI12.h b/src/SDI12.h index 2bddc34..3f56a7d 100644 --- a/src/SDI12.h +++ b/src/SDI12.h @@ -274,6 +274,18 @@ class SDI12 : public Stream { * * - if 0: indicates that we got a start bit * - if >0: indicates the number of bits received + * + * 0 - got start bit + * 1 - got data bit 0 + * 2 - got data bit 1 + * 3 - got data bit 2 + * 4 - got data bit 3 + * 5 - got data bit 4 + * 6 - got data bit 5 + * 7 - got data bit 6 + * 8 - got data bit 7 (parity) + * 9 - got stop bit + * 255 - waiting for next start bit */ static uint8_t rxState; /** @@ -606,7 +618,7 @@ class SDI12 : public Stream { * @param dataPin The data pin's digital pin number */ void setDataPin(int8_t dataPin); -#ifdef SDI12_CHECK_PARITY; +#ifdef SDI12_CHECK_PARITY bool _parityFailure; #endif /**@}*/ From 7869cccb18bb782f4a6d916d12a2af2b4a705bf8 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Tue, 6 Aug 2024 14:04:17 -0400 Subject: [PATCH 64/96] rename tools to extras Signed-off-by: Sara Damiano --- {tools => extras}/SDI12_spy/SDI12_spy.ino | 0 {tools => extras}/TestCommands/TestCommands.ino | 0 {tools => extras}/TestSensorTiming/TestSensorTiming.ino | 0 {tools => extras}/TestWarmUp/TestWarmUp.ino | 0 {tools => extras}/powerOn/powerOn.ino | 0 .../print_character_parity/print_character_parity.ino | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename {tools => extras}/SDI12_spy/SDI12_spy.ino (100%) rename {tools => extras}/TestCommands/TestCommands.ino (100%) rename {tools => extras}/TestSensorTiming/TestSensorTiming.ino (100%) rename {tools => extras}/TestWarmUp/TestWarmUp.ino (100%) rename {tools => extras}/powerOn/powerOn.ino (100%) rename {tools => extras}/print_character_parity/print_character_parity.ino (100%) diff --git a/tools/SDI12_spy/SDI12_spy.ino b/extras/SDI12_spy/SDI12_spy.ino similarity index 100% rename from tools/SDI12_spy/SDI12_spy.ino rename to extras/SDI12_spy/SDI12_spy.ino diff --git a/tools/TestCommands/TestCommands.ino b/extras/TestCommands/TestCommands.ino similarity index 100% rename from tools/TestCommands/TestCommands.ino rename to extras/TestCommands/TestCommands.ino diff --git a/tools/TestSensorTiming/TestSensorTiming.ino b/extras/TestSensorTiming/TestSensorTiming.ino similarity index 100% rename from tools/TestSensorTiming/TestSensorTiming.ino rename to extras/TestSensorTiming/TestSensorTiming.ino diff --git a/tools/TestWarmUp/TestWarmUp.ino b/extras/TestWarmUp/TestWarmUp.ino similarity index 100% rename from tools/TestWarmUp/TestWarmUp.ino rename to extras/TestWarmUp/TestWarmUp.ino diff --git a/tools/powerOn/powerOn.ino b/extras/powerOn/powerOn.ino similarity index 100% rename from tools/powerOn/powerOn.ino rename to extras/powerOn/powerOn.ino diff --git a/tools/print_character_parity/print_character_parity.ino b/extras/print_character_parity/print_character_parity.ino similarity index 100% rename from tools/print_character_parity/print_character_parity.ino rename to extras/print_character_parity/print_character_parity.ino From f9c7a8088fd01ffc3249c5478f69812dd1303e19 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Tue, 6 Aug 2024 14:38:40 -0400 Subject: [PATCH 65/96] remove errant trailing comma Signed-off-by: Sara Damiano --- .github/workflows/build_examples.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_examples.yaml b/.github/workflows/build_examples.yaml index 7a5505d..403abf9 100644 --- a/.github/workflows/build_examples.yaml +++ b/.github/workflows/build_examples.yaml @@ -14,5 +14,5 @@ jobs: uses: EnviroDIY/workflows/.github/workflows/build_examples.yaml@main with: boards_to_build: 'mayfly,uno,megaatmega2560,leonardo,zeroUSB,arduino_nano_esp32,feather328p,feather32u4,adafruit_feather_m0,adafruit_feather_m4,adafruit_grandcentral_m4,huzzah,featheresp32,nodemcu,nodemcuv2,esp32dev,esp32-c3-devkitm-1,esp32-s3-devkitm-1' - examples_to_build: 'examples/a_wild_card,examples/b_address_change,examples/c_check_all_addresses,examples/d_simple_logger,examples/e_continuous_measurement,examples/f_basic_data_request,examples/g_terminal_window,examples/h_SDI-12_slave_implementation,examples/i_SDI-12_interface,examples/j_external_pcint_library,examples/k_concurrent_logger,examples/l_verify_crc/,' + examples_to_build: 'examples/a_wild_card,examples/b_address_change,examples/c_check_all_addresses,examples/d_simple_logger,examples/e_continuous_measurement,examples/f_basic_data_request,examples/g_terminal_window,examples/h_SDI-12_slave_implementation,examples/i_SDI-12_interface,examples/j_external_pcint_library,examples/k_concurrent_logger,examples/l_verify_crc/' secrets: inherit From 28bce898dd8123a52164a5981db553ee82ffc216 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Tue, 6 Aug 2024 16:21:52 -0400 Subject: [PATCH 66/96] Remove lingering stop bit interrupt defines Signed-off-by: Sara Damiano --- src/SDI12_boards.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/SDI12_boards.h b/src/SDI12_boards.h index b2d137d..f2ebb5b 100644 --- a/src/SDI12_boards.h +++ b/src/SDI12_boards.h @@ -348,12 +348,6 @@ sensors. This library provides a general software solution, without requiring #define GCLK_SYNCBUSY_SDI12 GCLK_SYNCBUSY_GENCTRL6 /// The timer controller to use #define SDI12_TC TC2 -/// The interupt request number for the SDI-12 stop bit compare interrupt -#define SDI12_TC_IRQn TC2_IRQn -/// The interupt handler for the SDI-12 stop bit compare interrupt -#define SDI12_STOP_HANDLER TC2_Handler -/// The timer capture/compare channel to use -#define SDI12_TC_CHANNEL 0 // The peripheral index within the generic clock for the selected timer controller #define SDI12_TC_GCLK_ID TC2_GCLK_ID From 3715643b616aa4fc64bccd279247a50f4bda7be6 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Tue, 6 Aug 2024 16:47:55 -0400 Subject: [PATCH 67/96] Update doxygen config Signed-off-by: Sara Damiano --- .gitignore | 1 + docs/Doxyfile | 15 ++- docs/DoxygenLayout.xml | 266 ----------------------------------------- 3 files changed, 10 insertions(+), 272 deletions(-) delete mode 100644 docs/DoxygenLayout.xml diff --git a/.gitignore b/.gitignore index d764eb6..b3058cc 100644 --- a/.gitignore +++ b/.gitignore @@ -166,3 +166,4 @@ SDI12 Repo Token.txt boards/ lib/ variants/ +docs/DoxygenLayout.xml_archive diff --git a/docs/Doxyfile b/docs/Doxyfile index d299d8a..d56d0ee 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -836,7 +836,7 @@ FILE_VERSION_FILTER = # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. -LAYOUT_FILE = DoxygenLayout.xml +LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib @@ -1015,6 +1015,7 @@ FILE_PATTERNS = *.c \ *.hpp \ *.h++ \ *.tpp \ + *.ino \ *.inc \ *.m \ *.markdown \ @@ -1036,7 +1037,9 @@ RECURSIVE = YES # Note that relative paths are relative to the directory from which doxygen is # run. -EXCLUDE = ../src/ReadMe.md +EXCLUDE = ../lib \ + ../boards \ + ../variants # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded @@ -1354,7 +1357,7 @@ HTML_FILE_EXTENSION = .html # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_HEADER = header.html +HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard @@ -1364,7 +1367,7 @@ HTML_HEADER = header.html # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_FOOTER = footer.html +HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of @@ -2341,7 +2344,7 @@ GENERATE_AUTOGEN_DEF = NO # database with symbols found by doxygen stored in tables. # The default value is: NO. -GENERATE_SQLITE3 = YES +GENERATE_SQLITE3 = NO # The SQLITE3_OUTPUT tag is used to specify where the Sqlite3 database will be # put. If a relative path is entered the value of OUTPUT_DIRECTORY will be put @@ -2561,7 +2564,7 @@ HIDE_UNDOC_RELATIONS = NO # set to NO # The default value is: NO. -HAVE_DOT = YES +HAVE_DOT = NO # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed # to run in parallel. When set to 0 doxygen will base this on the number of diff --git a/docs/DoxygenLayout.xml b/docs/DoxygenLayout.xml deleted file mode 100644 index 6de61a2..0000000 --- a/docs/DoxygenLayout.xml +++ /dev/null @@ -1,266 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From d1895369939f698d4beba9bb5fdaf372b09370c0 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Tue, 6 Aug 2024 16:50:02 -0400 Subject: [PATCH 68/96] Move datasheet quotes from .h to markdown Signed-off-by: Sara Damiano --- docs/SupportedBoardsAndTimers.md | 369 ++++++++++++++++++----------- src/SDI12_boards.h | 385 ++++++------------------------- 2 files changed, 298 insertions(+), 456 deletions(-) diff --git a/docs/SupportedBoardsAndTimers.md b/docs/SupportedBoardsAndTimers.md index 04b4879..2956357 100644 --- a/docs/SupportedBoardsAndTimers.md +++ b/docs/SupportedBoardsAndTimers.md @@ -6,7 +6,9 @@ Unfortunately, the "ticks" of the processor clock aren't perfectly aligned with With the clocks not perfectly aligned, we can't know exactly the time that a bit started or ended, just the time of the last readable tick. This means we need to do some averaging and "fudging" to align the two. -@See @page rx_page for more information on how a character is created. +@see rx_page for more information on how a character is created. + +## SDI-12 Timing Rules SDI-12 Communicates at 1200 baud (bits/s) and sends each character using 10 bits (7E1). @@ -35,6 +37,8 @@ SDI-12 Communicates at 1200 baud (bits/s) and sends each character using 10 bits - Outer retries are used after >112.5ms of inner retries have been attempted - A minimum of 3 "outer" retries are required. +## Ideal Timer Settings + When setting up our timers, the goal is to be able to have as many ticks as possible for each bit. The more ticks we have, the better job we can do with the needed averaging and fudging. With <10 ticks/bit, we probably won't be accurate enough to be functional. @@ -43,7 +47,7 @@ When acting as a recording device, it would be even better if the timer could la There is no benefit to the timer lasting longer than 112.5ms before rolling over. Each timer has finite options for pre-scaling, often in powers of 2. -To catch all the bits we need, when selecting the prescaler, we must round **UP** to the next closest available prescaler number (round **DOWN** the Hz) to give us more ticks than required. +To catch all the bits we need, when selecting the prescaler, we must round **UP** to the next closest available prescaler number (round **DOWN** the Hz) to give us *more* ticks than required. Using a 16 bit counter, the counter rolls after 65536 ticks. @@ -74,54 +78,61 @@ If we only have an 8 bit timer, the counter rolls after 256 ticks. ## AVR Boards -### Available Processor Speeds and Timers - -#### ATmega164A/PA/324A/PA/644A/PA/1284/P: -- Up to 20MIPS throughput at 20MHz - - Most Arduino boards are run at 16 or 8 MHz with a few at 12 MHz -- Two 8-bit Timer/Counters with Separate Prescalers and Compare Modes - - Timers 0 and 2 - - Prescalers available at 8/64/256/1024 on Timer 0 - - Prescalers available at 8/32/64/128/256/1024 on Timer 2 -- One/two 16-bit Timer/Counter with Separate Prescaler, Compare Mode, and Capture Mode - - Timers 1 and 3 - - Prescalers available at 8/64/256/1024 on Timers 1 and 3 - - Timer 3 is only available on the 1284p - -#### ATmega640/V-1280/V-1281/V-2560/V-2561/V: -- Up to 16 MIPS Throughput at 16MHz -– Two 8-bit Timer/Counters with Separate Prescaler and Compare Mode - - Timers 0 and 2 - - Prescalers available at 8/64/256/1024 on Timer 0 - - Prescalers available at 8/32/64/128/256/1024 on Timer 2 -– Four 16-bit Timer/Counters with Separate Prescaler, Compare- and Capture Mode - - Timers 1, 3, 4, and 5 - - Prescalers available at 8/64/256/1024 on Timer 1, 3, 4, and 5 - -#### ATmega16U4/ATmega32U4: -– Up to 16 MIPS Throughput at 16MHz -– One 8-bit Timer/Counter with Separate Prescaler and Compare Mode - - Timer 0 - - Prescalers available at 8/64/256/1024 on Timer 0 -– Two 16-bit Timer/Counter with Separate Prescaler, Compare- and Capture Mode - - Timers 1 and 3 - - Prescalers available at 8/64/256/1024 on Timer 1 and 3 -– One 10-bit High-Speed Timer/Counter with PLL (64MHz) and Compare Mode - - Timer 4 - - Prescalers available at 2/4/8/16/32/64/128/256/512/1024/2048/8192/169384 on Timer 4 -- There is no Timer 2 on the 16U4 or the 32U4 - -#### ATtiny25/V / ATtiny45/V / ATtiny85/V: -- Up to 20MIPS throughput at 20MHz - - Most Arduino boards are run at 16 or 8 MHz with a few at 12 MHz -– One 8-bit Timer/Counter with Prescaler and Two PWM Channels - - Timer 0 - - Prescalers available at 8/64/256/1024 -– One 8-bit High Speed Timer/Counter with Separate Prescaler - - Timer 1 - - Prescalers available at 64/128/256/512/1024/2048/4096/8192/16384 - -### Commonly Used Timers +### Available Timers on AVR Boards + +#### ATmega164A/PA/324A/PA/644A/PA/1284/P + +> - Up to 20MIPS throughput at 20MHz +> - Most Arduino boards are run at 16 or 8 MHz with a few at 12 MHz +> - Two 8-bit Timer/Counters with Separate Prescalers and Compare Modes +> - Timers 0 and 2 +> - Prescalers available at 8/64/256/1024 on Timer 0 +> - Prescalers available at 8/32/64/128/256/1024 on Timer 2 +> - One/two 16-bit Timer/Counter with Separate Prescaler, Compare Mode, and Capture Mode +> - Timers 1 and 3 +> - Prescalers available at 8/64/256/1024 on Timers 1 and 3 +> - Timer 3 is only available on the 1284p + +#### ATmega640/V-1280/V-1281/V-2560/V-2561/V + +> - Up to 16 MIPS Throughput at 16MHz +> – Two 8-bit Timer/Counters with Separate Prescaler and Compare Mode +> - Timers 0 and 2 +> - Prescalers available at 8/64/256/1024 on Timer 0 +> - Prescalers available at 8/32/64/128/256/1024 on Timer 2 +> – Four 16-bit Timer/Counters with Separate Prescaler, Compare- and Capture Mode +> - Timers 1, 3, 4, and 5 +> - Prescalers available at 8/64/256/1024 on Timer 1, 3, 4, and 5 + +#### ATtiny25/V / ATtiny45/V / ATtiny85/V + +> - Up to 20MIPS throughput at 20MHz +> - Most Arduino boards are run at 16 or 8 MHz with a few at 12 MHz +> – One 8-bit Timer/Counter with Prescaler and Two PWM Channels +> - Timer 0 +> - Prescalers available at 8/64/256/1024 +> – One 8-bit High Speed Timer/Counter with Separate Prescaler +> - Timer 1 +> - Prescalers available at 64/128/256/512/1024/2048/4096/8192/16384 + +#### ATmega16U4/ATmega32U4 + +> – Up to 16 MIPS Throughput at 16MHz +> – One 8-bit Timer/Counter with Separate Prescaler and Compare Mode +> - Timer 0 +> - Prescalers available at 8/64/256/1024 on Timer 0 +> – Two 16-bit Timer/Counter with Separate Prescaler, Compare- and Capture Mode +> - Timers 1 and 3 +> - Prescalers available at 8/64/256/1024 on Timer 1 and 3 +> – One 10-bit High-Speed Timer/Counter with PLL (64MHz) and Compare Mode +> - Timer 4 +> - Prescalers available at 2/4/8/16/32/64/128/256/512/1024/2048/8192/169384 on Timer 4 +> +[!NOTE] +There is no Timer 2 on the 16U4 or the 32U4 + +### Timers Used by Arduino AVR Core + - Timer 0 - [The primary clock (millis)](https://github.com/arduino/ArduinoCore-avr/blob/master/cores/arduino/wiring.c) - Fast hardware PWM @@ -146,116 +157,173 @@ If we only have an 8 bit timer, the counter rolls after 256 ticks. #### Selected Timers for SDI-12 +#### ATmega164A/PA/324A/PA/644A/PA/1284/P and ATmega640/V-1280/V-1281/V-2560/V-2561/V + For simplicity, we use Timer/Counter 2 on most AVR boards. +> Timer/Counter2 (TC2) is a general purpose, single channel, 8-bit Timer/Counter module. +> +> Features +> +> - Single Channel Counter +> - Clear Timer on Compare Match (Auto Reload) +> - Glitch-free, Phase Correct Pulse Width Modulator (PWM) +> - Frequency Generator +> - 10-bit Clock Prescaler +> - Overflow and Compare Match Interrupt Sources (TOV2, OCF2A, and OCF2B) +> - Allows Clocking from External 32kHz Watch Crystal Independent of the I/O Clock + +#### ATtiny25/V / ATtiny45/V / ATtiny85/V + On the ATTiny boards, we use Timer/Counter 1 +> The Timer/Counter1 features a high resolution and a high accuracy usage with the lower prescaling opportunities. +> It can also support two accurate, high speed, 8-bit pulse width modulators using clock speeds up to 64MHz (or 32MHz in low speedmode). + +#### ATmega16U4/ATmega32U4 + On the AtMega16U4 and AtMega32U4, we use Timer/Counter 4 as an 8-bit timer. +> Timer/Counter4 is a general purpose high speed Timer/Counter module, with three independent Output Compare Units, and with enhanced PWM support. +> +> Features +> - Up to 10-Bit Accuracy +> - Three Independent Output Compare Units +> - Clear Timer on Compare Match (Auto Reload) +> - Glitch Free, Phase and Frequency Correct Pulse Width Modulator (PWM) +> - Enhanced PWM mode: one optional additional accuracy bit without effect on output frequency +> - Variable PWM Period +> - Independent Dead Time Generators for each PWM channels +> - Synchronous update of PWM registers +> - Five Independent Interrupt Sources (TOV4, OCF4A, OCF4B, OCF4D, FPF4) +> - High Speed Asynchronous and Synchronous Clocking Modes +> - Separate Prescaler Unit + +[!NOTE] +We only utilize the low byte register of Timer 4, effectively using the 10-bit timer as an 8-bit timer. + ## SAMD Boards ### SAMD21 -#### Available Processor Speeds and Timers - -The Generic Clock controller GCLK provides nine Generic Clock Generators that can provide a wide range of clock frequencies. -Generators can be set to use different external and internal oscillators as source. -The clock of each Generator can be divided. -The outputs from the Generators are used as sources for the Generic Clock Multiplexers, which provide the Generic Clock (GCLK_PERIPHERAL) to the peripheral modules, as shown in Generic Clock Controller -Block Diagram. - -Features -- Provides Generic Clocks -- Wide frequency range -- Clock source for the generator can be changed on the fly - -The TC consists of a counter, a prescaler, compare/capture channels and control logic. -The counter can be set to count events, or it can be configured to count clock pulses. -The counter, together with the compare/capture channels, can be configured to timestamp input events, allowing capture of frequency and pulse width. -It can also perform waveform generation, such as frequency generation and pulse-width modulation (PWM). - -Features -- Selectable configuration - – Up to five 16-bit Timer/Counters (TC) including one low-power TC, each configurable as: - - 8-bit TC with two compare/capture channels - - 16-bit TC with two compare/capture channels - - 32-bit TC with two compare/capture channels, by using two TCs -- Waveform generation - – Frequency generation - – Single-slope pulse-width modulation -- Input capture - – Event capture - – Frequency capture - – Pulse-width capture -- One input event -- Interrupts/output events on: - – Counter overflow/underflow - – Compare match or capture -- Internal prescaler -- Can be used with DMA and to trigger DMA transactions - -#### Commonly Used Timers +#### Available Clocks and Timers on SAMD21 Boards + +##### Generic Clock Generators + +> The Generic Clock controller GCLK provides nine Generic Clock Generators that can provide a wide range of clock frequencies. +> Generators can be set to use different external and internal oscillators as source. +> The clock of each Generator can be divided. +> The outputs from the Generators are used as sources for the Generic Clock Multiplexers, which provide the Generic Clock (GCLK_PERIPHERAL) to the peripheral modules, as shown in Generic Clock Controller Block Diagram. +> +> Features +> +> - Provides Generic Clocks +> - Wide frequency range +> - Clock source for the generator can be changed on the fly + +##### Timer Controllers + +> The TC consists of a counter, a prescaler, compare/capture channels and control logic. +> The counter can be set to count events, or it can be configured to count clock pulses. +> The counter, together with the compare/capture channels, can be configured to timestamp input events, allowing capture of frequency and pulse width. +> It can also perform waveform generation, such as frequency generation and pulse-width modulation (PWM). +> +> Features +> +> - Selectable configuration +> – Up to five 16-bit Timer/Counters (TC) including one low-power TC, each configurable as: +> - 8-bit TC with two compare/capture channels +> - 16-bit TC with two compare/capture channels +> - 32-bit TC with two compare/capture channels, by using two TCs +> - Waveform generation +> – Frequency generation +> – Single-slope pulse-width modulation +> - Input capture +> – Event capture +> – Frequency capture +> – Pulse-width capture +> - One input event +> - Interrupts/output events on: +> – Counter overflow/underflow +> – Compare match or capture +> - Internal prescaler +> - Can be used with DMA and to trigger DMA transactions + +#### Timers Used by Arduino SAMD21 Core The Adafruit Arduino core uses: + - 0 as GENERIC_CLOCK_GENERATOR_MAIN (the main clock) The Adafruit Arduino core uses: + - TC5 for Tone - TC4 for Servo +#### Selected Timers for SDI-12 + +For SDI-12, we'll use Generic Clock Generator 4 and Timer Controller 3 + ### SAMD51/SAME51 -#### Available Processor Speeds and Timers - -Depending on the application, peripherals may require specific clock frequencies to operate correctly. -The Generic Clock controller (GCLK) features 12 Generic Clock Generators [11:0] that can provide a wide range of clock frequencies. - -Generators can be set to use different external and internal oscillators as source. -The clock of each Generator can be divided. -The outputs from the Generators are used as sources for the Peripheral Channels, which provide the Generic Clock (GCLK_PERIPH) to the peripheral modules, as shown in Figure 14-2. -The number of Peripheral Clocks depends on how many peripherals the device has. - -NOTE: The Generator 0 is always the direct source of the GCLK_MAIN signal. - -Features -- Provides a device-defined, configurable number of Peripheral Channel clocks -- Wide frequency range - - Various clock sources - - Embedded dividers - -There are up to eight TC peripheral instances. - -Each TC consists of a counter, a prescaler, compare/capture channels and control logic. -The counter can be set to count events, or clock pulses. -The counter, together with the compare/capture channels, can be configured to timestamp input events or IO pin edges, allowing for capturing of frequency and/or pulse width. - -A TC can also perform waveform generation, such as frequency generation and pulse-width modulation. - -Features - - Selectable configuration - - 8-, 16- or 32-bit TC operation, with compare/capture channels - - 2 compare/capture channels (CC) with: - - Double buffered timer period setting (in 8-bit mode only) - - Double buffered compare channel - - Waveform generation - - Frequency generation - - Single-slope pulse-width modulation - - Input capture - - Event / IO pin edge capture - - Frequency capture - - Pulse-width capture - - Time-stamp capture - - Minimum and maximum capture - - One input event - - Interrupts/output events on: - - Counter overflow/underflow - - Compare match or capture - - Internal prescaler - - DMA support - -#### Commonly Used Timers +#### Available Clocks and Timers on SAMD51 Boards + +##### Generic Clock Generators + +> Depending on the application, peripherals may require specific clock frequencies to operate correctly. +> The Generic Clock controller (GCLK) features 12 Generic Clock Generators [11:0] that can provide a wide range of clock frequencies. +> +> Generators can be set to use different external and internal oscillators as source. +> The clock of each Generator can be divided. +> The outputs from the Generators are used as sources for the Peripheral Channels, which provide the Generic Clock (GCLK_PERIPH) to the peripheral modules, as shown in Figure 14-2. +> The number of Peripheral Clocks depends on how many peripherals the device has. +> +> NOTE: The Generator 0 is always the direct source of the GCLK_MAIN signal. +> +> Features +> +> - Provides a device-defined, configurable number of Peripheral Channel clocks +> - Wide frequency range +> - Various clock sources +> - Embedded dividers + +##### Timer Controllers + +> There are up to eight TC peripheral instances. +> +> Each TC consists of a counter, a prescaler, compare/capture channels and control logic. +> The counter can be set to count events, or clock pulses. +> The counter, together with the compare/capture channels, can be configured to timestamp input events or IO pin edges, allowing for capturing of frequency and/or pulse width. +> +> A TC can also perform waveform generation, such as frequency generation and pulse-width modulation. +> +> Features +> +> - Selectable configuration +> - 8-, 16- or 32-bit TC operation, with compare/capture channels +> - 2 compare/capture channels (CC) with: +> - Double buffered timer period setting (in 8-bit mode only) +> - Double buffered compare channel +> - Waveform generation +> - Frequency generation +> - Single-slope pulse-width modulation +> - Input capture +> - Event / IO pin edge capture +> - Frequency capture +> - Pulse-width capture +> - Time-stamp capture +> - Minimum and maximum capture +> - One input event +> - Interrupts/output events on: +> - Counter overflow/underflow +> - Compare match or capture +> - Internal prescaler +> - DMA support + +#### Timers Used by Arduino SAMD51 Core The Adafruit Arduino core uses: + - 0 as GENERIC_CLOCK_GENERATOR_MAIN (the main clock, sourced from MAIN_CLOCK_SOURCE = GCLK_GENCTRL_SRC_DPLL0) - 1 as GENERIC_CLOCK_GENERATOR_48M (48MHz clock for USB and 'stuff', sourced from GCLK_GENCTRL_SRC_DPLL0) - 2 as GENERIC_CLOCK_GENERATOR_100M (100MHz clock for other peripherals, sourced from GCLK_GENCTRL_SRC_DPLL1) @@ -263,16 +331,35 @@ The Adafruit Arduino core uses: - 4 as GENERIC_CLOCK_GENERATOR_12M (12MHz clock for DAC, sourced from GCLK_GENCTRL_SRC_DPLL0) - 5 as GENERIC_CLOCK_GENERATOR_1M (??, sourced from CLK_GENCTRL_SRC_DPLL0) -For SDI-12, we'll use Timer Control 2 - The Adafruit Arduino core uses: + - TC0 for Tone (though any other timer may be used, if another pin is selected) - TC1 for Servo (though any other timer may be used, if another pin is selected) +#### Selected Timers for SDI-12 + +For SDI-12, we'll use Generic Clock Generator 6 and Timer Controller 2 + ## Other Boards For sufficiently fast boards, instead of using a dedicated processor timer, we can use the built-in `micros()` function as the timer. -Both the ESP8266 and ESP32 are fast enough that this works. + +From calculations using https://github.com/SRGDamia1/avrcycle, the micros() function takes about 60 (!!) clock cycles on a Mayfly. +We're going to blindly assume that the micros() function takes up about the same number of clock cycles for all Arduino boards. +This is probably a huge assumption, but go with it. +If we're going to use micros() fortiming, lets set a minimum usable CPU speed of the micros() function being accurate to 1µs. +That means we need to get 60 ticks/1µs or 60MHz. +Ehh.. Maybe we'll be generous and allow it down to 48MHz in the code. +That will allow Rensas AVR processors to attempt SDI-12. + +I know from testing, that we *cannot* use micros on a board 8MHz AVR board, but that it does work on a 80MHz Espressif8266. + +[!WARNING] +I haven't actually tested the minimum speed that this will work at! + +@TODO: Test 48MHz + +Both the ESP8266 and ESP32 are definitely fast enough that this works. - The ESP8266 runs at either 80 or 160 MHz - The ESP32 runs at 160 or 240 MHz. diff --git a/src/SDI12_boards.h b/src/SDI12_boards.h index f2ebb5b..caf97f7 100644 --- a/src/SDI12_boards.h +++ b/src/SDI12_boards.h @@ -26,76 +26,66 @@ sensors. This library provides a general software solution, without requiring #endif // defined(ESP32) || defined(ESP8266) +/** + * @def TIMER_IN_USE_STR + * @brief A string description of the timer to use + * + * @def TIMER_INT_TYPE + * @brief The interger type of the timer. + * + * @def TIMER_INT_SIZE + * @brief The size in bits of the timer count value. + * + * @def READTIME + * @brief The function or macro used to read the clock timer value. + * + * @def PRESCALE_IN_USE_STR + * @brief A string description of the prescaler in use. + * + * @def TICKS_PER_SECOND + * @brief The number of clock ticks per second, after accounting for the prescaler. + * + * @def TICKS_PER_BIT + * @brief The number of "ticks" of the timer that occur within the timing of one bit at + * the SDI-12 baud rate of 1200 bits/second. + * + * @def BITS_PER_TICK_Q10 + * @brief The number of "ticks" of the timer per SDI-12 bit, shifted by 2^10. + * + * @def RX_WINDOW_FUDGE + * @brief A "fudge factor" to get the Rx to work well. It mostly works to ensure that + * uneven tick increments get rounded up. + * + * @see https://github.com/SlashDevin/NeoSWSerial/pull/13 + */ + + // Most 'standard' AVR boards #if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) || \ defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || \ defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__) || \ defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega1284__) -/** - * @brief A string description of the timer to use - * - * Timer/Counter2 (TC2) is a general purpose, single channel, 8-bit Timer/Counter - * module. - * - * Features - * - Single Channel Counter - * - Clear Timer on Compare Match (Auto Reload) - * - Glitch-free, Phase Correct Pulse Width Modulator (PWM) - * - Frequency Generator - * - 10-bit Clock Prescaler - * - Overflow and Compare Match Interrupt Sources (TOV2, OCF2A, and OCF2B) - * - Allows Clocking from External 32kHz Watch Crystal Independent of the I/O Clock - */ +// Use Timer/Counter 2 on most AVR boards #define TIMER_IN_USE_STR "Timer2" -/** - * @brief The interger type of the timer. - * - * Timer 2 on AtMega boards is an 8-bit timer - */ +// Timer 2 on AtMega boards is an 8-bit timer #define TIMER_INT_TYPE uint8_t #define TIMER_INT_SIZE 8 -/** - * @brief The function or macro used to read the clock timer value. - * - * The c macro name for the register used to access the timer/counter 2 value is TCNT2 - */ #define READTIME TCNT2 #if F_CPU == 16000000L -/** - * @brief A string description of the prescaler in use. - */ #define PRESCALE_IN_USE_STR "16MHz/1024=15.625kHz" -/** - * @brief The number of clock ticks per second, after accounting for the prescaler. - * - * 16MHz / 1024 prescaler = 15625 'ticks'/sec = 64 µs / 'tick' - */ +// 16MHz / 1024 prescaler = 15625 'ticks'/sec = 64 µs / 'tick' #define TICKS_PER_SECOND 15625 #elif F_CPU == 12000000L -/** - * @brief A string description of the prescaler in use. - */ #define PRESCALE_IN_USE_STR "12MHz/1024=11.7kHz" -/** - * @brief The number of clock ticks per second, after accounting for the prescaler. - * - * 12MHz / 1024 prescaler = 11719 'ticks'/sec = 85.33 µs / 'tick' - */ +// 12MHz / 1024 prescaler = 11719 'ticks'/sec = 85.33 µs / 'tick' #define TICKS_PER_SECOND 11719 #elif F_CPU == 8000000L -/** - * @brief A string description of the prescaler in use. - */ #define PRESCALE_IN_USE_STR "8MHz/256=31.25kHz" -/** - * @brief The number of clock ticks per second, after accounting for the prescaler. - * - * 8MHz / 256 prescaler = 31250 'ticks'/sec = 32 µs / 'tick' - */ +// 8MHz / 256 prescaler = 31250 'ticks'/sec = 32 µs / 'tick' #define TICKS_PER_SECOND 31250 #endif // F_CPU @@ -104,47 +94,21 @@ sensors. This library provides a general software solution, without requiring // ATtiny boards (ie, adafruit trinket) #elif defined(__AVR_ATtiny25__) | defined(__AVR_ATtiny45__) | defined(__AVR_ATtiny85__) -/** - * @brief A string description of the timer to use - * - * The Timer/Counter1 features a high resolution and a high accuracy usage with the - * lower prescaling opportunities. It can also support two accurate, high speed, 8-bit - * pulse width modulators using clock speeds up to 64MHz (or 32MHz in low speedmode). - */ +// On the ATTiny boards, we use Timer/Counter 1 #define TIMER_IN_USE_STR "Timer1" -/** - * @brief The interger type of the timer. - * - * Timer 1 on the ATTiny boards is an 8-bit timer - */ +// Timer 1 on the ATTiny boards is an 8-bit timer #define TIMER_INT_TYPE uint8_t #define TIMER_INT_SIZE 8 -/** - * @brief The function or macro used to read the clock timer value. - * - * The c macro name for the register used to access the timer/counter 1 value is TCNT1 - */ #define READTIME TCNT1 #if F_CPU == 16000000L -/** - * @brief A string description of the prescaler in use. - */ #define PRESCALE_IN_USE_STR "16MHz/1024=15.625kHz" -/** - * @brief The number of clock ticks per second, after accounting for the prescaler. - * - * 16MHz / 1024 prescaler = 15625 'ticks'/sec = 15.625 kHz = 64 µs / 'tick' - */ +// 16MHz / 1024 prescaler = 15625 'ticks'/sec = 15.625 kHz = 64 µs / 'tick' #define TICKS_PER_SECOND 15625 #elif F_CPU == 8000000L #define PRESCALE_IN_USE_STR "8MHz/512=15.625kHz" -/** - * @brief The number of clock ticks per second, after accounting for the prescaler. - * - * 8MHz / 512 prescaler = 15625 'ticks'/sec = 15.625 kHz = 64 µs / 'tick' - */ +// 8MHz / 512 prescaler = 15625 'ticks'/sec = 15.625 kHz = 64 µs / 'tick' #define TICKS_PER_SECOND 15625 #endif // F_CPU @@ -154,66 +118,23 @@ sensors. This library provides a general software solution, without requiring #elif defined(ARDUINO_AVR_YUN) || defined(ARDUINO_AVR_LEONARDO) || \ defined(__AVR_ATmega32U4__) -/** - * @brief A string description of the timer to use - * - * Timer/Counter4 is a general purpose high speed Timer/Counter module, with three - * independent Output Compare Units, and with enhanced PWM support. - * - * Features - * - Up to 10-Bit Accuracy - * - Three Independent Output Compare Units - * - Clear Timer on Compare Match (Auto Reload) - * - Glitch Free, Phase and Frequency Correct Pulse Width Modulator (PWM) - * - Enhanced PWM mode: one optional additional accuracy bit without effect on output - * frequency - * - Variable PWM Period - * - Independent Dead Time Generators for each PWM channels - * - Synchronous update of PWM registers - * - Five Independent Interrupt Sources (TOV4, OCF4A, OCF4B, OCF4D, FPF4) - * - High Speed Asynchronous and Synchronous Clocking Modes - * - Separate Prescaler Unit - */ +// On the AtMega16U4 and AtMega32U4, we use Timer/Counter 4 as an 8-bit timer. #define TIMER_IN_USE_STR "Timer4" /** - * @brief The interger type of the timer. - * * Timer 4 on the U4 series is an 10-bit timer, but we're only using the lower 8 bits */ #define TIMER_INT_TYPE uint8_t #define TIMER_INT_SIZE 8 -/** - * @brief The function or macro used to read the clock timer value. - * - * The c macro name for the register used to access the timer/counter 4 value is TCNT4. - * - * @note We only utilize the low byte register, effectively using the 10-bit timer as an - * 8-bit timer. - */ #define READTIME TCNT4 #if F_CPU == 16000000L -/** - * @brief A string description of the prescaler in use. - */ #define PRESCALE_IN_USE_STR "16MHz/1024=15.625kHz" -/** - * @brief The number of clock ticks per second, after accounting for the prescaler. - * - * 16MHz / 1024 prescaler = 15625 'ticks'/sec = 64 µs / 'tick' - */ +// 16MHz / 1024 prescaler = 15625 'ticks'/sec = 64 µs / 'tick' #define TICKS_PER_SECOND 15625 #elif F_CPU == 8000000L -/** - * @brief A string description of the prescaler in use. - */ #define PRESCALE_IN_USE_STR "8MHz/512=15.625kHz" -/** - * @brief The number of clock ticks per second, after accounting for the prescaler. - * - * 8MHz / 512 prescaler = 15625 'ticks'/sec = 64 µs / 'tick' - */ +// 8MHz / 512 prescaler = 15625 'ticks'/sec = 64 µs / 'tick' #define TICKS_PER_SECOND 15625 #endif // F_CPU @@ -223,122 +144,32 @@ sensors. This library provides a general software solution, without requiring #elif defined(ARDUINO_SAMD_ZERO) || defined(__SAMD21G18A__) || \ defined(__SAMD21J18A__) || defined(__SAMD21E18A__) -/** - * @brief A string description of the timer to use - * - * For SDI-12, we'll use generic clock generator 4 - * - * The Adafruit Arduino core uses: - * - 0 as GENERIC_CLOCK_GENERATOR_MAIN (the main clock) - * - * For SDI-12, we'll use Timer Control 3 - * The Adafruit Arduino core uses: - * - TC5 for Tone - * - TC4 for Servo - * - * The Generic Clock controller GCLK provides nine Generic Clock Generators that can - * provide a wide range of clock frequencies. - * - * Generators can be set to use different external and internal oscillators as source. - * The clock of each Generator can be divided. The outputs from the Generators are used - * as sources for the Generic Clock Multiplexers, which provide the Generic Clock - * (GCLK_PERIPHERAL) to the peripheral modules, as shown in Generic Clock Controller - * Block Diagram. - * - * Features - * - Provides Generic Clocks - * - Wide frequency range - * - Clock source for the generator can be changed on the fly - * - * The TC consists of a counter, a prescaler, compare/capture channels and control - * logic. The counter can be set to count events, or it can be configured to count clock - * pulses. The counter, together with the compare/capture channels, can be configured to - * timestamp input events, allowing capture of frequency and pulse width. It can also - * perform waveform generation, such as frequency generation and pulse-width modulation - * (PWM). - * - * Features - * - Selectable configuration - * – Up to five 16-bit Timer/Counters (TC) including one low-power TC, each - * configurable as: - * - 8-bit TC with two compare/capture channels - * - 16-bit TC with two compare/capture channels - * - 32-bit TC with two compare/capture channels, by using two TCs - * - Waveform generation - * – Frequency generation - * – Single-slope pulse-width modulation - * - Input capture - * – Event capture - * – Frequency capture - * – Pulse-width capture - * - One input event - * - Interrupts/output events on: - * – Counter overflow/underflow - * – Compare match or capture - * - Internal prescaler - * - Can be used with DMA and to trigger DMA transactions - */ +// For SDI-12, we'll use Generic Clock Generator 4 and Timer Controller 3 #define TIMER_IN_USE_STR "GCLK4-TC3" - -/** - * @brief The interger type of the timer. - * - * We're using the timer in 16-bit mode - */ +// We're using the timer in 16-bit mode #define TIMER_INT_TYPE uint16_t #define TIMER_INT_SIZE 16 +/// The timer controller to use #define SDI12_TC TC3 -/** - * @brief The function or macro used to read the clock timer value. - * - * This signifies the register of timer/counter 3, the 16-bit count, the count value - * - * This is equivalent to TC3->COUNT16.COUNT.reg - */ +// This signifies the register of timer/counter 3, the 16-bit count, the count value +// This is equivalent to TC3->COUNT16.COUNT.reg #define READTIME REG_TC3_COUNT16_COUNT -/** - * @brief A string description of the prescaler in use. - */ #define PRESCALE_IN_USE_STR "48MHz/6/16=500kHz" -/** - * @brief The number of clock ticks per second, after accounting for the prescaler. - * - * Start with 48MHz "main" clock source (GCLK_GENCTRL_SRC_DFLL48M) - * 48MHz / 6x clock source divider (GCLK_GENDIV_DIV(6)) = 8MHz - * 8MHz / 16x prescaler (TC_CTRLA_PRESCALER_DIV16) = 500kHz = 500,000 'ticks'/sec - */ +// Start with 48MHz "main" clock source (GCLK_GENCTRL_SRC_DFLL48M) +// 48MHz / 6x clock source divider (GCLK_GENDIV_DIV(6)) = 8MHz +// 8MHz / 16x prescaler (TC_CTRLA_PRESCALER_DIV16) = 500kHz = 500,000 'ticks'/sec #define TICKS_PER_SECOND 500000 // SAMD51 and SAME51 boards #elif defined(__SAMD51__) || defined(__SAME51__) -/** - * @brief A string description of the timer to use - * - * For SDI-12, we'll use generic clock generator 6 - * The Adafruit Arduino core uses: - * - 0 as GENERIC_CLOCK_GENERATOR_MAIN - * - 1 as GENERIC_CLOCK_GENERATOR_48M - * - 2 as GENERIC_CLOCK_GENERATOR_100M - * - 3 as GENERIC_CLOCK_GENERATOR_XOSC32K - * - 4 as GENERIC_CLOCK_GENERATOR_12M - * - * For SDI-12, we'll use Timer Control 2 - * The Adafruit Arduino core uses: - * - TC0 as primary for Tone (any other timer could be used, depending on the pin) - * - TC1 for Servo (any other timer could be used, depending on the pin) - - */ +// For SDI-12, we'll use Generic Clock Generator 6 and Timer Controller 2 #define TIMER_IN_USE_STR "GCLK6-TC2" -/** - * @brief The interger type of the timer. - * - * We're using the timer in 16-bit mode - */ +// We're using the timer in 16-bit mode #define TIMER_INT_TYPE uint16_t #define TIMER_INT_SIZE 16 @@ -351,27 +182,17 @@ sensors. This library provides a general software solution, without requiring // The peripheral index within the generic clock for the selected timer controller #define SDI12_TC_GCLK_ID TC2_GCLK_ID -/** - * @brief The function or macro used to read the clock timer value. - * - * For the SAMD51, reading the timer is a multi-step process of first writing a read - * sync bit, waiting, and then reading the register. Because of the steps, we need a - * function. - */ +// For the SAMD51, reading the timer is a multi-step process of first writing a read +// sync bit, waiting, and then reading the register. Because of the steps, we need a +// function. #define READTIME sdi12timer.SDI12TimerRead() -/** - * @brief A string description of the prescaler in use. - */ #define PRESCALE_IN_USE_STR "120MHz/15/16=500kHz" -/** - * @brief The number of clock ticks per second, after accounting for the prescaler. - * - * Start with 120MHz "main" clock source (MAIN_CLOCK_SOURCE = GCLK_GENCTRL_SRC_DPLL0) - * 120MHz / 15x clock source divider (GCLK_GENCTRL_DIV(15)) = 8MHz - * 8MHz / 16x prescaler (TC_CTRLA_PRESCALER_DIV16) = 500kHz = 500,000 'ticks'/sec = 2 µs - * / 'tick' (1 sec/1200 bits) * (1 tick/2 µs) = 416.66667 ticks/bit - */ +// Start with 120MHz "main" clock source (MAIN_CLOCK_SOURCE = GCLK_GENCTRL_SRC_DPLL0) +// 120MHz / 15x clock source divider (GCLK_GENCTRL_DIV(15)) = 8MHz +// 8MHz / 16x prescaler (TC_CTRLA_PRESCALER_DIV16) = 500kHz +// 500,000 'ticks'/sec = 2 µs / 'tick' (1 sec/1200 bits) * (1 tick/2 µs) = 416.66667 +// ticks/bit #define TICKS_PER_SECOND 500000 // Espressif ESP32/ESP8266 boards or any boards faster than 48MHz not mentioned above @@ -389,30 +210,15 @@ sensors. This library provides a general software solution, without requiring // I know from testing, that we *cannot* use micros on a board 8MHz AVR board, but that // it does work on a 80MHz Espressif8266. -// WARNING: I haven't actullay tested the minimum speed that this will work at! +// WARNING: I haven't actually tested the minimum speed that this will work at! -/** - * @brief A string description of the timer to use - */ +// Using the micros() function #define TIMER_IN_USE_STR "micros()" -/** - * @brief The interger type of the timer. - * - * Since we're using `micros()`, this is 32 bit - */ +// Since we're using `micros()`, this is 32 bit #define TIMER_INT_TYPE uint32_t #define TIMER_INT_SIZE 32 -/** - * @brief The function or macro used to read the clock timer value. - * - * The c macro name for the register used to access the timer/counter 2 value is TCNT2 - */ #define READTIME sdi12timer.SDI12TimerRead() -/** - * @brief The number of clock ticks per second, after accounting for the prescaler. - * - * Since we're using micros() each 'tick' is 1µs - */ +// Since we're using micros() each 'tick' is 1µs #define TICKS_PER_SECOND 1000000 // Unknown board @@ -423,9 +229,6 @@ sensors. This library provides a general software solution, without requiring #if TICKS_PER_SECOND == 15625 && TIMER_INT_SIZE == 8 /** - * @brief The number of "ticks" of the timer that occur within the timing of one bit at - * the SDI-12 baud rate of 1200 bits/second. - * * 15625 'ticks'/sec = 64 µs / 'tick' * (1 sec/1200 bits) * (1 tick/64 µs) = 13.0208 ticks/bit * @@ -435,24 +238,13 @@ sensors. This library provides a general software solution, without requiring */ #define TICKS_PER_BIT 13 /** - * @brief The number of "ticks" of the timer per SDI-12 bit, shifted by 2^10. - * * 1/(13.0208 ticks/bit) * 2^10 = 78.6432 */ #define BITS_PER_TICK_Q10 79 -/** - * @brief A "fudge factor" to get the Rx to work well. It mostly works to ensure that - * uneven tick increments get rounded up. - * - * @see https://github.com/SlashDevin/NeoSWSerial/pull/13 - */ #define RX_WINDOW_FUDGE 2 #elif TICKS_PER_SECOND == 11719 && TIMER_INT_SIZE == 8 /** - * @brief The number of "ticks" of the timer that occur within the timing of one bit at - * the SDI-12 baud rate of 1200 bits/second. - * * 11719 'ticks'/sec = 85 µs / 'tick' * (1 sec/1200 bits) * (1 tick/85 µs) = 9.765625 ticks/bit * @@ -462,24 +254,13 @@ sensors. This library provides a general software solution, without requiring */ #define TICKS_PER_BIT 10 /** - * @brief The number of "ticks" of the timer per SDI-12 bit, shifted by 2^10. - * * 1/(9.765625 ticks/bit) * 2^10 = 104.8576 */ #define BITS_PER_TICK_Q10 105 -/** - * @brief A "fudge factor" to get the Rx to work well. It mostly works to ensure that - * uneven tick increments get rounded up. - * - * @see https://github.com/SlashDevin/NeoSWSerial/pull/13 - */ #define RX_WINDOW_FUDGE 2 #elif TICKS_PER_SECOND == 31250 && TIMER_INT_SIZE == 8 /** - * @brief The number of "ticks" of the timer that occur within the timing of one bit at - * the SDI-12 baud rate of 1200 bits/second. - * * 31250 'ticks'/sec = 32 µs / 'tick' * (1 sec/1200 bits) * (1 tick/32 µs) = 26.04166667 ticks/bit * @@ -490,25 +271,14 @@ sensors. This library provides a general software solution, without requiring */ #define TICKS_PER_BIT 26 /** - * @brief The number of "ticks" of the timer per SDI-12 bit, shifted by 2^10. - * * 1/(26.04166667 ticks/bit) * 2^10 = 39.3216 */ #define BITS_PER_TICK_Q10 39 -/** - * @brief A "fudge factor" to get the Rx to work well. It mostly works to ensure that - * uneven tick increments get rounded up. - * - * @see https://github.com/SlashDevin/NeoSWSerial/pull/13 - */ #define RX_WINDOW_FUDGE 10 #elif TICKS_PER_SECOND == 500000 && TIMER_INT_SIZE == 16 /** - * @brief The number of "ticks" of the timer that occur within the timing of one bit at - * the SDI-12 baud rate of 1200 bits/second. - * * 500kHz = 500,000 'ticks'/sec = 2 µs / 'tick' * (1 sec/1200 bits) * (1 tick/2 µs) = 416.66667 ticks/bit * @@ -517,32 +287,17 @@ sensors. This library provides a general software solution, without requiring * (65536 ticks/roll-over) * (1 sec/500000 ticks) = 131.07 milliseconds */ #define TICKS_PER_BIT 416 -/** - * @brief A "fudge factor" to get the Rx to work well. It mostly works to ensure that - * uneven tick increments get rounded up. - * - * @see https://github.com/SlashDevin/NeoSWSerial/pull/13 - */ #define RX_WINDOW_FUDGE 30 #elif TICKS_PER_SECOND == 1000000 && TIMER_INT_SIZE == 32 /** - * @brief The number of "ticks" of the timer that occur within the timing of one bit at - * the SDI-12 baud rate of 1200 bits/second. - * * Using `micros()` 1 "tick" is 1 µsec * (1 sec/1200 bits) * (1 tick/1 µs) * (1000000 µsec/sec)= 833.33333 ticks/bit * * The 32-bit timer rolls over after 4294967296 ticks, or 4294.9673 seconds */ -#define TICKS_PER_BIT 834 -/** - * @brief A "fudge factor" to get the Rx to work well. It mostly works to ensure that - * uneven tick increments get rounded up. - * - * @see https://github.com/SlashDevin/NeoSWSerial/pull/13 - */ -#define RX_WINDOW_FUDGE 1 +#define TICKS_PER_BIT 833 +#define RX_WINDOW_FUDGE 50 #else #error "Board timer is incorrectly configured!" From 79235b2d0f9dc827b195cf254ce1d318bcb7cbc0 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Tue, 6 Aug 2024 16:50:39 -0400 Subject: [PATCH 69/96] Rename ESPFAMILY_USE_INSTRUCTION_RAM Signed-off-by: Sara Damiano --- src/SDI12.cpp | 6 +++--- src/SDI12_boards.cpp | 2 +- src/SDI12_boards.h | 28 ++++++++++------------------ 3 files changed, 14 insertions(+), 22 deletions(-) diff --git a/src/SDI12.cpp b/src/SDI12.cpp index d0bd0d3..3de26d0 100644 --- a/src/SDI12.cpp +++ b/src/SDI12.cpp @@ -687,19 +687,19 @@ bool SDI12::verifyCRC(String& respWithCRC) { /* ================ Interrupt Service Routine =======================================*/ // 7.1 - Passes off responsibility for the interrupt to the active object. -void ESPFAMILY_USE_INSTRUCTION_RAM SDI12::handleInterrupt() { +void ISR_MEM_ACCESS SDI12::handleInterrupt() { if (_activeObject) _activeObject->receiveISR(); } // 7.2 - Creates a blank slate of bits for an incoming character -void ESPFAMILY_USE_INSTRUCTION_RAM SDI12::startChar() { +void ISR_MEM_ACCESS SDI12::startChar() { rxState = 0; // got a start bit rxMask = 0x01; // 0b00000001, bit mask, lsb first rxValue = 0x00; // 0b00000000, RX character to be, a blank slate } // startChar // 7.3 - The actual interrupt service routine -void ESPFAMILY_USE_INSTRUCTION_RAM SDI12::receiveISR() { +void ISR_MEM_ACCESS SDI12::receiveISR() { sdi12timer_t thisBitTCNT = READTIME; // time of this data transition (plus ISR latency) diff --git a/src/SDI12_boards.cpp b/src/SDI12_boards.cpp index d74071a..5f88508 100644 --- a/src/SDI12_boards.cpp +++ b/src/SDI12_boards.cpp @@ -512,7 +512,7 @@ void SDI12Timer::configSDI12TimerPrescale(void) {} void SDI12Timer::resetSDI12TimerPrescale(void) {} -sdi12timer_t ESPFAMILY_USE_INSTRUCTION_RAM SDI12Timer::SDI12TimerRead(void) { +sdi12timer_t ISR_MEM_ACCESS SDI12Timer::SDI12TimerRead(void) { return ((sdi12timer_t)(micros())); } diff --git a/src/SDI12_boards.h b/src/SDI12_boards.h index caf97f7..af031cd 100644 --- a/src/SDI12_boards.h +++ b/src/SDI12_boards.h @@ -17,12 +17,17 @@ sensors. This library provides a general software solution, without requiring #define SRC_SDI12_BOARDS_H_ #include - +/** + * @def ISR_MEM_ACCESS + * @brief Defines a memory access location, if needed for the interrupts service + * routines. + * + * On espressif boards (ESP8266 and ESP32), the ISR must be stored in IRAM + */ #if defined(ESP32) || defined(ESP8266) -// On espressif boards (ESP8266 and ESP32), the ISR must be stored in IRAM -#define ESPFAMILY_USE_INSTRUCTION_RAM IRAM_ATTR +#define ISR_MEM_ACCESS IRAM_ATTR #else -#define ESPFAMILY_USE_INSTRUCTION_RAM +#define ISR_MEM_ACCESS #endif // defined(ESP32) || defined(ESP8266) @@ -196,21 +201,8 @@ sensors. This library provides a general software solution, without requiring #define TICKS_PER_SECOND 500000 // Espressif ESP32/ESP8266 boards or any boards faster than 48MHz not mentioned above -#elif defined(ESP32) || defined(ESP8266) || F_CPU >= 48000000L - -// From calculations using https://github.com/SRGDamia1/avrcycle, the micros() function -// takes 60 (!!) clock cycles. We're going to blindly assume that the micros() function -// takes up about the same number of clock cycles for all Arduino boards. This is -// probably a huge assumption, but go with it. If we're going to use micros() for -// timing, lets set a minimum usable CPU speed of the micros() function being accurate -// to 1µs. That means we need to get 60 ticks/1µs or 60MHz. Ehh.. Maybe we'll be -// generous and try it down to 48MHz. -// TODO: Test 48MHz - -// I know from testing, that we *cannot* use micros on a board 8MHz AVR board, but that -// it does work on a 80MHz Espressif8266. - // WARNING: I haven't actually tested the minimum speed that this will work at! +#elif defined(ESP32) || defined(ESP8266) || F_CPU >= 48000000L // Using the micros() function #define TIMER_IN_USE_STR "micros()" From 800f9bbbc68c7c81bb7e8f2e02a912013544dc3e Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Tue, 6 Aug 2024 16:58:22 -0400 Subject: [PATCH 70/96] Remove bad / Signed-off-by: Sara Damiano --- .github/workflows/build_examples.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_examples.yaml b/.github/workflows/build_examples.yaml index 403abf9..702e188 100644 --- a/.github/workflows/build_examples.yaml +++ b/.github/workflows/build_examples.yaml @@ -14,5 +14,5 @@ jobs: uses: EnviroDIY/workflows/.github/workflows/build_examples.yaml@main with: boards_to_build: 'mayfly,uno,megaatmega2560,leonardo,zeroUSB,arduino_nano_esp32,feather328p,feather32u4,adafruit_feather_m0,adafruit_feather_m4,adafruit_grandcentral_m4,huzzah,featheresp32,nodemcu,nodemcuv2,esp32dev,esp32-c3-devkitm-1,esp32-s3-devkitm-1' - examples_to_build: 'examples/a_wild_card,examples/b_address_change,examples/c_check_all_addresses,examples/d_simple_logger,examples/e_continuous_measurement,examples/f_basic_data_request,examples/g_terminal_window,examples/h_SDI-12_slave_implementation,examples/i_SDI-12_interface,examples/j_external_pcint_library,examples/k_concurrent_logger,examples/l_verify_crc/' + examples_to_build: 'examples/a_wild_card,examples/b_address_change,examples/c_check_all_addresses,examples/d_simple_logger,examples/e_continuous_measurement,examples/f_basic_data_request,examples/g_terminal_window,examples/h_SDI-12_slave_implementation,examples/i_SDI-12_interface,examples/j_external_pcint_library,examples/k_concurrent_logger,examples/l_verify_crc' secrets: inherit From 9502bdfb751a9397b879df061739b7c64d5baa5b Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Thu, 8 Aug 2024 10:35:13 -0400 Subject: [PATCH 71/96] Update doxyfile to 1.12 Signed-off-by: Sara Damiano --- docs/Doxyfile | 416 ++++++++++++++++++++++++++------------------------ 1 file changed, 220 insertions(+), 196 deletions(-) diff --git a/docs/Doxyfile b/docs/Doxyfile index d56d0ee..4375af5 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -1,7 +1,7 @@ -# Doxyfile 1.11.0 +# Doxyfile 1.12.0 # This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project. +# Doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. @@ -15,10 +15,10 @@ # # Note: # -# Use doxygen to compare the used configuration file with the template +# Use Doxygen to compare the used configuration file with the template # configuration file: # doxygen -x [configFile] -# Use doxygen to compare the used configuration file with the template +# Use Doxygen to compare the used configuration file with the template # configuration file without replacing the environment variables or CMake type # replacement variables: # doxygen -x_noenv [configFile] @@ -71,15 +71,15 @@ PROJECT_ICON = enviroDIY_Favicon.png # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is -# entered, it will be relative to the location where doxygen was started. If +# entered, it will be relative to the location where Doxygen was started. If # left blank the current directory will be used. OUTPUT_DIRECTORY = ../../Arduino-SDI-12_Doxygen -# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096 +# If the CREATE_SUBDIRS tag is set to YES then Doxygen will create up to 4096 # sub-directories (in 2 levels) under the output directory of each output format # and will distribute the generated files over these directories. Enabling this -# option can be useful when feeding doxygen a huge amount of source files, where +# option can be useful when feeding Doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes # performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to # control the number of sub-directories. @@ -98,7 +98,7 @@ CREATE_SUBDIRS = NO CREATE_SUBDIRS_LEVEL = 8 -# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# If the ALLOW_UNICODE_NAMES tag is set to YES, Doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode # U+3044. @@ -107,7 +107,7 @@ CREATE_SUBDIRS_LEVEL = 8 ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this +# documentation generated by Doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian, # Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English @@ -121,21 +121,21 @@ ALLOW_UNICODE_NAMES = NO OUTPUT_LANGUAGE = English -# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# If the BRIEF_MEMBER_DESC tag is set to YES, Doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES -# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# If the REPEAT_BRIEF tag is set to YES, Doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. # The default value is: YES. -REPEAT_BRIEF = YES +REPEAT_BRIEF = NO # This tag implements a quasi-intelligent brief description abbreviator that is # used to form the text in various listings. Each string in this list, if found @@ -159,13 +159,13 @@ ABBREVIATE_BRIEF = "The $name class" \ the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# doxygen will generate a detailed section even if there is only a brief +# Doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = NO -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# If the INLINE_INHERITED_MEMB tag is set to YES, Doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. @@ -173,7 +173,7 @@ ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO -# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# If the FULL_PATH_NAMES tag is set to YES, Doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. @@ -183,11 +183,11 @@ FULL_PATH_NAMES = YES # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the path to +# If left blank the directory from which Doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which -# will be relative from the directory where doxygen is started. +# will be relative from the directory where Doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = .. @@ -201,14 +201,14 @@ STRIP_FROM_PATH = .. STRIP_FROM_INC_PATH = -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# If the SHORT_NAMES tag is set to YES, Doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = NO -# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen will interpret the # first line (until the first dot) of a Javadoc-style comment as the brief # description. If set to NO, the Javadoc-style will behave just like regular Qt- # style comments (thus requiring an explicit @brief command for a brief @@ -217,17 +217,17 @@ SHORT_NAMES = NO JAVADOC_AUTOBRIEF = NO -# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line +# If the JAVADOC_BANNER tag is set to YES then Doxygen will interpret a line # such as # /*************** # as being the beginning of a Javadoc-style comment "banner". If set to NO, the # Javadoc-style will behave just like regular comments and it will not be -# interpreted by doxygen. +# interpreted by Doxygen. # The default value is: NO. JAVADOC_BANNER = NO -# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus # requiring an explicit \brief command for a brief description.) @@ -235,7 +235,7 @@ JAVADOC_BANNER = NO QT_AUTOBRIEF = NO -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this @@ -247,10 +247,10 @@ QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = YES -# By default Python docstrings are displayed as preformatted text and doxygen's +# By default Python docstrings are displayed as preformatted text and Doxygen's # special commands cannot be used. By setting PYTHON_DOCSTRING to NO the -# doxygen's special commands can be used and the contents of the docstring -# documentation blocks is shown as doxygen documentation. +# Doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as Doxygen documentation. # The default value is: YES. PYTHON_DOCSTRING = YES @@ -261,7 +261,7 @@ PYTHON_DOCSTRING = YES INHERIT_DOCS = YES -# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# If the SEPARATE_MEMBER_PAGES tag is set to YES then Doxygen will produce a new # page for each member. If set to NO, the documentation of a member will be part # of the file/class/namespace that contains it. # The default value is: NO. @@ -346,30 +346,30 @@ OPTIMIZE_OUTPUT_SLICE = NO # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and -# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, +# language is one of the parsers supported by Doxygen: IDL, Java, JavaScript, # Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice, # VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: # FortranFree, unknown formatted Fortran: Fortran. In the later case the parser # tries to guess whether the code is fixed or free formatted code, this is the -# default for Fortran type files). For instance to make doxygen treat .inc files +# default for Fortran type files). For instance to make Doxygen treat .inc files # as Fortran files (default is PHP), and .f files as C (default is Fortran), # use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise -# the files are not read by doxygen. When specifying no_extension you should add +# the files are not read by Doxygen. When specifying no_extension you should add # * to the FILE_PATTERNS. # # Note see also the list of default file extension mappings. EXTENSION_MAPPING = ino=C++ -# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# If the MARKDOWN_SUPPORT tag is enabled then Doxygen pre-processes all comments # according to the Markdown format, which allows for more readable # documentation. See https://daringfireball.net/projects/markdown/ for details. -# The output of markdown processing is further processed by doxygen, so you can -# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# The output of markdown processing is further processed by Doxygen, so you can +# mix Doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. @@ -395,7 +395,7 @@ TOC_INCLUDE_HEADINGS = 10 MARKDOWN_ID_STYLE = GITHUB -# When enabled doxygen tries to link words that correspond to documented +# When enabled Doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or # globally by setting AUTOLINK_SUPPORT to NO. @@ -405,7 +405,7 @@ AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this -# tag to YES in order to let doxygen match functions declarations and +# tag to YES in order to let Doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); # versus func(std::string) {}). This also makes the inheritance and # collaboration diagrams that involve STL classes more complete and accurate. @@ -429,7 +429,7 @@ SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make -# doxygen to replace the get and set methods by a property in the documentation. +# Doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. @@ -438,7 +438,7 @@ SIP_SUPPORT = NO IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES then doxygen will reuse the documentation of the first +# tag is set to YES then Doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. @@ -496,18 +496,18 @@ TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the -# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small -# doxygen will become slower. If the cache is too large, memory is wasted. The +# code, Doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# Doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 -# symbols. At the end of a run doxygen will report the cache usage and suggest +# symbols. At the end of a run Doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 -# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use -# during processing. When set to 0 doxygen will based this on the number of +# The NUM_PROC_THREADS specifies the number of threads Doxygen is allowed to use +# during processing. When set to 0 Doxygen will based this on the number of # cores available in the system. You can set it explicitly to a value larger # than 0 to get more control over the balance between CPU load and processing # speed. At this moment only the input processing can be done using multiple @@ -531,7 +531,7 @@ TIMESTAMP = YES # Build related configuration options #--------------------------------------------------------------------------- -# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# If the EXTRACT_ALL tag is set to YES, Doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. @@ -597,7 +597,7 @@ EXTRACT_ANON_NSPACES = NO RESOLVE_UNNAMED_PARAMS = YES -# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. @@ -605,7 +605,7 @@ RESOLVE_UNNAMED_PARAMS = YES HIDE_UNDOC_MEMBERS = NO -# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO, these classes will be included in the various overviews. This option # will also hide undocumented C++ concepts if enabled. This option has no effect @@ -614,14 +614,14 @@ HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all friend # declarations. If set to NO, these declarations will be included in the # documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO -# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. If set to NO, these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. @@ -635,7 +635,7 @@ HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO -# With the correct setting of option CASE_SENSE_NAMES doxygen will better be +# With the correct setting of option CASE_SENSE_NAMES Doxygen will better be # able to match the capabilities of the underlying filesystem. In case the # filesystem is case sensitive (i.e. it supports files in the same directory # whose names only differ in casing), the option must be set to YES to properly @@ -644,7 +644,7 @@ INTERNAL_DOCS = NO # output files written for symbols that only differ in casing, such as for two # classes, one named CLASS and the other named Class, and to also support # references to files without having to specify the exact matching casing. On -# Windows (including Cygwin) and MacOS, users should typically set this option +# Windows (including Cygwin) and macOS, users should typically set this option # to NO, whereas on Linux or other Unix flavors it should typically be set to # YES. # Possible values are: SYSTEM, NO and YES. @@ -652,14 +652,14 @@ INTERNAL_DOCS = NO CASE_SENSE_NAMES = NO -# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# If the HIDE_SCOPE_NAMES tag is set to NO then Doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES, the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO -# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then Doxygen will # append additional text to a page's title, such as Class Reference. If set to # YES the compound reference will be hidden. # The default value is: NO. @@ -672,7 +672,7 @@ HIDE_COMPOUND_REFERENCE= NO SHOW_HEADERFILE = YES -# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# If the SHOW_INCLUDE_FILES tag is set to YES then Doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. @@ -685,7 +685,7 @@ SHOW_INCLUDE_FILES = YES SHOW_GROUPED_MEMB_INC = YES -# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. @@ -697,14 +697,14 @@ FORCE_LOCAL_INCLUDES = NO INLINE_INFO = YES -# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# If the SORT_MEMBER_DOCS tag is set to YES then Doxygen will sort the # (detailed) documentation of file and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = NO -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# If the SORT_BRIEF_DOCS tag is set to YES then Doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. @@ -712,7 +712,7 @@ SORT_MEMBER_DOCS = NO SORT_BRIEF_DOCS = NO -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then Doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. @@ -724,7 +724,7 @@ SORT_BRIEF_DOCS = NO SORT_MEMBERS_CTORS_1ST = NO -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# If the SORT_GROUP_NAMES tag is set to YES then Doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. @@ -741,11 +741,11 @@ SORT_GROUP_NAMES = NO SORT_BY_SCOPE_NAME = YES -# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# If the STRICT_PROTO_MATCHING option is enabled and Doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a -# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# simple string match. By disabling STRICT_PROTO_MATCHING Doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. @@ -815,25 +815,25 @@ SHOW_FILES = YES SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from +# Doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command command input-file, where command is the value of the # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided -# by doxygen. Whatever the program writes to standard output is used as the file +# by Doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed -# by doxygen. The layout file controls the global structure of the generated +# by Doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file -# that represents doxygen's defaults, run doxygen with the -l option. You can +# that represents Doxygen's defaults, run Doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml # will be used as the name of the layout file. See also section "Changing the # layout of pages" for information. # -# Note that if you run doxygen from a directory containing a file called -# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# Note that if you run Doxygen from a directory containing a file called +# DoxygenLayout.xml, Doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = @@ -848,19 +848,35 @@ LAYOUT_FILE = CITE_BIB_FILES = +# The EXTERNAL_TOOL_PATH tag can be used to extend the search path (PATH +# environment variable) so that external tools such as latex and gs can be +# found. +# Note: Directories specified with EXTERNAL_TOOL_PATH are added in front of the +# path already specified by the PATH variable, and are added in the order +# specified. +# Note: This option is particularly useful for macOS version 14 (Sonoma) and +# higher, when running Doxygen from Doxywizard, because in this case any user- +# defined changes to the PATH are ignored. A typical example on macOS is to set +# EXTERNAL_TOOL_PATH = /Library/TeX/texbin /usr/local/bin +# together with the standard path, the full search path used by doxygen when +# launching external tools will then become +# PATH=/Library/TeX/texbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin + +EXTERNAL_TOOL_PATH = + #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to -# standard output by doxygen. If QUIET is set to YES this implies that the +# standard output by Doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are -# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# generated to standard error (stderr) by Doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. @@ -868,14 +884,14 @@ QUIET = NO WARNINGS = YES -# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# If the WARN_IF_UNDOCUMENTED tag is set to YES then Doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = YES -# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# If the WARN_IF_DOC_ERROR tag is set to YES, Doxygen will generate warnings for # potential errors in the documentation, such as documenting some parameters in # a documented function twice, or documenting parameters that don't exist or # using markup commands wrongly. @@ -883,8 +899,8 @@ WARN_IF_UNDOCUMENTED = YES WARN_IF_DOC_ERROR = YES -# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete -# function parameter documentation. If set to NO, doxygen will accept that some +# If WARN_IF_INCOMPLETE_DOC is set to YES, Doxygen will warn about incomplete +# function parameter documentation. If set to NO, Doxygen will accept that some # parameters have no documentation without warning. # The default value is: YES. @@ -892,7 +908,7 @@ WARN_IF_INCOMPLETE_DOC = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return -# value. If set to NO, doxygen will only warn about wrong parameter +# value. If set to NO, Doxygen will only warn about wrong parameter # documentation, but not about the absence of documentation. If EXTRACT_ALL is # set to YES then this flag will automatically be disabled. See also # WARN_IF_INCOMPLETE_DOC @@ -900,20 +916,20 @@ WARN_IF_INCOMPLETE_DOC = YES WARN_NO_PARAMDOC = YES -# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about -# undocumented enumeration values. If set to NO, doxygen will accept +# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, Doxygen will warn about +# undocumented enumeration values. If set to NO, Doxygen will accept # undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: NO. WARN_IF_UNDOC_ENUM_VAL = YES -# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# If the WARN_AS_ERROR tag is set to YES then Doxygen will immediately stop when # a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS -# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but -# at the end of the doxygen process doxygen will return with a non-zero status. -# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves -# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not +# then Doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the Doxygen process Doxygen will return with a non-zero status. +# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then Doxygen behaves +# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined Doxygen will not # write the warning messages in between other messages but write them at the end # of a run, in case a WARN_LOGFILE is defined the warning messages will be # besides being in the defined file also be shown at the end of a run, unless @@ -924,7 +940,7 @@ WARN_IF_UNDOC_ENUM_VAL = YES WARN_AS_ERROR = FAIL_ON_WARNINGS_PRINT -# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# The WARN_FORMAT tag determines the format of the warning messages that Doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will @@ -937,7 +953,7 @@ WARN_FORMAT = "$file:$line: $text" # In the $text part of the WARN_FORMAT command it is possible that a reference # to a more specific place is given. To make it easier to jump to this place -# (outside of doxygen) the user can define a custom "cut" / "paste" string. +# (outside of Doxygen) the user can define a custom "cut" / "paste" string. # Example: # WARN_LINE_FORMAT = "'vi $file +$line'" # See also: WARN_FORMAT @@ -967,7 +983,7 @@ WARN_LOGFILE = output_doxygen.log INPUT = .. # This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# that Doxygen parses. Internally Doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: # https://www.gnu.org/software/libiconv/) for the list of possible encodings. @@ -977,7 +993,7 @@ INPUT = .. INPUT_ENCODING = UTF-8 # This tag can be used to specify the character encoding of the source files -# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify +# that Doxygen parses The INPUT_FILE_ENCODING tag can be used to specify # character encoding on a per file pattern basis. Doxygen will compare the file # name with each pattern and apply the encoding instead of the default # INPUT_ENCODING) if there is a match. The character encodings are a list of the @@ -992,7 +1008,7 @@ INPUT_FILE_ENCODING = # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not -# read by doxygen. +# read by Doxygen. # # Note the list of default checked file patterns might differ from the list of # default file extension mappings. @@ -1001,7 +1017,7 @@ INPUT_FILE_ENCODING = # *.cpp, *.cppm, *.ccm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, # *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d, # *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to -# be provided as doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# be provided as Doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, # *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. FILE_PATTERNS = *.c \ @@ -1015,7 +1031,6 @@ FILE_PATTERNS = *.c \ *.hpp \ *.h++ \ *.tpp \ - *.ino \ *.inc \ *.m \ *.markdown \ @@ -1034,10 +1049,13 @@ RECURSIVE = YES # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # -# Note that relative paths are relative to the directory from which doxygen is +# Note that relative paths are relative to the directory from which Doxygen is # run. -EXCLUDE = ../lib \ +EXCLUDE = ../src/ReadMe.md \ + ../docs/mermaidDiagrams.md \ + ../docs/dotDiagrams.dox \ + ../lib \ ../boards \ ../variants @@ -1098,7 +1116,7 @@ EXAMPLE_RECURSIVE = YES IMAGE_PATH = -# The INPUT_FILTER tag can be used to specify a program that doxygen should +# The INPUT_FILTER tag can be used to specify a program that Doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # @@ -1113,14 +1131,14 @@ IMAGE_PATH = # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. # -# Note that doxygen will use the data processed and written to standard output +# Note that Doxygen will use the data processed and written to standard output # for further processing, therefore nothing else, like debug statements or used # commands (so in case of a Windows batch file always use @echo OFF), should be # written to standard output. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not -# properly processed by doxygen. +# properly processed by Doxygen. INPUT_FILTER = @@ -1133,7 +1151,7 @@ INPUT_FILTER = # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not -# properly processed by doxygen. +# properly processed by Doxygen. FILTER_PATTERNS = "*.md=python markdown_prefilter.py" @@ -1155,7 +1173,7 @@ FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub -# and want to reuse the introduction page also for the doxygen output. +# and want to reuse the introduction page also for the Doxygen output. USE_MDFILE_AS_MAINPAGE = @@ -1188,7 +1206,7 @@ SOURCE_BROWSER = YES INLINE_SOURCES = NO -# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct Doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. @@ -1226,7 +1244,7 @@ REFERENCES_LINK_SOURCE = YES SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will -# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# point to the HTML generated by the htags(1) tool instead of Doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system # (see https://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. @@ -1240,14 +1258,14 @@ SOURCE_TOOLTIPS = YES # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # -# The result: instead of the source browser generated by doxygen, the links to +# The result: instead of the source browser generated by Doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO -# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# If the VERBATIM_HEADERS tag is set the YES then Doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. @@ -1255,19 +1273,19 @@ USE_HTAGS = NO VERBATIM_HEADERS = YES -# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# If the CLANG_ASSISTED_PARSING tag is set to YES then Doxygen will use the # clang parser (see: # http://clang.llvm.org/) for more accurate parsing at the cost of reduced # performance. This can be particularly helpful with template rich C++ code for -# which doxygen's built-in parser lacks the necessary type information. -# Note: The availability of this option depends on whether or not doxygen was +# which Doxygen's built-in parser lacks the necessary type information. +# Note: The availability of this option depends on whether or not Doxygen was # generated with the -Duse_libclang=ON option for CMake. # The default value is: NO. CLANG_ASSISTED_PARSING = NO # If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS -# tag is set to YES then doxygen will add the directory of each input to the +# tag is set to YES then Doxygen will add the directory of each input to the # include path. # The default value is: YES. # This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. @@ -1276,7 +1294,7 @@ CLANG_ADD_INC_PATHS = YES # If clang assisted parsing is enabled you can provide the compiler with command # line options that you would normally use when invoking the compiler. Note that -# the include paths will already be set by doxygen for the files and directories +# the include paths will already be set by Doxygen for the files and directories # specified with INPUT and INCLUDE_PATH. # This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. @@ -1290,7 +1308,7 @@ CLANG_OPTIONS = # specifying the -p option to a clang tool, such as clang-check. These options # will then be passed to the parser. Any options specified with CLANG_OPTIONS # will be added as well. -# Note: The availability of this option depends on whether or not doxygen was +# Note: The availability of this option depends on whether or not Doxygen was # generated with the -Duse_libclang=ON option for CMake. CLANG_DATABASE_PATH = @@ -1319,7 +1337,7 @@ IGNORE_PREFIX = # Configuration options related to the HTML output #--------------------------------------------------------------------------- -# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# If the GENERATE_HTML tag is set to YES, Doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES @@ -1340,40 +1358,40 @@ HTML_OUTPUT = html HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for -# each generated HTML page. If the tag is left blank doxygen will generate a +# each generated HTML page. If the tag is left blank Doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets -# that doxygen needs, which is dependent on the configuration options used (e.g. +# that Doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" -# for information on how to generate the default header that doxygen normally +# for information on how to generate the default header that Doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the -# default header when upgrading to a newer version of doxygen. For a description +# default header when upgrading to a newer version of Doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each -# generated HTML page. If the tag is left blank doxygen will generate a standard +# generated HTML page. If the tag is left blank Doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer -# that doxygen normally uses. +# that Doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of -# the HTML output. If left blank doxygen will generate a default style sheet. +# the HTML output. If left blank Doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style -# sheet that doxygen normally uses. +# sheet that Doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. @@ -1383,7 +1401,7 @@ HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets -# created by doxygen. Using this option one can overrule certain style aspects. +# created by Doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet files to the output directory. @@ -1478,7 +1496,7 @@ HTML_DYNAMIC_SECTIONS = YES HTML_CODE_FOLDING = YES -# If the HTML_COPY_CLIPBOARD tag is set to YES then doxygen will show an icon in +# If the HTML_COPY_CLIPBOARD tag is set to YES then Doxygen will show an icon in # the top right corner of code and text fragments that allows the user to copy # its content to the clipboard. Note this only works if supported by the browser # and the web page is served via a secure context (see: @@ -1491,7 +1509,7 @@ HTML_COPY_CLIPBOARD = YES # Doxygen stores a couple of settings persistently in the browser (via e.g. # cookies). By default these settings apply to all HTML pages generated by -# doxygen across all projects. The HTML_PROJECT_COOKIE tag can be used to store +# Doxygen across all projects. The HTML_PROJECT_COOKIE tag can be used to store # the settings under a project specific key, such that the user preferences will # be stored separately. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1515,7 +1533,7 @@ HTML_INDEX_NUM_ENTRIES = 500 # generated that can be used as input for Apple's Xcode 3 integrated development # environment (see: # https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To -# create a documentation set, doxygen will generate a Makefile in the HTML +# create a documentation set, Doxygen will generate a Makefile in the HTML # output directory. Running make will produce the docset in that directory and # running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at @@ -1563,7 +1581,7 @@ DOCSET_PUBLISHER_ID = org.doxygen.Publisher DOCSET_PUBLISHER_NAME = Publisher -# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# If the GENERATE_HTMLHELP tag is set to YES then Doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop # on Windows. In the beginning of 2021 Microsoft took the original page, with @@ -1574,7 +1592,7 @@ DOCSET_PUBLISHER_NAME = Publisher # ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). # # The HTML Help Workshop contains a compiler that can convert all HTML output -# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# generated by Doxygen into a single compiled HTML file (.chm). Compiled HTML # files are now used as the Windows 98 help format, and will replace the old # Windows help format (.hlp) on all Windows platforms in the future. Compressed # HTML files also contain an index, a table of contents, and you can search for @@ -1594,7 +1612,7 @@ CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler (hhc.exe). If non-empty, -# doxygen will try to run the HTML help compiler on the generated index.hhp. +# Doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. @@ -1696,7 +1714,7 @@ QHP_CUST_FILTER_ATTRS = QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location (absolute path -# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to +# including file name) of Qt's qhelpgenerator. If non-empty Doxygen will try to # run qhelpgenerator on the generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1741,7 +1759,7 @@ DISABLE_INDEX = NO # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can # further fine tune the look of the index (see "Fine-tuning the output"). As an -# example, the default style sheet generated by doxygen has an example that +# example, the default style sheet generated by Doxygen has an example that # shows how to put an image at the root of the tree instead of the PROJECT_NAME. # Since the tree basically has the same information as the tab index, you could # consider setting DISABLE_INDEX to YES when enabling this option. @@ -1763,7 +1781,7 @@ GENERATE_TREEVIEW = YES FULL_SIDEBAR = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that -# doxygen will group on one line in the generated HTML documentation. +# Doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. @@ -1772,6 +1790,12 @@ FULL_SIDEBAR = NO ENUM_VALUES_PER_LINE = 4 +# When the SHOW_ENUM_VALUES tag is set doxygen will show the specified +# enumeration values besides the enumeration mnemonics. +# The default value is: NO. + +SHOW_ENUM_VALUES = YES + # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. @@ -1779,21 +1803,21 @@ ENUM_VALUES_PER_LINE = 4 TREEVIEW_WIDTH = 200 -# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# If the EXT_LINKS_IN_WINDOW option is set to YES, Doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO -# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email +# If the OBFUSCATE_EMAILS tag is set to YES, Doxygen will obfuscate email # addresses. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. OBFUSCATE_EMAILS = YES -# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg +# If the HTML_FORMULA_FORMAT option is set to svg, Doxygen will use the pdf2svg # tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see # https://inkscape.org) to generate formulas as SVG images instead of PNGs for # the HTML output. These images will generally look nicer at scaled resolutions. @@ -1806,7 +1830,7 @@ HTML_FORMULA_FORMAT = png # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful -# doxygen run you need to manually remove any form_*.png images from the HTML +# Doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1884,7 +1908,7 @@ MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@2 MATHJAX_EXTENSIONS = -# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# The MATHJAX_CODEFILE tag can be used to specify a file with JavaScript pieces # of code that will be used on startup of the MathJax code. See the MathJax site # (see: # http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an @@ -1893,12 +1917,12 @@ MATHJAX_EXTENSIONS = MATHJAX_CODEFILE = -# When the SEARCHENGINE tag is enabled doxygen will generate a search box for -# the HTML output. The underlying search engine uses javascript and DHTML and +# When the SEARCHENGINE tag is enabled Doxygen will generate a search box for +# the HTML output. The underlying search engine uses JavaScript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. -# For large projects the javascript based search engine can be slow, then +# For large projects the JavaScript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically @@ -1917,7 +1941,7 @@ SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a web server instead of a web client using JavaScript. There # are two flavors of web server based searching depending on the EXTERNAL_SEARCH -# setting. When disabled, doxygen will generate a PHP script for searching and +# setting. When disabled, Doxygen will generate a PHP script for searching and # an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing # and searching needs to be provided by external tools. See the section # "External Indexing and Searching" for details. @@ -1926,7 +1950,7 @@ SEARCHENGINE = YES SERVER_BASED_SEARCH = NO -# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP +# When EXTERNAL_SEARCH tag is enabled Doxygen will no longer generate the PHP # script for searching. Instead the search results are written to an XML file # which needs to be processed by an external indexer. Doxygen will invoke an # external search engine pointed to by the SEARCHENGINE_URL option to obtain the @@ -1971,7 +1995,7 @@ SEARCHDATA_FILE = searchdata.xml EXTERNAL_SEARCH_ID = -# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen +# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through Doxygen # projects other than the one defined by this configuration file, but that are # all added to the same external search index. Each project needs to have a # unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of @@ -1985,7 +2009,7 @@ EXTRA_SEARCH_MAPPINGS = # Configuration options related to the LaTeX output #--------------------------------------------------------------------------- -# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. +# If the GENERATE_LATEX tag is set to YES, Doxygen will generate LaTeX output. # The default value is: YES. GENERATE_LATEX = NO @@ -2030,7 +2054,7 @@ MAKEINDEX_CMD_NAME = makeindex LATEX_MAKEINDEX_CMD = makeindex -# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX +# If the COMPACT_LATEX tag is set to YES, Doxygen generates more compact LaTeX # documents. This may be useful for small projects and may help to save some # trees in general. # The default value is: NO. @@ -2061,15 +2085,15 @@ EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for # the generated LaTeX document. The header should contain everything until the -# first chapter. If it is left blank doxygen will generate a standard header. It +# first chapter. If it is left blank Doxygen will generate a standard header. It # is highly recommended to start with a default header using # doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty # and then modify the file new_header.tex. See also section "Doxygen usage" for -# information on how to generate the default header that doxygen normally uses. +# information on how to generate the default header that Doxygen normally uses. # # Note: Only use a user-defined header if you know what you are doing! # Note: The header is subject to change so you typically have to regenerate the -# default header when upgrading to a newer version of doxygen. The following +# default header when upgrading to a newer version of Doxygen. The following # commands have a special meaning inside the header (and footer): For a # description of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -2078,10 +2102,10 @@ LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for # the generated LaTeX document. The footer should contain everything after the -# last chapter. If it is left blank doxygen will generate a standard footer. See +# last chapter. If it is left blank Doxygen will generate a standard footer. See # LATEX_HEADER for more information on how to generate a default footer and what # special commands can be used inside the footer. See also section "Doxygen -# usage" for information on how to generate the default footer that doxygen +# usage" for information on how to generate the default footer that Doxygen # normally uses. Note: Only use a user-defined footer if you know what you are # doing! # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -2090,7 +2114,7 @@ LATEX_FOOTER = # The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined # LaTeX style sheets that are included after the standard style sheets created -# by doxygen. Using this option one can overrule certain style aspects. Doxygen +# by Doxygen. Using this option one can overrule certain style aspects. Doxygen # will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the @@ -2116,7 +2140,7 @@ LATEX_EXTRA_FILES = PDF_HYPERLINKS = YES -# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as +# If the USE_PDFLATEX tag is set to YES, Doxygen will use the engine as # specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX # files. Set this option to YES, to get a higher quality PDF documentation. # @@ -2141,7 +2165,7 @@ USE_PDFLATEX = YES LATEX_BATCHMODE = NO -# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the +# If the LATEX_HIDE_INDICES tag is set to YES then Doxygen will not include the # index chapters (such as File Index, Compound Index, etc.) in the output. # The default value is: NO. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -2168,7 +2192,7 @@ LATEX_EMOJI_DIRECTORY = # Configuration options related to the RTF output #--------------------------------------------------------------------------- -# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The +# If the GENERATE_RTF tag is set to YES, Doxygen will generate RTF output. The # RTF output is optimized for Word 97 and may not look too pretty with other RTF # readers/editors. # The default value is: NO. @@ -2183,7 +2207,7 @@ GENERATE_RTF = NO RTF_OUTPUT = rtf -# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF +# If the COMPACT_RTF tag is set to YES, Doxygen generates more compact RTF # documents. This may be useful for small projects and may help to save some # trees in general. # The default value is: NO. @@ -2203,18 +2227,18 @@ COMPACT_RTF = NO RTF_HYPERLINKS = NO -# Load stylesheet definitions from file. Syntax is similar to doxygen's +# Load stylesheet definitions from file. Syntax is similar to Doxygen's # configuration file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. # # See also section "Doxygen usage" for information on how to generate the -# default style sheet that doxygen normally uses. +# default style sheet that Doxygen normally uses. # This tag requires that the tag GENERATE_RTF is set to YES. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an RTF document. Syntax is -# similar to doxygen's configuration file. A template extensions file can be +# similar to Doxygen's configuration file. A template extensions file can be # generated using doxygen -e rtf extensionFile. # This tag requires that the tag GENERATE_RTF is set to YES. @@ -2232,7 +2256,7 @@ RTF_EXTRA_FILES = # Configuration options related to the man page output #--------------------------------------------------------------------------- -# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for +# If the GENERATE_MAN tag is set to YES, Doxygen will generate man pages for # classes and files. # The default value is: NO. @@ -2263,7 +2287,7 @@ MAN_EXTENSION = .3 MAN_SUBDIR = -# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, then it # will generate one additional man file for each entity documented in the real # man page(s). These additional files only source the real man page, but without # them the man command would be unable to find the correct page. @@ -2276,7 +2300,7 @@ MAN_LINKS = NO # Configuration options related to the XML output #--------------------------------------------------------------------------- -# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that +# If the GENERATE_XML tag is set to YES, Doxygen will generate an XML file that # captures the structure of the code including all documentation. # The default value is: NO. @@ -2290,7 +2314,7 @@ GENERATE_XML = YES XML_OUTPUT = xml -# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program +# If the XML_PROGRAMLISTING tag is set to YES, Doxygen will dump the program # listings (including syntax highlighting and cross-referencing information) to # the XML output. Note that enabling this will significantly increase the size # of the XML output. @@ -2299,7 +2323,7 @@ XML_OUTPUT = xml XML_PROGRAMLISTING = NO -# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include +# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, Doxygen will include # namespace members in file scope as well, matching the HTML output. # The default value is: NO. # This tag requires that the tag GENERATE_XML is set to YES. @@ -2310,7 +2334,7 @@ XML_NS_MEMB_FILE_SCOPE = YES # Configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- -# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files +# If the GENERATE_DOCBOOK tag is set to YES, Doxygen will generate Docbook files # that can be used to generate PDF. # The default value is: NO. @@ -2328,7 +2352,7 @@ DOCBOOK_OUTPUT = docbook # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- -# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an +# If the GENERATE_AUTOGEN_DEF tag is set to YES, Doxygen will generate an # AutoGen Definitions (see https://autogen.sourceforge.net/) file that captures # the structure of the code including all documentation. Note that this feature # is still experimental and incomplete at the moment. @@ -2340,8 +2364,8 @@ GENERATE_AUTOGEN_DEF = NO # Configuration options related to Sqlite3 output #--------------------------------------------------------------------------- -# If the GENERATE_SQLITE3 tag is set to YES doxygen will generate a Sqlite3 -# database with symbols found by doxygen stored in tables. +# If the GENERATE_SQLITE3 tag is set to YES Doxygen will generate a Sqlite3 +# database with symbols found by Doxygen stored in tables. # The default value is: NO. GENERATE_SQLITE3 = NO @@ -2355,7 +2379,7 @@ GENERATE_SQLITE3 = NO SQLITE3_OUTPUT = sqlite3 # The SQLITE3_RECREATE_DB tag is set to YES, the existing doxygen_sqlite3.db -# database file will be recreated with each doxygen run. If set to NO, doxygen +# database file will be recreated with each Doxygen run. If set to NO, Doxygen # will warn if a database file is already found and not modify it. # The default value is: YES. # This tag requires that the tag GENERATE_SQLITE3 is set to YES. @@ -2366,7 +2390,7 @@ SQLITE3_RECREATE_DB = YES # Configuration options related to the Perl module output #--------------------------------------------------------------------------- -# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module +# If the GENERATE_PERLMOD tag is set to YES, Doxygen will generate a Perl module # file that captures the structure of the code including all documentation. # # Note that this feature is still experimental and incomplete at the moment. @@ -2374,7 +2398,7 @@ SQLITE3_RECREATE_DB = YES GENERATE_PERLMOD = NO -# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary +# If the PERLMOD_LATEX tag is set to YES, Doxygen will generate the necessary # Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI # output from the Perl module output. # The default value is: NO. @@ -2404,13 +2428,13 @@ PERLMOD_MAKEVAR_PREFIX = # Configuration options related to the preprocessor #--------------------------------------------------------------------------- -# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all +# If the ENABLE_PREPROCESSING tag is set to YES, Doxygen will evaluate all # C-preprocessor directives found in the sources and include files. # The default value is: YES. ENABLE_PREPROCESSING = YES -# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names +# If the MACRO_EXPANSION tag is set to YES, Doxygen will expand all macro names # in the source code. If set to NO, only conditional compilation will be # performed. Macro expansion can be done in a controlled way by setting # EXPAND_ONLY_PREDEF to YES. @@ -2478,7 +2502,7 @@ PREDEFINED = DOXYGEN=1 \ ARDUINO_ARCH_AVR \ ARDUINO=10808 \ __AVR_ATmega1284P__ \ - SERIAL_PORT_USBVIRTUAL=0\ + SERIAL_PORT_USBVIRTUAL=0 \ SDI12_EXTERNAL_PCINT \ ESP32 @@ -2491,7 +2515,7 @@ PREDEFINED = DOXYGEN=1 \ EXPAND_AS_DEFINED = -# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will +# If the SKIP_FUNCTION_MACROS tag is set to YES then Doxygen's preprocessor will # remove all references to function-like macros that are alone on a line, have # an all uppercase name, and do not end with a semicolon. Such function macros # are typically used for boiler-plate code, and will confuse the parser if not @@ -2515,12 +2539,12 @@ SKIP_FUNCTION_MACROS = YES # section "Linking to external documentation" for more information about the use # of tag files. # Note: Each tag file must have a unique name (where the name does NOT include -# the path). If a tag file is not located in the directory in which doxygen is +# the path). If a tag file is not located in the directory in which Doxygen is # run, you must also specify the path to the tagfile here. TAGFILES = -# When a file name is specified after GENERATE_TAGFILE, doxygen will create a +# When a file name is specified after GENERATE_TAGFILE, Doxygen will create a # tag file that is based on the input files it reads. See section "Linking to # external documentation" for more information about the usage of tag files. @@ -2557,7 +2581,7 @@ EXTERNAL_PAGES = YES HIDE_UNDOC_RELATIONS = NO -# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# If you set the HAVE_DOT tag to YES then Doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz (see: # https://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent # Bell Labs. The other options in this section have no effect if this option is @@ -2566,8 +2590,8 @@ HIDE_UNDOC_RELATIONS = NO HAVE_DOT = NO -# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed -# to run in parallel. When set to 0 doxygen will base this on the number of +# The DOT_NUM_THREADS specifies the number of dot invocations Doxygen is allowed +# to run in parallel. When set to 0 Doxygen will base this on the number of # processors available in the system. You can set it explicitly to a value # larger than 0 to get control over the balance between CPU load and processing # speed. @@ -2578,7 +2602,7 @@ DOT_NUM_THREADS = 0 # DOT_COMMON_ATTR is common attributes for nodes, edges and labels of # subgraphs. When you want a differently looking font in the dot files that -# doxygen generates you can specify fontname, fontcolor and fontsize attributes. +# Doxygen generates you can specify fontname, fontcolor and fontsize attributes. # For details please see Node, # Edge and Graph Attributes specification You need to make sure dot is able # to find the font, which can be done by putting it in a standard location or by @@ -2612,7 +2636,7 @@ DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4" DOT_FONTPATH = -# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then doxygen will +# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then Doxygen will # generate a graph for each documented class showing the direct and indirect # inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and # HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case @@ -2629,7 +2653,7 @@ DOT_FONTPATH = CLASS_GRAPH = NO -# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a +# If the COLLABORATION_GRAPH tag is set to YES then Doxygen will generate a # graph for each documented class showing the direct and indirect implementation # dependencies (inheritance, containment, and class references variables) of the # class with other documented classes. Explicit enabling a collaboration graph, @@ -2641,7 +2665,7 @@ CLASS_GRAPH = NO COLLABORATION_GRAPH = NO -# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for +# If the GROUP_GRAPHS tag is set to YES then Doxygen will generate a graph for # groups, showing the direct groups dependencies. Explicit enabling a group # dependency graph, when GROUP_GRAPHS is set to NO, can be accomplished by means # of the command \groupgraph. Disabling a directory graph can be accomplished by @@ -2652,7 +2676,7 @@ COLLABORATION_GRAPH = NO GROUP_GRAPHS = NO -# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and +# If the UML_LOOK tag is set to YES, Doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. # The default value is: NO. @@ -2673,10 +2697,10 @@ UML_LOOK = NO UML_LIMIT_NUM_FIELDS = 10 -# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and +# If the DOT_UML_DETAILS tag is set to NO, Doxygen will show attributes and # methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS -# tag is set to YES, doxygen will add type and arguments for attributes and -# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen +# tag is set to YES, Doxygen will add type and arguments for attributes and +# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, Doxygen # will not generate fields with class member information in the UML graphs. The # class diagrams will look similar to the default class diagrams but using UML # notation for the relationships. @@ -2704,7 +2728,7 @@ DOT_WRAP_THRESHOLD = 17 TEMPLATE_RELATIONS = NO # If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to -# YES then doxygen will generate a graph for each documented file showing the +# YES then Doxygen will generate a graph for each documented file showing the # direct and indirect include dependencies of the file with other documented # files. Explicit enabling an include graph, when INCLUDE_GRAPH is is set to NO, # can be accomplished by means of the command \includegraph. Disabling an @@ -2715,7 +2739,7 @@ TEMPLATE_RELATIONS = NO INCLUDE_GRAPH = NO # If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are -# set to YES then doxygen will generate a graph for each documented file showing +# set to YES then Doxygen will generate a graph for each documented file showing # the direct and indirect include dependencies of the file with other documented # files. Explicit enabling an included by graph, when INCLUDED_BY_GRAPH is set # to NO, can be accomplished by means of the command \includedbygraph. Disabling @@ -2726,7 +2750,7 @@ INCLUDE_GRAPH = NO INCLUDED_BY_GRAPH = NO -# If the CALL_GRAPH tag is set to YES then doxygen will generate a call +# If the CALL_GRAPH tag is set to YES then Doxygen will generate a call # dependency graph for every global function or class method. # # Note that enabling this option will significantly increase the time of a run. @@ -2738,7 +2762,7 @@ INCLUDED_BY_GRAPH = NO CALL_GRAPH = NO -# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller +# If the CALLER_GRAPH tag is set to YES then Doxygen will generate a caller # dependency graph for every global function or class method. # # Note that enabling this option will significantly increase the time of a run. @@ -2750,14 +2774,14 @@ CALL_GRAPH = NO CALLER_GRAPH = NO -# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical +# If the GRAPHICAL_HIERARCHY tag is set to YES then Doxygen will graphical # hierarchy of all classes instead of a textual one. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. GRAPHICAL_HIERARCHY = NO -# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the +# If the DIRECTORY_GRAPH tag is set to YES then Doxygen will show the # dependencies a directory has on other directories in a graphical way. The # dependency relations are determined by the #include relations between the # files in the directories. Explicit enabling a directory graph, when @@ -2816,7 +2840,7 @@ DOT_PATH = DOTFILE_DIRS = -# You can include diagrams made with dia in doxygen documentation. Doxygen will +# You can include diagrams made with dia in Doxygen documentation. Doxygen will # then run dia to produce the diagram and insert it in the documentation. The # DIA_PATH tag allows you to specify the directory where the dia binary resides. # If left empty dia is assumed to be found in the default search path. @@ -2829,7 +2853,7 @@ DIA_PATH = DIAFILE_DIRS = -# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the +# When using PlantUML, the PLANTUML_JAR_PATH tag should be used to specify the # path where java can find the plantuml.jar file or to the filename of jar file # to be used. If left blank, it is assumed PlantUML is not used or called during # a preprocessing step. Doxygen will generate a warning when it encounters a @@ -2837,19 +2861,19 @@ DIAFILE_DIRS = PLANTUML_JAR_PATH = -# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a -# configuration file for plantuml. +# When using PlantUML, the PLANTUML_CFG_FILE tag can be used to specify a +# configuration file for PlantUML. PLANTUML_CFG_FILE = -# When using plantuml, the specified paths are searched for files specified by -# the !include statement in a plantuml block. +# When using PlantUML, the specified paths are searched for files specified by +# the !include statement in a PlantUML block. PLANTUML_INCLUDE_PATH = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes # that will be shown in the graph. If the number of nodes in a graph becomes -# larger than this value, doxygen will truncate the graph, which is visualized +# larger than this value, Doxygen will truncate the graph, which is visualized # by representing a node as a red box. Note that if the number of direct # children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that @@ -2880,17 +2904,17 @@ MAX_DOT_GRAPH_DEPTH = 0 DOT_MULTI_TARGETS = NO -# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page +# If the GENERATE_LEGEND tag is set to YES Doxygen will generate a legend page # explaining the meaning of the various boxes and arrows in the dot generated # graphs. -# Note: This tag requires that UML_LOOK isn't set, i.e. the doxygen internal +# Note: This tag requires that UML_LOOK isn't set, i.e. the Doxygen internal # graphical representation for inheritance and collaboration diagrams is used. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. GENERATE_LEGEND = YES -# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate +# If the DOT_CLEANUP tag is set to YES, Doxygen will remove the intermediate # files that are used to generate the various graphs. # # Note: This setting is not only used for dot files but also for msc temporary @@ -2899,11 +2923,11 @@ GENERATE_LEGEND = YES DOT_CLEANUP = YES -# You can define message sequence charts within doxygen comments using the \msc -# command. If the MSCGEN_TOOL tag is left empty (the default), then doxygen will +# You can define message sequence charts within Doxygen comments using the \msc +# command. If the MSCGEN_TOOL tag is left empty (the default), then Doxygen will # use a built-in version of mscgen tool to produce the charts. Alternatively, # the MSCGEN_TOOL tag can also specify the name an external tool. For instance, -# specifying prog as the value, doxygen will call the tool as prog -T +# specifying prog as the value, Doxygen will call the tool as prog -T # -o . The external tool should support # output file formats "png", "eps", "svg", and "ismap". From b8812bef520013259e6db45497315d6fe279ff90 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Thu, 8 Aug 2024 11:51:12 -0400 Subject: [PATCH 72/96] Add example dependency file Signed-off-by: Sara Damiano --- examples/example_dependencies.json | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 examples/example_dependencies.json diff --git a/examples/example_dependencies.json b/examples/example_dependencies.json new file mode 100644 index 0000000..3bfcbbe --- /dev/null +++ b/examples/example_dependencies.json @@ -0,0 +1,10 @@ +{ + "action_cache_version": 1, + "dependencies": [ + { + "name": "EnableInterrupt", + "owner": "greygnome", + "version": "~1.1.0" + } + ] +} From 10890630f567477f208579583e6fb8123bbb7623 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Thu, 8 Aug 2024 14:57:43 -0400 Subject: [PATCH 73/96] Separated job for external interrupts Signed-off-by: Sara Damiano --- .github/dependabot.yml | 2 +- .github/workflows/build_examples.yaml | 22 ++++++++++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 0f88bec..7318fd3 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,7 +3,7 @@ updates: - package-ecosystem: 'github-actions' directory: '/' schedule: - interval: 'daily' + interval: 'weekly' labels: - 'CI/CD' commit-message: diff --git a/.github/workflows/build_examples.yaml b/.github/workflows/build_examples.yaml index 702e188..ca754e3 100644 --- a/.github/workflows/build_examples.yaml +++ b/.github/workflows/build_examples.yaml @@ -9,10 +9,28 @@ concurrency: jobs: build_examples: - name: Build all examples with PlatformIO and the Arduino CLI + name: Build standard examples with PlatformIO and the Arduino CLI if: ${{ ! contains(github.event.head_commit.message, 'ci skip') }} uses: EnviroDIY/workflows/.github/workflows/build_examples.yaml@main with: boards_to_build: 'mayfly,uno,megaatmega2560,leonardo,zeroUSB,arduino_nano_esp32,feather328p,feather32u4,adafruit_feather_m0,adafruit_feather_m4,adafruit_grandcentral_m4,huzzah,featheresp32,nodemcu,nodemcuv2,esp32dev,esp32-c3-devkitm-1,esp32-s3-devkitm-1' - examples_to_build: 'examples/a_wild_card,examples/b_address_change,examples/c_check_all_addresses,examples/d_simple_logger,examples/e_continuous_measurement,examples/f_basic_data_request,examples/g_terminal_window,examples/h_SDI-12_slave_implementation,examples/i_SDI-12_interface,examples/j_external_pcint_library,examples/k_concurrent_logger,examples/l_verify_crc' + examples_to_build: 'examples/a_wild_card,examples/b_address_change,examples/c_check_all_addresses,examples/d_simple_logger,examples/e_continuous_measurement,examples/f_basic_data_request,examples/g_terminal_window,examples/h_SDI-12_slave_implementation,examples/i_SDI-12_interface,examples/k_concurrent_logger,examples/l_verify_crc' secrets: inherit + build_ext_ints: + name: Build the External Interrupt Example + runs-on: ubuntu-latest + if: ${{ ! contains(github.event.head_commit.message, 'ci skip') }} + env: + PLATFORMIO_BUILD_CACHE_DIR: ~/.platformio/caches + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PlatformIO + uses: EnviroDIY/setup-platformio-action@v1.0.2 + + - name: Build PlatformIO examples + env: + PLATFORMIO_BUILD_FLAGS: -D SDI12_EXTERNAL_PCINT + run: pio ci --board=mayfly --board=uno --board=megaatmega2560 --board=leonardo --board=zeroUSB --board=arduino_nano_esp32 --board=feather328p --board=feather32u4 --board=adafruit_feather_m0 --board=adafruit_feather_m4 --board=adafruit_grandcentral_m4 --board=huzzah --board=featheresp32 --board=nodemcu --board=nodemcuv2 --board=esp32dev --board=esp32-c3-devkitm-1 --board=esp32-s3-devkitm-1 --project-option="lib_deps=greygnome/EnableInterrupt@^1.1.0" From 9393782ba562a0dfbfb9a591a39ddaea440fb167 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Fri, 9 Aug 2024 14:25:01 -0400 Subject: [PATCH 74/96] Fix unused var in example f Signed-off-by: Sara Damiano --- examples/f_basic_data_request/f_basic_data_request.ino | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/f_basic_data_request/f_basic_data_request.ino b/examples/f_basic_data_request/f_basic_data_request.ino index 2574c93..0ebf2d7 100644 --- a/examples/f_basic_data_request/f_basic_data_request.ino +++ b/examples/f_basic_data_request/f_basic_data_request.ino @@ -50,9 +50,8 @@ void loop() { do { // wait for a response from the serial terminal to do anything delay(30); } while (!Serial.available()); - char nogo = - Serial.read(); // simply hit enter in the terminal window or press send and the - // characters get discarded but now the rest of the loop continues + Serial.read(); // simply hit enter in the terminal window or press send and the + // characters get discarded but now the rest of the loop continues // first command to take a measurement myCommand = String(sensorAddress) + "M!"; From d6e6489611bb8523c402e6c8c5fe2b8660fe843c Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Fri, 9 Aug 2024 14:55:54 -0400 Subject: [PATCH 75/96] Updates to documentation Signed-off-by: Sara Damiano --- .github/workflows/build_examples.yaml | 2 + README.md | 12 +- docs/CreatingACharacter.md | 28 ---- docs/SupportedBoardsAndTimers.md | 119 ++++++++++++----- docs/documentExamples.py | 121 ------------------ docs/dotDiagrams.dox | 29 +++++ docs/mcss-conf.py | 42 +++--- examples/ReadMe.md | 68 +++------- examples/a_wild_card/ReadMe.md | 4 +- examples/a_wild_card/a_wild_card.ino | 3 +- examples/b_address_change/ReadMe.md | 4 +- .../b_address_change/b_address_change.ino | 2 +- examples/c_check_all_addresses/ReadMe.md | 4 +- .../c_check_all_addresses.ino | 2 +- examples/d_simple_logger/ReadMe.md | 4 +- examples/d_simple_logger/d_simple_logger.ino | 2 +- examples/e_continuous_measurement/ReadMe.md | 4 +- .../e_continuous_measurement.ino | 2 +- examples/f_basic_data_request/ReadMe.md | 4 +- .../f_basic_data_request.ino | 4 +- examples/g_terminal_window/ReadMe.md | 4 +- .../g_terminal_window/g_terminal_window.ino | 4 +- .../h_SDI-12_slave_implementation.ino | 2 +- .../i_SDI-12_interface/i_SDI-12_interface.ino | 2 +- examples/j_external_pcint_library/ReadMe.md | 4 +- .../j_external_pcint_library.ino | 2 +- examples/k_concurrent_logger/ReadMe.md | 4 +- .../k_concurrent_logger.ino | 2 +- examples/l_verify_crc/ReadMe.md | 4 +- examples/l_verify_crc/l_verify_crc.ino | 12 +- extras/SDI12_spy/SDI12_spy.ino | 29 ----- extras/TestCommands/TestCommands.ino | 2 +- extras/TestSensorTiming/TestSensorTiming.ino | 3 +- extras/TestWarmUp/TestWarmUp.ino | 2 +- 34 files changed, 202 insertions(+), 334 deletions(-) delete mode 100644 docs/documentExamples.py create mode 100644 docs/dotDiagrams.dox diff --git a/.github/workflows/build_examples.yaml b/.github/workflows/build_examples.yaml index ca754e3..47134e9 100644 --- a/.github/workflows/build_examples.yaml +++ b/.github/workflows/build_examples.yaml @@ -16,6 +16,8 @@ jobs: boards_to_build: 'mayfly,uno,megaatmega2560,leonardo,zeroUSB,arduino_nano_esp32,feather328p,feather32u4,adafruit_feather_m0,adafruit_feather_m4,adafruit_grandcentral_m4,huzzah,featheresp32,nodemcu,nodemcuv2,esp32dev,esp32-c3-devkitm-1,esp32-s3-devkitm-1' examples_to_build: 'examples/a_wild_card,examples/b_address_change,examples/c_check_all_addresses,examples/d_simple_logger,examples/e_continuous_measurement,examples/f_basic_data_request,examples/g_terminal_window,examples/h_SDI-12_slave_implementation,examples/i_SDI-12_interface,examples/k_concurrent_logger,examples/l_verify_crc' secrets: inherit + + build_ext_ints: name: Build the External Interrupt Example runs-on: ubuntu-latest diff --git a/README.md b/README.md index b45a9ca..b107aa3 100644 --- a/README.md +++ b/README.md @@ -35,9 +35,9 @@ Extensive documentation on the SDI-12 functions and classes is available here: - [Origins and Inherited Limitations](#origins-and-inherited-limitations) - [Compatibility Considerations](#compatibility-considerations) - [Variants and Branches](#variants-and-branches) - - [EnviroDIY\_SDI12](#envirodiy_sdi12) - - [EnviroDIY\_SDI12\_PCINT3](#envirodiy_sdi12_pcint3) - - [EnviroDIY\_SDI12\_ExtInts](#envirodiy_sdi12_extints) + - [EnviroDIY\_SDI12](#envirodiy_sdi12) + - [EnviroDIY\_SDI12\_PCINT3](#envirodiy_sdi12_pcint3) + - [EnviroDIY\_SDI12\_ExtInts](#envirodiy_sdi12_extints) - [Contribute](#contribute) - [License](#license) - [Credits](#credits) @@ -129,12 +129,12 @@ As we've described, the default "master" branch of this library will conflict wi To allow simultaneous use of SDI-12 and SoftwareSerial, we have created additional variants of these libraries that we maintain as separate branches of this repository. For background information, my be helpful to read our [Overview of Interrupts](https://github.com/EnviroDIY/Arduino-SDI-12/wiki/2b.-Overview-of-Interrupts) wiki page or this [Arduino Pin Change Interrupts article](https://thewanderingengineer.com/2014/08/11/arduino-pin-change-interrupts/). -#### EnviroDIY_SDI12 +### EnviroDIY_SDI12 EnviroDIY_SDI12 is the default master branch of this repository. It controls and monopolizes all pin change interrupt vectors, and can therefore have conflicts with any variant of SoftwareSerial and other libraries that use interrupts. -#### EnviroDIY_SDI12_PCINT3 +### EnviroDIY_SDI12_PCINT3 EnviroDIY_SDI12_PCINT3 is in the Mayfly branch of this repository, and was historically was called "SDI12_mod". It's been cropped to only control interrupt vector 3, or PCINT3 (D), which on the Mayfly (or Sodaq Mbili) corresponds to Pins D0-D7. @@ -142,7 +142,7 @@ It is designed to be compatible with [EnviroDIY_SoftwareSerial_PCINT12](https:// Note that different AtMega1284p boards have a different mapping from the physical PIN numbers to the listed digital PIN numbers that are printed on the board. One of the most helpful lists of pins and interrupts vectors is in the the [Pin/Port Bestiary wiki page for the Enable Interrupt library](https://github.com/GreyGnome/EnableInterrupt/wiki/Usage#PIN__PORT_BESTIARY). -#### EnviroDIY_SDI12_ExtInts +### EnviroDIY_SDI12_ExtInts EnviroDIY_SDI12_ExtInts is the ExtInt branch of this repository. It doesn't control any of the interrupts, but instead relies on an external interrupt management library (like [EnableInterrupt](https://github.com/GreyGnome/EnableInterrupt)) to assign the SDI-12 receive data function to the right pin. diff --git a/docs/CreatingACharacter.md b/docs/CreatingACharacter.md index b24fb16..d53687e 100644 --- a/docs/CreatingACharacter.md +++ b/docs/CreatingACharacter.md @@ -93,20 +93,6 @@ For **each bit time that passed**, we apply the `rxMask` to the `rxValue`. #### A LOW/1 Bit - if the data bit received is LOW (1) we do an `|=` (bitwise OR) between the `rxMask` and the `rxValue` -[//]: # ( @dot ) - -digraph xor { - graph[rankdir=BT]; - node[shape=record]; - - rxValue[label="{ {rxValue|0|0|0|0|0|0|0|1} }"]; - maskState[label=" {rxMask|rxState}|{0|0}|{0|0}|{0|0}|{0|0}|{0|0}|{0|0}|{0|0}|{0|0}"]; - - maskState:rxMask0 -> rxValue:rxValue0[label="bit-wise or (|=) puts the one \nfrom the rxMask into the rxValue"]; -} - -[//]: # ( @enddot ) - ``` rxValue: | 0 0 0 0 0 0 0 1 @@ -119,20 +105,6 @@ digraph xor { - if the data bit received is HIGH (0) we do nothing - -[//]: # ( @dot ) - -digraph xor { - graph[rankdir=BT]; - node[shape=record]; - - rxValue[label="{ {rxValue|0|0|0|0|0|0|0|1} }"]; - maskState[label=" {rxMask|rxState}|{0|0}|{0|0}|{0|0}|{0|0}|{0|0}|{0|0}|{0|0}|{0|0}"]; - - maskState:rxMask0 -> rxValue:rxValue0[label="nothing happens",arrowhead="obox"]; -} - -[//]: # ( @enddot ) ``` rxValue: | 0 0 0 0 0 0 0 0 -------------|---------------------------------x- nothing happens diff --git a/docs/SupportedBoardsAndTimers.md b/docs/SupportedBoardsAndTimers.md index 2956357..f92ac04 100644 --- a/docs/SupportedBoardsAndTimers.md +++ b/docs/SupportedBoardsAndTimers.md @@ -1,5 +1,40 @@ # Bits and Ticks +[//]: # ( @tableofcontents ) + +[//]: # ( @cond GitHub ) + +- [Bits and Ticks](#bits-and-ticks) + - [SDI-12 Timing Rules](#sdi-12-timing-rules) + - [Ideal Timer Settings](#ideal-timer-settings) + - [AVR Boards](#avr-boards) + - [Available Timers on AVR Boards](#available-timers-on-avr-boards) + - [ATmega AVR Available Timers](#atmega-avr-available-timers) + - [ATtiny AVR Available Timers](#attiny-avr-available-timers) + - [ATmegaXU AVR Available Timers](#atmegaxu-avr-available-timers) + - [Timers Used by Arduino AVR Core](#timers-used-by-arduino-avr-core) + - [Selected AVR Timers for SDI-12](#selected-avr-timers-for-sdi-12) + - [ATmega AVR Selected Timers](#atmega-avr-selected-timers) + - [ATtiny AVR Selected Timers](#attiny-avr-selected-timers) + - [ATmegaXU Selected Timers](#atmegaxu-selected-timers) + - [SAMD Boards](#samd-boards) + - [SAMD21](#samd21) + - [Available Clocks and Timers on SAMD21 Boards](#available-clocks-and-timers-on-samd21-boards) + - [SAMD21 Generic Clock Generators](#samd21-generic-clock-generators) + - [SAMD21 Timer Controllers](#samd21-timer-controllers) + - [Timers Used by Arduino SAMD21 Core](#timers-used-by-arduino-samd21-core) + - [Selected SAMD21 Timers for SDI-12](#selected-samd21-timers-for-sdi-12) + - [SAMD51/SAME51](#samd51same51) + - [Available Clocks and Timers on SAMD51 Boards](#available-clocks-and-timers-on-samd51-boards) + - [SAMD51 Generic Clock Generators](#samd51-generic-clock-generators) + - [SAMD51 Timer Controllers](#samd51-timer-controllers) + - [Timers Used by Arduino SAMD51 Core](#timers-used-by-arduino-samd51-core) + - [Selected SAMD51 Timers for SDI-12](#selected-samd51-timers-for-sdi-12) + - [Other Boards](#other-boards) + + +[//]: # ( @endcond ) + This library listens for pin level changes and then use a timer to calculate how many databits have been sent since the last change and to convert that to a character. The speed of the timer is dependent on the speed of the processor and "dividers" and "prescalers" used to slow the effective clock. Unfortunately, the "ticks" of the processor clock aren't perfectly aligned with the times of the level changes from the SDI-12 device. @@ -74,14 +109,15 @@ If we only have an 8 bit timer, the counter rolls after 256 ticks. - Conclusion: With a 8-bit timer, select the smallest prescaler possible that keeps the speed *below* 30 kHz -# Supported Processors and Timers - ## AVR Boards ### Available Timers on AVR Boards -#### ATmega164A/PA/324A/PA/644A/PA/1284/P +#### ATmega AVR Available Timers + +[ATmega164A/PA/324A/PA/644A/PA/1284/P](https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-8272-8-bit-AVR-microcontroller-ATmega164A_PA-324A_PA-644A_PA-1284_P_datasheet.pdf) +> > - Up to 20MIPS throughput at 20MHz > - Most Arduino boards are run at 16 or 8 MHz with a few at 12 MHz > - Two 8-bit Timer/Counters with Separate Prescalers and Compare Modes @@ -93,8 +129,9 @@ If we only have an 8 bit timer, the counter rolls after 256 ticks. > - Prescalers available at 8/64/256/1024 on Timers 1 and 3 > - Timer 3 is only available on the 1284p -#### ATmega640/V-1280/V-1281/V-2560/V-2561/V +[ATmega640/V-1280/V-1281/V-2560/V-2561/V](https://ww1.microchip.com/downloads/en/devicedoc/atmel-2549-8-bit-avr-microcontroller-atmega640-1280-1281-2560-2561_datasheet.pdf) +> > - Up to 16 MIPS Throughput at 16MHz > – Two 8-bit Timer/Counters with Separate Prescaler and Compare Mode > - Timers 0 and 2 @@ -104,8 +141,11 @@ If we only have an 8 bit timer, the counter rolls after 256 ticks. > - Timers 1, 3, 4, and 5 > - Prescalers available at 8/64/256/1024 on Timer 1, 3, 4, and 5 -#### ATtiny25/V / ATtiny45/V / ATtiny85/V +#### ATtiny AVR Available Timers + +[ATtiny25/V / ATtiny45/V / ATtiny85/V](https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2586-AVR-8-bit-Microcontroller-ATtiny25-ATtiny45-ATtiny85_Datasheet.pdf) +> > - Up to 20MIPS throughput at 20MHz > - Most Arduino boards are run at 16 or 8 MHz with a few at 12 MHz > – One 8-bit Timer/Counter with Prescaler and Two PWM Channels @@ -115,8 +155,11 @@ If we only have an 8 bit timer, the counter rolls after 256 ticks. > - Timer 1 > - Prescalers available at 64/128/256/512/1024/2048/4096/8192/16384 -#### ATmega16U4/ATmega32U4 +#### ATmegaXU AVR Available Timers + +[ATmega16U4/ATmega32U4](https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7766-8-bit-AVR-ATmega16U4-32U4_Datasheet.pdf) +> > – Up to 16 MIPS Throughput at 16MHz > – One 8-bit Timer/Counter with Separate Prescaler and Compare Mode > - Timer 0 @@ -127,9 +170,9 @@ If we only have an 8 bit timer, the counter rolls after 256 ticks. > – One 10-bit High-Speed Timer/Counter with PLL (64MHz) and Compare Mode > - Timer 4 > - Prescalers available at 2/4/8/16/32/64/128/256/512/1024/2048/8192/169384 on Timer 4 -> -[!NOTE] -There is no Timer 2 on the 16U4 or the 32U4 + +> [!NOTE] +> There is no Timer 2 on the 16U4 or the 32U4 ### Timers Used by Arduino AVR Core @@ -155,15 +198,16 @@ There is no Timer 2 on the 16U4 or the 32U4 - Optional for [Tone](https://github.com/arduino/ArduinoCore-avr/blob/master/cores/arduino/Tone.cpp) on some boards - Optional for [Servo](https://github.com/arduino-libraries/Servo) on some boards -#### Selected Timers for SDI-12 +### Selected AVR Timers for SDI-12 -#### ATmega164A/PA/324A/PA/644A/PA/1284/P and ATmega640/V-1280/V-1281/V-2560/V-2561/V +#### ATmega AVR Selected Timers -For simplicity, we use Timer/Counter 2 on most AVR boards. +For simplicity, we use Timer/Counter 2 for both ATmega164A/PA/324A/PA/644A/PA/1284/P and ATmega640/V-1280/V-1281/V-2560/V-2561/V series boards. +> > Timer/Counter2 (TC2) is a general purpose, single channel, 8-bit Timer/Counter module. > -> Features +> **Features of Timer/Counter 2** > > - Single Channel Counter > - Clear Timer on Compare Match (Auto Reload) @@ -173,20 +217,23 @@ For simplicity, we use Timer/Counter 2 on most AVR boards. > - Overflow and Compare Match Interrupt Sources (TOV2, OCF2A, and OCF2B) > - Allows Clocking from External 32kHz Watch Crystal Independent of the I/O Clock -#### ATtiny25/V / ATtiny45/V / ATtiny85/V +#### ATtiny AVR Selected Timers -On the ATTiny boards, we use Timer/Counter 1 +On the ATTiny series (ATtiny25/V / ATtiny45/V / ATtiny85/V) boards, we use Timer/Counter 1 +> > The Timer/Counter1 features a high resolution and a high accuracy usage with the lower prescaling opportunities. > It can also support two accurate, high speed, 8-bit pulse width modulators using clock speeds up to 64MHz (or 32MHz in low speedmode). -#### ATmega16U4/ATmega32U4 +#### ATmegaXU Selected Timers On the AtMega16U4 and AtMega32U4, we use Timer/Counter 4 as an 8-bit timer. +> > Timer/Counter4 is a general purpose high speed Timer/Counter module, with three independent Output Compare Units, and with enhanced PWM support. > -> Features +> **Features of Timer/Counter 4** +> > - Up to 10-Bit Accuracy > - Three Independent Output Compare Units > - Clear Timer on Compare Match (Auto Reload) @@ -199,8 +246,8 @@ On the AtMega16U4 and AtMega32U4, we use Timer/Counter 4 as an 8-bit timer. > - High Speed Asynchronous and Synchronous Clocking Modes > - Separate Prescaler Unit -[!NOTE] -We only utilize the low byte register of Timer 4, effectively using the 10-bit timer as an 8-bit timer. +> [!NOTE] +> We only utilize the low byte register of Timer 4, effectively using the 10-bit timer as an 8-bit timer. ## SAMD Boards @@ -208,27 +255,29 @@ We only utilize the low byte register of Timer 4, effectively using the 10-bit t #### Available Clocks and Timers on SAMD21 Boards -##### Generic Clock Generators +##### SAMD21 Generic Clock Generators +> > The Generic Clock controller GCLK provides nine Generic Clock Generators that can provide a wide range of clock frequencies. > Generators can be set to use different external and internal oscillators as source. > The clock of each Generator can be divided. > The outputs from the Generators are used as sources for the Generic Clock Multiplexers, which provide the Generic Clock (GCLK_PERIPHERAL) to the peripheral modules, as shown in Generic Clock Controller Block Diagram. > -> Features +> **Features of the Generic Clock Generator** > > - Provides Generic Clocks > - Wide frequency range > - Clock source for the generator can be changed on the fly -##### Timer Controllers +##### SAMD21 Timer Controllers +> > The TC consists of a counter, a prescaler, compare/capture channels and control logic. > The counter can be set to count events, or it can be configured to count clock pulses. > The counter, together with the compare/capture channels, can be configured to timestamp input events, allowing capture of frequency and pulse width. > It can also perform waveform generation, such as frequency generation and pulse-width modulation (PWM). > -> Features +> **Features of the Timer Controller** > > - Selectable configuration > – Up to five 16-bit Timer/Counters (TC) including one low-power TC, each configurable as: @@ -255,12 +304,13 @@ The Adafruit Arduino core uses: - 0 as GENERIC_CLOCK_GENERATOR_MAIN (the main clock) + The Adafruit Arduino core uses: - TC5 for Tone - TC4 for Servo -#### Selected Timers for SDI-12 +#### Selected SAMD21 Timers for SDI-12 For SDI-12, we'll use Generic Clock Generator 4 and Timer Controller 3 @@ -268,8 +318,9 @@ For SDI-12, we'll use Generic Clock Generator 4 and Timer Controller 3 #### Available Clocks and Timers on SAMD51 Boards -##### Generic Clock Generators +##### SAMD51 Generic Clock Generators +> > Depending on the application, peripherals may require specific clock frequencies to operate correctly. > The Generic Clock controller (GCLK) features 12 Generic Clock Generators [11:0] that can provide a wide range of clock frequencies. > @@ -280,15 +331,16 @@ For SDI-12, we'll use Generic Clock Generator 4 and Timer Controller 3 > > NOTE: The Generator 0 is always the direct source of the GCLK_MAIN signal. > -> Features +> **Features of the Generic Clock Generators** > > - Provides a device-defined, configurable number of Peripheral Channel clocks > - Wide frequency range > - Various clock sources > - Embedded dividers -##### Timer Controllers +##### SAMD51 Timer Controllers +> > There are up to eight TC peripheral instances. > > Each TC consists of a counter, a prescaler, compare/capture channels and control logic. @@ -297,10 +349,10 @@ For SDI-12, we'll use Generic Clock Generator 4 and Timer Controller 3 > > A TC can also perform waveform generation, such as frequency generation and pulse-width modulation. > -> Features +> **Features of the Timer Controllers** > > - Selectable configuration -> - 8-, 16- or 32-bit TC operation, with compare/capture channels +> - 8-, 16- or 32-bit TC operation, with compare/capture channels > - 2 compare/capture channels (CC) with: > - Double buffered timer period setting (in 8-bit mode only) > - Double buffered compare channel @@ -331,12 +383,13 @@ The Adafruit Arduino core uses: - 4 as GENERIC_CLOCK_GENERATOR_12M (12MHz clock for DAC, sourced from GCLK_GENCTRL_SRC_DPLL0) - 5 as GENERIC_CLOCK_GENERATOR_1M (??, sourced from CLK_GENCTRL_SRC_DPLL0) + The Adafruit Arduino core uses: - TC0 for Tone (though any other timer may be used, if another pin is selected) - TC1 for Servo (though any other timer may be used, if another pin is selected) -#### Selected Timers for SDI-12 +#### Selected SAMD51 Timers for SDI-12 For SDI-12, we'll use Generic Clock Generator 6 and Timer Controller 2 @@ -354,10 +407,10 @@ That will allow Rensas AVR processors to attempt SDI-12. I know from testing, that we *cannot* use micros on a board 8MHz AVR board, but that it does work on a 80MHz Espressif8266. -[!WARNING] -I haven't actually tested the minimum speed that this will work at! +> [!WARNING] +> I haven't actually tested the minimum speed that this will work at! -@TODO: Test 48MHz +@todo: Test 48MHz Both the ESP8266 and ESP32 are definitely fast enough that this works. diff --git a/docs/documentExamples.py b/docs/documentExamples.py deleted file mode 100644 index 5882ae4..0000000 --- a/docs/documentExamples.py +++ /dev/null @@ -1,121 +0,0 @@ -#!/usr/bin/env python -import fileinput -import re -import os -import glob - -fileDir = os.path.dirname(os.path.realpath("__file__")) -# print(fileDir) - -output_file = "examples.dox" -read_mes = [ - "../examples/ReadMe.md", - "../examples/a_wild_card/ReadMe.md", - "../examples/b_address_change/ReadMe.md", - "../examples/c_check_all_addresses/ReadMe.md", - "../examples/d_simple_logger/ReadMe.md", - "../examples/e_continuous_measurement/ReadMe.md", - "../examples/f_basic_data_request/ReadMe.md", - "../examples/g_terminal_window/ReadMe.md", - "../examples/h_SDI-12_slave_implementation/ReadMe.md", - "../examples/i_SDI-12_interface/ReadMe.md", - "../examples/j_external_pcint_library/ReadMe.md", - "../examples/k_concurrent_logger/ReadMe.md", -] - -if not os.path.exists(os.path.join(fileDir, "examples")): - os.makedirs(os.path.join(fileDir, "examples")) - -for filename in read_mes: - out_path = os.path.join(fileDir, "examples") - out_dir = filename.split("/")[2] - out_name = out_dir + ".dox" - if out_name == "ReadMe.md.dox": - out_name = "examples.dox" - abs_out = os.path.join(out_path, out_name) - # print(abs_out) - # with open(output_file, 'w+') as out_file: - with open(abs_out, "w+") as out_file: - - abs_file_path = os.path.join(fileDir, filename) - abs_file_path = os.path.abspath(os.path.realpath(abs_file_path)) - # print(abs_file_path) - - with open(abs_file_path, "r") as in_file: # open in readonly mode - out_file.write("/**\n") - if out_name != "examples.dox": - # out_file.write( - # "@example{{lineno}} {} @m_examplenavigation{{examples_page,{}/}} @m_footernavigation \n\n".format( - # filename.replace("..\\examples\\", "").replace( - # "\\ReadMe.md", ".ino" - # ), out_dir - # ) - # ) - out_file.write( - "@example{{lineno}} {} @m_examplenavigation{{examples_page,}} @m_footernavigation \n\n".format( - filename.replace("../examples/", "").replace( - "/ReadMe.md", ".ino" - ) - ) - ) - # out_file.write( - # "@example{{lineno}} {} \n\n".format( - # filename.replace("..\\examples\\", "").replace( - # "\\ReadMe.md", ".ino" - # ) - # ) - # ) - # out_file.write('\n@tableofcontents\n\n') - - print_me = True - skip_me = False - i = 1 - lines = in_file.readlines() - for line in lines: - # print(i, print_me, skip_me, line) - - # Remove markdown comment tags from doxygen commands within the markdown - if print_me and not skip_me: - new_line = ( - re.sub(r"\[//\]: # \( @(\w+?.*) \)", r"@\1", line) - .replace("```ini", "@code{.ini}") - .replace("```cpp", "@code{.cpp}") - .replace("```", "@endcode") - ) - if out_name != "examples.dox": - new_line = new_line.replace("@page", "@section") - # .replace('@section', '') - # .replace('@subsection', '') - # .replace('@subsubsection', '') - # .replace('@paragraph', '') - # .replace('@par', '') - out_file.write(new_line) - - # using skip_me to skip single lines, so unset it after reading a line - if skip_me: - skip_me = False - - # a page, section, subsection, or subsubsection commands followed - # immediately with by a markdown header leads to that section appearing - # twice in the doxygen html table of contents. - # I'm putting the section markers right above the header and then will skip the header. - if re.match(r"\[//\]: # \( @mainpage", line) is not None: - skip_me = True - if re.match(r"\[//\]: # \( @page", line) is not None: - skip_me = True - if re.match(r"\[//\]: # \( @.*section", line) is not None: - skip_me = True - if re.match(r"\[//\]: # \( @paragraph", line) is not None: - skip_me = True - - # I'm using these comments to fence off content that is only intended for - # github mardown rendering - if "[//]: # ( @cond GitHub )" in line: - print_me = False - - if "[//]: # ( @endcond )" in line: - print_me = True - - i += 1 - - out_file.write("\n*/\n\n") diff --git a/docs/dotDiagrams.dox b/docs/dotDiagrams.dox new file mode 100644 index 0000000..5ea9af7 --- /dev/null +++ b/docs/dotDiagrams.dox @@ -0,0 +1,29 @@ +[//]: # ( @dot ) + +digraph xor { + graph[rankdir=BT]; + node[shape=record]; + + rxValue[label="{ {rxValue|0|0|0|0|0|0|0|1} }"]; + maskState[label=" {rxMask|rxState}|{0|0}|{0|0}|{0|0}|{0|0}|{0|0}|{0|0}|{0|0}|{0|0}"]; + + maskState:rxMask0 -> rxValue:rxValue0[label="bit-wise or (|=) puts the one \nfrom the rxMask into the rxValue"]; +} + +[//]: # ( @enddot ) + + + +[//]: # ( @dot ) + +digraph xor { + graph[rankdir=BT]; + node[shape=record]; + + rxValue[label="{ {rxValue|0|0|0|0|0|0|0|1} }"]; + maskState[label=" {rxMask|rxState}|{0|0}|{0|0}|{0|0}|{0|0}|{0|0}|{0|0}|{0|0}|{0|0}"]; + + maskState:rxMask0 -> rxValue:rxValue0[label="nothing happens",arrowhead="obox"]; +} + +[//]: # ( @enddot ) diff --git a/docs/mcss-conf.py b/docs/mcss-conf.py index 596a03e..d5b3875 100644 --- a/docs/mcss-conf.py +++ b/docs/mcss-conf.py @@ -6,32 +6,34 @@ "Functions", "class_s_d_i12", [ - ('Constructor, Destructor, Begins, and Setters', ), - ('Waking Up and Talking To Sensors', ), - ('Reading from the SDI-12 Buffer', ), - ('Data Line States', ), - ('Using more than one SDI-12 Object', ), - ('Interrupt Service Routine', ), + ( + 'Constructor, Destructor, Begins, and Setters', + ), + ( + 'Waking Up and Talking To Sensors', + ), + ( + 'Reading from the SDI-12 Buffer', + ), + ('Data Line States',), + ( + 'Using more than one SDI-12 Object', + ), + ( + 'Interrupt Service Routine', + ), ], ), ( "Examples", "examples_page", - [ - ('Getting Sensor Information', ), - ('Address Change', ), - ('Checking all Addresses', ), - ('Logging Data', ), - ('Parsing Data', ), - ('Simple Data Request', ), - ('Terminal Emulator 1', ), - ('Slave Implementation',), - ('Terminal Emulator 2', ), - ('External Interrupts', ), - ('Concurrent Measurements', ), - ], + [], + ), + ( + "Classes", + "annotated", + [], ), - ("Classes", "annotated", [],), # ("Files", "files", []), ( "Notes", diff --git a/examples/ReadMe.md b/examples/ReadMe.md index d9ebdfb..c0c6907 100644 --- a/examples/ReadMe.md +++ b/examples/ReadMe.md @@ -1,74 +1,38 @@ # Examples using the SDI-12 Library -[//]: # ( @brief Examples using the SDI-12 Library ) - -[//]: # ( @cond GitHub ) -- [Example A](@ref a_wild_card.ino): - - Gets sensor information from a single attached sensor and prints it to the serial port - - [GitHub](https://github.com/EnviroDIY/Arduino-SDI-12/tree/master/examples/a_wild_card) -- [Example B](@ref b_address_change.ino): - - Allows you to change the address of your SDI-12 Sensor - - [GitHub](https://github.com/EnviroDIY/Arduino-SDI-12/tree/master/examples/b_address_change) -- [Example C](@ref c_check_all_addresses.ino): - - Checks all addresses for active sensors, and prints their status to the serial port - - [GitHub](https://github.com/EnviroDIY/Arduino-SDI-12/tree/master/examples/c_check_all_addresses) -- [Example D](@ref d_simple_logger.ino): - - Checks all addresses for active sensors, and logs data for each sensor every minute - - [GitHub](https://github.com/EnviroDIY/Arduino-SDI-12/tree/master/examples/d_simple_logger) -- [Example E](@ref e_simple_parsing.ino): - - Demonstrates the ability to parse integers and floats from the buffer. - - [GitHub](https://github.com/EnviroDIY/Arduino-SDI-12/tree/master/examples/e_simple_parsing) -- [Example F](@ref f_basic_data_request.ino): - - Issues a data request to a single specified sensor - - [GitHub](https://github.com/EnviroDIY/Arduino-SDI-12/tree/master/examples/f_basic_data_request) -- [Example G](@ref g_terminal_window.ino): - - Demonstrates using the Arduino as a command terminal for SDI-12 sensors. - - [GitHub](https://github.com/EnviroDIY/Arduino-SDI-12/tree/master/examples/g_terminal_window) -- [Example H](@ref h_SDI-12_slave_implementation.ino): - - Demonstrates using SDI-12 in slave mode - - [GitHub](https://github.com/EnviroDIY/Arduino-SDI-12/tree/master/examples/h_SDI-12_slave_implementation) -- [Example I](@ref i_SDI-12_interface.ino): - - Shows code for an Arduino-based USB dongle to translate between SDI-12 and a PC - - [GitHub](https://github.com/EnviroDIY/Arduino-SDI-12/tree/master/examples/i_SDI-12_interface) -- [Example J](@ref j_external_pcint_library.ino): - - Shows how to use an external PCInt library to call the interrupts for this library. - - [GitHub](https://github.com/EnviroDIY/Arduino-SDI-12/tree/master/examples/j_external_pcint_library) -- [Example K](@ref k_concurrent_logger.ino): - - Shows how to request concurrent measurements - - [GitHub](https://github.com/EnviroDIY/Arduino-SDI-12/tree/master/examples/k_concurrent_logger) - -[//]: # ( @endcond ) - -- [Example A](@ref a_wild_card.ino): +- Example A - Gets sensor information from a single attached sensor and prints it to the serial port - [GitHub](https://github.com/EnviroDIY/Arduino-SDI-12/tree/master/examples/a_wild_card) -- [Example B](@ref b_address_change.ino): +- Example B - Allows you to change the address of your SDI-12 Sensor - [GitHub](https://github.com/EnviroDIY/Arduino-SDI-12/tree/master/examples/b_address_change) -- [Example C](@ref c_check_all_addresses.ino): +- Example C - Checks all addresses for active sensors, and prints their status to the serial port - [GitHub](https://github.com/EnviroDIY/Arduino-SDI-12/tree/master/examples/c_check_all_addresses) -- [Example D](@ref d_simple_logger.ino): +- Example D - Checks all addresses for active sensors, and logs data for each sensor every minute - [GitHub](https://github.com/EnviroDIY/Arduino-SDI-12/tree/master/examples/d_simple_logger) -- [Example E](@ref e_simple_parsing.ino): - - Demonstrates the ability to parse integers and floats from the buffer. - - [GitHub](https://github.com/EnviroDIY/Arduino-SDI-12/tree/master/examples/e_simple_parsing) -- [Example F](@ref f_basic_data_request.ino): +- Example E + - Checks all addresses for active sensors, and requests continuous data output from all of the sensors + - [GitHub](https://github.com/EnviroDIY/Arduino-SDI-12/tree/master/examples/e_continuous_measurement) +- Example F - Issues a data request to a single specified sensor - [GitHub](https://github.com/EnviroDIY/Arduino-SDI-12/tree/master/examples/f_basic_data_request) -- [Example G](@ref g_terminal_window.ino): +- Example G - Demonstrates using the Arduino as a command terminal for SDI-12 sensors. - [GitHub](https://github.com/EnviroDIY/Arduino-SDI-12/tree/master/examples/g_terminal_window) -- [Example H](@ref h_SDI-12_slave_implementation.ino): +- Example H - Demonstrates using SDI-12 in slave mode - [GitHub](https://github.com/EnviroDIY/Arduino-SDI-12/tree/master/examples/h_SDI-12_slave_implementation) -- [Example I](@ref i_SDI-12_interface.ino): +- Example I - Shows code for an Arduino-based USB dongle to translate between SDI-12 and a PC - [GitHub](https://github.com/EnviroDIY/Arduino-SDI-12/tree/master/examples/i_SDI-12_interface) -- [Example J](@ref j_external_pcint_library.ino): +- Example J - Shows how to use an external PCInt library to call the interrupts for this library. - [GitHub](https://github.com/EnviroDIY/Arduino-SDI-12/tree/master/examples/j_external_pcint_library) -- [Example K](@ref k_concurrent_logger.ino): +- Example K - Shows how to request concurrent measurements - [GitHub](https://github.com/EnviroDIY/Arduino-SDI-12/tree/master/examples/k_concurrent_logger) +- Example L + - Shows how to verify the CRC of received data + - [GitHub](https://github.com/EnviroDIY/Arduino-SDI-12/tree/master/examples/l_verify_crc) diff --git a/examples/a_wild_card/ReadMe.md b/examples/a_wild_card/ReadMe.md index 09535a2..87ca613 100644 --- a/examples/a_wild_card/ReadMe.md +++ b/examples/a_wild_card/ReadMe.md @@ -5,8 +5,8 @@ It requests information about a single attached sensor, including its address an [//]: # ( @section a_wild_card_pio PlatformIO Configuration ) -[//]: # ( @example{lineno} a_wild_card.ino @m_examplenavigation{examples_page,} @m_footernavigation ) - [//]: # ( @include{lineno} a_wild_card/platformio.ini ) [//]: # ( @section a_wild_card_code The Complete Example ) + +[//]: # ( @include{lineno} a_wild_card/a_wild_card.ino ) diff --git a/examples/a_wild_card/a_wild_card.ino b/examples/a_wild_card/a_wild_card.ino index 4d42339..f391f74 100644 --- a/examples/a_wild_card/a_wild_card.ino +++ b/examples/a_wild_card/a_wild_card.ino @@ -1,6 +1,5 @@ - /** - * @file a_wild_card.ino + * @example{lineno} a_wild_card.ino * @copyright Stroud Water Research Center * @license This example is published under the BSD-3 license. * @author Kevin M.Smith diff --git a/examples/b_address_change/ReadMe.md b/examples/b_address_change/ReadMe.md index f7c864f..dbc5bb1 100644 --- a/examples/b_address_change/ReadMe.md +++ b/examples/b_address_change/ReadMe.md @@ -18,8 +18,8 @@ If you are using a Meter Group Hydros 21 CTD sensor, change the channel to 1 in [//]: # ( @section b_address_change_pio PlatformIO Configuration ) -[//]: # ( @example{lineno} b_address_change.ino @m_examplenavigation{examples_page,} @m_footernavigation ) - [//]: # ( @include{lineno} b_address_change/platformio.ini ) [//]: # ( @section b_address_change_code The Complete Example ) + +[//]: # ( @include{lineno} b_address_change/b_address_change.ino ) diff --git a/examples/b_address_change/b_address_change.ino b/examples/b_address_change/b_address_change.ino index 6245b02..f6da5b6 100644 --- a/examples/b_address_change/b_address_change.ino +++ b/examples/b_address_change/b_address_change.ino @@ -1,5 +1,5 @@ /** - * @file b_address_change.ino + * @example{lineno} b_address_change.ino * @copyright Stroud Water Research Center * @license This example is published under the BSD-3 license. * @author Kevin M.Smith diff --git a/examples/c_check_all_addresses/ReadMe.md b/examples/c_check_all_addresses/ReadMe.md index 55ac981..d59be48 100644 --- a/examples/c_check_all_addresses/ReadMe.md +++ b/examples/c_check_all_addresses/ReadMe.md @@ -10,8 +10,8 @@ To address a sensor, please see Example B: b_address_change.ino [//]: # ( @section c_check_all_addresses_pio PlatformIO Configuration ) -[//]: # ( @example{lineno} c_check_all_addresses.ino @m_examplenavigation{examples_page,} @m_footernavigation ) - [//]: # ( @include{lineno} c_check_all_addresses/platformio.ini ) [//]: # ( @section c_check_all_addresses_code The Complete Example ) + +[//]: # ( @include{lineno} c_check_all_addresses/c_check_all_addresses.ino ) diff --git a/examples/c_check_all_addresses/c_check_all_addresses.ino b/examples/c_check_all_addresses/c_check_all_addresses.ino index d306fac..eae5755 100644 --- a/examples/c_check_all_addresses/c_check_all_addresses.ino +++ b/examples/c_check_all_addresses/c_check_all_addresses.ino @@ -1,5 +1,5 @@ /** - * @file c_check_all_addresses.ino + * @example{lineno} c_check_all_addresses.ino * @copyright Stroud Water Research Center * @license This example is published under the BSD-3 license. * @author Kevin M.Smith diff --git a/examples/d_simple_logger/ReadMe.md b/examples/d_simple_logger/ReadMe.md index 485915c..bb458ec 100644 --- a/examples/d_simple_logger/ReadMe.md +++ b/examples/d_simple_logger/ReadMe.md @@ -12,8 +12,8 @@ To address a sensor, please see Example B: b_address_change.ino [//]: # ( @section d_simple_logger_pio PlatformIO Configuration ) -[//]: # ( @example{lineno} d_simple_logger.ino @m_examplenavigation{examples_page,} @m_footernavigation ) - [//]: # ( @include{lineno} d_simple_logger/platformio.ini ) [//]: # ( @section d_simple_logger_code The Complete Example ) + +[//]: # ( @include{lineno} d_simple_logger/d_simple_logger.ino ) diff --git a/examples/d_simple_logger/d_simple_logger.ino b/examples/d_simple_logger/d_simple_logger.ino index 79d9e19..8393fb2 100644 --- a/examples/d_simple_logger/d_simple_logger.ino +++ b/examples/d_simple_logger/d_simple_logger.ino @@ -1,5 +1,5 @@ /** - * @file d_simple_logger.ino + * @example{lineno} d_simple_logger.ino * @copyright Stroud Water Research Center * @license This example is published under the BSD-3 license. * @author Kevin M.Smith diff --git a/examples/e_continuous_measurement/ReadMe.md b/examples/e_continuous_measurement/ReadMe.md index 0f8f00b..976b2c7 100644 --- a/examples/e_continuous_measurement/ReadMe.md +++ b/examples/e_continuous_measurement/ReadMe.md @@ -12,8 +12,8 @@ To address a sensor, please see Example B: b_address_change.ino [//]: # ( @section e_continuous_measurement_pio PlatformIO Configuration ) -[//]: # ( @example{lineno} e_continuous_measurement.ino @m_examplenavigation{examples_page,} @m_footernavigation ) - [//]: # ( @include{lineno} e_continuous_measurement/platformio.ini ) [//]: # ( @section e_continuous_measurement_code The Complete Example ) + +[//]: # ( @include{lineno} e_continuous_measurement/e_continuous_measurement.ino ) diff --git a/examples/e_continuous_measurement/e_continuous_measurement.ino b/examples/e_continuous_measurement/e_continuous_measurement.ino index 19a61b5..f38bc29 100644 --- a/examples/e_continuous_measurement/e_continuous_measurement.ino +++ b/examples/e_continuous_measurement/e_continuous_measurement.ino @@ -1,5 +1,5 @@ /** - * @file d_simple_logger.ino + * @example{lineno} e_continuous_measurement.ino * @copyright Stroud Water Research Center * @license This example is published under the BSD-3 license. * @author Kevin M.Smith diff --git a/examples/f_basic_data_request/ReadMe.md b/examples/f_basic_data_request/ReadMe.md index 4387063..2304116 100644 --- a/examples/f_basic_data_request/ReadMe.md +++ b/examples/f_basic_data_request/ReadMe.md @@ -6,8 +6,8 @@ This is a very basic (stripped down) example where the user initiates a measurem [//]: # ( @section f_basic_data_request_pio PlatformIO Configuration ) -[//]: # ( @example{lineno} f_basic_data_request.ino @m_examplenavigation{examples_page,} @m_footernavigation ) - [//]: # ( @include{lineno} f_basic_data_request/platformio.ini ) [//]: # ( @section f_basic_data_request_code The Complete Example ) + +[//]: # ( @include{lineno} f_basic_data_request/f_basic_data_request.ino ) diff --git a/examples/f_basic_data_request/f_basic_data_request.ino b/examples/f_basic_data_request/f_basic_data_request.ino index 0ebf2d7..129e3a4 100644 --- a/examples/f_basic_data_request/f_basic_data_request.ino +++ b/examples/f_basic_data_request/f_basic_data_request.ino @@ -1,8 +1,8 @@ /** - * @file f_basic_data_request.ino + * @example{lineno} f_basic_data_request.ino * @copyright Stroud Water Research Center * @license This example is published under the BSD-3 license. - * @author Ruben Kertesz or @rinnamon on twitter + * @author Ruben Kertesz or \@rinnamon on twitter * @date 2/10/2016 * * @brief Example F: Basic Data Request to a Single Sensor diff --git a/examples/g_terminal_window/ReadMe.md b/examples/g_terminal_window/ReadMe.md index 666adb3..40a1a52 100644 --- a/examples/g_terminal_window/ReadMe.md +++ b/examples/g_terminal_window/ReadMe.md @@ -6,8 +6,8 @@ It's purpose is to allow a user to interact with an SDI-12 sensor directly, issu [//]: # ( @section g_terminal_window_pio PlatformIO Configuration ) -[//]: # ( @example{lineno} g_terminal_window.ino @m_examplenavigation{examples_page,} @m_footernavigation ) - [//]: # ( @include{lineno} g_terminal_window/platformio.ini ) [//]: # ( @section g_terminal_window_code The Complete Example ) + +[//]: # ( @include{lineno} g_terminal_window/g_terminal_window.ino ) diff --git a/examples/g_terminal_window/g_terminal_window.ino b/examples/g_terminal_window/g_terminal_window.ino index a51a59d..fef0aec 100644 --- a/examples/g_terminal_window/g_terminal_window.ino +++ b/examples/g_terminal_window/g_terminal_window.ino @@ -1,10 +1,10 @@ /** - * @file g_terminal_window.ino + * @example{lineno} g_terminal_window.ino * @copyright Stroud Water Research Center * @license This example is published under the BSD-3 license. * @author Kevin M.Smith * @date August 2013 - * @author Ruben Kertesz or @rinnamon on twitter + * @author Ruben Kertesz or \@rinnamon on twitter * @date 2016 * * @brief Example G: Using the Arduino as a Command Terminal for SDI-12 Sensors diff --git a/examples/h_SDI-12_slave_implementation/h_SDI-12_slave_implementation.ino b/examples/h_SDI-12_slave_implementation/h_SDI-12_slave_implementation.ino index eafeffd..eda434c 100644 --- a/examples/h_SDI-12_slave_implementation/h_SDI-12_slave_implementation.ino +++ b/examples/h_SDI-12_slave_implementation/h_SDI-12_slave_implementation.ino @@ -1,5 +1,5 @@ /** - * @file h_SDI-12_slave_implementation.ino + * @example{lineno} h_SDI-12_slave_implementation.ino * @copyright Stroud Water Research Center * @license This example is published under the BSD-3 license. * @date 2016 diff --git a/examples/i_SDI-12_interface/i_SDI-12_interface.ino b/examples/i_SDI-12_interface/i_SDI-12_interface.ino index 8006469..559c544 100644 --- a/examples/i_SDI-12_interface/i_SDI-12_interface.ino +++ b/examples/i_SDI-12_interface/i_SDI-12_interface.ino @@ -1,5 +1,5 @@ /** - * @file h_SDI-12_slave_implementation.ino + * @example{lineno} i_SDI-12_interface.ino * @copyright Stroud Water Research Center * @license This example is published under the BSD-3 license. * @date 2016 diff --git a/examples/j_external_pcint_library/ReadMe.md b/examples/j_external_pcint_library/ReadMe.md index 33ed303..15625c0 100644 --- a/examples/j_external_pcint_library/ReadMe.md +++ b/examples/j_external_pcint_library/ReadMe.md @@ -6,8 +6,8 @@ To use this example, you must remove the comment braces around `#define SDI12_EX [//]: # ( @section j_external_pcint_library_pio PlatformIO Configuration ) -[//]: # ( @example{lineno} j_external_pcint_library.ino @m_examplenavigation{examples_page,} @m_footernavigation ) - [//]: # ( @include{lineno} j_external_pcint_library/platformio.ini ) [//]: # ( @section j_external_pcint_library_code The Complete Example ) + +[//]: # ( @include{lineno} j_external_pcint_library/j_external_pcint_library.ino ) diff --git a/examples/j_external_pcint_library/j_external_pcint_library.ino b/examples/j_external_pcint_library/j_external_pcint_library.ino index baf26e1..68e4924 100644 --- a/examples/j_external_pcint_library/j_external_pcint_library.ino +++ b/examples/j_external_pcint_library/j_external_pcint_library.ino @@ -1,5 +1,5 @@ /** - * @file j_external_pcint_library.ino + * @example{lineno} j_external_pcint_library.ino * @copyright Stroud Water Research Center * @license This example is published under the BSD-3 license. * @author Kevin M.Smith diff --git a/examples/k_concurrent_logger/ReadMe.md b/examples/k_concurrent_logger/ReadMe.md index 095d0a9..02fc3b4 100644 --- a/examples/k_concurrent_logger/ReadMe.md +++ b/examples/k_concurrent_logger/ReadMe.md @@ -6,8 +6,8 @@ This can be much faster than waiting for each sensor when you have multiple sens [//]: # ( @section k_concurrent_logger_pio PlatformIO Configuration ) -[//]: # ( @example{lineno} k_concurrent_logger.ino @m_examplenavigation{examples_page,} @m_footernavigation ) - [//]: # ( @include{lineno} k_concurrent_logger/platformio.ini ) [//]: # ( @section k_concurrent_logger_code The Complete Example ) + +[//]: # ( @include{lineno} k_concurrent_logger/k_concurrent_logger.ino ) diff --git a/examples/k_concurrent_logger/k_concurrent_logger.ino b/examples/k_concurrent_logger/k_concurrent_logger.ino index e5ab327..8a09e02 100644 --- a/examples/k_concurrent_logger/k_concurrent_logger.ino +++ b/examples/k_concurrent_logger/k_concurrent_logger.ino @@ -1,5 +1,5 @@ /** - * @file k_concurrent_logger.ino + * @example{lineno} k_concurrent_logger.ino * @copyright Stroud Water Research Center * @license This example is published under the BSD-3 license. * @author Sara Geleskie Damiano diff --git a/examples/l_verify_crc/ReadMe.md b/examples/l_verify_crc/ReadMe.md index 07f1b41..c793bc7 100644 --- a/examples/l_verify_crc/ReadMe.md +++ b/examples/l_verify_crc/ReadMe.md @@ -6,8 +6,8 @@ This is a very basic (stripped down) example where the user initiates a measurem [//]: # ( @section l_verify_crc_pio PlatformIO Configuration ) -[//]: # ( @example{lineno} l_verify_crc.ino @m_examplenavigation{examples_page,} @m_footernavigation ) - [//]: # ( @include{lineno} l_verify_crc/platformio.ini ) [//]: # ( @section l_verify_crc_code The Complete Example ) + +[//]: # ( @include{lineno} l_verify_crc/l_verify_crc.ino ) diff --git a/examples/l_verify_crc/l_verify_crc.ino b/examples/l_verify_crc/l_verify_crc.ino index b8bb99c..67e7f86 100644 --- a/examples/l_verify_crc/l_verify_crc.ino +++ b/examples/l_verify_crc/l_verify_crc.ino @@ -1,17 +1,13 @@ /** - * @file f_basic_data_request.ino + * @example{lineno} l_verify_crc.ino * @copyright Stroud Water Research Center * @license This example is published under the BSD-3 license. - * @author Ruben Kertesz or @rinnamon on twitter + * @author Ruben Kertesz or \@rinnamon on twitter * @date 2/10/2016 * - * @brief Example F: Basic Data Request to a Single Sensor + * @brief Example L: Verify CRC * - * This is a very basic (stripped down) example where the user initiates a measurement - * and receives the results to a terminal window without typing numerous commands into - * the terminal. - * - * Edited by Ruben Kertesz for ISCO Nile 502 2/10/2016 + * This example initiates a measurement anc checks the CRC on the returns. */ #include diff --git a/extras/SDI12_spy/SDI12_spy.ino b/extras/SDI12_spy/SDI12_spy.ino index ab6da5f..c2d42a0 100644 --- a/extras/SDI12_spy/SDI12_spy.ino +++ b/extras/SDI12_spy/SDI12_spy.ino @@ -1,32 +1,3 @@ -/** - * @file h_SDI-12_slave_implementation.ino - * @copyright Stroud Water Research Center - * @license This example is published under the BSD-3 license. - * @date 2016 - * @author D. Wasielewski - * - * @brief Example H: Using SDI-12 in Slave Mode - * - * Example sketch demonstrating how to implement an arduino as a slave on an SDI-12 bus. - * This may be used, for example, as a middleman between an I2C sensor and an SDI-12 - * datalogger. - * - * Note that an SDI-12 slave must respond to M! or C! with the number of values it will - * report and the max time until these values will be available. This example uses 9 - * values available in 21 s, but references to these numbers and the output array size - * and datatype should be changed for your specific application. - * - * D. Wasielewski, 2016 - * Builds upon work started by: - * https://github.com/jrzondagh/AgriApps-SDI-12-Arduino-Sensor - * https://github.com/Jorge-Mendes/Agro-Shield/tree/master/SDI-12ArduinoSensor - * - * Suggested improvements: - * - Get away from memory-hungry arduino String objects in favor of char buffers - * - Make an int variable for the "number of values to report" instead of the - * hard-coded 9s interspersed throughout the code - */ - #include int8_t dataPin = 7; /*!< The pin of the SDI-12 data bus */ diff --git a/extras/TestCommands/TestCommands.ino b/extras/TestCommands/TestCommands.ino index 55d757a..b333865 100644 --- a/extras/TestCommands/TestCommands.ino +++ b/extras/TestCommands/TestCommands.ino @@ -1,5 +1,5 @@ /** - * @file TestCommands.ino + * @example{lineno} TestCommands.ino * @copyright Stroud Water Research Center * @license This example is published under the BSD-3 license. * @author Sara Damiano diff --git a/extras/TestSensorTiming/TestSensorTiming.ino b/extras/TestSensorTiming/TestSensorTiming.ino index ce77f01..505e5e6 100644 --- a/extras/TestSensorTiming/TestSensorTiming.ino +++ b/extras/TestSensorTiming/TestSensorTiming.ino @@ -1,5 +1,5 @@ /** - * @file TestWarmUp.ino + * @example{lineno} TestSensorTiming.ino * @copyright Stroud Water Research Center * @license This example is published under the BSD-3 license. * @author Sara Damiano @@ -378,6 +378,7 @@ bool checkActive(char address, int8_t numPings = 3, bool printCommands = true) { * port * * @param i a character between '0'-'9', 'a'-'z', or 'A'-'Z'. + * @param printCommands true to print the raw output and input from the command */ bool printInfo(char i, bool printCommands = true) { String command = ""; diff --git a/extras/TestWarmUp/TestWarmUp.ino b/extras/TestWarmUp/TestWarmUp.ino index b58616d..df734ec 100644 --- a/extras/TestWarmUp/TestWarmUp.ino +++ b/extras/TestWarmUp/TestWarmUp.ino @@ -1,5 +1,5 @@ /** - * @file TestWarmUp.ino + * @example{lineno} TestWarmUp.ino * @copyright Stroud Water Research Center * @license This example is published under the BSD-3 license. * @author Sara Damiano From d50b5b998accc1879398b5089acdba1b652005b6 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Fri, 9 Aug 2024 14:56:06 -0400 Subject: [PATCH 76/96] Tweek address change script Signed-off-by: Sara Damiano --- .../b_address_change/b_address_change.ino | 49 +++++++++++++++++-- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/examples/b_address_change/b_address_change.ino b/examples/b_address_change/b_address_change.ino index f6da5b6..3d0e641 100644 --- a/examples/b_address_change/b_address_change.ino +++ b/examples/b_address_change/b_address_change.ino @@ -13,9 +13,10 @@ #include -uint32_t serialBaud = 115200; /*!< The baud rate for the output serial port */ +uint32_t serialBaud = 57600; /*!< The baud rate for the output serial port */ int8_t dataPin = 7; /*!< The pin of the SDI-12 data bus */ int8_t powerPin = 22; /*!< The sensor power pin (or -1 if not switching power) */ +uint32_t wake_delay = 0; /*!< Extra time needed for the sensor to wake (0-100ms) */ /** Define the SDI-12 bus */ SDI12 mySDI12(dataPin); @@ -23,6 +24,36 @@ SDI12 mySDI12(dataPin); String myCommand = ""; // empty to start char oldAddress = '!'; // invalid address as placeholder +/** + * @brief gets identification information from a sensor, and prints it to the serial + * port + * + * @param i a character between '0'-'9', 'a'-'z', or 'A'-'Z'. + */ +void printInfo(char i) { + String command = ""; + command += (char)i; + command += "I!"; + mySDI12.sendCommand(command, wake_delay); + delay(100); + + String sdiResponse = mySDI12.readStringUntil('\n'); + sdiResponse.trim(); + // allccccccccmmmmmmvvvxxx...xx + Serial.print(sdiResponse.substring(0, 1)); // address + Serial.print(", "); + Serial.print(sdiResponse.substring(1, 3).toFloat() / 10); // SDI-12 version number + Serial.print(", "); + Serial.print(sdiResponse.substring(3, 11)); // vendor id + Serial.print(", "); + Serial.print(sdiResponse.substring(11, 17)); // sensor model + Serial.print(", "); + Serial.print(sdiResponse.substring(17, 20)); // sensor version + Serial.print(", "); + Serial.print(sdiResponse.substring(20)); // sensor id + Serial.print(", "); +} + // this checks for activity at a particular address // expects a char, '0'-'9', 'a'-'z', or 'A'-'Z' boolean checkActive(byte i) { // this checks for activity at a particular address @@ -34,8 +65,8 @@ boolean checkActive(byte i) { // this checks for activity at a particular addre myCommand += "!"; for (int j = 0; j < 3; j++) { // goes through three rapid contact attempts - mySDI12.sendCommand(myCommand); - delay(30); + mySDI12.sendCommand(myCommand, wake_delay); + delay(100); if (mySDI12.available()) { // If we here anything, assume we have an active sensor Serial.println("Occupied"); mySDI12.clearBuffer(); @@ -57,12 +88,15 @@ void setup() { mySDI12.begin(); delay(500); // allow things to settle + Serial.println("Timeout value: "); + Serial.println(mySDI12.TIMEOUT); + // Power the sensors; if (powerPin >= 0) { - Serial.println("Powering up sensors..."); + Serial.println("Powering up sensors, wait 30s..."); pinMode(powerPin, OUTPUT); digitalWrite(powerPin, HIGH); - delay(200); + delay(30000L); } } @@ -74,6 +108,7 @@ void loop() { if (checkActive(i)) { found = true; oldAddress = i; + printInfo(i); } } @@ -82,6 +117,7 @@ void loop() { if (checkActive(i)) { found = true; oldAddress = i; + printInfo(i); } } @@ -90,6 +126,7 @@ void loop() { if (checkActive(i)) { found = true; oldAddress = i; + printInfo(i); } } @@ -97,6 +134,8 @@ void loop() { Serial.println( "No sensor detected. Check physical connections."); // couldn't find a sensor. // check connections.. + while (1) // die + ; } else { Serial.print("Sensor active at address "); // found a sensor! Serial.print(oldAddress); From 1d9a6436edde9a8752827d6553accf249abf6308 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Mon, 12 Aug 2024 09:57:21 -0400 Subject: [PATCH 77/96] Add src for extra example, update changelog Signed-off-by: Sara Damiano --- .github/workflows/build_examples.yaml | 1 + ChangeLog.md | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/.github/workflows/build_examples.yaml b/.github/workflows/build_examples.yaml index 47134e9..7cff046 100644 --- a/.github/workflows/build_examples.yaml +++ b/.github/workflows/build_examples.yaml @@ -35,4 +35,5 @@ jobs: - name: Build PlatformIO examples env: PLATFORMIO_BUILD_FLAGS: -D SDI12_EXTERNAL_PCINT + PLATFORMIO_CI_SRC: examples/j_external_pcint_library run: pio ci --board=mayfly --board=uno --board=megaatmega2560 --board=leonardo --board=zeroUSB --board=arduino_nano_esp32 --board=feather328p --board=feather32u4 --board=adafruit_feather_m0 --board=adafruit_feather_m4 --board=adafruit_grandcentral_m4 --board=huzzah --board=featheresp32 --board=nodemcu --board=nodemcuv2 --board=esp32dev --board=esp32-c3-devkitm-1 --board=esp32-s3-devkitm-1 --project-option="lib_deps=greygnome/EnableInterrupt@^1.1.0" diff --git a/ChangeLog.md b/ChangeLog.md index 3ddb745..0a95388 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -12,11 +12,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Added python version to GitHub actions (for PlatformIO) +- Switched to reusable workflows for GitHub actions +- Consolidated timer prescaler math ### Added +- Added support for SAMD51 processors using dedicated timers +- Allowing (_**without testing**_) processors over 48MHz to use `micros()` function + ### Removed +- Offloaded some internal header file documentation to markdown files + ### Fixed *** From 448b1e566b7876513f6cc56696a5dd41f2d74c68 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Mon, 12 Aug 2024 15:42:01 -0400 Subject: [PATCH 78/96] Doc renames Signed-off-by: Sara Damiano --- .gitignore | 1 + README.md | 2 +- docs/{SupportedBoardsAndTimers.md => TimerConfigurations.md} | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) rename docs/{SupportedBoardsAndTimers.md => TimerConfigurations.md} (99%) diff --git a/.gitignore b/.gitignore index b3058cc..7a423e9 100644 --- a/.gitignore +++ b/.gitignore @@ -167,3 +167,4 @@ boards/ lib/ variants/ docs/DoxygenLayout.xml_archive +/pio_common_libdeps.ini diff --git a/README.md b/README.md index b107aa3..491cabb 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,7 @@ The known compatibile pins for common variants are shown below: - 0, 11, 12, 13, 14, 15, 50, 51, 52, 53, A8 (62), A9 (63), A10 (64), A11 (65), A12 (66), A13 (67), A14 (68), A15 (69) -**AtMega32u4 / Arduino Leonardo or Adafruit Feather:** +**AtMega32u4 / Arduino Leonardo or Adafruit Feather 32u4:** - 8, 9, 10, 11, 14 (MISO), 15 (SCK), 16 (MOSI) diff --git a/docs/SupportedBoardsAndTimers.md b/docs/TimerConfigurations.md similarity index 99% rename from docs/SupportedBoardsAndTimers.md rename to docs/TimerConfigurations.md index f92ac04..2b282fc 100644 --- a/docs/SupportedBoardsAndTimers.md +++ b/docs/TimerConfigurations.md @@ -1,10 +1,10 @@ -# Bits and Ticks +# Timer Configurations [//]: # ( @tableofcontents ) [//]: # ( @cond GitHub ) -- [Bits and Ticks](#bits-and-ticks) +- [Timer Configurations](#timer-configurations) - [SDI-12 Timing Rules](#sdi-12-timing-rules) - [Ideal Timer Settings](#ideal-timer-settings) - [AVR Boards](#avr-boards) From 5944b3b3ee27dae1c3a1e084e0604225c570cea8 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Mon, 12 Aug 2024 15:42:33 -0400 Subject: [PATCH 79/96] Build fewer boards Signed-off-by: Sara Damiano --- .github/workflows/build_examples.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_examples.yaml b/.github/workflows/build_examples.yaml index 7cff046..27f4a05 100644 --- a/.github/workflows/build_examples.yaml +++ b/.github/workflows/build_examples.yaml @@ -13,7 +13,7 @@ jobs: if: ${{ ! contains(github.event.head_commit.message, 'ci skip') }} uses: EnviroDIY/workflows/.github/workflows/build_examples.yaml@main with: - boards_to_build: 'mayfly,uno,megaatmega2560,leonardo,zeroUSB,arduino_nano_esp32,feather328p,feather32u4,adafruit_feather_m0,adafruit_feather_m4,adafruit_grandcentral_m4,huzzah,featheresp32,nodemcu,nodemcuv2,esp32dev,esp32-c3-devkitm-1,esp32-s3-devkitm-1' + boards_to_build: 'mayfly,uno,megaatmega2560,leonardo,zeroUSB,arduino_nano_esp32,feather328p,feather32u4,adafruit_feather_m0,adafruit_feather_m4,huzzah,featheresp32,esp32-c3-devkitm-1,esp32-s3-devkitm-1' examples_to_build: 'examples/a_wild_card,examples/b_address_change,examples/c_check_all_addresses,examples/d_simple_logger,examples/e_continuous_measurement,examples/f_basic_data_request,examples/g_terminal_window,examples/h_SDI-12_slave_implementation,examples/i_SDI-12_interface,examples/k_concurrent_logger,examples/l_verify_crc' secrets: inherit @@ -36,4 +36,4 @@ jobs: env: PLATFORMIO_BUILD_FLAGS: -D SDI12_EXTERNAL_PCINT PLATFORMIO_CI_SRC: examples/j_external_pcint_library - run: pio ci --board=mayfly --board=uno --board=megaatmega2560 --board=leonardo --board=zeroUSB --board=arduino_nano_esp32 --board=feather328p --board=feather32u4 --board=adafruit_feather_m0 --board=adafruit_feather_m4 --board=adafruit_grandcentral_m4 --board=huzzah --board=featheresp32 --board=nodemcu --board=nodemcuv2 --board=esp32dev --board=esp32-c3-devkitm-1 --board=esp32-s3-devkitm-1 --project-option="lib_deps=greygnome/EnableInterrupt@^1.1.0" + run: pio ci --board=mayfly --board=uno --board=megaatmega2560 --board=leonardo --board=zeroUSB --board=arduino_nano_esp32 --board=feather328p --board=feather32u4 --board=adafruit_feather_m0 --board=adafruit_feather_m4 --board=huzzah --board=featheresp32 --board=esp32-c3-devkitm-1 --board=esp32-s3-devkitm-1 --project-option="lib_deps=greygnome/EnableInterrupt@^1.1.0" From a4d5ed17ffd4101a8d6142317c736fb0cb1f0fc3 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Mon, 12 Aug 2024 15:45:19 -0400 Subject: [PATCH 80/96] Tool changes Signed-off-by: Sara Damiano --- extras/TestCommands/TestCommands.ino | 305 +++++++++++++------ extras/TestSensorTiming/TestSensorTiming.ino | 85 ++++-- 2 files changed, 274 insertions(+), 116 deletions(-) diff --git a/extras/TestCommands/TestCommands.ino b/extras/TestCommands/TestCommands.ino index b333865..0fe5275 100644 --- a/extras/TestCommands/TestCommands.ino +++ b/extras/TestCommands/TestCommands.ino @@ -7,19 +7,32 @@ #include +#ifndef SDI12_DATA_PIN +#define SDI12_DATA_PIN 7 +#endif +#ifndef SDI12_POWER_PIN +#define SDI12_POWER_PIN 22 +#endif + /* connection information */ -uint32_t serialBaud = 115200; /*!< The baud rate for the output serial port */ -int8_t dataPin = 7; /*!< The pin of the SDI-12 data bus */ -int8_t powerPin = 22; /*!< The sensor power pin (or -1 if not switching power) */ -uint32_t wake_delay = 0; /*!< Extra time needed for the sensor to wake (0-100ms) */ -int8_t firstAddress = 0; /* The first address in the address space to check (0='0') */ -int8_t lastAddress = 62; /* The last address in the address space to check (62='z') */ -int8_t commandsToTest = +uint32_t serialBaud = 115200; /*!< The baud rate for the output serial port */ +int8_t dataPin = SDI12_DATA_PIN; /*!< The pin of the SDI-12 data bus */ +int8_t powerPin = + SDI12_POWER_PIN; /*!< The sensor power pin (or -1 if not switching power) */ +uint32_t wake_delay = 10; /*!< Extra time needed for the sensor to wake (0-100ms) */ +const int8_t firstAddress = + 0; /* The first address in the address space to check (0='0') */ +const int8_t lastAddress = + 6; /* The last address in the address space to check (62='z') */ +const int8_t commandsToTest = 1; /*!< The number of measurement commands to test, between 1 and 11. */ /** Define the SDI-12 bus */ SDI12 mySDI12(dataPin); +/** Define some testing specs */ +const int8_t n_addresses = (lastAddress - firstAddress) + 1; + /** Error codes, if returned */ int8_t error_result_number = 7; float no_error_value = 0; @@ -30,22 +43,25 @@ boolean flip = 0; String commands[] = {"", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}; // keeps track of active addresses -bool isActive[62]; +bool isActive[n_addresses]; // keeps track of the wait time for each active addresses -uint32_t meas_time_ms[62]; +uint32_t meas_time_ms[n_addresses]; // keeps track of the time each sensor was started -uint32_t millisStarted[62]; +uint32_t millisStarted[n_addresses]; // keeps track of the time each sensor will be ready -uint32_t millisReady[62]; +uint32_t millisReady[n_addresses]; // keeps track of the number of results expected -uint8_t returnedResults[62]; +uint8_t expectedResults[n_addresses]; -String prev_result[62]; -String this_result[62]; +// keeps track of the number of results returned +uint8_t returnedResults[n_addresses]; + +String prev_result[n_addresses]; +String this_result[n_addresses]; uint8_t numSensors = 0; struct startMeasurementResult { // Structure declaration @@ -83,8 +99,17 @@ char decToChar(byte i) { return i; } -bool getResults(char address, int resultsExpected, bool verify_crc = false, - bool printCommands = true) { +struct getResultsResult { // Structure declaration + uint8_t resultsReceived; + uint8_t maxDataCommand; + bool addressMatch; + bool crcMatch; + bool errorCode; + bool success; +}; + +getResultsResult getResults(char address, int resultsExpected, bool verify_crc = false, + bool printCommands = true) { uint8_t resultsReceived = 0; uint8_t cmd_number = 0; // The maximum number of characters that can be returned in the part of the @@ -98,6 +123,17 @@ bool getResults(char address, int resultsExpected, bool verify_crc = false, String compiled_response = ""; + bool success = true; + + // Create the return struct + getResultsResult return_result; + return_result.resultsReceived = 0; + return_result.maxDataCommand = 0; + return_result.addressMatch = true; + return_result.crcMatch = true; + return_result.errorCode = false; + return_result.success = true; + while (resultsReceived < resultsExpected && cmd_number <= 9) { String command = ""; command += address; @@ -106,7 +142,7 @@ bool getResults(char address, int resultsExpected, bool verify_crc = false, command += "!"; // SDI-12 command to get data [address][D][dataOption][!] mySDI12.sendCommand(command, wake_delay); - uint32_t start = millis(); + // uint32_t start = millis(); if (printCommands) { Serial.print(">>>"); Serial.println(command); @@ -142,7 +178,7 @@ bool getResults(char address, int resultsExpected, bool verify_crc = false, } mySDI12.clearBuffer(); - // check the address, return if it's incorrect + // check the address, break if it's incorrect char returned_address = resp_buffer[0]; if (returned_address != address) { if (printCommands) { @@ -153,10 +189,12 @@ bool getResults(char address, int resultsExpected, bool verify_crc = false, Serial.println(String(returned_address)); Serial.println(String(resp_buffer)); } - return false; + success = false; + return_result.addressMatch = false; + break; } - // check the crc, return if it's incorrect + // check the crc, break if it's incorrect if (verify_crc) { bool crcMatch = mySDI12.verifyCRC(sdiResponse); data_bytes_read = data_bytes_read - 3; @@ -164,7 +202,9 @@ bool getResults(char address, int resultsExpected, bool verify_crc = false, if (printCommands) { Serial.println("CRC valid"); } } else { if (printCommands) { Serial.println("CRC check failed!"); } - return false; + return_result.crcMatch = false; + success = false; + break; } } @@ -225,13 +265,12 @@ bool getResults(char address, int resultsExpected, bool verify_crc = false, // check for a failure error code at the end if (error_result_number >= 1) { if (resultsReceived == error_result_number && result != no_error_value) { - gotResults = false; - resultsReceived = 0; + success = false; + return_result.errorCode = true; if (printCommands) { Serial.print("Got a failure code of "); Serial.println(String(result, strnlen(dec_pl, max_sdi_digits) - 1)); } - return false; } } @@ -271,9 +310,12 @@ bool getResults(char address, int resultsExpected, bool verify_crc = false, Serial.println(resultsReceived == resultsExpected ? "success." : "failure."); } - bool success = resultsReceived == resultsExpected; - if (success) { this_result[charToDec(address)] = compiled_response; } - return success; + success &= resultsReceived == resultsExpected; + this_result[charToDec(address)] = compiled_response; + return_result.resultsReceived = resultsReceived; + return_result.maxDataCommand = cmd_number; + return_result.success = success; + return return_result; } bool getContinuousResults(char address, int resultsExpected, @@ -393,6 +435,30 @@ startMeasurementResult startMeasurement(char address, bool is_concurrent = false return return_result; } +// This is a separate function in this example so the wait times can all be filled into +// the appropriate arrays +int startConcurrentMeasurement(char address, bool request_crc = false, + String meas_type = "", bool printCommands = true) { + startMeasurementResult startResult = startMeasurement(address, true, request_crc, + meas_type, printCommands); + + uint8_t sensorNum = + charToDec(address); // e.g. convert '0' to 0, 'a' to 10, 'Z' to 61. + meas_time_ms[sensorNum] = ((uint32_t)(startResult.meas_time_s)) * 1000; + millisStarted[sensorNum] = millis(); + if (startResult.meas_time_s == 0) { + millisReady[sensorNum] = millis(); + } else { + // give an extra second + // millisReady[sensorNum] = millis() + meas_time_ms[sensorNum] + 1000; + // subtract a second to start polling early + millisReady[sensorNum] = millis() + meas_time_ms[sensorNum] - 1000; + } + expectedResults[sensorNum] = startResult.numberResults; + + return startResult.numberResults; +} + uint32_t takeMeasurement(char address, bool request_crc = false, String meas_type = "", bool printCommands = true) { startMeasurementResult startResult = startMeasurement(address, false, request_crc, @@ -418,33 +484,14 @@ uint32_t takeMeasurement(char address, bool request_crc = false, String meas_typ } // if we got results, return the measurement time, else -1 - if (getResults(address, startResult.numberResults, request_crc, printCommands)) { + if (getResults(address, startResult.numberResults, request_crc, printCommands) + .success) { return measTime; } return -1; } -int startConcurrentMeasurement(char address, bool request_crc = false, - String meas_type = "", bool printCommands = true) { - startMeasurementResult startResult = startMeasurement(address, true, request_crc, - meas_type, printCommands); - - uint8_t sensorNum = - charToDec(address); // e.g. convert '0' to 0, 'a' to 10, 'Z' to 61. - meas_time_ms[sensorNum] = ((uint32_t)(startResult.meas_time_s)) * 1000; - millisStarted[sensorNum] = millis(); - if (startResult.meas_time_s == 0) { - millisReady[sensorNum] = millis(); - } else { - // give an extra second - millisReady[sensorNum] = millis() + meas_time_ms[sensorNum] + 1000; - } - returnedResults[sensorNum] = startResult.numberResults; - - return startResult.numberResults; -} - // this checks for activity at a particular address // expects a char, '0'-'9', 'a'-'z', or 'A'-'Z' bool checkActive(char address, int8_t numPings = 3, bool printCommands = true) { @@ -483,6 +530,7 @@ bool checkActive(char address, int8_t numPings = 3, bool printCommands = true) { * port * * @param i a character between '0'-'9', 'a'-'z', or 'A'-'Z'. + * @param printCommands true to print the raw output and input from the command */ bool printInfo(char i, bool printCommands = true) { String command = ""; @@ -526,18 +574,22 @@ void setup() { while (!Serial) ; - Serial.println("Opening SDI-12 bus..."); + Serial.print("Opening SDI-12 bus on pin "); + Serial.print(String(dataPin)); + Serial.println("..."); mySDI12.begin(); delay(500); // allow things to settle Serial.println("Timeout value: "); Serial.println(mySDI12.TIMEOUT); - for (int8_t i = firstAddress; i < lastAddress; i++) { + // Fill arrays with 0's + for (int8_t i = firstAddress; i <= lastAddress; i++) { isActive[i] = false; meas_time_ms[i] = 0; millisStarted[i] = 0; millisReady[i] = 0; + expectedResults[i] = 0; returnedResults[i] = 0; prev_result[i] = ""; this_result[i] = ""; @@ -545,19 +597,15 @@ void setup() { // Power the sensors; if (powerPin >= 0) { - Serial.println("Powering down sensors..."); - pinMode(powerPin, OUTPUT); - digitalWrite(powerPin, LOW); - // delay(2500L); - delay(250L); - } - - // Power the sensors; - if (powerPin >= 0) { - Serial.println("Powering up sensors, wait 30s..."); + Serial.println("Powering up sensors with pin "); + Serial.print(String(powerPin)); + Serial.println(", wait 30s..."); pinMode(powerPin, OUTPUT); digitalWrite(powerPin, HIGH); delay(30000L); + } else { + Serial.println("Wait 5s..."); + delay(5000L); } // Quickly Scan the Address Space @@ -604,25 +652,18 @@ void loop() { // Serial.print("Flip: "); // Serial.println(flip); - // // Power the sensors; - // if (powerPin >= 0) { - // Serial.println("Powering down sensors..."); - // pinMode(powerPin, OUTPUT); - // digitalWrite(powerPin, LOW); - // delay(5000L); - // } - - // // Power the sensors; - // if (powerPin >= 0) { - // Serial.println("Powering up sensors..."); - // pinMode(powerPin, OUTPUT); - // digitalWrite(powerPin, HIGH); - // delay(125); - // } + // Fill arrays with 0's + for (int8_t i = firstAddress; i <= lastAddress; i++) { + meas_time_ms[i] = 0; + millisStarted[i] = 0; + millisReady[i] = 0; + expectedResults[i] = 0; + returnedResults[i] = 0; + } if (flip) { // measure one at a time - for (int8_t i = firstAddress; i < lastAddress; i++) { + for (int8_t i = firstAddress; i <= lastAddress; i++) { char addr = decToChar(i); if (isActive[i]) { for (uint8_t a = 0; a < commandsToTest; a++) { @@ -649,7 +690,7 @@ void loop() { uint32_t max_wait = 0; uint32_t for_start = millis(); // start all sensors measuring concurrently - for (int8_t i = firstAddress; i < lastAddress; i++) { + for (int8_t i = firstAddress; i <= lastAddress; i++) { char addr = decToChar(i); if (isActive[i]) { Serial.print("Command "); @@ -666,42 +707,112 @@ void loop() { Serial.println(" is not active"); } } - min_wait = 800; - // min_wait = max(10, min_wait / 2); - max_wait = max((uint32_t)1000, max_wait + (uint32_t)1000); + // min_wait = 800; + min_wait = max((uint32_t)10, min_wait / 2); + max_wait = max((uint32_t)1000, max_wait + (uint32_t)2000); Serial.print("minimum expected wait for all sensors: "); Serial.println(min_wait); Serial.print("maximum expected wait for all sensors: "); Serial.println(max_wait); +#if defined(TEST_PRINT_ARRAY) + Serial.print("i,\t"); + Serial.print("addr,\t"); + Serial.print("isActive[i],\t"); + Serial.print("millis,\t"); + Serial.print("timeWaited,\t"); + Serial.print("millisReady[i],\t"); + Serial.print("expectedResults[i],\t"); + Serial.print("returnedResults[i],\t"); + Serial.print("millis() > millisReady[i],\t"); + Serial.print("expectedResults[i] > 0,\t"); + Serial.print("returnedResults[i] < expectedResults[i],\t"); + Serial.print("numSensors,\t"); + Serial.print("numReadingsRecorded,\t"); + Serial.print("maxDataCommand,\t"); + Serial.print("resultsReceived,\t"); + Serial.print("errorCode,\t"); + Serial.print("crcMatch,\t"); + Serial.print("gotGoodResults,\t"); + Serial.print("numReadingsRecorded"); + Serial.println(); +#endif uint8_t numReadingsRecorded = 0; delay(min_wait); + do { // get all readings - for (int8_t i = firstAddress; i < lastAddress; i++) { - uint32_t timeWaited = millis() - millisStarted[i]; + for (int8_t i = firstAddress; i <= lastAddress; i++) { + uint32_t timeWaited = 0; + if (millisStarted[i] != 0) { timeWaited = millis() - millisStarted[i]; } if (this_result[i] != "") { prev_result[i] = this_result[i]; } char addr = decToChar(i); - if (isActive[i]) { - // if (millis() > millisReady[i]) { - // if (millis() > millisStarted[i] + a) { - if (returnedResults[i] > 0) { - Serial.print("timeWaited: "); - Serial.println(timeWaited); - bool resultsReady = getResults(addr, returnedResults[i], true); - if (resultsReady) { - numReadingsRecorded++; - Serial.print("Got results from "); - Serial.print(numReadingsRecorded); - Serial.print(" of "); - Serial.print(numSensors); - Serial.println(" sensors"); - } + +#if defined(TEST_PRINT_ARRAY) + Serial.print(i); + Serial.print(",\t\""); + Serial.print(decToChar(i)); + Serial.print("\",\t"); + Serial.print(isActive[i]); + Serial.print(",\t"); + Serial.print(millis()); + Serial.print(",\t"); + Serial.print(timeWaited); + Serial.print(",\t"); + Serial.print(millisReady[i]); + Serial.print(",\t"); + Serial.print(expectedResults[i]); + Serial.print(",\t"); + Serial.print(returnedResults[i]); + Serial.print(",\t"); + Serial.print(millis() > millisReady[i]); + Serial.print(",\t"); + Serial.print(expectedResults[i] > 0); + Serial.print(",\t"); + Serial.print(returnedResults[i] < expectedResults[i]); + Serial.print(",\t"); + Serial.print(numSensors); + Serial.print(",\t"); + Serial.print(numReadingsRecorded); +#endif + + if (isActive[i] && (millis() > millisReady[i]) && (expectedResults[i] > 0) && + (returnedResults[i] < expectedResults[i])) { +#ifndef TEST_PRINT_ARRAY + Serial.print("timeWaited: "); + Serial.println(timeWaited); +#endif + getResultsResult cResult = getResults(addr, expectedResults[i], true); + returnedResults[i] = cResult.resultsReceived; + bool gotGoodResults = cResult.success; + if (gotGoodResults) { + numReadingsRecorded++; +#ifndef TEST_PRINT_ARRAY + Serial.print("Got results from "); + Serial.print(numReadingsRecorded); + Serial.print(" of "); + Serial.print(numSensors); + Serial.print(" sensors"); +#endif } +#if defined(TEST_PRINT_ARRAY) + Serial.print(",\t"); + Serial.print(cResult.maxDataCommand); + Serial.print(",\t"); + Serial.print(cResult.resultsReceived); + Serial.print(",\t"); + Serial.print(cResult.errorCode); + Serial.print(",\t"); + Serial.print(cResult.crcMatch); + Serial.print(",\t"); + Serial.print(gotGoodResults); +#endif } + Serial.println(); } + } while (millis() - for_start < max_wait && numReadingsRecorded < numSensors); } Serial.print("Total Time for Concurrent Measurements: "); diff --git a/extras/TestSensorTiming/TestSensorTiming.ino b/extras/TestSensorTiming/TestSensorTiming.ino index 505e5e6..0d21426 100644 --- a/extras/TestSensorTiming/TestSensorTiming.ino +++ b/extras/TestSensorTiming/TestSensorTiming.ino @@ -8,11 +8,20 @@ #include +#ifndef SDI12_DATA_PIN +#define SDI12_DATA_PIN 7 +#endif +#ifndef SDI12_POWER_PIN +#define SDI12_POWER_PIN 22 +#endif + /* connection information */ -uint32_t serialBaud = 115200; /*!< The baud rate for the output serial port */ -int8_t dataPin = 7; /*!< The pin of the SDI-12 data bus */ -char sensorAddress = '0'; /*!< The address of the SDI-12 sensor */ -int8_t powerPin = 22; /*!< The sensor power pin (or -1 if not switching power) */ +uint32_t serialBaud = 115200; /*!< The baud rate for the output serial port */ +int8_t dataPin = SDI12_DATA_PIN; /*!< The pin of the SDI-12 data bus */ +int8_t powerPin = + SDI12_POWER_PIN; /*!< The sensor power pin (or -1 if not switching power) */ +uint32_t wake_delay = 0; /*!< Extra time needed for the sensor to wake (0-100ms) */ +char sensorAddress = '0'; /*!< The address of the SDI-12 sensor */ /** Define the SDI-12 bus */ SDI12 mySDI12(dataPin); @@ -43,8 +52,8 @@ int32_t increment_wake = 5; /*!< The time to lengthen waits between reps. */ int32_t power_delay = min_power_delay; int32_t wake_delay = min_wake_delay; -int32_t total_meas_time = 0; -int32_t total_meas_made = 0; +int32_t total_meas_time = 0; +int32_t total_meas_made = 0; uint32_t max_meas_time = 0; struct startMeasurementResult { // Structure declaration @@ -53,8 +62,17 @@ struct startMeasurementResult { // Structure declaration int numberResults; }; -bool getResults(char address, int resultsExpected, bool verify_crc = false, - bool printCommands = true) { +struct getResultsResult { // Structure declaration + uint8_t resultsReceived; + uint8_t maxDataCommand; + bool addressMatch; + bool crcMatch; + bool errorCode; + bool success; +}; + +getResultsResult getResults(char address, int resultsExpected, bool verify_crc = false, + bool printCommands = true) { uint8_t resultsReceived = 0; uint8_t cmd_number = 0; // The maximum number of characters that can be returned in the part of the @@ -66,6 +84,19 @@ bool getResults(char address, int resultsExpected, bool verify_crc = false, // max chars in a unsigned 64 bit number int max_sdi_digits = 21; + String compiled_response = ""; + + bool success = true; + + // Create the return struct + getResultsResult return_result; + return_result.resultsReceived = 0; + return_result.maxDataCommand = 0; + return_result.addressMatch = true; + return_result.crcMatch = true; + return_result.errorCode = false; + return_result.success = true; + while (resultsReceived < resultsExpected && cmd_number <= 9) { String command = ""; command += address; @@ -88,6 +119,7 @@ bool getResults(char address, int resultsExpected, bool verify_crc = false, size_t data_bytes_read = bytes_read - 1; // subtract one for the /r before the /n String sdiResponse = String(resp_buffer); + compiled_response += sdiResponse; sdiResponse.trim(); if (printCommands) { Serial.print("<<<"); @@ -109,7 +141,7 @@ bool getResults(char address, int resultsExpected, bool verify_crc = false, } mySDI12.clearBuffer(); - // check the address, return if it's incorrect + // check the address, break if it's incorrect char returned_address = resp_buffer[0]; if (returned_address != address) { if (printCommands) { @@ -120,10 +152,12 @@ bool getResults(char address, int resultsExpected, bool verify_crc = false, Serial.println(String(returned_address)); Serial.println(String(resp_buffer)); } - return false; + success = false; + return_result.addressMatch = false; + break; } - // check the crc, return if it's incorrect + // check the crc, break if it's incorrect if (verify_crc) { bool crcMatch = mySDI12.verifyCRC(sdiResponse); data_bytes_read = data_bytes_read - 3; @@ -131,7 +165,9 @@ bool getResults(char address, int resultsExpected, bool verify_crc = false, if (printCommands) { Serial.println("CRC valid"); } } else { if (printCommands) { Serial.println("CRC check failed!"); } - return false; + return_result.crcMatch = false; + success = false; + break; } } @@ -192,8 +228,8 @@ bool getResults(char address, int resultsExpected, bool verify_crc = false, // check for a failure error code at the end if (error_result_number >= 1) { if (resultsReceived == error_result_number && result != no_error_value) { - gotResults = false; - resultsReceived = 0; + success = false; + return_result.errorCode = true; if (printCommands) { Serial.print("Got a failure code of "); Serial.println(String(result, strnlen(dec_pl, max_sdi_digits) - 1)); @@ -237,8 +273,11 @@ bool getResults(char address, int resultsExpected, bool verify_crc = false, Serial.println(resultsReceived == resultsExpected ? "success." : "failure."); } - bool success = resultsReceived == resultsExpected; - return success; + success &= resultsReceived == resultsExpected; + return_result.resultsReceived = resultsReceived; + return_result.maxDataCommand = cmd_number; + return_result.success = success; + return return_result; } startMeasurementResult startMeasurement(char address, bool is_concurrent = false, @@ -333,7 +372,8 @@ uint32_t takeMeasurement(char address, bool request_crc = false, String meas_typ } // if we got results, return the measurement time, else -1 - if (getResults(address, startResult.numberResults, request_crc, printCommands)) { + if (getResults(address, startResult.numberResults, request_crc, printCommands) + .success) { return measTime; } @@ -422,7 +462,9 @@ void setup() { while (!Serial) ; - Serial.println("Opening SDI-12 bus..."); + Serial.print("Opening SDI-12 bus on pin "); + Serial.print(String(dataPin)); + Serial.println("..."); mySDI12.begin(); delay(500); // allow things to settle @@ -431,10 +473,15 @@ void setup() { // Power the sensors; if (powerPin >= 0 && !testPowerOff) { - Serial.println("Powering up sensors, wait 30s..."); + Serial.println("Powering up sensors with pin "); + Serial.print(String(powerPin)); + Serial.println(", wait 30s..."); pinMode(powerPin, OUTPUT); digitalWrite(powerPin, HIGH); delay(30000L); + } else { + Serial.println("Wait 5s..."); + delay(5000L); } } From 65b067a43f2b2186ebeb5592d9dcc070a4d3790d Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Mon, 12 Aug 2024 16:54:26 -0400 Subject: [PATCH 81/96] Fiddle with baud Signed-off-by: Sara Damiano --- extras/TestCommands/TestCommands.ino | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/extras/TestCommands/TestCommands.ino b/extras/TestCommands/TestCommands.ino index 0fe5275..01c7924 100644 --- a/extras/TestCommands/TestCommands.ino +++ b/extras/TestCommands/TestCommands.ino @@ -15,7 +15,11 @@ #endif /* connection information */ +#if F_CPU > 48000000L +uint32_t serialBaud = 921600; /*!< The baud rate for the output serial port */ +#else uint32_t serialBaud = 115200; /*!< The baud rate for the output serial port */ +#endif int8_t dataPin = SDI12_DATA_PIN; /*!< The pin of the SDI-12 data bus */ int8_t powerPin = SDI12_POWER_PIN; /*!< The sensor power pin (or -1 if not switching power) */ From 034aeeedeaa6512843d282e8a2c631eea692279f Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Mon, 12 Aug 2024 16:56:47 -0400 Subject: [PATCH 82/96] Fix TCNT sizes Signed-off-by: Sara Damiano --- src/SDI12.cpp | 15 +++++++++++---- src/SDI12.h | 2 +- src/SDI12_boards.h | 2 +- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/SDI12.cpp b/src/SDI12.cpp index 3de26d0..57357e5 100644 --- a/src/SDI12.cpp +++ b/src/SDI12.cpp @@ -70,10 +70,10 @@ const sdi12timer_t SDI12::txBitWidth = TICKS_PER_BIT; // A mask waiting for a start bit; 0b11111111 const uint8_t SDI12::WAITING_FOR_START_BIT = 0xFF; -uint16_t SDI12::prevBitTCNT; // previous RX transition in micros -uint8_t SDI12::rxState = WAITING_FOR_START_BIT; // 0: got start bit; >0: bits rcvd -uint8_t SDI12::rxMask; // bit mask for building received character -uint8_t SDI12::rxValue; // character being built +sdi12timer_t SDI12::prevBitTCNT; // previous RX transition in micros +uint8_t SDI12::rxState = WAITING_FOR_START_BIT; // 0: got start bit; >0: bits rcvd +uint8_t SDI12::rxMask; // bit mask for building received character +uint8_t SDI12::rxValue; // character being built /* ================ Buffer Setup ====================================================*/ uint8_t SDI12::_rxBuffer[SDI12_BUFFER_SIZE]; // The Rx buffer @@ -238,6 +238,11 @@ SDI12::~SDI12() { // Begin void SDI12::begin() { +#if defined(ESP32) || defined(ESP8266) + // Add and remove a fake interrupt to avoid errors with gpio_install_isr_service + attachInterrupt(digitalPinToInterrupt(_dataPin), nullptr, CHANGE); + detachInterrupt(digitalPinToInterrupt(_dataPin)); +#endif // setState(SDI12_HOLDING); setActive(); // Set up the prescaler as needed for timers @@ -489,7 +494,9 @@ void SDI12::writeChar(uint8_t outChar) { // Set the line low for the all remaining 1's and the stop bit digitalWrite(_dataPin, LOW); +#if F_CPU < 48000000UL interrupts(); // Re-enable universal interrupts as soon as critical timing is past +#endif // Hold the line low until the end of the 10th bit sdi12timer_t bitTimeRemaining = txBitWidth * (10 - lastHighBit); diff --git a/src/SDI12.h b/src/SDI12.h index 3f56a7d..aba9330 100644 --- a/src/SDI12.h +++ b/src/SDI12.h @@ -268,7 +268,7 @@ class SDI12 : public Stream { /** * @brief Stores the time of the previous RX transition in micros */ - static uint16_t prevBitTCNT; + static sdi12timer_t prevBitTCNT; /** * @brief Tracks how many bits are accounted for on an incoming character. * diff --git a/src/SDI12_boards.h b/src/SDI12_boards.h index af031cd..ccc654b 100644 --- a/src/SDI12_boards.h +++ b/src/SDI12_boards.h @@ -288,7 +288,7 @@ sensors. This library provides a general software solution, without requiring * * The 32-bit timer rolls over after 4294967296 ticks, or 4294.9673 seconds */ -#define TICKS_PER_BIT 833 +#define TICKS_PER_BIT 833UL #define RX_WINDOW_FUDGE 50 #else From a71ae13abecdd8a369d8749208c2a693a1caac11 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Mon, 12 Aug 2024 18:01:27 -0400 Subject: [PATCH 83/96] fix strnlen Signed-off-by: Sara Damiano --- extras/TestCommands/TestCommands.ino | 8 +++++--- extras/TestSensorTiming/TestSensorTiming.ino | 10 ++++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/extras/TestCommands/TestCommands.ino b/extras/TestCommands/TestCommands.ino index 01c7924..5d57ca3 100644 --- a/extras/TestCommands/TestCommands.ino +++ b/extras/TestCommands/TestCommands.ino @@ -256,10 +256,12 @@ getResultsResult getResults(char address, int resultsExpected, bool verify_crc = Serial.print(", Raw value: "); Serial.print(float_buffer); dec_pl = strchr(float_buffer, '.'); - // Serial.print(", Len after decimal: "); - // Serial.print(strnlen(dec_pl, max_sdi_digits)); + size_t len_post_dec = 0; + if (dec_pl != nullptr) { len_post_dec = strnlen(dec_pl, max_sdi_digits) - 1; } + Serial.print(", Len after decimal: "); + Serial.print(len_post_dec); Serial.print(", Parsed value: "); - Serial.println(String(result, strnlen(dec_pl, max_sdi_digits) - 1)); + Serial.println(String(result, len_post_dec)); } // add how many results we have if (result != -9999) { diff --git a/extras/TestSensorTiming/TestSensorTiming.ino b/extras/TestSensorTiming/TestSensorTiming.ino index 0d21426..efcfce3 100644 --- a/extras/TestSensorTiming/TestSensorTiming.ino +++ b/extras/TestSensorTiming/TestSensorTiming.ino @@ -105,7 +105,7 @@ getResultsResult getResults(char address, int resultsExpected, bool verify_crc = command += "!"; // SDI-12 command to get data [address][D][dataOption][!] mySDI12.sendCommand(command, wake_delay); - uint32_t start = millis(); + // uint32_t start = millis(); if (printCommands) { Serial.print(">>>"); Serial.println(command); @@ -215,10 +215,12 @@ getResultsResult getResults(char address, int resultsExpected, bool verify_crc = Serial.print(", Raw value: "); Serial.print(float_buffer); dec_pl = strchr(float_buffer, '.'); - // Serial.print(", Len after decimal: "); - // Serial.print(strnlen(dec_pl, max_sdi_digits)); + size_t len_post_dec = 0; + if (dec_pl != nullptr) { len_post_dec = strnlen(dec_pl, max_sdi_digits) - 1; } + Serial.print(", Len after decimal: "); + Serial.print(len_post_dec); Serial.print(", Parsed value: "); - Serial.println(String(result, strnlen(dec_pl, max_sdi_digits) - 1)); + Serial.println(String(result, len_post_dec)); } // add how many results we have if (result != -9999) { From a2eaa8b0c7f9547e8da32445909ce27ba2d49ad6 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Mon, 12 Aug 2024 18:51:43 -0400 Subject: [PATCH 84/96] Fix esp platform names Signed-off-by: Sara Damiano --- library.json | 5 +---- library.properties | 2 +- src/SDI12.cpp | 3 ++- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/library.json b/library.json index 95b25f7..d2fd955 100644 --- a/library.json +++ b/library.json @@ -24,10 +24,7 @@ ], "license": "BSD-3-Clause", "frameworks": "arduino", - "platforms": [ - "atmelavr", - "atmelsam" - ], + "platforms": ["atmelavr", "atmelsam", "espressif8266", "espressif32"], "export": { "exclude": ["doc/*"] }, diff --git a/library.properties b/library.properties index 83a2da9..68bdb80 100644 --- a/library.properties +++ b/library.properties @@ -6,5 +6,5 @@ sentence=An Arduino library for SDI-12 communication with a wide variety of envi paragraph=This library provides a general software solution, without requiring any additional hardware. category=Communication url=https://github.com/EnviroDIY/Arduino-SDI-12 -architectures=avr,sam,samd,espressif +architectures=avr,sam,samd,esp8266,esp32 includes=SDI12.h diff --git a/src/SDI12.cpp b/src/SDI12.cpp index 57357e5..db75a09 100644 --- a/src/SDI12.cpp +++ b/src/SDI12.cpp @@ -817,7 +817,8 @@ void ISR_MEM_ACCESS SDI12::receiveISR() { // character (but have gotten all the data bits) then this should be a // stop bit and we can start looking for a new start bit. if ((pinLevel == LOW) || !nextCharStarted) { - rxState = WAITING_FOR_START_BIT; // reset the rx state, stop waiting for stop bit + rxState = + WAITING_FOR_START_BIT; // reset the rx state, stop waiting for stop bit } else { // If we just switched to HIGH, or we've exceeded the total number of // bits in a character, then the character must have ended with 1's/LOW, From 52b185b143988fe0c16aa01f32e3441b944b328d Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Mon, 12 Aug 2024 18:54:23 -0400 Subject: [PATCH 85/96] Fix concurrent example Signed-off-by: Sara Damiano --- .../k_concurrent_logger.ino | 127 ++++++++---------- 1 file changed, 59 insertions(+), 68 deletions(-) diff --git a/examples/k_concurrent_logger/k_concurrent_logger.ino b/examples/k_concurrent_logger/k_concurrent_logger.ino index 8a09e02..983ef0d 100644 --- a/examples/k_concurrent_logger/k_concurrent_logger.ino +++ b/examples/k_concurrent_logger/k_concurrent_logger.ino @@ -26,29 +26,22 @@ int8_t lastAddress = 62; /* The last address in the address space to check (62 SDI12 mySDI12(dataPin); // keeps track of active addresses -bool isActive[64] = { - 0, -}; +bool isActive[64]; // keeps track of the wait time for each active addresses -uint8_t meas_time_ms[64] = { - 0, -}; +uint8_t meas_time_ms[64]; // keeps track of the time each sensor was started -uint32_t millisStarted[64] = { - 0, -}; +uint32_t millisStarted[64]; // keeps track of the time each sensor will be ready -uint32_t millisReady[64] = { - 0, -}; +uint32_t millisReady[64]; // keeps track of the number of results expected -uint8_t returnedResults[64] = { - 0, -}; +uint8_t expectedResults[64]; + +// keeps track of the number of results returned +uint8_t returnedResults[64]; uint8_t numSensors = 0; @@ -81,44 +74,13 @@ char decToChar(byte i) { return i; } -/** - * @brief gets identification information from a sensor, and prints it to the serial - * port - * - * @param i a character between '0'-'9', 'a'-'z', or 'A'-'Z'. - */ -void printInfo(char i) { - String command = ""; - command += (char)i; - command += "I!"; - mySDI12.sendCommand(command); - delay(100); - - String sdiResponse = mySDI12.readStringUntil('\n'); - sdiResponse.trim(); - // allccccccccmmmmmmvvvxxx...xx - Serial.print(sdiResponse.substring(0, 1)); // address - Serial.print(", "); - Serial.print(sdiResponse.substring(1, 3).toFloat() / 10); // SDI-12 version number - Serial.print(", "); - Serial.print(sdiResponse.substring(3, 11)); // vendor id - Serial.print(", "); - Serial.print(sdiResponse.substring(11, 17)); // sensor model - Serial.print(", "); - Serial.print(sdiResponse.substring(17, 20)); // sensor version - Serial.print(", "); - Serial.print(sdiResponse.substring(20)); // sensor id - Serial.print(", "); -} - -bool getResults(char i, int resultsExpected) { +bool getResults(char address, int resultsExpected) { uint8_t resultsReceived = 0; uint8_t cmd_number = 0; while (resultsReceived < resultsExpected && cmd_number <= 9) { String command = ""; - // in this example we will only take the 'DO' measurement - command = ""; - command += i; + command = ""; + command += address; command += "D"; command += cmd_number; command += "!"; // SDI-12 command to get data [address][D][dataOption][!] @@ -148,7 +110,7 @@ bool getResults(char i, int resultsExpected) { cmd_number++; } mySDI12.clearBuffer(); - + returnedResults[charToDec(address)] = resultsReceived; return resultsReceived == resultsExpected; } @@ -181,7 +143,7 @@ int startConcurrentMeasurement(char i, String meas_type = "") { } else { millisReady[sensorNum] = millis() + wait * 1000; } - returnedResults[sensorNum] = numResults; + expectedResults[sensorNum] = numResults; return numResults; } @@ -206,6 +168,38 @@ boolean checkActive(char i) { return false; } +/** + * @brief gets identification information from a sensor, and prints it to the serial + * port + * + * @param i a character between '0'-'9', 'a'-'z', or 'A'-'Z'. + * @param printCommands true to print the raw output and input from the command + */ +void printInfo(char i) { + String command = ""; + command += (char)i; + command += "I!"; + mySDI12.sendCommand(command); + delay(100); + + String sdiResponse = mySDI12.readStringUntil('\n'); + sdiResponse.trim(); + // allccccccccmmmmmmvvvxxx...xx + Serial.print("Address: "); + Serial.print(sdiResponse.substring(0, 1)); // address + Serial.print(", SDI-12 Version: "); + Serial.print(sdiResponse.substring(1, 3).toFloat() / 10); // SDI-12 version number + Serial.print(", Vendor ID: "); + Serial.print(sdiResponse.substring(3, 11)); // vendor id + Serial.print(", Sensor Model: "); + Serial.print(sdiResponse.substring(11, 17)); // sensor model + Serial.print(", Sensor Version: "); + Serial.print(sdiResponse.substring(17, 20)); // sensor version + Serial.print(", Sensor ID: "); + Serial.print(sdiResponse.substring(20)); // sensor id + Serial.println(); +} + void setup() { Serial.begin(serialBaud); while (!Serial) @@ -231,7 +225,7 @@ void setup() { Serial.println("Protocol Version, Sensor Address, Sensor Vendor, Sensor Model, " "Sensor Version, Sensor ID"); - for (byte i = firstAddress; i < lastAddress; i++) { + for (int8_t i = firstAddress; i <= lastAddress; i++) { char addr = decToChar(i); if (checkActive(addr)) { numSensors++; @@ -257,31 +251,28 @@ void setup() { void loop() { // start all sensors measuring concurrently - for (byte i = firstAddress; i < lastAddress; i++) { + for (int8_t i = firstAddress; i <= lastAddress; i++) { char addr = decToChar(i); if (isActive[i]) { startConcurrentMeasurement(addr); } } // get all readings uint8_t numReadingsRecorded = 0; - while (numReadingsRecorded < numSensors) { - for (byte i = firstAddress; i < lastAddress; i++) { + do { + for (int8_t i = firstAddress; i <= lastAddress; i++) { char addr = decToChar(i); - if (isActive[i]) { - if (millis() > millisReady[i]) { - if (returnedResults[i] > 0) { - Serial.print(millis() / 1000); - Serial.print(", "); - Serial.print(addr); - Serial.print(", "); - getResults(addr, returnedResults[i]); - Serial.println(); - } - numReadingsRecorded++; - } + if (isActive[i] && millis() > millisReady[i] && expectedResults[i] > 0 && + (returnedResults[i] < expectedResults[i])) { + Serial.print(millis() / 1000); + Serial.print(", "); + Serial.print(addr); + Serial.print(", "); + getResults(addr, expectedResults[i]); + numReadingsRecorded++; + Serial.println(); } } - } + } while (numReadingsRecorded < numSensors); delay(10000); // wait ten seconds between measurement attempts. } From 7f184fbc318426799dde9ae0097b4ff50729534e Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Tue, 13 Aug 2024 09:44:35 -0400 Subject: [PATCH 86/96] use c++ rather than c-style casts Signed-off-by: Sara Damiano --- docs/CreatingACharacter.md | 2 +- examples/l_verify_crc/l_verify_crc.ino | 2 +- extras/TestCommands/TestCommands.ino | 18 ++++++----- extras/TestSensorTiming/TestSensorTiming.ino | 13 ++++---- src/SDI12.cpp | 32 ++++++++++---------- src/SDI12_boards.cpp | 5 +-- 6 files changed, 38 insertions(+), 34 deletions(-) diff --git a/docs/CreatingACharacter.md b/docs/CreatingACharacter.md index d53687e..9122fec 100644 --- a/docs/CreatingACharacter.md +++ b/docs/CreatingACharacter.md @@ -177,7 +177,7 @@ void SDI12::receiveISR() { // data, parity, or stop bit. // Check how many bit times have passed since the last change - uint16-t rxBits = bitTimes((uint8_t)(thisBitTCNT - prevBitTCNT)); + uint16-t rxBits = bitTimes(static_cast(thisBitTCNT - prevBitTCNT)); // Calculate how many *data+parity* bits should be left in the current character // - Each character has a total of 10 bits, 1 start bit, 7 data bits, 1 parity // bit, and 1 stop bit diff --git a/examples/l_verify_crc/l_verify_crc.ino b/examples/l_verify_crc/l_verify_crc.ino index 67e7f86..65b8f20 100644 --- a/examples/l_verify_crc/l_verify_crc.ino +++ b/examples/l_verify_crc/l_verify_crc.ino @@ -101,7 +101,7 @@ void loop() { // listen for measurement to finish unsigned long timerStart = millis(); - while ((millis() - timerStart) < (meas_time_s + 1) * 1000) { + while ((millis() - timerStart) < (static_cast(meas_time_s) + 1) * 1000) { if (mySDI12.available()) // sensor can interrupt us to let us know it is done early { unsigned long measTime = millis() - timerStart; diff --git a/extras/TestCommands/TestCommands.ino b/extras/TestCommands/TestCommands.ino index 5d57ca3..0384c3a 100644 --- a/extras/TestCommands/TestCommands.ino +++ b/extras/TestCommands/TestCommands.ino @@ -18,10 +18,10 @@ #if F_CPU > 48000000L uint32_t serialBaud = 921600; /*!< The baud rate for the output serial port */ #else -uint32_t serialBaud = 115200; /*!< The baud rate for the output serial port */ +uint32_t serialBaud = 115200; /*!< The baud rate for the output serial port */ #endif -int8_t dataPin = SDI12_DATA_PIN; /*!< The pin of the SDI-12 data bus */ -int8_t powerPin = +int8_t dataPin = SDI12_DATA_PIN; /*!< The pin of the SDI-12 data bus */ +int8_t powerPin = SDI12_POWER_PIN; /*!< The sensor power pin (or -1 if not switching power) */ uint32_t wake_delay = 10; /*!< Extra time needed for the sensor to wake (0-100ms) */ const int8_t firstAddress = @@ -255,7 +255,7 @@ getResultsResult getResults(char address, int resultsExpected, bool verify_crc = Serial.print(resultsReceived); Serial.print(", Raw value: "); Serial.print(float_buffer); - dec_pl = strchr(float_buffer, '.'); + dec_pl = strchr(float_buffer, '.'); size_t len_post_dec = 0; if (dec_pl != nullptr) { len_post_dec = strnlen(dec_pl, max_sdi_digits) - 1; } Serial.print(", Len after decimal: "); @@ -450,7 +450,7 @@ int startConcurrentMeasurement(char address, bool request_crc = false, uint8_t sensorNum = charToDec(address); // e.g. convert '0' to 0, 'a' to 10, 'Z' to 61. - meas_time_ms[sensorNum] = ((uint32_t)(startResult.meas_time_s)) * 1000; + meas_time_ms[sensorNum] = (static_cast(startResult.meas_time_s)) * 1000; millisStarted[sensorNum] = millis(); if (startResult.meas_time_s == 0) { millisReady[sensorNum] = millis(); @@ -474,7 +474,8 @@ uint32_t takeMeasurement(char address, bool request_crc = false, String meas_typ uint32_t timerStart = millis(); uint32_t measTime = -1; // wait up to 1 second longer than the specified return time - while ((millis() - timerStart) < ((uint32_t)startResult.meas_time_s + 1) * 1000) { + while ((millis() - timerStart) < + (static_cast(startResult.meas_time_s) + 1) * 1000) { if (mySDI12.available()) { break; } // sensor can interrupt us to let us know it is done early @@ -714,8 +715,9 @@ void loop() { } } // min_wait = 800; - min_wait = max((uint32_t)10, min_wait / 2); - max_wait = max((uint32_t)1000, max_wait + (uint32_t)2000); + min_wait = max(static_cast(10), min_wait / 2); + max_wait = max(static_cast(1000), + max_wait + static_cast(2000)); Serial.print("minimum expected wait for all sensors: "); Serial.println(min_wait); Serial.print("maximum expected wait for all sensors: "); diff --git a/extras/TestSensorTiming/TestSensorTiming.ino b/extras/TestSensorTiming/TestSensorTiming.ino index efcfce3..19cb802 100644 --- a/extras/TestSensorTiming/TestSensorTiming.ino +++ b/extras/TestSensorTiming/TestSensorTiming.ino @@ -33,8 +33,8 @@ int8_t error_result_number = 7; float no_error_value = 0; /** Testing turning off power */ -bool testPowerOff = false; -int32_t min_power_delay = 100L; /*!< The min time to test wake after power on. */ +bool testPowerOff = false; +int32_t min_power_delay = 100L; /*!< The min time to test wake after power on. */ int32_t max_power_delay = 180000L; /*!< The max time to test wake after power on. */ int32_t increment_power_delay = 100L; /*!< The time to lengthen waits between reps. */ int32_t power_off_time = 60000L; /*!< The time to power off between tests. */ @@ -214,7 +214,7 @@ getResultsResult getResults(char address, int resultsExpected, bool verify_crc = Serial.print(resultsReceived); Serial.print(", Raw value: "); Serial.print(float_buffer); - dec_pl = strchr(float_buffer, '.'); + dec_pl = strchr(float_buffer, '.'); size_t len_post_dec = 0; if (dec_pl != nullptr) { len_post_dec = strnlen(dec_pl, max_sdi_digits) - 1; } Serial.print(", Len after decimal: "); @@ -358,7 +358,8 @@ uint32_t takeMeasurement(char address, bool request_crc = false, String meas_typ uint32_t timerStart = millis(); uint32_t measTime = -1; // wait up to 1 second longer than the specified return time - while ((millis() - timerStart) < ((uint32_t)startResult.meas_time_s + 1) * 1000) { + while ((millis() - timerStart) < + (static_cast(startResult.meas_time_s) + 1) * 1000) { if (mySDI12.available()) { break; } // sensor can interrupt us to let us know it is done early @@ -520,7 +521,7 @@ void loop() { // checkActive(sensorAddress, true); uint32_t this_meas_time = takeMeasurement(sensorAddress, true, "", true); - bool this_result_good = this_meas_time != (uint32_t)-1; + bool this_result_good = this_meas_time != static_cast(-1); if (this_result_good) { total_meas_time += this_meas_time; @@ -553,7 +554,7 @@ void loop() { // measurements take while (got_good_results) { uint32_t this_meas_time = takeMeasurement(sensorAddress, true, "", false); - if (this_meas_time != (uint32_t)-1) { + if (this_meas_time != static_cast(-1)) { total_meas_time += this_meas_time; total_meas_made++; if (this_meas_time > max_meas_time) { max_meas_time = this_meas_time; } diff --git a/src/SDI12.cpp b/src/SDI12.cpp index db75a09..c93c6a3 100644 --- a/src/SDI12.cpp +++ b/src/SDI12.cpp @@ -59,11 +59,11 @@ SDI12Timer SDI12::sdi12timer; // The size of a bit in microseconds // 1200 baud = 1200 bits/second ~ 833.333 µs/bit -const uint16_t SDI12::bitWidth_micros = (uint16_t)833; +const uint16_t SDI12::bitWidth_micros = static_cast(833); // The required "break" before sending commands, >= 12ms -const uint16_t SDI12::lineBreak_micros = (uint16_t)12300; +const uint16_t SDI12::lineBreak_micros = static_cast(12300); // The required mark before a command or response, >= 8.33ms -const uint16_t SDI12::marking_micros = (uint16_t)8500; +const uint16_t SDI12::marking_micros = static_cast(8500); // the width of a single bit in "ticks" of the cpu clock. const sdi12timer_t SDI12::txBitWidth = TICKS_PER_BIT; @@ -473,7 +473,7 @@ void SDI12::writeChar(uint8_t outChar) { // We've used up roughly 150 clock cycles messing with parity, but a bit is 833µs, so // we've got time. - while ((sdi12timer_t)(READTIME - t0) < txBitWidth) {} + while (static_cast(READTIME - t0) < txBitWidth) {} t0 = READTIME; // advance start time // repeat for all data bits until the last bit different from marking @@ -485,7 +485,7 @@ void SDI12::writeChar(uint8_t outChar) { digitalWrite(_dataPin, HIGH); // set the pin state to HIGH for 0's } // Hold the line for this bit duration - while ((sdi12timer_t)(READTIME - t0) < txBitWidth) {} + while (static_cast(READTIME - t0) < txBitWidth) {} t0 = READTIME; // advance start time outChar = outChar >> 1; // shift character to expose the following bit @@ -500,7 +500,7 @@ void SDI12::writeChar(uint8_t outChar) { // Hold the line low until the end of the 10th bit sdi12timer_t bitTimeRemaining = txBitWidth * (10 - lastHighBit); - while ((sdi12timer_t)(READTIME - t0) < bitTimeRemaining) {} + while (static_cast(READTIME - t0) < bitTimeRemaining) {} } // The typical write functionality for a stream object @@ -604,8 +604,8 @@ uint16_t SDI12::calculateCRC(String& resp) { uint16_t crc = 0; for (uint16_t i = 0; i < resp.length(); i++) { - crc ^= (uint16_t) - resp[i]; // Set the CRC equal to the exclusive OR of the character and itself + crc ^= static_cast( + resp[i]); // Set the CRC equal to the exclusive OR of the character and itself for (int j = 0; j < 8; j++) { // count = 1 to 8 if (crc & 0x0001) { // if the least significant bit of the CRC is one crc >>= 1; // right shift the CRC one bit @@ -623,8 +623,8 @@ uint16_t SDI12::calculateCRC(const char* resp) { uint16_t crc = 0; for (size_t i = 0; i < strlen(resp); i++) { - crc ^= (uint16_t) - resp[i]; // Set the CRC equal to the exclusive OR of the character and itself + crc ^= static_cast( + resp[i]); // Set the CRC equal to the exclusive OR of the character and itself for (int j = 0; j < 8; j++) { // count = 1 to 8 if (crc & 0x0001) { // if the least significant bit of the CRC is one crc >>= 1; // right shift the CRC one bit @@ -643,12 +643,12 @@ uint16_t SDI12::calculateCRC(FlashString resp) { for (size_t i = 0; i < strlen_P((PGM_P)resp); i++) { responsechar = (char)pgm_read_byte((const char*)resp + i); - crc ^= (uint16_t)responsechar; // Set the CRC equal to the exclusive OR of the - // character and itself - for (int j = 0; j < 8; j++) { // count = 1 to 8 - if (crc & 0x0001) { // if the least significant bit of the CRC is one - crc >>= 1; // right shift the CRC one bit - crc ^= POLY; // set CRC equal to the exclusive OR of POLY and itself + crc ^= static_cast(responsechar); // Set the CRC equal to the exclusive + // OR of the character and itself + for (int j = 0; j < 8; j++) { // count = 1 to 8 + if (crc & 0x0001) { // if the least significant bit of the CRC is one + crc >>= 1; // right shift the CRC one bit + crc ^= POLY; // set CRC equal to the exclusive OR of POLY and itself } else { crc >>= 1; // right shift the CRC one bit } diff --git a/src/SDI12_boards.cpp b/src/SDI12_boards.cpp index 5f88508..13cd648 100644 --- a/src/SDI12_boards.cpp +++ b/src/SDI12_boards.cpp @@ -33,7 +33,8 @@ uint16_t SDI12Timer::bitTimes(sdi12timer_t dt) { #elif TIMER_INT_SIZE == 16 || TIMER_INT_SIZE == 32 uint16_t SDI12Timer::bitTimes(sdi12timer_t dt) { // divide the number of ticks by the ticks per bit - return (uint16_t)((dt + (sdi12timer_t)RX_WINDOW_FUDGE) / (sdi12timer_t)TICKS_PER_BIT); + return static_cast((dt + static_cast(RX_WINDOW_FUDGE)) / + static_cast(TICKS_PER_BIT)); } #else #error "Board timer is incorrectly configured!" @@ -513,7 +514,7 @@ void SDI12Timer::configSDI12TimerPrescale(void) {} void SDI12Timer::resetSDI12TimerPrescale(void) {} sdi12timer_t ISR_MEM_ACCESS SDI12Timer::SDI12TimerRead(void) { - return ((sdi12timer_t)(micros())); + return (static_cast(micros())); } // Unknown board From 683f2eeac8135a9ed647b3de9a86d5ae539d4b4a Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Tue, 13 Aug 2024 10:10:14 -0400 Subject: [PATCH 87/96] Expand changelog Signed-off-by: Sara Damiano --- .github/workflows/build_examples.yaml | 2 +- ChangeLog.md | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build_examples.yaml b/.github/workflows/build_examples.yaml index 27f4a05..a98389a 100644 --- a/.github/workflows/build_examples.yaml +++ b/.github/workflows/build_examples.yaml @@ -34,6 +34,6 @@ jobs: - name: Build PlatformIO examples env: - PLATFORMIO_BUILD_FLAGS: -D SDI12_EXTERNAL_PCINT + PLATFORMIO_BUILD_FLAGS: -D SDI12_EXTERNAL_PCINT -I/src PLATFORMIO_CI_SRC: examples/j_external_pcint_library run: pio ci --board=mayfly --board=uno --board=megaatmega2560 --board=leonardo --board=zeroUSB --board=arduino_nano_esp32 --board=feather328p --board=feather32u4 --board=adafruit_feather_m0 --board=adafruit_feather_m4 --board=huzzah --board=featheresp32 --board=esp32-c3-devkitm-1 --board=esp32-s3-devkitm-1 --project-option="lib_deps=greygnome/EnableInterrupt@^1.1.0" diff --git a/ChangeLog.md b/ChangeLog.md index 0a95388..d794e6f 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -14,18 +14,36 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added python version to GitHub actions (for PlatformIO) - Switched to reusable workflows for GitHub actions - Consolidated timer prescaler math +- Moved bitTimes and mul8x8to16 functions to the timers files +- Replace c-style casts with c++ style casts +- Do not disable interrupts during Tx for processors over 48MHz +- Cast all timer values and math to the proper type for the processor timer being read + - SAMD21 boards now use the full 16-bits of the timer rather than only the first 8-bits. + - AVR xU4 boards (ie, 32u4/Leonardo) still use the 10-bit timer as an 8-bit timer. +- Shortened name of espressif ISR access macro from `ESPFAMILY_USE_INSTRUCTION_RAM` to `ISR_MEM_ACCESS` + - This does not change any functionality of the macro, just the name. +- Moved defines to the top of the SDI12_boards.h file +- Renamed the "tools" directory to "extras" in compliance with Arduino library standards. ### Added - Added support for SAMD51 processors using dedicated timers +- Added parity checking on character reception + - This can be disabled by defining the macro `SDI12_IGNORE_PARITY` - Allowing (_**without testing**_) processors over 48MHz to use `micros()` function +- Added a 'yield' function within stream functions to allow the buffer to fill + - The yield time can be modified using the macro `SDI12_YIELD_MS` ### Removed - Offloaded some internal header file documentation to markdown files +- Consolidated redundant `READTIME` and `TCNTX` macros, removing `TCNTX` +- Removed documation @m_span commands ### Fixed +- Added an extra addition/removal of interrupts for espressif boards in the `begin` function to properly initialize the interrupts and avoid a later error with the `gpio_install_isr_service`. + *** ## v2.1.4 (2021-05-05) [Revert wake delay to 0ms](https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v2.1.4) From b59e48adbf02e1484a7bb2315ef539b63399b127 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Tue, 13 Aug 2024 11:35:02 -0400 Subject: [PATCH 88/96] Markdown codeblock changes Signed-off-by: Sara Damiano --- ChangeLog.md | 2 +- README.md | 3 +- docs/CreatingACharacter.md | 64 ++++++++++++++++++------------------ docs/OverviewOfInterrupts.md | 39 ++++++++++++---------- src/SDI12.h | 4 +-- 5 files changed, 58 insertions(+), 54 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index d794e6f..aea3e59 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -38,7 +38,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Offloaded some internal header file documentation to markdown files - Consolidated redundant `READTIME` and `TCNTX` macros, removing `TCNTX` -- Removed documation @m_span commands +- Removed documation m_span commands ### Fixed diff --git a/README.md b/README.md index 491cabb..200c26f 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,8 @@ Extensive documentation on the SDI-12 functions and classes is available here: ### Renaming Notice -**As of version 2.0.0 this library was renamed from "Arduino-SDI-12" to simply "SDI-12" to comply with requirements for inclusion in the Arduino.cc's IDE and Library Manager.** +> [!IMPORTANT] +> **As of version 2.0.0 this library was renamed from "Arduino-SDI-12" to simply "SDI-12" to comply with requirements for inclusion in the Arduino.cc's IDE and Library Manager.** [//]: # ( @tableofcontents ) diff --git a/docs/CreatingACharacter.md b/docs/CreatingACharacter.md index 9122fec..4fcaecc 100644 --- a/docs/CreatingACharacter.md +++ b/docs/CreatingACharacter.md @@ -59,22 +59,22 @@ And lets remind ourselves of the static variables we're using to store states: The `rxState`, `rxMask`, and `rxValue` all work together to form a character. When we're waiting for a start bit `rxValue` is empty, `rxMask` has only the bottom bit set, and `rxState` is set to WAITING-FOR-START-BIT: -``` - rxValue: | 0 0 0 0 0 0 0 0 --------------|----------------------------------- - rxMask: | 0 0 0 0 0 0 0 1 - rxState: | 1 1 1 1 1 1 1 1 +```unparsed +| rxValue: | 0 0 0 0 0 0 0 0 | +| -------- | ----------------------------- | +| rxMask: | 0 0 0 0 0 0 0 1 | +| rxState: | 1 1 1 1 1 1 1 1 | ``` ### The Start of a Character After we get a start bit, the `startChar()` function creates a blank slate for the new character, so our values are: -``` - rxValue: | 0 0 0 0 0 0 0 0 --------------|----------------------------------- - rxMask: | 0 0 0 0 0 0 0 1 - rxState: | 0 0 0 0 0 0 0 0 +```unparsed +| rxValue: | 0 0 0 0 0 0 0 0 | +| -------- | ----------------------------- | +| rxMask: | 0 0 0 0 0 0 0 1 | +| rxState: | 0 0 0 0 0 0 0 0 | ``` ### The Interrupt Fires @@ -94,22 +94,22 @@ For **each bit time that passed**, we apply the `rxMask` to the `rxValue`. - if the data bit received is LOW (1) we do an `|=` (bitwise OR) between the `rxMask` and the `rxValue` -``` - rxValue: | 0 0 0 0 0 0 0 1 --------------|---------------------------------^- bit-wise or puts the one - rxMask: | 0 0 0 0 0 0 0 1 from the rxMask into - rxState: | 0 0 0 0 0 0 0 0 the rxValue +```unparsed +| rxValue: | 0 0 0 0 0 0 0 1 | +| -------- | ----------------------------- |^- bit-wise or puts the one +| rxMask: | 0 0 0 0 0 0 0 1 | from the rxMask into +| rxState: | 0 0 0 0 0 0 0 0 | the rxValue ``` #### A HIGH/0 Bit - if the data bit received is HIGH (0) we do nothing -``` - rxValue: | 0 0 0 0 0 0 0 0 --------------|---------------------------------x- nothing happens - rxMask: | 0 0 0 0 0 0 0 1 - rxState: | 0 0 0 0 0 0 0 0 +```unparsed +| rxValue: | 0 0 0 0 0 0 0 0 | +| -------- | ----------------------------- |x- nothing happens +| rxMask: | 0 0 0 0 0 0 0 1 | +| rxState: | 0 0 0 0 0 0 0 0 | ``` #### Shifting Up @@ -120,13 +120,13 @@ The top bit falls off. - we always add a 0 on the `rxMask` and the `rxValue` - the values of the second bit of the `rxValue` (?) depends on what we did in the step above -``` - rxValue: | 0 <--- | 0 0 0 0 0 0 ? 0 <--- add a zero --------------|-------------------|---------------------------|--- - rxMask: | 0 <--- | 0 0 0 0 0 0 1 0 <--- add a zero - rxState: | 0 <--- | 0 0 0 0 0 0 0 1 <--- add a one --------------|-------------------|---------------------------|--- - | falls off the top | | added to the bottom +```unparsed +| rxValue: | 0 <--- | 0 0 0 0 0 0 ? 0 <--- add a zero | +| ----------------- | ------------------- | --------------------------------------------- | +| rxMask: | 0 <--- | 0 0 0 0 0 0 1 0 <--- add a zero | +| rxState: | 0 <--- | 0 0 0 0 0 0 0 1 <--- add a one | +| ----------------- | ------------------- | --------------------------------------------- | +| ----------------- | ^ falls off the top | ------- added to the bottom ^ | ``` ### A Finished Character @@ -136,11 +136,11 @@ The `rxMask` will have the one in the top bit. And the rxState will be filled - which just happens to be the value of `WAITING-FOR-START-BIT` for the next character. -``` - rxValue: | ? ? ? ? ? ? ? ? --------------|----------------------------------- - rxMask: | 1 0 0 0 0 0 0 0 - rxState: | 1 1 1 1 1 1 1 1 +```unparsed +| rxValue: | ? ? ? ? ? ? ? ? | +| -------- | ----------------------------- | +| rxMask: | 1 0 0 0 0 0 0 0 | +| rxState: | 1 1 1 1 1 1 1 1 | ``` ## The Full Interrupt Function diff --git a/docs/OverviewOfInterrupts.md b/docs/OverviewOfInterrupts.md index 28370af..1d6e597 100644 --- a/docs/OverviewOfInterrupts.md +++ b/docs/OverviewOfInterrupts.md @@ -3,6 +3,7 @@ [//]: # ( @tableofcontents ) [//]: # ( @cond GitHub ) + - [Overview of Interrupts](#overview-of-interrupts) - [What is an Interrupt?](#what-is-an-interrupt) - [Directly Controlling Interrupts on an AVR Board](#directly-controlling-interrupts-on-an-avr-board) @@ -13,6 +14,7 @@ [//]: # ( @endcond ) ## What is an Interrupt? + An interrupt is a signal that causes the microcontroller to halt execution of the program, and perform a subroutine known as an interrupt handler or Interrupt Service Routine (ISR). After the ISR, program execution continues where it left off. This allows the microcontroller to efficiently handle a time-sensitive function such as receiving a burst of data on one of its pins, by not forcing the microcontroller to wait for the data. @@ -32,22 +34,26 @@ For AVR processors, like the Arduino Uno or the EnviroDIY Mayfly, we have to use **Registers**: small 1-byte (8-bit) stores of memory directly accessible by processor PCMSK0, PCMSK1, PCMSK2, PCMSK3 -**PCICRx**: a register where the three least significant bits enable or disable pin change interrupts on a range of pins +`PCICRx`: a register where the three least significant bits enable or disable pin change interrupts on a range of pins + - i.e. {0,0,0,0,0,PCIE2,PCIE1,PCIE0}, where PCIE2 maps to PCMSK2, PCIE1 maps to PCMSK1, and PCIE0 maps to PCMSK0. -**PCMSKx**: a register that stores the state (enabled/disabled) of pin change interrupts on a single pin +`PCMSKx`: a register that stores the state (enabled/disabled) of pin change interrupts on a single pin + - Each bit stores a 1 (enabled) or 0 (disabled). On an Arduino Uno: + - There is on PCICR register controlling three ranges of pins - There are three mask registers (PCMSK0, PCMSK1, and PCMSK2) controlling individual pins. - Looking at one mask register, PCMSK0: - - the 8 bits represent: PCMSK0 {PCINT7, PCINT6, PCINT5, PCINT4, PCINT3, PCINT2, PCINT1, PCINT0} - - these map to: PCMSK0 {XTAL2, XTAL1, Pin 13, Pin 12, Pin 11, Pin 10, Pin 9, Pin 8} + - the 8 bits represent: PCMSK0 {PCINT7, PCINT6, PCINT5, PCINT4, PCINT3, PCINT2, PCINT1, PCINT0} + - these map to: PCMSK0 {XTAL2, XTAL1, Pin 13, Pin 12, Pin 11, Pin 10, Pin 9, Pin 8} + +`noInterrupts()`: a function to globally disable interrupts (of all types) -**noInterrupts()**: a function to globally disable interrupts (of all types) +`interrupts()`: a function to globally enable interrupts (of all types) -**interrupts()**: a function to globally enable interrupts (of all types) - interrupts will only occur if the requisite registers are set (e.g. PCMSK and PCICR). ### Enabling an Interrupt @@ -87,15 +93,15 @@ Or equivalently: `(1<<1)`, we get: `{00000010}`. To use the mask to set the bit of interest we use the bitwise or operator `|`. We will use the compact `|=` notation which does the operation and then stores the result back into the left hand side. - So the operation: ```cpp *digitalPinToPCMSK(_dataPin) |= (1< When the we would like to put the SDI-12 object in the DISABLED state, (e.g. the destructor is called), we need to make sure the bit corresponding to the data pin is unset. -Let us consider again the case of where an interrupt has been enabled on pin 13: {00100010}. +Let us consider again the case of where an interrupt has been enabled on pin 13: `{00100010}`. We want to be sure not to disturb this interrupt when disabling the interrupt on pin 9. We will make use of similar macros, but this time we will use an inverted bit mask and the AND operation. @@ -150,7 +153,7 @@ The inversion symbol `~` modifies the result to `{11111101}` So to finish our example: -``` +```unparsed ~(1< Date: Tue, 13 Aug 2024 15:55:57 -0400 Subject: [PATCH 89/96] Fix external int builder Signed-off-by: Sara Damiano --- .github/workflows/build_examples.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_examples.yaml b/.github/workflows/build_examples.yaml index a98389a..ef3f83e 100644 --- a/.github/workflows/build_examples.yaml +++ b/.github/workflows/build_examples.yaml @@ -34,6 +34,6 @@ jobs: - name: Build PlatformIO examples env: - PLATFORMIO_BUILD_FLAGS: -D SDI12_EXTERNAL_PCINT -I/src + PLATFORMIO_BUILD_FLAGS: -D SDI12_EXTERNAL_PCINT PLATFORMIO_CI_SRC: examples/j_external_pcint_library - run: pio ci --board=mayfly --board=uno --board=megaatmega2560 --board=leonardo --board=zeroUSB --board=arduino_nano_esp32 --board=feather328p --board=feather32u4 --board=adafruit_feather_m0 --board=adafruit_feather_m4 --board=huzzah --board=featheresp32 --board=esp32-c3-devkitm-1 --board=esp32-s3-devkitm-1 --project-option="lib_deps=greygnome/EnableInterrupt@^1.1.0" + run: pio ci --board=mayfly --board=uno --board=megaatmega2560 --board=leonardo --board=zeroUSB --board=feather328p --board=feather32u4 --board=adafruit_feather_m0 --board=adafruit_feather_m4 --lib="." --project-option="lib_deps=greygnome/EnableInterrupt@^1.1.0" From b3d51d5625b487afa2ff502298738042ee333815 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Tue, 13 Aug 2024 15:58:27 -0400 Subject: [PATCH 90/96] Don't use extints on samd51 Signed-off-by: Sara Damiano --- .github/workflows/build_examples.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_examples.yaml b/.github/workflows/build_examples.yaml index ef3f83e..ba96fc9 100644 --- a/.github/workflows/build_examples.yaml +++ b/.github/workflows/build_examples.yaml @@ -36,4 +36,4 @@ jobs: env: PLATFORMIO_BUILD_FLAGS: -D SDI12_EXTERNAL_PCINT PLATFORMIO_CI_SRC: examples/j_external_pcint_library - run: pio ci --board=mayfly --board=uno --board=megaatmega2560 --board=leonardo --board=zeroUSB --board=feather328p --board=feather32u4 --board=adafruit_feather_m0 --board=adafruit_feather_m4 --lib="." --project-option="lib_deps=greygnome/EnableInterrupt@^1.1.0" + run: pio ci --board=mayfly --board=uno --board=megaatmega2560 --board=leonardo --board=zeroUSB --board=feather328p --board=feather32u4 --board=adafruit_feather_m0 --lib="." --project-option="lib_deps=greygnome/EnableInterrupt@^1.1.0" From d9b2de5cfde9efb0228f3ece2f54ffc4f8dda327 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Wed, 14 Aug 2024 10:06:37 -0400 Subject: [PATCH 91/96] Update changelog Signed-off-by: Sara Damiano --- ChangeLog.md | 4 ++++ src/SDI12.cpp | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ChangeLog.md b/ChangeLog.md index aea3e59..74975a7 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -24,9 +24,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - This does not change any functionality of the macro, just the name. - Moved defines to the top of the SDI12_boards.h file - Renamed the "tools" directory to "extras" in compliance with Arduino library standards. +- Updated copyright and license texts +- SAMD boards now *partially* revert clock and prescaler settings when an SDI-12 instance is ended. + - Prescalers are reset to factory settings, the clock divisor is not reset ### Added +- Added CRC checking - Added support for SAMD51 processors using dedicated timers - Added parity checking on character reception - This can be disabled by defining the macro `SDI12_IGNORE_PARITY` diff --git a/src/SDI12.cpp b/src/SDI12.cpp index c93c6a3..f8b1030 100644 --- a/src/SDI12.cpp +++ b/src/SDI12.cpp @@ -700,7 +700,7 @@ void ISR_MEM_ACCESS SDI12::handleInterrupt() { // 7.2 - Creates a blank slate of bits for an incoming character void ISR_MEM_ACCESS SDI12::startChar() { - rxState = 0; // got a start bit + rxState = 0x00; // 0b00000000, got a start bit rxMask = 0x01; // 0b00000001, bit mask, lsb first rxValue = 0x00; // 0b00000000, RX character to be, a blank slate } // startChar From 97fbf5d7a85e2bc336b6ec3d0f2c847242c32c46 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Wed, 14 Aug 2024 10:18:55 -0400 Subject: [PATCH 92/96] Fix slave implementation example Signed-off-by: Sara Damiano --- .../h_SDI-12_slave_implementation.ino | 71 +++++++++++-------- 1 file changed, 40 insertions(+), 31 deletions(-) diff --git a/examples/h_SDI-12_slave_implementation/h_SDI-12_slave_implementation.ino b/examples/h_SDI-12_slave_implementation/h_SDI-12_slave_implementation.ino index 220a37b..05a3042 100644 --- a/examples/h_SDI-12_slave_implementation/h_SDI-12_slave_implementation.ino +++ b/examples/h_SDI-12_slave_implementation/h_SDI-12_slave_implementation.ino @@ -37,7 +37,7 @@ int state = 0; #define WAIT 0 #define INITIATE_CONCURRENT 1 #define INITIATE_MEASUREMENT 2 -#define PROCESS_COMMAND 3 +#define PROCESS_COMMAND 3 // Create object by which to communicate with the SDI-12 bus on SDIPIN SDI12 slaveSDI12(dataPin); @@ -196,7 +196,7 @@ void loop() { // Character '!' indicates the end of an SDI-12 command; if the current // character is '!', stop listening and respond to the command if (charReceived == '!') { - state = PROCESS_COMMAND; + state = PROCESS_COMMAND; // Command string is completed; do something with it parseSdi12Cmd(commandReceived, dValues); // Clear command string to reset for next command @@ -220,37 +220,46 @@ void loop() { // For aM! and aC! commands, parseSdi12Cmd will modify "state" to indicate that // a measurement should be taken switch (state) { - case WAIT: break; + case WAIT: + { + break; + } case INITIATE_CONCURRENT: - // Do whatever the sensor is supposed to do here - // For this example, we will just create arbitrary "simulated" sensor data - // NOTE: Your application might have a different data type (e.g. int) and - // number of values to report! - pollSensor(measurementValues); - // Populate the "dValues" String array with the values in SDI-12 format - formatOutputSDI(measurementValues, dValues, 75); - state = WAIT; - slaveSDI12.forceListen(); // sets SDI-12 pin as input to prepare for incoming - // message AGAIN - break; + { + // Do whatever the sensor is supposed to do here + // For this example, we will just create arbitrary "simulated" sensor data + // NOTE: Your application might have a different data type (e.g. int) and + // number of values to report! + pollSensor(measurementValues); + // Populate the "dValues" String array with the values in SDI-12 format + formatOutputSDI(measurementValues, dValues, 75); + state = WAIT; + slaveSDI12.forceListen(); // sets SDI-12 pin as input to prepare for incoming + // message AGAIN + break; + } case INITIATE_MEASUREMENT: - // Do whatever the sensor is supposed to do here - // For this example, we will just create arbitrary "simulated" sensor data - // NOTE: Your application might have a different data type (e.g. int) and - // number of values to report! - pollSensor(measurementValues); - // Populate the "dValues" String array with the values in SDI-12 format - formatOutputSDI(measurementValues, dValues, 35); - // For aM!, Send "service request" (
) when data is ready - String fullResponse = String(sensorAddress) + "\r\n"; - slaveSDI12.sendResponse(fullResponse); - state = WAIT; - slaveSDI12.forceListen(); // sets SDI-12 pin as input to prepare for incoming - // message AGAIN - break; + { + // Do whatever the sensor is supposed to do here + // For this example, we will just create arbitrary "simulated" sensor data + // NOTE: Your application might have a different data type (e.g. int) and + // number of values to report! + pollSensor(measurementValues); + // Populate the "dValues" String array with the values in SDI-12 format + formatOutputSDI(measurementValues, dValues, 35); + // For aM!, Send "service request" (
) when data is ready + String fullResponse = String(sensorAddress) + "\r\n"; + slaveSDI12.sendResponse(fullResponse); + state = WAIT; + slaveSDI12.forceListen(); // sets SDI-12 pin as input to prepare for incoming + // message AGAIN + break; + } case PROCESS_COMMAND: - state = WAIT; - slaveSDI12.forceListen(); - break; + { + state = WAIT; + slaveSDI12.forceListen(); + break; + } } } From 307de774412feea50cb8513d9061d7aa77dd06dd Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Wed, 14 Aug 2024 10:25:57 -0400 Subject: [PATCH 93/96] Reorder AVR interrupt attachment defines (no functional change) Signed-off-by: Sara Damiano --- src/SDI12.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/SDI12.cpp b/src/SDI12.cpp index f8b1030..d7c7b0a 100644 --- a/src/SDI12.cpp +++ b/src/SDI12.cpp @@ -314,14 +314,7 @@ uint8_t SDI12::parity_even_bit(uint8_t v) { // a helper function to switch pin interrupts on or off void SDI12::setPinInterrupts(bool enable) { -#if defined(ARDUINO_ARCH_SAMD) || defined(ESP32) || defined(ESP8266) - // Merely need to attach the interrupt function to the pin - if (enable) attachInterrupt(digitalPinToInterrupt(_dataPin), handleInterrupt, CHANGE); - // Merely need to detach the interrupt function from the pin - else - detachInterrupt(digitalPinToInterrupt(_dataPin)); - -#elif defined(__AVR__) && not defined(SDI12_EXTERNAL_PCINT) +#if defined(__AVR__) && not defined(SDI12_EXTERNAL_PCINT) if (enable) { // Enable interrupts on the register with the pin of interest *digitalPinToPCICR(_dataPin) |= (1 << digitalPinToPCICRbit(_dataPin)); @@ -339,12 +332,20 @@ void SDI12::setPinInterrupts(bool enable) { } // We don't detach the function from the interrupt for AVR processors } -#else + // if using AVR with external interrupts, do nothing +#elif defined(__AVR__) if (enable) { return; } else { return; } +// for other boards (SAMD/Espressif/??) use the attachInterrupt function +#else + // Merely need to attach the interrupt function to the pin + if (enable) attachInterrupt(digitalPinToInterrupt(_dataPin), handleInterrupt, CHANGE); + // Merely need to detach the interrupt function from the pin + else + detachInterrupt(digitalPinToInterrupt(_dataPin)); #endif } From 920997dfd122794346e5b76fb34d31dfb908c793 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Wed, 14 Aug 2024 12:23:31 -0400 Subject: [PATCH 94/96] Version bump Signed-off-by: Sara Damiano --- ChangeLog.md | 12 ++++++++++++ VERSION | 2 +- docs/Doxyfile | 2 +- library.json | 2 +- library.properties | 2 +- 5 files changed, 16 insertions(+), 4 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 74975a7..db6a26c 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -11,6 +11,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +### Added + +### Removed + +### Fixed + +*** + +## v2.2.0 (2024-08-14) [CRC and SAMD51 Support](https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v2.2.0) + +### Changed + - Added python version to GitHub actions (for PlatformIO) - Switched to reusable workflows for GitHub actions - Consolidated timer prescaler math diff --git a/VERSION b/VERSION index 0f84bed..a4b6ac3 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v2.1.4 \ No newline at end of file +v2.2.0 diff --git a/docs/Doxyfile b/docs/Doxyfile index 4375af5..c96899f 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -48,7 +48,7 @@ PROJECT_NAME = "SDI-12 for Arduino" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 2.1.4 +PROJECT_NUMBER = 2.2.0 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/library.json b/library.json index d2fd955..b7d1c8e 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "SDI-12", - "version": "2.1.4", + "version": "2.2.0", "keywords": "SDI-12, sdi12, communication, bus, sensor, Decagon", "description": "An Arduino library for SDI-12 communication with a wide variety of environmental sensors.", "repository": { diff --git a/library.properties b/library.properties index 68bdb80..25b51e6 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=SDI-12 -version=2.1.4 +version=2.2.0 author=Kevin M. Smith , Shannon Hicks maintainer=Sara Damiano sentence=An Arduino library for SDI-12 communication with a wide variety of environmental sensors. From b7327bd3175e92dde57795140e5b040790e7835a Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Wed, 14 Aug 2024 13:03:36 -0400 Subject: [PATCH 95/96] Reformat changelog Signed-off-by: Sara Damiano --- ChangeLog.md | 69 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 56 insertions(+), 13 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index db6a26c..7f57010 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -19,7 +19,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 *** -## v2.2.0 (2024-08-14) [CRC and SAMD51 Support](https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v2.2.0) +## [2.2.0] - 2024-08-14 + +CRC and SAMD51 Support ### Changed @@ -62,7 +64,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 *** -## v2.1.4 (2021-05-05) [Revert wake delay to 0ms](https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v2.1.4) +## [2.1.4] - 2021-05-05 + +Revert wake delay to 0ms ### Possibly breaking changes @@ -70,19 +74,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - In 92055d377b26fa862c43d1429de1ccbef054af01 this was bumped up to 10ms, which caused problems for several people. - The delay can now also be set using the build flag `-D SDI12_WAKE_DELAY=#` -## v2.1.3 (2021-03-24) [Migrate to GitHub Actions](https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v2.1.3) +## [2.1.3] - 2021-03-24 + +Migrate to GitHub Actions ### Improvements - Migrate from Travis to GitHub actions -## v2.1.1 (2020-08-20) [Patches for ATTiny](https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v2.1.1) +## [2.1.1] - 2020-08-20 + +Patches for ATTiny ### Bug Fixes - fixes for the timer and pre-scaler for the ATTiny, courtesy of \@gabbas1 -## v2.1.0 (2020-07-10) [Library Rename and ESP support](https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v2.1.0) +## [2.1.0] - 2020-07-10 + +Library Rename and ESP support [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.3939731.svg)](https://doi.org/10.5281/zenodo.3939731) @@ -94,20 +104,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add option of adding a delay before sending a command to allow the sensor to wake. Take advantage of this by calling the function `sendCommand(command, extraWakeTime)`. This may resolve issues with some Campbell sensors that would not previous communicate with this library. See - Adds Doxygen (Javadoc) style comments to **ALL** members of the library. The generated documentation is available at . -## v1.3.6 (2019-08-29) [Fixed extra compiler warnings](https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v1.3.6) +## [1.3.6] - 2019-08-29 + +Fixed extra compiler warnings ### Bug Fixes - A very minor update to fix compiler warnings found when using -Wextra in addition to -Wall. -## v1.3.5 (2019-07-01) [Removed SAMD Tone Conflict](https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v1.3.5) +## [1.3.5] - 2019-07-01 + +Removed SAMD Tone Conflict ### Improvements - SAMD boards will no longer have a conflict with the Tone functions in the Arduino core. AVR boards will still conflict. If you need to use Tone and SDI-12 together for some reason on an AVR boards, you must use the "delayBase" branch. - Examples were also updated and given platformio.ini files. -## v1.3.4 (2019-10-29) [Timer class](https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v1.3.4) +## [1.3.4] - 2019-10-29 + +Timer class ### Improvements @@ -115,14 +131,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 Maintaining interrupt control for SAMD processors as there are no interrupt vectors to be in conflict. Because the pin mode changes from input to output and back, allowing another library to control interrupts doesn't work. -## v1.3.3 (2018-05-11) [Unset prescalers](https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v1.3.3) +## [1.3.3] - 2018-05-11 + +Unset prescalers ### Improvements - Now unsetting timer prescalers and setting the isActive pointer to NULL in both the end and the destructor functions. - Also some clean-up of the examples. -## v1.3.1 (2018-04-06) [Added processor timer for greater stability](https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v1.3.1) +## [1.3.1] - 2018-04-06 + +Added processor timer for greater stability ### New Features @@ -133,18 +153,41 @@ Maintaining interrupt control for SAMD processors as there are no interrupt vect - Made changes to the write functions to use the timer to reduce the amount of time that all system interrupts are off. - Forcing all SDI-12 objects to use the same buffer to reduce ram usage. -## v1.1.0 (2018-03-15) [Better integration inside other libraries](https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v1.1.0) +## [1.1.0] - 2018-03-15 + +Better integration inside other libraries ### Improvements - Added notes and an empty constructor/populated begin method to allow this library to be more easily called inside of other libraries. -## v1.0.6 (2018-03-09) [Fixed timeout values](https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v1.0.6) +## [1.0.6] - 2018-03-09 + +Fixed timeout values ### Bug Fixes - Fixes the time-out values for the ParseInt and ParseFloat to be -9999. This was the intended behavior all along, but at some point those functions changed in the stream library and the identically named functions within SDI-12 intended to "hide" the stream functions ceased to be called. -## v1.0.1 (2017-05-16) [Initial Release](https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v1.0.1) +## [1.0.1] - 2017-05-16 + +Initial Release The first "official" release of this interrupt-based SDI-12 library for AVR and SAMD Arduino boards. + +*** + +[Unreleased]: https://github.com/EnviroDIY/Arduino-SDI-12/compare/v2.2.0...HEAD +[2.2.0]: https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v2.2.0 +[2.1.4]: https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v2.1.4 +[2.1.3]: https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v2.1.3 +[2.1.1]: https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v2.1.1 +[2.1.0]: https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v2.1.0 +[1.3.6]: https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v1.3.6 +[1.3.5]: https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v1.3.5 +[1.3.4]: https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v1.3.4 +[1.3.3]: https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v1.3.3 +[1.3.1]: https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v1.3.1 +[1.1.0]: https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v1.1.0 +[1.0.6]: https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v1.0.6 +[1.0.1]: https://github.com/EnviroDIY/Arduino-SDI-12/releases/tag/v1.0.1 From 71228e4e544341d23364b268ec4e99d093a15185 Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Wed, 14 Aug 2024 13:22:24 -0400 Subject: [PATCH 96/96] Make changelog adhere to Common Changelog Signed-off-by: Sara Damiano --- ChangeLog.md | 53 ++++++++++++++++++++++++++-------------------------- VERSION | 2 +- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 7f57010..f60fa7a 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -2,8 +2,9 @@ All notable changes to this project will be documented in this file. -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and its stricter, better defined, brother [Common Changelog](https://common-changelog.org/). + +This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). *** @@ -21,7 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [2.2.0] - 2024-08-14 -CRC and SAMD51 Support +_CRC and SAMD51 Support_ ### Changed @@ -66,39 +67,39 @@ CRC and SAMD51 Support ## [2.1.4] - 2021-05-05 -Revert wake delay to 0ms +_Revert wake delay to 0ms_ -### Possibly breaking changes +### Changed -- Reverted the default wake delay to 0ms. +- **BREAKING** Reverted the default wake delay to 0ms. - In 92055d377b26fa862c43d1429de1ccbef054af01 this was bumped up to 10ms, which caused problems for several people. - The delay can now also be set using the build flag `-D SDI12_WAKE_DELAY=#` ## [2.1.3] - 2021-03-24 -Migrate to GitHub Actions +_Migrate to GitHub Actions_ -### Improvements +### Changed - Migrate from Travis to GitHub actions ## [2.1.1] - 2020-08-20 -Patches for ATTiny +_Patches for ATTiny_ -### Bug Fixes +### Fixed - fixes for the timer and pre-scaler for the ATTiny, courtesy of \@gabbas1 ## [2.1.0] - 2020-07-10 -Library Rename and ESP support +_Library Rename and ESP support_ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.3939731.svg)](https://doi.org/10.5281/zenodo.3939731) **To comply with requirements for inclusion in the Arduino IDE, the word Arduino has been removed from the name of this library!** The repository name is unchanged. -### New Features +### Changed - Adds support for Espressif ESP8266 and ESP32 - Add option of adding a delay before sending a command to allow the sensor to wake. Take advantage of this by calling the function `sendCommand(command, extraWakeTime)`. This may resolve issues with some Campbell sensors that would not previous communicate with this library. See @@ -106,36 +107,36 @@ Library Rename and ESP support ## [1.3.6] - 2019-08-29 -Fixed extra compiler warnings +_Fixed extra compiler warnings_ -### Bug Fixes +### Fixed - A very minor update to fix compiler warnings found when using -Wextra in addition to -Wall. ## [1.3.5] - 2019-07-01 -Removed SAMD Tone Conflict +_Removed SAMD Tone Conflict_ -### Improvements +### Changed - SAMD boards will no longer have a conflict with the Tone functions in the Arduino core. AVR boards will still conflict. If you need to use Tone and SDI-12 together for some reason on an AVR boards, you must use the "delayBase" branch. - Examples were also updated and given platformio.ini files. ## [1.3.4] - 2019-10-29 -Timer class +_Timer class_ -### Improvements +### Changed - Made the timer changes into a compiled class. - -Maintaining interrupt control for SAMD processors as there are no interrupt vectors to be in conflict. Because the pin mode changes from input to output and back, allowing another library to control interrupts doesn't work. +- NOTE: Maintaining interrupt control for SAMD processors as there are no interrupt vectors to be in conflict. +Because the pin mode changes from input to output and back, allowing another library to control interrupts doesn't work. ## [1.3.3] - 2018-05-11 -Unset prescalers +_Unset prescalers_ -### Improvements +### Changed - Now unsetting timer prescalers and setting the isActive pointer to NULL in both the end and the destructor functions. - Also some clean-up of the examples. @@ -144,18 +145,18 @@ Unset prescalers Added processor timer for greater stability -### New Features +### Added - Changed the incoming data ISR to use a processor timer, this makes the reception more stable, especially when the ISR is controlled by an external library. This also creates some conflicts with other libraries that use Timer2. -### Improvements +### Changed - Made changes to the write functions to use the timer to reduce the amount of time that all system interrupts are off. - Forcing all SDI-12 objects to use the same buffer to reduce ram usage. ## [1.1.0] - 2018-03-15 -Better integration inside other libraries +_Better integration inside other libraries_ ### Improvements @@ -171,7 +172,7 @@ Fixed timeout values ## [1.0.1] - 2017-05-16 -Initial Release +_Initial Release_ The first "official" release of this interrupt-based SDI-12 library for AVR and SAMD Arduino boards. diff --git a/VERSION b/VERSION index a4b6ac3..ccbccc3 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v2.2.0 +2.2.0