This advanced example has functionality similar to Example K, but it never waits inside a recorder transaction. loop() repeatedly calls SDI12Recorder::update() while a small application state machine scans the bus, starts concurrent measurements, and retrieves each sensor's complete data when isMeasurementReady() reports readiness.
The logger starts as many measurements as the recorder's fixed context table can retain. A RESULT_CONTEXT_CAPACITY result ends the start pass without changing the library's default configuration.
PlatformIO Configuration
1; PlatformIO Project Configuration File
4description = SDI-12 Library Example L: Non-Blocking Concurrent Logger
5src_dir = .piolibdeps/Arduino-SDI-12_ID1486/examples/m_non_blocking_concurrent_logger
The Complete Example
2 * @example{lineno} m_non_blocking_concurrent_logger.ino
3 * @copyright Stroud Water Research Center
4 * @license This example is published under the BSD-3 license.
5 * @author Codex, at instruction of Sara Geleskie Damiano <sdamiano@stroudcenter.org>
7 * @brief Example M: Non-blocking concurrent measurements.
9 * This example discovers sensors, starts concurrent measurements, and retrieves each
10 * result when ready. Every recorder transaction is advanced from loop() with update().
13#include <SDI12Config.h>
14#if (defined(__AVR__) || defined(ARDUINO_ARCH_AVR)) && !defined(SDI12_INTERNAL_PCINT)
15#include <EnableInterrupt.h>
18#include <SDI12Recorder.h>
21#define SDI12_DATA_PIN 7
23#ifndef SDI12_POWER_PIN
24#define SDI12_POWER_PIN 22
26#ifndef SDI12_TERMINAL_BAUD
27#define SDI12_TERMINAL_BAUD 115200
30const int8_t dataPin = SDI12_DATA_PIN;
31const int8_t powerPin = SDI12_POWER_PIN;
32const uint32_t serialBaud = SDI12_TERMINAL_BAUD;
33const int8_t firstAddress = 0;
34const int8_t lastAddress = 7;
35const uint8_t addressCount = lastAddress - firstAddress + 1;
36const bool useCRC = true;
37const uint32_t loggingPeriod = 10000UL;
39SDI12 mySDI12(dataPin);
40SDI12Recorder mySDI12Recorder(mySDI12);
42bool isActive[lastAddress + 1] = {};
43bool measurementPending[lastAddress + 1] = {};
46enum LoggerPhase : uint8_t { SCAN_BUS, START_MEASUREMENTS, READ_MEASUREMENTS, PAUSE };
48enum PendingOperation : uint8_t {
52 CONCURRENT_MEASUREMENT,
56LoggerPhase loggerPhase = SCAN_BUS;
57PendingOperation pendingOperation = NO_OPERATION;
58int8_t nextAddress = firstAddress;
59int8_t startAddress = firstAddress;
60int8_t pendingIndex = -1;
61uint8_t sensorsFound = 0U;
62uint8_t sensorsStarted = 0U;
63uint8_t sensorsRead = 0U;
64uint8_t addressesChecked = 0U;
65uint32_t roundEndMillis = 0U;
67int8_t nextAddressAfter(int8_t address) {
68 return address < lastAddress ? address + 1 : firstAddress;
71int8_t nextActiveAddressAfter(int8_t address) {
72 for (uint8_t i = 0U; i < addressCount; ++i) {
73 address = nextAddressAfter(address);
74 if (isActive[address]) { return address; }
79void printOperationFailure(char address) {
80 Serial.print(F("Address "));
81 Serial.print(address);
82 Serial.print(F(": "));
83 mySDI12Recorder.printRecorderResult(Serial);
87void printValues(char address) {
88 Serial.print(millis() / 1000UL);
89 Serial.print(F(", "));
90 Serial.print(address);
91 for (size_t i = 0U; i < mySDI12Recorder.dataValueCount(); ++i) {
92 Serial.print(F(", "));
93 Serial.print(values[i], 7);
98void handleCompletedOperation() {
99 const char address = SDI12Address::fromIndex(pendingIndex);
100 const bool success = mySDI12Recorder.result() == SDI12Recorder::RESULT_SUCCESS;
102 switch (pendingOperation) {
103 case ACKNOWLEDGEMENT:
105 isActive[pendingIndex] = true;
107 mySDI12Recorder.startIdentify(address);
108 pendingOperation = IDENTIFICATION;
116 mySDI12Recorder.printSensorInfo(mySDI12Recorder.identification(), Serial);
118 printOperationFailure(address);
123 case CONCURRENT_MEASUREMENT:
125 measurementPending[pendingIndex] = true;
128 printOperationFailure(address);
129 if (mySDI12Recorder.result() == SDI12Recorder::RESULT_CONTEXT_CAPACITY) {
130 loggerPhase = READ_MEASUREMENTS;
133 nextAddress = nextAddressAfter(nextAddress);
139 printValues(address);
141 printOperationFailure(address);
143 measurementPending[pendingIndex] = false;
147 case NO_OPERATION: break;
150 pendingOperation = NO_OPERATION;
155 Serial.begin(serialBaud);
156 while (!Serial && millis() < 10000UL);
159 pinMode(powerPin, OUTPUT);
160 digitalWrite(powerPin, HIGH);
166 Serial.println(F("Scanning the SDI-12 bus without blocking loop()."));
170 mySDI12Recorder.update();
172 if (mySDI12Recorder.isBusy()) { return; }
173 if (pendingOperation != NO_OPERATION) { handleCompletedOperation(); }
174 if (pendingOperation != NO_OPERATION) { return; }
176 switch (loggerPhase) {
178 if (nextAddress <= lastAddress) {
179 pendingIndex = nextAddress;
180 mySDI12Recorder.startRequestAcknowledgement(
181 SDI12Address::fromIndex(pendingIndex));
182 pendingOperation = ACKNOWLEDGEMENT;
184 Serial.print(F("Sensors found: "));
185 Serial.println(sensorsFound);
186 startAddress = nextActiveAddressAfter(lastAddress);
187 nextAddress = startAddress;
188 addressesChecked = 0U;
189 loggerPhase = START_MEASUREMENTS;
193 case START_MEASUREMENTS:
194 while (addressesChecked < addressCount && !isActive[nextAddress]) {
195 nextAddress = nextAddressAfter(nextAddress);
198 if (addressesChecked < addressCount) {
199 pendingIndex = nextAddress;
200 mySDI12Recorder.startConcurrentMeasurement(
201 SDI12Address::fromIndex(pendingIndex), 0U, useCRC);
202 pendingOperation = CONCURRENT_MEASUREMENT;
204 loggerPhase = READ_MEASUREMENTS;
208 case READ_MEASUREMENTS:
209 if (sensorsRead >= sensorsStarted) {
210 roundEndMillis = millis();
214 for (int8_t i = firstAddress; i <= lastAddress; ++i) {
215 const char address = SDI12Address::fromIndex(i);
216 if (measurementPending[i] && mySDI12Recorder.isMeasurementReady(address)) {
218 mySDI12Recorder.requestData(address, values, 99U);
219 pendingOperation = DATA_RETRIEVAL;
226 if (millis() - roundEndMillis >= loggingPeriod) {
227 for (int8_t i = firstAddress; i <= lastAddress; ++i) {
228 measurementPending[i] = false;
232 startAddress = nextActiveAddressAfter(startAddress);
233 nextAddress = startAddress;
234 addressesChecked = 0U;
235 loggerPhase = START_MEASUREMENTS;