Example J: Using External Interrupts

This is identical to example D, except that instead of using internal definitions of pin change interrupt vectors, it depends on another library to define them for it.

To use this example, you must remove the comment braces around #define SDI12_EXTERNAL_PCINT in the library and re-compile it.

PlatformIO Configuration

1; PlatformIO Project Configuration File
2
3[platformio]
4description = SDI-12 Library Example J: Using an External Interrupt Library
5src_dir = .piolibdeps/Arduino-SDI-12_ID1486/examples/j_external_pcint_library
6
7[env:mayfly]
8monitor_speed = 115200
9board = mayfly
10platform = atmelavr
11framework = arduino
12lib_ldf_mode = deep+
13lib_ignore = RTCZero
14build_flags =
15 -DSDI12_EXTERNAL_PCINT
16lib_deps =
17 SDI-12
18 EnableInterrupt

The Complete Example

1/**
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>
6 *
7 * @brief Example J: Using External Interrupts
8 *
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.
13 *
14 * For this to work, you must remove the comment braces around
15 * `#define SDI12_EXTERNAL_PCINT` in the library and re-compile it.
16 */
17
18#include <EnableInterrupt.h>
19#include <SDI12.h>
20
21/* connection information */
22uint32_t serialBaud = 115200; /*!< The baud rate for the output serial port */
23int8_t dataPin = 7; /*!< The pin of the SDI-12 data bus */
24int8_t powerPin = 22; /*!< The sensor power pin (or -1 if not switching power) */
25int8_t firstAddress = 0; /* The first address in the address space to check (0='0') */
26int8_t lastAddress = 62; /* The last address in the address space to check (62='z') */
27
28/** Define the SDI-12 bus */
29SDI12 mySDI12(dataPin);
30
31// keeps track of active addresses
32bool isActive[64] = {
33 0,
34};
35
36uint8_t numSensors = 0;
37
38/**
39 * @brief converts allowable address characters ('0'-'9', 'a'-'z', 'A'-'Z') to a
40 * decimal number between 0 and 61 (inclusive) to cover the 62 possible
41 * addresses.
42 */
43byte charToDec(char i) {
44 if ((i >= '0') && (i <= '9')) return i - '0';
45 if ((i >= 'a') && (i <= 'z')) return i - 'a' + 10;
46 if ((i >= 'A') && (i <= 'Z'))
47 return i - 'A' + 36;
48 else
49 return i;
50}
51
52/**
53 * @brief maps a decimal number between 0 and 61 (inclusive) to allowable
54 * address characters '0'-'9', 'a'-'z', 'A'-'Z',
55 *
56 * THIS METHOD IS UNUSED IN THIS EXAMPLE, BUT IT MAY BE HELPFUL.
57 */
58char decToChar(byte i) {
59 if (i < 10) return i + '0';
60 if ((i >= 10) && (i < 36)) return i + 'a' - 10;
61 if ((i >= 36) && (i <= 62))
62 return i + 'A' - 36;
63 else
64 return i;
65}
66
67/**
68 * @brief gets identification information from a sensor, and prints it to the serial
69 * port
70 *
71 * @param i a character between '0'-'9', 'a'-'z', or 'A'-'Z'.
72 */
73void printInfo(char i) {
74 String command = "";
75 command += (char)i;
76 command += "I!";
77 mySDI12.sendCommand(command);
78 delay(100);
79
80 String sdiResponse = mySDI12.readStringUntil('\n');
81 sdiResponse.trim();
82 // allccccccccmmmmmmvvvxxx...xx<CR><LF>
83 Serial.print(sdiResponse.substring(0, 1)); // address
84 Serial.print(", ");
85 Serial.print(sdiResponse.substring(1, 3).toFloat() / 10); // SDI-12 version number
86 Serial.print(", ");
87 Serial.print(sdiResponse.substring(3, 11)); // vendor id
88 Serial.print(", ");
89 Serial.print(sdiResponse.substring(11, 17)); // sensor model
90 Serial.print(", ");
91 Serial.print(sdiResponse.substring(17, 20)); // sensor version
92 Serial.print(", ");
93 Serial.print(sdiResponse.substring(20)); // sensor id
94 Serial.print(", ");
95}
96
97bool getResults(char i, int resultsExpected) {
98 uint8_t resultsReceived = 0;
99 uint8_t cmd_number = 0;
100 while (resultsReceived < resultsExpected && cmd_number <= 9) {
101 String command = "";
102 // in this example we will only take the 'DO' measurement
103 command = "";
104 command += i;
105 command += "D";
106 command += cmd_number;
107 command += "!"; // SDI-12 command to get data [address][D][dataOption][!]
108 mySDI12.sendCommand(command);
109
110 uint32_t start = millis();
111 while (mySDI12.available() < 3 && (millis() - start) < 1500) {}
112 mySDI12.read(); // ignore the repeated SDI12 address
113 char c = mySDI12.peek(); // check if there's a '+' and toss if so
114 if (c == '+') { mySDI12.read(); }
115
116 while (mySDI12.available()) {
117 char c = mySDI12.peek();
118 if (c == '-' || (c >= '0' && c <= '9') || c == '.') {
119 float result = mySDI12.parseFloat(SKIP_NONE);
120 Serial.print(String(result, 10));
121 if (result != -9999) { resultsReceived++; }
122 } else if (c == '+') {
123 mySDI12.read();
124 Serial.print(", ");
125 } else {
126 mySDI12.read();
127 }
128 delay(10); // 1 character ~ 7.5ms
129 }
130 if (resultsReceived < resultsExpected) { Serial.print(", "); }
131 cmd_number++;
132 }
133 mySDI12.clearBuffer();
134
135 return resultsReceived == resultsExpected;
136}
137
138bool takeMeasurement(char i, String meas_type = "") {
139 mySDI12.clearBuffer();
140 String command = "";
141 command += i;
142 command += "M";
143 command += meas_type;
144 command += "!"; // SDI-12 measurement command format [address]['M'][!]
145 mySDI12.sendCommand(command);
146 delay(100);
147
148 // wait for acknowlegement with format [address][ttt (3 char, seconds)][number of
149 // measurments available, 0-9]
150 String sdiResponse = mySDI12.readStringUntil('\n');
151 sdiResponse.trim();
152
153 String addr = sdiResponse.substring(0, 1);
154 Serial.print(addr);
155 Serial.print(", ");
156
157 // find out how long we have to wait (in seconds).
158 uint8_t wait = sdiResponse.substring(1, 4).toInt();
159 Serial.print(wait);
160 Serial.print(", ");
161
162 // Set up the number of results to expect
163 int numResults = sdiResponse.substring(4).toInt();
164 Serial.print(numResults);
165 Serial.print(", ");
166
167 unsigned long timerStart = millis();
168 while ((millis() - timerStart) < (1000 * (wait + 1))) {
169 if (mySDI12.available()) // sensor can interrupt us to let us know it is done early
170 {
171 Serial.print(millis() - timerStart);
172 Serial.print(", ");
173 mySDI12.clearBuffer();
174 break;
175 }
176 }
177 // Wait for anything else and clear it out
178 delay(30);
179 mySDI12.clearBuffer();
180
181 if (numResults > 0) { return getResults(i, numResults); }
182
183 return true;
184}
185
186// this checks for activity at a particular address
187// expects a char, '0'-'9', 'a'-'z', or 'A'-'Z'
188boolean checkActive(char i) {
189 String myCommand = "";
190 myCommand = "";
191 myCommand += (char)i; // sends basic 'acknowledge' command [address][!]
192 myCommand += "!";
193
194 for (int j = 0; j < 3; j++) { // goes through three rapid contact attempts
195 mySDI12.sendCommand(myCommand);
196 delay(100);
197 if (mySDI12.available()) { // If we here anything, assume we have an active sensor
198 mySDI12.clearBuffer();
199 return true;
200 }
201 }
202 mySDI12.clearBuffer();
203 return false;
204}
205
206void setup() {
207 Serial.begin(serialBaud);
208 while (!Serial)
209 ;
210
211 Serial.println("Opening SDI-12 bus...");
212 mySDI12.begin();
213 delay(500); // allow things to settle
214
215 Serial.println("Timeout value: ");
216 Serial.println(mySDI12.TIMEOUT);
217
218 // Power the sensors;
219 if (powerPin >= 0) {
220 Serial.println("Powering up sensors...");
221 pinMode(powerPin, OUTPUT);
222 digitalWrite(powerPin, HIGH);
223 delay(200);
224 }
225
226 // Enable interrupts for the recieve pin
227 pinMode(dataPin, INPUT_PULLUP);
228 enableInterrupt(dataPin, SDI12::handleInterrupt, CHANGE);
229
230 // Quickly Scan the Address Space
231 Serial.println("Scanning all addresses, please wait...");
232 Serial.println("Protocol Version, Sensor Address, Sensor Vendor, Sensor Model, "
233 "Sensor Version, Sensor ID");
234
235 for (byte i = firstAddress; i < lastAddress; i++) {
236 char addr = decToChar(i);
237 if (checkActive(addr)) {
238 numSensors++;
239 isActive[i] = 1;
240 printInfo(addr);
241 Serial.println();
242 }
243 }
244 Serial.print("Total number of sensors found: ");
245 Serial.println(numSensors);
246
247 if (numSensors == 0) {
248 Serial.println(
249 "No sensors found, please check connections and restart the Arduino.");
250 while (true) { delay(10); } // do nothing forever
251 }
252
253 Serial.println();
254 Serial.println("Time Elapsed (s), Est Measurement Time (s), Number Measurements, "
255 "Real Measurement Time (ms), Measurement 1, Measurement 2, ... etc.");
256 Serial.println(
257 "-------------------------------------------------------------------------------");
258}
259
260void loop() {
261 // measure one at a time
262 for (byte i = firstAddress; i < lastAddress; i++) {
263 char addr = decToChar(i);
264 if (isActive[i]) {
265 Serial.print(millis() / 1000);
266 Serial.print(", ");
267 takeMeasurement(addr);
268 Serial.println();
269 }
270 }
271
272 delay(10000); // wait ten seconds between measurement attempts.
273}