e_continuous_measurement.ino example

Example D: Check all Addresses for Active Sensors and Log Data.

Example D: Check all Addresses for Active Sensors and Log Data

This is a simple demonstration of the SDI-12 library for Arduino.

It discovers the address of all sensors active on a single bus and takes continuous measurements from them.

1/**
2 * @example{lineno} e_continuous_measurement.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 * @date August 2013
7 *
8 * @brief Example D: Check all Addresses for Active Sensors and Log Data
9 *
10 * This is a simple demonstration of the SDI-12 library for Arduino.
11 *
12 * It discovers the address of all sensors active on a single bus and takes continuous
13 * measurements from them.
14 */
15
16#include <SDI12.h>
17
18#ifndef SDI12_DATA_PIN
19#define SDI12_DATA_PIN 7
20#endif
21#ifndef SDI12_POWER_PIN
22#define SDI12_POWER_PIN 22
23#endif
24
25/* connection information */
26uint32_t serialBaud = 115200; /*!< The baud rate for the output serial port */
27int8_t dataPin = SDI12_DATA_PIN; /*!< The pin of the SDI-12 data bus */
28int8_t powerPin = SDI12_POWER_PIN; /*!< The sensor power pin (or -1) */
29uint32_t wake_delay = 0; /*!< Extra time needed for the sensor to wake (0-100ms) */
30int8_t firstAddress = 0; /* The first address in the address space to check (0='0') */
31int8_t lastAddress = 61; /* The last address in the address space to check (61='z') */
32bool printIO = false;
33
34/** Define the SDI-12 bus */
35SDI12 mySDI12(dataPin);
36
37// keeps track of active addresses
38bool isActive[64];
39
40uint8_t numSensors = 0;
41
42/**
43 * @brief converts allowable address characters ('0'-'9', 'a'-'z', 'A'-'Z') to a
44 * decimal number between 0 and 61 (inclusive) to cover the 62 possible
45 * addresses.
46 */
47byte charToDec(char i) {
48 if ((i >= '0') && (i <= '9')) return i - '0';
49 if ((i >= 'a') && (i <= 'z')) return i - 'a' + 10;
50 if ((i >= 'A') && (i <= 'Z'))
51 return i - 'A' + 36;
52 else
53 return i;
54}
55
56/**
57 * @brief maps a decimal number between 0 and 61 (inclusive) to allowable
58 * address characters '0'-'9', 'a'-'z', 'A'-'Z',
59 *
60 * THIS METHOD IS UNUSED IN THIS EXAMPLE, BUT IT MAY BE HELPFUL.
61 */
62char decToChar(byte i) {
63 if (i < 10) return i + '0';
64 if ((i >= 10) && (i < 36)) return i + 'a' - 10;
65 if ((i >= 36) && (i <= 62))
66 return i + 'A' - 36;
67 else
68 return i;
69}
70
71/**
72 * @brief gets identification information from a sensor, and prints it to the serial
73 * port
74 *
75 * @param i a character between '0'-'9', 'a'-'z', or 'A'-'Z'.
76 */
77void printInfo(char i) {
78 String command = "";
79 command += (char)i;
80 command += "I!";
81 mySDI12.sendCommand(command, wake_delay);
82 if (printIO) {
83 Serial.print(">>>");
84 Serial.println(command);
85 }
86 delay(30);
87
88 String sdiResponse = mySDI12.readStringUntil('\n');
89 sdiResponse.trim();
90 // allccccccccmmmmmmvvvxxx...xx<CR><LF>
91 if (printIO) {
92 Serial.print("<<<");
93 Serial.println(sdiResponse);
94 }
95 Serial.print(sdiResponse.substring(0, 1)); // address
96 Serial.print(", ");
97 Serial.print(sdiResponse.substring(1, 3).toFloat() / 10); // SDI-12 version number
98 Serial.print(", ");
99 Serial.print(sdiResponse.substring(3, 11)); // vendor id
100 Serial.print(", ");
101 Serial.print(sdiResponse.substring(11, 17)); // sensor model
102 Serial.print(", ");
103 Serial.print(sdiResponse.substring(17, 20)); // sensor version
104 Serial.print(", ");
105 Serial.print(sdiResponse.substring(20)); // sensor id
106 Serial.print(", ");
107}
108
109bool getContinuousResults(char addr, int resultsExpected) {
110 uint8_t resultsReceived = 0;
111 uint8_t cmd_number = 0;
112 uint8_t cmd_retries = 0;
113 while (resultsReceived < resultsExpected && cmd_number <= 9 && cmd_retries < 5) {
114 bool gotResults = false;
115 uint8_t cmd_results = 0;
116 // Assemble the command based on how many commands we've already sent,
117 // starting with R0 and ending with R9
118 // SDI-12 command to get data [address][R][dataOption][!]
119 mySDI12.clearBuffer();
120 String command = "";
121 command += addr;
122 command += "R";
123 command += cmd_number;
124 command += "!";
125 mySDI12.sendCommand(command, wake_delay);
126 delay(30);
127 if (printIO) {
128 Serial.print(">>>");
129 Serial.println(command);
130 }
131
132 // Wait for the first few characters to arrive. The response from a continuous
133 // measurement request should always have more than three characters
134 uint32_t start = millis();
135 while (mySDI12.available() < 3 && (millis() - start) < 1500) {}
136
137 // read the returned address to remove it from the buffer
138 char returnedAddress = mySDI12.read();
139 if (printIO) {
140 if (returnedAddress != addr) {
141 Serial.println("Wrong address returned!");
142 Serial.print("Expected ");
143 Serial.print(addr);
144 Serial.print(F("but got data from"));
145 Serial.println(returnedAddress);
146 }
147 Serial.print("<<<");
148 Serial.write(returnedAddress);
149 Serial.print(", ");
150 Serial.println();
151 }
152
153 bool bad_read = false;
154 // While there is any data left in the buffer
155 while (mySDI12.available() && (millis() - start) < 3000) {
156 char c = mySDI12.peek();
157 // if there's a polarity sign, a number, or a decimal next in the
158 // buffer, start reading it as a float.
159 if (c == '-' || c == '+' || (c >= '0' && c <= '9') || c == '.') {
160 float result = mySDI12.parseFloat();
161 if (printIO) {
162 Serial.print("<<<");
163 Serial.println(String(result, 7));
164 } else {
165 Serial.print(String(result, 7));
166 Serial.print(", ");
167 }
168 if (result != -9999) {
169 gotResults = true;
170 cmd_results++;
171 }
172 // if we get to a new line, we've made it to the end of the response
173 } else if (c == '\r' || c == '\n') {
174 if (printIO) { Serial.write(c); }
175 mySDI12.read();
176 } else {
177 if (printIO) {
178 Serial.print(F("<<< INVALID CHARACTER IN RESPONSE:"));
179 Serial.write(c);
180 Serial.println();
181 }
182 // Read the character to make sure it's removed from the buffer
183 mySDI12.read();
184 bad_read = true;
185 }
186 delay(10); // 1 character ~ 7.5ms
187 }
188 if (!gotResults) {
189 if (printIO) {
190 Serial.println(F(" No results received, will not continue requests!"));
191 break; // don't do another loop if we got nothing
192 }
193 }
194 if (gotResults && !bad_read) {
195 resultsReceived = resultsReceived + cmd_results;
196 if (printIO) {
197 Serial.print(F(" Total Results Received: "));
198 Serial.print(resultsReceived);
199 Serial.print(F(", Remaining: "));
200 Serial.println(resultsExpected - resultsReceived);
201 }
202 cmd_number++;
203 } else {
204 // if we got a bad charater in the response, add one to the retry
205 // attempts but do not bump up the command number or transfer any
206 // results because we want to retry the same data command to try get
207 // a valid response
208 cmd_retries++;
209 }
210 mySDI12.clearBuffer();
211 }
212
213 return resultsReceived == resultsExpected;
214}
215
216// this checks for activity at a particular address
217// expects a char, '0'-'9', 'a'-'z', or 'A'-'Z'
218bool checkActive(char i) {
219 String myCommand = "";
220 myCommand = "";
221 myCommand += (char)i; // sends basic 'acknowledge' command [address][!]
222 myCommand += "!";
223
224 for (int j = 0; j < 3; j++) { // goes through three rapid contact attempts
225 mySDI12.sendCommand(myCommand, wake_delay);
226 if (printIO) {
227 Serial.print(">>>");
228 Serial.println(myCommand);
229 }
230 delay(30);
231 if (mySDI12.available()) { // If we hear anything, assume we have an active sensor
232 if (printIO) {
233 Serial.print("<<<");
234 while (mySDI12.available()) { Serial.write(mySDI12.read()); }
235 } else {
236 mySDI12.clearBuffer();
237 }
238 return true;
239 }
240 }
241 mySDI12.clearBuffer();
242 return false;
243}
244
245void setup() {
246 Serial.begin(serialBaud);
247 while (!Serial && millis() < 10000L);
248
249 Serial.println("Opening SDI-12 bus...");
250 mySDI12.begin();
251 delay(500); // allow things to settle
252
253 Serial.println("Timeout value: ");
254 Serial.println(mySDI12.TIMEOUT);
255
256 // Power the sensors;
257 if (powerPin >= 0) {
258 Serial.println("Powering up sensors, wait...");
259 pinMode(powerPin, OUTPUT);
260 digitalWrite(powerPin, HIGH);
261 delay(5000L);
262 }
263
264 // Quickly scan the address space
265 Serial.println("Scanning all addresses, please wait...");
266 Serial.println("Sensor Address, Protocol Version, Sensor Vendor, Sensor Model, "
267 "Sensor Version, Sensor ID");
268
269 for (int8_t i = firstAddress; i <= lastAddress; i++) {
270 char addr = decToChar(i);
271 if (checkActive(addr)) {
272 numSensors++;
273 isActive[i] = 1;
274 printInfo(addr);
275 Serial.println();
276 }
277 }
278 Serial.print("Total number of sensors found: ");
279 Serial.println(numSensors);
280
281 if (numSensors == 0) {
282 Serial.println(
283 "No sensors found, please check connections and restart the Arduino.");
284 while (true) { delay(10); } // do nothing forever
285 }
286
287 Serial.println();
288 Serial.println(
289 "Time Elapsed (s), Sensor Address, Est Measurement Time (s), Number Measurements, "
290 "Real Measurement Time (ms), Measurement 1, Measurement 2, ... etc.");
291 Serial.println(
292 "-------------------------------------------------------------------------------");
293}
294
295void loop() {
296 // measure one at a time
297 for (int8_t i = firstAddress; i <= lastAddress; i++) {
298 char addr = decToChar(i);
299 if (isActive[i]) {
300 if (printIO) {
301 Serial.print(addr);
302 Serial.print(" - millis: ");
303 Serial.print(millis());
304 Serial.println();
305 } else {
306 Serial.print(millis());
307 Serial.print(", ");
308 }
309 getContinuousResults(addr, 0);
310 Serial.println();
311 }
312 }
313
314 delay(10000L); // wait ten seconds between measurement attempts.
315}