Skip to content

Commit a3ddb53

Browse files
committed
Add option of additional delay for wake
Signed-off-by: Sara Damiano <sdamiano@stroudcenter.org>
1 parent 115a6e3 commit a3ddb53

File tree

5 files changed

+89
-48
lines changed

5 files changed

+89
-48
lines changed

Doxyfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ PROJECT_NAME = "SDI-12 for Arduino"
3838
# could be handy for archiving the generated documentation or if some version
3939
# control system is used.
4040

41-
PROJECT_NUMBER = 2.0.0
41+
PROJECT_NUMBER = 2.1.0
4242

4343
# Using the PROJECT_BRIEF tag one can provide an optional one line description
4444
# for a project that appears at the top of each page and should give viewer a

library.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "SDI-12",
3-
"version": "2.0.0",
3+
"version": "2.1.0",
44
"keywords": "SDI-12, sdi12, communication, bus, sensor, Decagon",
55
"description": "An Arduino library for SDI-12 communication with a wide variety of environmental sensors.",
66
"repository": {

library.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name=SDI-12
2-
version=2.0.0
2+
version=2.1.0
33
author=Kevin M. Smith <Kevin@elite-education.org>, Shannon Hicks <shicks@stroudcenter.org>
44
maintainer=Sara Damiano <sdamiano@stroudcenter.org>
55
sentence=An Arduino library for SDI-12 communication with a wide variety of environmental sensors.

src/SDI12.cpp

+43-31
Original file line numberDiff line numberDiff line change
@@ -211,11 +211,32 @@ float SDI12::parseFloat(LookaheadMode lookahead, char ignore) {
211211
/* ================ Constructor, Destructor, begin(), end(), and timeout ============*/
212212
// Constructor
213213
SDI12::SDI12() {
214+
_dataPin = NULL;
214215
_bufferOverflow = false;
216+
// SDI-12 protocol says sensors must respond within 15 milliseconds
217+
// We'll bump that up to 150, just for good measure, but we don't want to
218+
// wait the whole stream default of 1s for a response.
219+
setTimeout(150);
220+
// Because SDI-12 is mostly used for environmental sensors, we want to be able
221+
// to distinguish between the '0' that parseInt and parseFloat usually return
222+
// on timeouts and a real measured 0 value. So we force the timeout response
223+
// to be -9999, which is not a common value for most variables measured by
224+
// in-site environmental sensors.
225+
setTimeoutValue(-9999);
215226
}
216227
SDI12::SDI12(int8_t dataPin) {
217-
_bufferOverflow = false;
218228
_dataPin = dataPin;
229+
_bufferOverflow = false;
230+
// SDI-12 protocol says sensors must respond within 15 milliseconds
231+
// We'll bump that up to 150, just for good measure, but we don't want to
232+
// wait the whole stream default of 1s for a response.
233+
setTimeout(150);
234+
// Because SDI-12 is mostly used for environmental sensors, we want to be able
235+
// to distinguish between the '0' that parseInt and parseFloat usually return
236+
// on timeouts and a real measured 0 value. So we force the timeout response
237+
// to be -9999, which is not a common value for most variables measured by
238+
// in-site environmental sensors.
239+
setTimeoutValue(-9999);
219240
}
220241

221242
// Destructor
@@ -231,16 +252,6 @@ SDI12::~SDI12() {
231252
void SDI12::begin() {
232253
// setState(SDI12_HOLDING);
233254
setActive();
234-
// SDI-12 protocol says sensors must respond within 15 milliseconds
235-
// We'll bump that up to 150, just for good measure, but we don't want to
236-
// wait the whole stream default of 1s for a response.
237-
setTimeout(150);
238-
// Because SDI-12 is mostly used for environmental sensors, we want to be able
239-
// to distinguish between the '0' that parseInt and parseFloat usually return
240-
// on timeouts and a real measured 0 value. So we force the timeout response
241-
// to be -9999, which is not a common value for most variables measured by
242-
// in-site environmental sensors.
243-
setTimeoutValue(-9999);
244255
// Set up the prescaler as needed for timers
245256
// This function is defined in SDI12_boards.h
246257
sdi12timer.configSDI12TimerPrescale();
@@ -352,7 +363,7 @@ void SDI12::setState(SDI12_STATES state) {
352363
case SDI12_HOLDING: {
353364
pinMode(_dataPin, INPUT); // Turn off the pull-up resistor
354365
pinMode(_dataPin, OUTPUT); // Pin mode = output
355-
digitalWrite(_dataPin, LOW); // Pin state = low - hold the line low
366+
digitalWrite(_dataPin, LOW); // Pin state = low - marking
356367
setPinInterrupts(false); // Interrupts disabled on data pin
357368
break;
358369
}
@@ -363,7 +374,7 @@ void SDI12::setState(SDI12_STATES state) {
363374
break;
364375
}
365376
case SDI12_LISTENING: {
366-
digitalWrite(_dataPin, LOW); // Pin state = low
377+
digitalWrite(_dataPin, LOW); // Pin state = low (turns off pull-up)
367378
pinMode(_dataPin, INPUT); // Pin mode = input, pull-up resistor off
368379
interrupts(); // Enable general interrupts
369380
setPinInterrupts(true); // Enable Rx interrupts on data pin
@@ -372,7 +383,7 @@ void SDI12::setState(SDI12_STATES state) {
372383
}
373384
default: // SDI12_DISABLED or SDI12_ENABLED
374385
{
375-
digitalWrite(_dataPin, LOW); // Pin state = low
386+
digitalWrite(_dataPin, LOW); // Pin state = low (turns off pull-up)
376387
pinMode(_dataPin, INPUT); // Pin mode = input, pull-up resistor off
377388
setPinInterrupts(false); // Interrupts disabled on data pin
378389
break;
@@ -393,15 +404,16 @@ void SDI12::forceListen() {
393404

394405
/* ================ Waking Up and Talking To Sensors ================================*/
395406
// this function wakes up the entire sensor bus
396-
void SDI12::wakeSensors() {
407+
void SDI12::wakeSensors(int8_t extraWakeTime) {
397408
setState(SDI12_TRANSMITTING);
398409
// Universal interrupts can be on while the break and marking happen because
399410
// timings for break and from the recorder are not critical.
400411
// Interrupts on the pin are disabled for the entire transmitting state
401-
digitalWrite(_dataPin, HIGH);
402-
delayMicroseconds(lineBreak_micros); // Required break of 12 milliseconds
403-
digitalWrite(_dataPin, LOW);
404-
delayMicroseconds(marking_micros); // Required marking of 8.33 milliseconds
412+
digitalWrite(_dataPin, HIGH); // break is HIGH
413+
delayMicroseconds(lineBreak_micros); // Required break of 12 milliseconds (12,000 µs)
414+
delay(extraWakeTime); // allow the sensors to wake
415+
digitalWrite(_dataPin, LOW); // marking is LOW
416+
delayMicroseconds(marking_micros); // Required marking of 8.33 milliseconds(8,333 µs)
405417
}
406418

407419
// this function writes a character out on the data line
@@ -475,24 +487,24 @@ size_t SDI12::write(uint8_t byte) {
475487
}
476488

477489
// this function sends out the characters of the String cmd, one by one
478-
void SDI12::sendCommand(String& cmd) {
479-
wakeSensors(); // set state to transmitting and send break/marking
490+
void SDI12::sendCommand(String& cmd, int8_t extraWakeTime) {
491+
wakeSensors(extraWakeTime); // wake up sensors
480492
for (int unsigned i = 0; i < cmd.length(); i++) {
481493
writeChar(cmd[i]); // write each character
482494
}
483495
setState(SDI12_LISTENING); // listen for reply
484496
}
485497

486-
void SDI12::sendCommand(const char* cmd) {
487-
wakeSensors(); // wake up sensors
498+
void SDI12::sendCommand(const char* cmd, int8_t extraWakeTime) {
499+
wakeSensors(extraWakeTime); // wake up sensors
488500
for (int unsigned i = 0; i < strlen(cmd); i++) {
489501
writeChar(cmd[i]); // write each character
490502
}
491503
setState(SDI12_LISTENING); // listen for reply
492504
}
493505

494-
void SDI12::sendCommand(FlashString cmd) {
495-
wakeSensors(); // wake up sensors
506+
void SDI12::sendCommand(FlashString cmd, int8_t extraWakeTime) {
507+
wakeSensors(extraWakeTime); // wake up sensors
496508
for (int unsigned i = 0; i < strlen_P((PGM_P)cmd); i++) {
497509
// write each character
498510
writeChar(static_cast<char>(pgm_read_byte((const char*)cmd + i)));
@@ -505,8 +517,8 @@ void SDI12::sendCommand(FlashString cmd) {
505517
// that is, when the Arduino itself is acting as an SDI-12 device rather than a
506518
// recorder).
507519
void SDI12::sendResponse(String& resp) {
508-
setState(SDI12_TRANSMITTING); // Get ready to send data to the recorder
509-
digitalWrite(_dataPin, LOW);
520+
setState(SDI12_TRANSMITTING); // Get ready to send data to the recorder
521+
digitalWrite(_dataPin, LOW); // marking is LOW
510522
delayMicroseconds(marking_micros); // 8.33 ms marking before response
511523
for (int unsigned i = 0; i < resp.length(); i++) {
512524
writeChar(resp[i]); // write each character
@@ -515,8 +527,8 @@ void SDI12::sendResponse(String& resp) {
515527
}
516528

517529
void SDI12::sendResponse(const char* resp) {
518-
setState(SDI12_TRANSMITTING); // Get ready to send data to the recorder
519-
digitalWrite(_dataPin, LOW);
530+
setState(SDI12_TRANSMITTING); // Get ready to send data to the recorder
531+
digitalWrite(_dataPin, LOW); // marking is LOW
520532
delayMicroseconds(marking_micros); // 8.33 ms marking before response
521533
for (int unsigned i = 0; i < strlen(resp); i++) {
522534
writeChar(resp[i]); // write each character
@@ -525,8 +537,8 @@ void SDI12::sendResponse(const char* resp) {
525537
}
526538

527539
void SDI12::sendResponse(FlashString resp) {
528-
setState(SDI12_TRANSMITTING); // Get ready to send data to the recorder
529-
digitalWrite(_dataPin, LOW);
540+
setState(SDI12_TRANSMITTING); // Get ready to send data to the recorder
541+
digitalWrite(_dataPin, LOW); // marking is LOW
530542
delayMicroseconds(marking_micros); // 8.33 ms marking before response
531543
for (int unsigned i = 0; i < strlen_P((PGM_P)resp); i++) {
532544
// write each character

src/SDI12.h

+43-14
Original file line numberDiff line numberDiff line change
@@ -795,17 +795,41 @@ class SDI12 : public Stream {
795795
/**
796796
* @brief Used to wake up the SDI12 bus.
797797
*
798-
* Set the SDI-12 state to transmitting, hold the data line high for the required
799-
* break of 12 milliseconds, and hold the line low for the required marking of 8.33
800-
* milliseconds
801-
*
802-
* Literally wakes up all the sensors on the bus. The SDI-12 protocol requires a
803-
* pulse of HIGH voltage for at least 12 milliseconds followed immediately by a pulse
804-
* of LOW voltage for at least 8.3 milliseconds. Setting the SDI-12 object into the
798+
* @param extraWakeTime The amount of additional time in milliseconds that the sensor
799+
* takes to wake before being ready to receive a command. Default is 0ms - meaning
800+
* the sensor is ready for a command by the end of the 12ms break. Should be lower
801+
* than 100.
802+
*
803+
* Wakes up all the sensors on the bus. Set the SDI-12 state to transmitting, hold
804+
* the data line high for the required break of 12 milliseconds plus any needed
805+
* additional delay to allow the sensor to wake, then hold the line low for the
806+
* required marking of 8.33 milliseconds.
807+
*
808+
* The SDI-12 protocol requires a pulse of HIGH voltage for at least 12 milliseconds
809+
* (the break) followed immediately by a pulse of LOW voltage for at least 8.33, but
810+
* not more than 100, milliseconds. Setting the SDI-12 object into the
805811
* SDI12_TRANSMITTING allows us to assert control of the line without triggering any
806812
* interrupts.
807-
*/
808-
void wakeSensors();
813+
*
814+
* Per specifications:
815+
* > • A data recorder transmits a break by setting the data line to spacing for at
816+
* > least 12 milliseconds.
817+
* >
818+
* > • The sensor will not recognize a break condition for a continuous spacing time
819+
* > of less than 6.5 milliseconds and will always recognize a break when the line is
820+
* > continuously spacing for more than 12 milliseconds.
821+
*
822+
* > • Upon receiving a break, a sensor must detect 8.33 milliseconds of marking on
823+
* > the data line before it looks for an address.
824+
* >
825+
* > • A sensor must wake up from a low-power standby mode and be capable of detecting
826+
* > a start bit from a valid command within 100 milliseconds after detecting a break
827+
* >
828+
* > • Sensors must return to a low-power standby mode after receiving an invalid
829+
* > address or after detecting a marking state on the data line for 100 milliseconds.
830+
* > (Tolerance: +0.40 milliseconds.)
831+
*/
832+
void wakeSensors(int8_t extraWakeTime = 0);
809833
/**
810834
* @brief Used to send a character out on the data line
811835
*
@@ -845,14 +869,19 @@ class SDI12 : public Stream {
845869
*
846870
* @param cmd the command to send
847871
*
848-
* A publicly accessible function that wakes sensors and sends out a command byte by
849-
* byte on the data line.
872+
* A publicly accessible function that sends a break to wake sensors and sends out a
873+
* command byte by byte on the data line.
874+
*
875+
* @param extraWakeTime The amount of additional time in milliseconds that the sensor
876+
* takes to wake before being ready to receive a command. Default is 0ms - meaning
877+
* the sensor is ready for a command by the end of the 12ms break. Should be lower
878+
* than 100.
850879
*
851880
* @ingroup communication
852881
*/
853-
void sendCommand(String& cmd);
854-
void sendCommand(const char* cmd);
855-
void sendCommand(FlashString cmd);
882+
void sendCommand(String& cmd, int8_t extraWakeTime = 0);
883+
void sendCommand(const char* cmd, int8_t extraWakeTime = 0);
884+
void sendCommand(FlashString cmd, int8_t extraWakeTime = 0);
856885
///@}
857886
///@{
858887
/**

0 commit comments

Comments
 (0)