Skip to content

Commit

Permalink
Merge pull request #82 from peterj43/Add-ESP32-Support
Browse files Browse the repository at this point in the history
Add esp32 support with CRC handling
  • Loading branch information
SRGDamia1 authored Sep 21, 2023
2 parents c5aac42 + 014d58f commit 7abc730
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 23 deletions.
130 changes: 114 additions & 16 deletions src/SDI12.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -542,32 +542,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 @@ -911,6 +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

/**
* @brief Send a response out on the data line (for slave use)
*
Expand All @@ -927,7 +937,6 @@ class SDI12 : public Stream {
void sendResponse(FlashString resp);
///@}


/**
* @anchor interrupt_fxns
* @name Interrupt Service Routine
Expand Down Expand Up @@ -977,6 +986,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

0 comments on commit 7abc730

Please sign in to comment.