Example K: Concurrent Measurements

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
2
3[platformio]
4description = SDI-12 Library Example K: Using Concurrent Measurements
5src_dir = .piolibdeps/Arduino-SDI-12_ID1486/examples/k_concurrent_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} 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>
6 *
7 * @brief Example K: Concurrent Measurements
8 *
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.
14 */
15
16#include <SDI12Config.h>
17#if (defined(__AVR__) || defined(ARDUINO_ARCH_AVR)) && !defined(SDI12_INTERNAL_PCINT)
18#include <EnableInterrupt.h>
19#endif
20
21#include <SDI12Recorder.h>
22
23#ifndef SDI12_DATA_PIN
24#define SDI12_DATA_PIN 7
25#endif
26#ifndef SDI12_POWER_PIN
27#define SDI12_POWER_PIN 22
28#endif
29#ifndef SDI12_TERMINAL_BAUD
30#define SDI12_TERMINAL_BAUD 115200
31#endif
32
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') */
40
41/** Define the SDI-12 bus */
42SDI12 mySDI12(dataPin);
43SDI12Recorder mySDI12Recorder(mySDI12);
44
45// keeps track of active addresses
46bool isActive[64];
47
48// keeps track of the wait time for each active addresses
49uint8_t meas_time_ms[64];
50
51// keeps track of the time each sensor was started
52uint32_t millisStarted[64];
53
54// keeps track of the time each sensor will be ready
55uint32_t millisReady[64];
56
57// keeps track of the number of results expected
58uint8_t expectedResults[64];
59
60// keeps track of the number of results returned
61uint8_t returnedResults[64];
62
63uint8_t numSensors = 0;
64
65bool printInfo(char address) {
66 SDI12Recorder::Identification sensorInfo;
67 if (mySDI12Recorder.identify(address, sensorInfo)) {
68 mySDI12Recorder.printSensorInfo(sensorInfo, Serial);
69 return true;
70 } else {
71 Serial.print(F("Identification failed for address "));
72 Serial.print(address);
73 Serial.print(F(": "));
74 mySDI12Recorder.printRecorderResult(Serial);
75 Serial.println();
76 }
77 return false;
78}
79
80bool getResults(char address, int resultsExpected) {
81 if (resultsExpected < 0 || resultsExpected > 99) { return false; }
82
83 float values[99];
84 mySDI12Recorder.requestData(address, values, 99U);
85 while (mySDI12Recorder.isBusy()) {
86 mySDI12Recorder.update();
87 yield();
88 }
89 if (mySDI12Recorder.result() != SDI12Recorder::RESULT_SUCCESS) {
90 mySDI12Recorder.printRecorderResult(Serial);
91 Serial.println();
92 return false;
93 }
94
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(", "));
99 }
100 return valueCount == static_cast<size_t>(resultsExpected);
101}
102
103int startConcurrentMeasurement(char i, uint8_t measurementNumber = 0U) {
104 mySDI12Recorder.startConcurrentMeasurement(i, measurementNumber, useCRC);
105 while (mySDI12Recorder.isBusy()) {
106 mySDI12Recorder.update();
107 yield();
108 }
109 if (mySDI12Recorder.result() != SDI12Recorder::RESULT_SUCCESS) {
110 Serial.print(F("Could not start concurrent measurement for address "));
111 Serial.print(i);
112 Serial.print(F(": "));
113 mySDI12Recorder.printRecorderResult(Serial);
114 Serial.println();
115 return 0;
116 }
117
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];
125}
126
127void setup() {
128 Serial.begin(serialBaud);
129 while (!Serial && millis() < 10000L);
130 char tbuf[3] = {'\0'};
131
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));
136 Serial.print(')');
137 Serial.println(F("..."));
138 mySDI12.begin();
139 delay(500); // allow things to settle
140
141 Serial.println(F("Timeout value: "));
142 Serial.println(mySDI12.TIMEOUT);
143
144 // Power the sensors;
145 if (powerPin >= 0) {
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);
151 delay(10000L);
152 } else {
153 Serial.println(F("Wait 5s..."));
154 delay(5000L);
155 }
156
157 // Quickly scan the address space
158 Serial.println(F("\n\nScanning all addresses, please wait..."));
159
160 for (int8_t i = firstAddress; i <= lastAddress; i++) {
161 char addr = SDI12Address::fromIndex(i);
162 if (mySDI12Recorder.requestAcknowledgement(addr)) {
163 numSensors++;
164 isActive[i] = 1;
165 printInfo(addr);
166 }
167 }
168 Serial.print(F("Total number of sensors found: "));
169 Serial.println(numSensors);
170
171 if (numSensors == 0) {
172 Serial.println(
173 "No sensors found, please check connections and restart the Arduino.");
174 while (true); // do nothing forever
175 }
176
177 Serial.println();
178 Serial.println(F("Time Elapsed (s), Measurement 1, Measurement 2, ... etc."));
179 Serial.println(
180 "-------------------------------------------------------------------------------");
181}
182
183void loop() {
184 static int8_t firstAddressThisRound = firstAddress;
185
186 // re-zero before starting a new measurement round
187 for (int8_t i = firstAddress; i <= lastAddress; i++) {
188 millisReady[i] = 0;
189 expectedResults[i] = 0;
190 returnedResults[i] = 0;
191 }
192
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);
200 if (isActive[i]) {
201 if (startConcurrentMeasurement(addr) > 0) { ++sensorsStarted; }
202 }
203 }
204
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];
210 }
211 }
212 while ((millis() - first_ready) > 50) { delay(1); }
213
214 // get all readings
215 uint8_t numReadingsRecorded = 0;
216 uint8_t round = 1;
217
218 do {
219 for (int8_t i = firstAddress; i <= lastAddress; i++) {
220 if (isActive[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(", "));
226 Serial.print(addr);
227 Serial.print(F(", "));
228 getResults(addr, expectedResults[i]);
229 numReadingsRecorded++;
230 Serial.println();
231 }
232 }
233 }
234 round++;
235 } while (numReadingsRecorded < sensorsStarted);
236
237 ++firstAddressThisRound;
238 if (firstAddressThisRound > lastAddress) { firstAddressThisRound = firstAddress; }
239
240 delay(10000L); // wait ten seconds between measurement attempts.
241}