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 measurements from them.

Every SDI-12 device is different in the time it takes to take a measurement, and the amount of data it returns. This sketch will not serve every sensor type, but it will likely be helpful in getting you started.

Each sensor should have a unique address already - if not, multiple sensors may respond simultaneously to the same request and the output will not be readable by the Arduino.

To address a sensor, please see Example B: b_address_change.ino

PlatformIO Configuration

1; PlatformIO Project Configuration File
2
3[platformio]
4description = SDI-12 Library Example D: Getting Data from All Attached Sensors
5src_dir = .piolibdeps/Arduino-SDI-12_ID1486/examples/d_simple_logger
6
7[env:mayfly]
8monitor_speed = 115200
9board = mayfly
10platform = atmelavr
11framework = arduino
12lib_ldf_mode = deep+
13lib_ignore = RTCZero
14lib_deps =
15 SDI-12

The Complete Example

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