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
4description = SDI-12 Library Example D: Getting Data from All Attached Sensors
5src_dir = .piolibdeps/Arduino-SDI-12_ID1486/examples/d_simple_logger
The Complete Example
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>
8 * @brief Example D: Check all Addresses for Active Sensors and Log Data
10 * This is a simple demonstration of the SDI-12 library for Arduino.
12 * It discovers the address of all sensors active on a single bus and takes measurements
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.
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
23 * To address a sensor, please see Example B: b_address_change.ino
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') */
35/** Define the SDI-12 bus */
36SDI12 mySDI12(dataPin);
38// 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 i, int resultsExpected) {
105 uint8_t resultsReceived = 0;
106 uint8_t cmd_number = 0;
107 while (resultsReceived < resultsExpected && cmd_number <= 9) {
109 // in this example we will only take the 'DO' measurement
113 command += cmd_number;
114 command += "!"; // SDI-12 command to get data [address][D][dataOption][!]
115 mySDI12.sendCommand(command, wake_delay);
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(); }
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 == '+') {
135 delay(10); // 1 character ~ 7.5ms
137 if (resultsReceived < resultsExpected) { Serial.print(", "); }
140 mySDI12.clearBuffer();
142 return resultsReceived == resultsExpected;
145bool takeMeasurement(char i, String meas_type = "") {
146 mySDI12.clearBuffer();
150 command += meas_type;
151 command += "!"; // SDI-12 measurement command format [address]['M'][!]
152 mySDI12.sendCommand(command, wake_delay);
155 Serial.print(command);
158 // wait for acknowlegement with format [address][ttt (3 char, seconds)][number of
159 // measurments available, 0-9]
160 String sdiResponse = mySDI12.readStringUntil('\n');
163 String addr = sdiResponse.substring(0, 1);
167 // find out how long we have to wait (in seconds).
168 uint8_t wait = sdiResponse.substring(1, 4).toInt();
172 // Set up the number of results to expect
173 int numResults = sdiResponse.substring(4).toInt();
174 Serial.print(numResults);
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
181 Serial.print(millis() - timerStart);
183 mySDI12.clearBuffer();
187 // Wait for anything else and clear it out
189 mySDI12.clearBuffer();
191 if (numResults > 0) { return getResults(i, numResults); }
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 = "";
201 myCommand += (char)i; // sends basic 'acknowledge' command [address][!]
204 for (int j = 0; j < 3; j++) { // goes through three rapid contact attempts
205 mySDI12.sendCommand(myCommand, wake_delay);
207 if (mySDI12.available()) { // If we here anything, assume we have an active sensor
208 mySDI12.clearBuffer();
212 mySDI12.clearBuffer();
217 Serial.begin(serialBaud);
221 Serial.println("Opening SDI-12 bus...");
223 delay(500); // allow things to settle
225 Serial.println("Timeout value: ");
226 Serial.println(mySDI12.TIMEOUT);
228 // Power the sensors;
230 Serial.println("Powering up sensors, wait...");
231 pinMode(powerPin, OUTPUT);
232 digitalWrite(powerPin, HIGH);
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");
242 for (byte i = firstAddress; i < lastAddress; i++) {
243 char addr = decToChar(i);
244 if (checkActive(addr)) {
251 Serial.print("Total number of sensors found: ");
252 Serial.println(numSensors);
254 if (numSensors == 0) {
256 "No sensors found, please check connections and restart the Arduino.");
257 while (true) { delay(10); } // do nothing forever
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.");
265 "-------------------------------------------------------------------------------");
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);
275 // Serial.print(millis() / 1000);
276 Serial.print(millis());
278 takeMeasurement(addr, commands[a]);
284 delay(10000L); // wait ten seconds between measurement attempts.