Skip to content

Add esp32 support with CRC handling #82

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Sep 21, 2023
130 changes: 114 additions & 16 deletions src/SDI12.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
12 changes: 11 additions & 1 deletion src/SDI12.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@
#define SRC_SDI12_H_

// Import Required Libraries

#include <inttypes.h> // integer types library
#include <Arduino.h> // Arduino core library
#include <Stream.h> // Arduino Stream library
Expand Down Expand Up @@ -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.
*
Expand All @@ -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
/**
Expand Down Expand Up @@ -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)
*
Expand All @@ -926,7 +936,6 @@ class SDI12 : public Stream {
void sendResponse(FlashString resp);
///@}


/**
* @anchor interrupt_fxns
* @name Interrupt Service Routine
Expand Down Expand Up @@ -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_
13 changes: 7 additions & 6 deletions src/SDI12_boards.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions src/SDI12_boards.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,13 @@ sensors. This library provides a general software solution, without requiring
#include <Arduino.h>

#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
Expand Down