This is very similar to example D - finding all attached sensors and logging data from them. Unlike example D, however, which waits for each sensor to complete a measurement, this asks all sensors to take measurements concurrently and then waits until each is finished to query for results. This can be much faster than waiting for each sensor when you have multiple sensor attached.
The recorder retains each sensor's readiness, expected value count, and CRC policy in its fixed context table. The example starts as many active sensors as that table can hold, interleaves their measurement times, and retrieves complete data only after isMeasurementReady() reports that a sensor is ready.
PlatformIO Configuration
1; PlatformIO Project Configuration File
4description = SDI-12 Library Example K: Using Concurrent Measurements
5src_dir = .piolibdeps/Arduino-SDI-12_ID1486/examples/k_concurrent_logger
The Complete Example
2 * @example{lineno} k_concurrent_logger.ino
3 * @copyright Stroud Water Research Center
4 * @license This example is published under the BSD-3 license.
5 * @author Sara Geleskie Damiano <sdamiano@stroudcenter.org>
7 * @brief Example K: Concurrent Measurements
9 * This is very similar to example D - finding all attached sensors and logging data
10 * from them. Unlike example D, however, which waits for each sensor to complete a
11 * measurement, this starts concurrent measurements on as many sensors as the recorder
12 * can track and queries each one when it is ready. This can be much faster than waiting
13 * for each sensor when you have multiple sensors attached.
16#include <SDI12Config.h>
17#if (defined(__AVR__) || defined(ARDUINO_ARCH_AVR)) && !defined(SDI12_INTERNAL_PCINT)
18#include <EnableInterrupt.h>
21#include <SDI12Recorder.h>
24#define SDI12_DATA_PIN 7
26#ifndef SDI12_POWER_PIN
27#define SDI12_POWER_PIN 22
29#ifndef SDI12_TERMINAL_BAUD
30#define SDI12_TERMINAL_BAUD 115200
33/* connection information */
34uint32_t serialBaud = SDI12_TERMINAL_BAUD; /*!< The baud rate for the serial port */
35int8_t dataPin = SDI12_DATA_PIN; /*!< The pin of the SDI-12 data bus */
36int8_t powerPin = SDI12_POWER_PIN; /*!< The sensor power pin (or -1) */
37bool useCRC = false; /*!< Whether to use CRC checking */
38int8_t firstAddress = 0; /* The first address in the address space to check (0='0') */
39int8_t lastAddress = 61; /* The last address in the address space to check (61='z') */
41/** Define the SDI-12 bus */
42SDI12 mySDI12(dataPin);
43SDI12Recorder mySDI12Recorder(mySDI12);
45// keeps track of active addresses
48// keeps track of the wait time for each active addresses
49uint8_t meas_time_ms[64];
51// keeps track of the time each sensor was started
52uint32_t millisStarted[64];
54// keeps track of the time each sensor will be ready
55uint32_t millisReady[64];
57// keeps track of the number of results expected
58uint8_t expectedResults[64];
60// keeps track of the number of results returned
61uint8_t returnedResults[64];
63uint8_t numSensors = 0;
65bool printInfo(char address) {
66 SDI12Recorder::Identification sensorInfo;
67 if (mySDI12Recorder.identify(address, sensorInfo)) {
68 mySDI12Recorder.printSensorInfo(sensorInfo, Serial);
71 Serial.print(F("Identification failed for address "));
72 Serial.print(address);
73 Serial.print(F(": "));
74 mySDI12Recorder.printRecorderResult(Serial);
80bool getResults(char address, int resultsExpected) {
81 if (resultsExpected < 0 || resultsExpected > 99) { return false; }
84 mySDI12Recorder.requestData(address, values, 99U);
85 while (mySDI12Recorder.isBusy()) {
86 mySDI12Recorder.update();
89 if (mySDI12Recorder.result() != SDI12Recorder::RESULT_SUCCESS) {
90 mySDI12Recorder.printRecorderResult(Serial);
95 const size_t valueCount = mySDI12Recorder.dataValueCount();
96 for (size_t valueNumber = 0U; valueNumber < valueCount; ++valueNumber) {
97 Serial.print(String(values[valueNumber], 7));
98 Serial.print(F(", "));
100 return valueCount == static_cast<size_t>(resultsExpected);
103int startConcurrentMeasurement(char i, uint8_t measurementNumber = 0U) {
104 mySDI12Recorder.startConcurrentMeasurement(i, measurementNumber, useCRC);
105 while (mySDI12Recorder.isBusy()) {
106 mySDI12Recorder.update();
109 if (mySDI12Recorder.result() != SDI12Recorder::RESULT_SUCCESS) {
110 Serial.print(F("Could not start concurrent measurement for address "));
112 Serial.print(F(": "));
113 mySDI12Recorder.printRecorderResult(Serial);
118 const uint8_t sensorNum = static_cast<uint8_t>(SDI12Address::toIndex(i));
119 meas_time_ms[sensorNum] = mySDI12Recorder.measurementSeconds();
120 millisStarted[sensorNum] = millis();
121 millisReady[sensorNum] = millis() +
122 static_cast<uint32_t>(mySDI12Recorder.measurementSeconds()) * 1000UL;
123 expectedResults[sensorNum] = mySDI12Recorder.expectedValueCount();
124 return expectedResults[sensorNum];
128 Serial.begin(serialBaud);
129 while (!Serial && millis() < 10000L);
130 char tbuf[3] = {'\0'};
132 Serial.print(F("Opening SDI-12 bus on pin "));
133 Serial.print(dataPin);
134 Serial.print(F(" ("));
135 Serial.print(itoa(dataPin, tbuf, 10));
137 Serial.println(F("..."));
139 delay(500); // allow things to settle
141 Serial.println(F("Timeout value: "));
142 Serial.println(mySDI12.TIMEOUT);
144 // Power the sensors;
146 Serial.print(F("Powering up sensors with pin "));
147 Serial.print(String(powerPin));
148 Serial.println(F(", wait 10s..."));
149 pinMode(powerPin, OUTPUT);
150 digitalWrite(powerPin, HIGH);
153 Serial.println(F("Wait 5s..."));
157 // Quickly scan the address space
158 Serial.println(F("\n\nScanning all addresses, please wait..."));
160 for (int8_t i = firstAddress; i <= lastAddress; i++) {
161 char addr = SDI12Address::fromIndex(i);
162 if (mySDI12Recorder.requestAcknowledgement(addr)) {
168 Serial.print(F("Total number of sensors found: "));
169 Serial.println(numSensors);
171 if (numSensors == 0) {
173 "No sensors found, please check connections and restart the Arduino.");
174 while (true); // do nothing forever
178 Serial.println(F("Time Elapsed (s), Measurement 1, Measurement 2, ... etc."));
180 "-------------------------------------------------------------------------------");
184 static int8_t firstAddressThisRound = firstAddress;
186 // re-zero before starting a new measurement round
187 for (int8_t i = firstAddress; i <= lastAddress; i++) {
189 expectedResults[i] = 0;
190 returnedResults[i] = 0;
193 // Start as many sensors as the configured recorder context table can retain.
194 uint8_t sensorsStarted = 0U;
195 const int8_t addressCount = lastAddress - firstAddress + 1;
196 for (int8_t offset = 0; offset < addressCount; ++offset) {
197 int8_t i = firstAddressThisRound + offset;
198 if (i > lastAddress) { i -= addressCount; }
199 char addr = SDI12Address::fromIndex(i);
201 if (startConcurrentMeasurement(addr) > 0) { ++sensorsStarted; }
205 // Wait for the first sensor to be ready
206 uint32_t first_ready = millis() + 120000;
207 for (int8_t i = firstAddress; i <= lastAddress; i++) {
208 if (millisReady[i] != 0 && millisReady[i] < first_ready) {
209 first_ready = millisReady[i];
212 while ((millis() - first_ready) > 50) { delay(1); }
215 uint8_t numReadingsRecorded = 0;
219 for (int8_t i = firstAddress; i <= lastAddress; i++) {
221 char addr = SDI12Address::fromIndex(i);
222 if (isActive[i] && mySDI12Recorder.isMeasurementReady(addr) &&
223 expectedResults[i] > 0 && (returnedResults[i] < expectedResults[i])) {
224 Serial.print(millis() / 1000);
225 Serial.print(F(", "));
227 Serial.print(F(", "));
228 getResults(addr, expectedResults[i]);
229 numReadingsRecorded++;
235 } while (numReadingsRecorded < sensorsStarted);
237 ++firstAddressThisRound;
238 if (firstAddressThisRound > lastAddress) { firstAddressThisRound = firstAddress; }
240 delay(10000L); // wait ten seconds between measurement attempts.