Example J: Using External Interrupts.
Example J: Using External Interrupts
This is identical to example B, except that it uses the library EnableInterrupt to assign the pin change interrupt vector. This allows it to play nicely with any other libraries that try to assign functionality to the pin change interrupt vectors.
For this to work, you must remove the comment braces around #define SDI12_EXTERNAL_PCINT
in the library and re-compile it.
2 * @example{lineno} j_external_pcint_library.ino
3 * @copyright Stroud Water Research Center
4 * @license This example is published under the BSD-3 license.
5 * @author Kevin M.Smith <SDI12@ethosengineering.org>
7 * @brief Example J: Using External Interrupts
9 * This is identical to example B, except that it uses the library
10 * [EnableInterrupt](https://github.com/GreyGnome/EnableInterrupt) to assign the pin
11 * change interrupt vector. This allows it to play nicely with any other libraries that
12 * try to assign functionality to the pin change interrupt vectors.
14 * For this to work, you must remove the comment braces around
15 * `#define SDI12_EXTERNAL_PCINT` in the library and re-compile it.
18#include <EnableInterrupt.h>
22#define SDI12_DATA_PIN 7
24#ifndef SDI12_POWER_PIN
25#define SDI12_POWER_PIN 22
28/* connection information */
29uint32_t serialBaud = 115200; /*!< The baud rate for the output serial port */
30int8_t dataPin = SDI12_DATA_PIN; /*!< The pin of the SDI-12 data bus */
31int8_t powerPin = SDI12_POWER_PIN; /*!< The sensor power pin (or -1) */
32uint32_t wake_delay = 0; /*!< Extra time needed for the sensor to wake (0-100ms) */
33int8_t firstAddress = 0; /* The first address in the address space to check (0='0') */
34int8_t lastAddress = 61; /* The last address in the address space to check (61='z') */
37/** Define the SDI-12 bus */
38SDI12 mySDI12(dataPin);
40// keeps track of active addresses
43uint8_t numSensors = 0;
46 * @brief converts allowable address characters ('0'-'9', 'a'-'z', 'A'-'Z') to a
47 * decimal number between 0 and 61 (inclusive) to cover the 62 possible
50byte charToDec(char i) {
51 if ((i >= '0') && (i <= '9')) return i - '0';
52 if ((i >= 'a') && (i <= 'z')) return i - 'a' + 10;
53 if ((i >= 'A') && (i <= 'Z'))
60 * @brief maps a decimal number between 0 and 61 (inclusive) to allowable
61 * address characters '0'-'9', 'a'-'z', 'A'-'Z',
63 * THIS METHOD IS UNUSED IN THIS EXAMPLE, BUT IT MAY BE HELPFUL.
65char decToChar(byte i) {
66 if (i < 10) return i + '0';
67 if ((i >= 10) && (i < 36)) return i + 'a' - 10;
68 if ((i >= 36) && (i <= 62))
75 * @brief gets identification information from a sensor, and prints it to the serial
78 * @param i a character between '0'-'9', 'a'-'z', or 'A'-'Z'.
80void printInfo(char i) {
84 mySDI12.sendCommand(command, wake_delay);
87 String sdiResponse = mySDI12.readStringUntil('\n');
89 // allccccccccmmmmmmvvvxxx...xx<CR><LF>
90 Serial.print(sdiResponse.substring(0, 1)); // address
92 Serial.print(sdiResponse.substring(1, 3).toFloat() / 10); // SDI-12 version number
94 Serial.print(sdiResponse.substring(3, 11)); // vendor id
96 Serial.print(sdiResponse.substring(11, 17)); // sensor model
98 Serial.print(sdiResponse.substring(17, 20)); // sensor version
100 Serial.print(sdiResponse.substring(20)); // sensor id
104bool getResults(char addr, int resultsExpected) {
105 uint8_t resultsReceived = 0;
106 uint8_t cmd_number = 0;
107 uint8_t cmd_retries = 0;
109 // When requesting data, the sensor sends back up to ~80 characters at a
110 // time to each data request. If it needs to return more results than can
111 // fit in the first data request (D0), we need to make additional requests
112 // (D1-9). Since this is a parent to all sensors, we're going to keep
113 // requesting data until we either get as many results as we expect or no
114 // more data is returned.
115 while (resultsReceived < resultsExpected && cmd_number <= 9 && cmd_retries < 5) {
116 bool gotResults = false;
117 uint8_t cmd_results = 0;
118 // Assemble the command based on how many commands we've already sent,
119 // starting with D0 and ending with D9
120 // SDI-12 command to get data [address][D][dataOption][!]
121 mySDI12.clearBuffer();
125 command += cmd_number;
127 mySDI12.sendCommand(command, wake_delay);
131 Serial.println(command);
134 // Wait for the first few characters to arrive. The response from a data
135 // request should always have more than three characters
136 uint32_t start = millis();
137 while (mySDI12.available() < 3 && (millis() - start) < 1500) {}
139 // read the returned address to remove it from the buffer
140 char returnedAddress = mySDI12.read();
142 if (returnedAddress != addr) {
143 Serial.println("Wrong address returned!");
144 Serial.print("Expected ");
145 Serial.print(String(addr));
146 Serial.print(" Got ");
147 Serial.println(String(returnedAddress));
150 Serial.write(returnedAddress);
155 bool bad_read = false;
156 // While there is any data left in the buffer
157 while (mySDI12.available() && (millis() - start) < 3000) {
158 char c = mySDI12.peek();
159 // if there's a polarity sign, a number, or a decimal next in the
160 // buffer, start reading it as a float.
161 if (c == '-' || c == '+' || (c >= '0' && c <= '9') || c == '.') {
162 float result = mySDI12.parseFloat();
165 Serial.println(String(result, 7));
167 Serial.print(String(result, 7));
170 if (result != -9999) {
174 // if we get to a new line, we've made it to the end of the response
175 } else if (c == '\r' || c == '\n') {
176 if (printIO) { Serial.write(c); }
180 Serial.print(F("<<< INVALID CHARACTER IN RESPONSE:"));
184 // Read the character to make sure it's removed from the buffer
188 delay(10); // 1 character ~ 7.5ms
192 Serial.println(F(" No results received, will not continue requests!"));
193 break; // don't do another loop if we got nothing
196 if (gotResults && !bad_read) {
197 resultsReceived = resultsReceived + cmd_results;
199 Serial.print(F(" Total Results Received: "));
200 Serial.print(resultsReceived);
201 Serial.print(F(", Remaining: "));
202 Serial.println(resultsExpected - resultsReceived);
206 // if we got a bad charater in the response, add one to the retry
207 // attempts but do not bump up the command number or transfer any
208 // results because we want to retry the same data command to try get
212 mySDI12.clearBuffer();
215 return resultsReceived == resultsExpected;
218bool takeMeasurement(char i, String meas_type = "") {
219 mySDI12.clearBuffer();
223 command += meas_type;
224 command += "!"; // SDI-12 measurement command format [address]['M'][!]
225 mySDI12.sendCommand(command, wake_delay);
230 Serial.println(command);
233 // wait for acknowledgement with format [address][ttt (3 char, seconds)][number of
234 // measurements available, 0-9]
235 String sdiResponse = mySDI12.readStringUntil('\n');
239 Serial.println(sdiResponse);
242 String addr = sdiResponse.substring(0, 1);
246 // find out how long we have to wait (in seconds).
247 uint8_t wait = sdiResponse.substring(1, 4).toInt();
251 // Set up the number of results to expect
252 int numResults = sdiResponse.substring(4).toInt();
253 Serial.print(numResults);
256 if (printIO) { Serial.println(); }
258 unsigned long timerStart = millis();
259 while ((millis() - timerStart) < (1000UL * (wait + 1))) {
260 if (mySDI12.available()) // sensor can interrupt us to let us know it is done early
264 Serial.write(mySDI12.read());
267 Serial.print(millis() - timerStart);
273 mySDI12.clearBuffer();
277 // Wait for anything else and clear it out
279 mySDI12.clearBuffer();
281 if (numResults > 0) { return getResults(i, numResults); }
286// this checks for activity at a particular address
287// expects a char, '0'-'9', 'a'-'z', or 'A'-'Z'
288bool checkActive(char i) {
289 String myCommand = "";
291 myCommand += (char)i; // sends basic 'acknowledge' command [address][!]
294 for (int j = 0; j < 3; j++) { // goes through three rapid contact attempts
295 mySDI12.sendCommand(myCommand, wake_delay);
298 Serial.println(myCommand);
301 if (mySDI12.available()) { // If we hear anything, assume we have an active sensor
304 while (mySDI12.available()) { Serial.write(mySDI12.read()); }
306 mySDI12.clearBuffer();
311 mySDI12.clearBuffer();
316 Serial.begin(serialBaud);
317 while (!Serial && millis() < 10000L);
319 Serial.println("Opening SDI-12 bus...");
321 delay(500); // allow things to settle
323 Serial.println("Timeout value: ");
324 Serial.println(mySDI12.TIMEOUT);
326 // Power the sensors;
328 Serial.println("Powering up sensors, wait...");
329 pinMode(powerPin, OUTPUT);
330 digitalWrite(powerPin, HIGH);
334 // Enable interrupts for the receive pin
335 pinMode(dataPin, INPUT_PULLUP);
336 enableInterrupt(dataPin, SDI12::handleInterrupt, CHANGE);
338 // Quickly scan the address space
339 Serial.println("Scanning all addresses, please wait...");
340 Serial.println("Sensor Address, Protocol Version, Sensor Vendor, Sensor Model, "
341 "Sensor Version, Sensor ID");
343 for (int8_t i = firstAddress; i <= lastAddress; i++) {
344 char addr = decToChar(i);
345 if (checkActive(addr)) {
352 Serial.print("Total number of sensors found: ");
353 Serial.println(numSensors);
355 if (numSensors == 0) {
357 "No sensors found, please check connections and restart the Arduino.");
358 while (true) { delay(10); } // do nothing forever
362 Serial.println("Time Elapsed (s), Sensor Address, Est Measurement "
363 "Time (s), Number Measurements, "
364 "Real Measurement Time (ms), Measurement 1, Measurement 2, ... etc.");
365 Serial.println("-------------------------------------------------------------------"
370 // measure one at a time
371 for (int8_t i = firstAddress; i <= lastAddress; i++) {
372 char addr = decToChar(i);
376 Serial.print(" - millis: ");
377 Serial.print(millis());
380 Serial.print(millis());
383 takeMeasurement(addr);
388 delay(10000L); // wait ten seconds between measurement attempts.