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.
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>
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 continuous
13 * measurements from them.
19#define SDI12_DATA_PIN 7
21#ifndef SDI12_POWER_PIN
22#define SDI12_POWER_PIN 22
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') */
34/** Define the SDI-12 bus */
35SDI12 mySDI12(dataPin);
37// keeps track of active addresses
40uint8_t numSensors = 0;
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
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'))
57 * @brief maps a decimal number between 0 and 61 (inclusive) to allowable
58 * address characters '0'-'9', 'a'-'z', 'A'-'Z',
60 * THIS METHOD IS UNUSED IN THIS EXAMPLE, BUT IT MAY BE HELPFUL.
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))
72 * @brief gets identification information from a sensor, and prints it to the serial
75 * @param i a character between '0'-'9', 'a'-'z', or 'A'-'Z'.
77void printInfo(char i) {
81 mySDI12.sendCommand(command, wake_delay);
84 Serial.println(command);
88 String sdiResponse = mySDI12.readStringUntil('\n');
90 // allccccccccmmmmmmvvvxxx...xx<CR><LF>
93 Serial.println(sdiResponse);
95 Serial.print(sdiResponse.substring(0, 1)); // address
97 Serial.print(sdiResponse.substring(1, 3).toFloat() / 10); // SDI-12 version number
99 Serial.print(sdiResponse.substring(3, 11)); // vendor id
101 Serial.print(sdiResponse.substring(11, 17)); // sensor model
103 Serial.print(sdiResponse.substring(17, 20)); // sensor version
105 Serial.print(sdiResponse.substring(20)); // sensor id
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();
123 command += cmd_number;
125 mySDI12.sendCommand(command, wake_delay);
129 Serial.println(command);
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) {}
137 // read the returned address to remove it from the buffer
138 char returnedAddress = mySDI12.read();
140 if (returnedAddress != addr) {
141 Serial.println("Wrong address returned!");
142 Serial.print("Expected ");
144 Serial.print(F("but got data from"));
145 Serial.println(returnedAddress);
148 Serial.write(returnedAddress);
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();
163 Serial.println(String(result, 7));
165 Serial.print(String(result, 7));
168 if (result != -9999) {
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); }
178 Serial.print(F("<<< INVALID CHARACTER IN RESPONSE:"));
182 // Read the character to make sure it's removed from the buffer
186 delay(10); // 1 character ~ 7.5ms
190 Serial.println(F(" No results received, will not continue requests!"));
191 break; // don't do another loop if we got nothing
194 if (gotResults && !bad_read) {
195 resultsReceived = resultsReceived + cmd_results;
197 Serial.print(F(" Total Results Received: "));
198 Serial.print(resultsReceived);
199 Serial.print(F(", Remaining: "));
200 Serial.println(resultsExpected - resultsReceived);
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
210 mySDI12.clearBuffer();
213 return resultsReceived == resultsExpected;
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 = "";
221 myCommand += (char)i; // sends basic 'acknowledge' command [address][!]
224 for (int j = 0; j < 3; j++) { // goes through three rapid contact attempts
225 mySDI12.sendCommand(myCommand, wake_delay);
228 Serial.println(myCommand);
231 if (mySDI12.available()) { // If we hear anything, assume we have an active sensor
234 while (mySDI12.available()) { Serial.write(mySDI12.read()); }
236 mySDI12.clearBuffer();
241 mySDI12.clearBuffer();
246 Serial.begin(serialBaud);
247 while (!Serial && millis() < 10000L);
249 Serial.println("Opening SDI-12 bus...");
251 delay(500); // allow things to settle
253 Serial.println("Timeout value: ");
254 Serial.println(mySDI12.TIMEOUT);
256 // Power the sensors;
258 Serial.println("Powering up sensors, wait...");
259 pinMode(powerPin, OUTPUT);
260 digitalWrite(powerPin, HIGH);
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");
269 for (int8_t i = firstAddress; i <= lastAddress; i++) {
270 char addr = decToChar(i);
271 if (checkActive(addr)) {
278 Serial.print("Total number of sensors found: ");
279 Serial.println(numSensors);
281 if (numSensors == 0) {
283 "No sensors found, please check connections and restart the Arduino.");
284 while (true) { delay(10); } // do nothing forever
289 "Time Elapsed (s), Sensor Address, Est Measurement Time (s), Number Measurements, "
290 "Real Measurement Time (ms), Measurement 1, Measurement 2, ... etc.");
292 "-------------------------------------------------------------------------------");
296 // measure one at a time
297 for (int8_t i = firstAddress; i <= lastAddress; i++) {
298 char addr = decToChar(i);
302 Serial.print(" - millis: ");
303 Serial.print(millis());
306 Serial.print(millis());
309 getContinuousResults(addr, 0);
314 delay(10000L); // wait ten seconds between measurement attempts.