diff --git a/src/SDI12.cpp b/src/SDI12.cpp index c71e998..265f5c0 100644 --- a/src/SDI12.cpp +++ b/src/SDI12.cpp @@ -547,32 +547,130 @@ void SDI12::sendResponse(FlashString resp) { 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; + + 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])); +} -/* ================ Interrupt Service Routine =======================================*/ -// 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() { - if (_activeObject) _activeObject->receiveISR(); +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)); +} + +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])); } -#else -void SDI12::handleInterrupt() { + +#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(){ if (_activeObject) _activeObject->receiveISR(); } -#endif -// Creates a blank slate of bits for an incoming character -void SDI12::startChar() { - rxState = 0x00; // 0b00000000, got a start bit +// 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 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() { - // time of this data transition (plus ISR latency) - sdi12timer_t thisBitTCNT = READTIME; +// 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) uint8_t pinLevel = digitalRead(_dataPin); // current RX data level diff --git a/src/SDI12.h b/src/SDI12.h index ec949e3..ae0b6e9 100644 --- a/src/SDI12.h +++ b/src/SDI12.h @@ -126,6 +126,7 @@ #define SRC_SDI12_H_ // Import Required Libraries + #include // integer types library #include // Arduino core library #include // Arduino Stream library @@ -180,6 +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. * @@ -190,6 +192,7 @@ enum LookaheadMode { * use the micros function and must use the faster assembly macros to read the * processor timer directly. */ + #define READTIME sdi12timer.SDI12TimerRead() #else /** @@ -910,6 +913,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 + /** * @brief Send a response out on the data line (for slave use) * @@ -926,7 +936,6 @@ class SDI12 : public Stream { void sendResponse(FlashString resp); ///@} - /** * @anchor interrupt_fxns * @name Interrupt Service Routine @@ -976,6 +985,7 @@ 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 beb28e8..ac7c6bf 100644 --- a/src/SDI12_boards.cpp +++ b/src/SDI12_boards.cpp @@ -266,12 +266,13 @@ void SDI12Timer::resetSDI12TimerPrescale(void) { // #elif defined(ESP32) || defined(ESP8266) -void SDI12Timer::configSDI12TimerPrescale(void) {} -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 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 diff --git a/src/SDI12_boards.h b/src/SDI12_boards.h index a2a533e..f5e044a 100644 --- a/src/SDI12_boards.h +++ b/src/SDI12_boards.h @@ -19,9 +19,13 @@ 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 +#define ESPFAMILY_USE_INSTRUCTION_RAM /** The interger type (size) of the timer return value */ typedef uint8_t sdi12timer_t; #endif