Example E: Check all Addresses for Active Sensors and Start Continuous Measurements

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.

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} 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
18uint32_t serialBaud = 115200; /*!< The baud rate for the output serial port */
19int8_t dataPin = 7; /*!< The pin of the SDI-12 data bus */
20int8_t powerPin = 22; /*!< The sensor power pin (or -1 if not switching power) */
21int8_t firstAddress = 0; /* The first address in the address space to check (0='0') */
22int8_t lastAddress = 62; /* The last address in the address space to check (62='z') */
23
24/** Define the SDI-12 bus */
25SDI12 mySDI12(dataPin);
26
27// keeps track of active addresses
28bool isActive[64] = {
29 0,
30};
31
32uint8_t numSensors = 0;
33
34/**
35 * @brief converts allowable address characters ('0'-'9', 'a'-'z', 'A'-'Z') to a
36 * decimal number between 0 and 61 (inclusive) to cover the 62 possible
37 * addresses.
38 */
39byte charToDec(char i) {
40 if ((i >= '0') && (i <= '9')) return i - '0';
41 if ((i >= 'a') && (i <= 'z')) return i - 'a' + 10;
42 if ((i >= 'A') && (i <= 'Z'))
43 return i - 'A' + 36;
44 else
45 return i;
46}
47
48/**
49 * @brief maps a decimal number between 0 and 61 (inclusive) to allowable
50 * address characters '0'-'9', 'a'-'z', 'A'-'Z',
51 *
52 * THIS METHOD IS UNUSED IN THIS EXAMPLE, BUT IT MAY BE HELPFUL.
53 */
54char decToChar(byte i) {
55 if (i < 10) return i + '0';
56 if ((i >= 10) && (i < 36)) return i + 'a' - 10;
57 if ((i >= 36) && (i <= 62))
58 return i + 'A' - 36;
59 else
60 return i;
61}
62
63/**
64 * @brief gets identification information from a sensor, and prints it to the serial
65 * port
66 *
67 * @param i a character between '0'-'9', 'a'-'z', or 'A'-'Z'.
68 */
69void printInfo(char i) {
70 String command = "";
71 command += (char)i;
72 command += "I!";
73 mySDI12.sendCommand(command);
74 delay(100);
75
76 String sdiResponse = mySDI12.readStringUntil('\n');
77 sdiResponse.trim();
78 // allccccccccmmmmmmvvvxxx...xx<CR><LF>
79 Serial.print(sdiResponse.substring(0, 1)); // address
80 Serial.print(", ");
81 Serial.print(sdiResponse.substring(1, 3).toFloat() / 10); // SDI-12 version number
82 Serial.print(", ");
83 Serial.print(sdiResponse.substring(3, 11)); // vendor id
84 Serial.print(", ");
85 Serial.print(sdiResponse.substring(11, 17)); // sensor model
86 Serial.print(", ");
87 Serial.print(sdiResponse.substring(17, 20)); // sensor version
88 Serial.print(", ");
89 Serial.print(sdiResponse.substring(20)); // sensor id
90 Serial.print(", ");
91}
92
93bool getContinuousResults(char i, int resultsExpected) {
94 uint8_t resultsReceived = 0;
95 uint8_t cmd_number = 0;
96 while (resultsReceived < resultsExpected && cmd_number <= 9) {
97 String command = "";
98 // in this example we will only take the 'DO' measurement
99 command = "";
100 command += i;
101 command += "R";
102 command += cmd_number;
103 command += "!"; // SDI-12 command to get data [address][D][dataOption][!]
104 mySDI12.sendCommand(command);
105
106 uint32_t start = millis();
107 while (mySDI12.available() < 3 && (millis() - start) < 1500) {}
108 mySDI12.read(); // ignore the repeated SDI12 address
109 char c = mySDI12.peek(); // check if there's a '+' and toss if so
110 if (c == '+') { mySDI12.read(); }
111
112 while (mySDI12.available()) {
113 char c = mySDI12.peek();
114 if (c == '-' || (c >= '0' && c <= '9') || c == '.') {
115 float result = mySDI12.parseFloat(SKIP_NONE);
116 Serial.print(String(result, 10));
117 if (result != -9999) { resultsReceived++; }
118 } else if (c == '+') {
119 mySDI12.read();
120 Serial.print(", ");
121 } else {
122 mySDI12.read();
123 }
124 delay(10); // 1 character ~ 7.5ms
125 }
126 if (resultsReceived < resultsExpected) { Serial.print(", "); }
127 cmd_number++;
128 }
129 mySDI12.clearBuffer();
130
131 return resultsReceived == resultsExpected;
132}
133
134// this checks for activity at a particular address
135// expects a char, '0'-'9', 'a'-'z', or 'A'-'Z'
136boolean checkActive(char i) {
137 String myCommand = "";
138 myCommand = "";
139 myCommand += (char)i; // sends basic 'acknowledge' command [address][!]
140 myCommand += "!";
141
142 for (int j = 0; j < 3; j++) { // goes through three rapid contact attempts
143 mySDI12.sendCommand(myCommand);
144 delay(100);
145 if (mySDI12.available()) { // If we here anything, assume we have an active sensor
146 mySDI12.clearBuffer();
147 return true;
148 }
149 }
150 mySDI12.clearBuffer();
151 return false;
152}
153
154void setup() {
155 Serial.begin(serialBaud);
156 while (!Serial)
157 ;
158
159 Serial.println("Opening SDI-12 bus...");
160 mySDI12.begin();
161 delay(500); // allow things to settle
162
163 Serial.println("Timeout value: ");
164 Serial.println(mySDI12.TIMEOUT);
165
166 // Power the sensors;
167 if (powerPin >= 0) {
168 Serial.println("Powering up sensors, wait...");
169 pinMode(powerPin, OUTPUT);
170 digitalWrite(powerPin, HIGH);
171 delay(10000L);
172 }
173
174 // Quickly Scan the Address Space
175 Serial.println("Scanning all addresses, please wait...");
176 Serial.println("Sensor Address, Protocol Version, Sensor Vendor, Sensor Model, "
177 "Sensor Version, Sensor ID");
178
179 for (byte i = firstAddress; i < lastAddress; i++) {
180 char addr = decToChar(i);
181 if (checkActive(addr)) {
182 numSensors++;
183 isActive[i] = 1;
184 printInfo(addr);
185 Serial.println();
186 }
187 }
188 Serial.print("Total number of sensors found: ");
189 Serial.println(numSensors);
190
191 if (numSensors == 0) {
192 Serial.println(
193 "No sensors found, please check connections and restart the Arduino.");
194 while (true) { delay(10); } // do nothing forever
195 }
196
197 Serial.println();
198 Serial.println(
199 "Time Elapsed (s), Sensor Address, Est Measurement Time (s), Number Measurements, "
200 "Real Measurement Time (ms), Measurement 1, Measurement 2, ... etc.");
201 Serial.println(
202 "-------------------------------------------------------------------------------");
203}
204
205void loop() {
206 // measure one at a time
207 for (byte i = firstAddress; i < lastAddress; i++) {
208 char addr = decToChar(i);
209 if (isActive[i]) {
210 // Serial.print(millis() / 1000);
211 Serial.print(millis());
212 Serial.print(", ");
213 getContinuousResults(addr, 4);
214 Serial.println();
215 }
216 }
217
218 delay(5000L); // wait ten seconds between measurement attempts.
219}