Example N: Non-Blocking Measurement

This advanced example uses the polling API for acknowledgement, identification, a CRC-requesting normal measurement, and complete data retrieval. It also makes one intentionally undersized data request to show that the recorder reports RESULT_DATA_BUFFER_TOO_SMALL without transmitting or discarding the retained measurement context.

The caller-owned data array remains in scope for the entire asynchronous data operation, and SDI12Recorder::update() is called on every pass through loop().

PlatformIO Configuration

1; PlatformIO Project Configuration File
2
3[platformio]
4description = SDI-12 Library Example N: Non-Blocking Measurement
5src_dir = .piolibdeps/Arduino-SDI-12_ID1486/examples/n_non_blocking_measurement
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} n_non_blocking_measurement.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>
6 *
7 * @brief Example N: Advanced non-blocking measurement workflow.
8 *
9 * This example acknowledges and identifies one sensor, requests a CRC measurement,
10 * demonstrates recovery from an undersized data buffer, and retrieves every value.
11 * No recorder operation blocks the loop; update() advances the active transaction.
12 */
13
14#include <SDI12Config.h>
15#if (defined(__AVR__) || defined(ARDUINO_ARCH_AVR)) && !defined(SDI12_INTERNAL_PCINT)
16#include <EnableInterrupt.h>
17#endif
18
19#include <SDI12Recorder.h>
20
21#ifndef SDI12_DATA_PIN
22#define SDI12_DATA_PIN 7
23#endif
24#ifndef SDI12_POWER_PIN
25#define SDI12_POWER_PIN 22
26#endif
27#ifndef SDI12_TERMINAL_BAUD
28#define SDI12_TERMINAL_BAUD 115200
29#endif
30
31const int8_t dataPin = SDI12_DATA_PIN;
32const int8_t powerPin = SDI12_POWER_PIN;
33const uint32_t serialBaud = SDI12_TERMINAL_BAUD;
34const char sensorAddress = '0';
35const bool useCRC = true;
36
37SDI12 mySDI12(dataPin);
38SDI12Recorder mySDI12Recorder(mySDI12);
39
40float values[9];
41
42enum WorkflowStage : uint8_t {
43 START_ACKNOWLEDGEMENT,
44 WAIT_ACKNOWLEDGEMENT,
45 START_IDENTIFICATION,
46 WAIT_IDENTIFICATION,
47 START_MEASUREMENT,
48 WAIT_MEASUREMENT,
49 START_CAPACITY_CHECK,
50 WAIT_CAPACITY_CHECK,
51 START_DATA,
52 WAIT_DATA,
53 WAIT_NEXT_MEASUREMENT
54};
55
56WorkflowStage stage = START_ACKNOWLEDGEMENT;
57WorkflowStage restartStage = START_MEASUREMENT;
58uint32_t measurementEnd = 0U;
59
60void printFailure(const __FlashStringHelper* operation,
61 WorkflowStage stageAfterWait = START_MEASUREMENT) {
62 Serial.print(operation);
63 Serial.print(F(" failed: "));
64 mySDI12Recorder.printRecorderResult(Serial);
65 Serial.println();
66 measurementEnd = millis();
67 restartStage = stageAfterWait;
68 stage = WAIT_NEXT_MEASUREMENT;
69}
70
71void setup() {
72 Serial.begin(serialBaud);
73 while (!Serial && millis() < 10000UL);
74
75 if (powerPin >= 0) {
76 pinMode(powerPin, OUTPUT);
77 digitalWrite(powerPin, HIGH);
78 delay(10000UL);
79 }
80
81 mySDI12.begin();
82
83 Serial.println(F("Starting the non-blocking recorder workflow."));
84}
85
86void loop() {
87 mySDI12Recorder.update();
88
89 switch (stage) {
90 case START_ACKNOWLEDGEMENT:
91 mySDI12Recorder.startRequestAcknowledgement(sensorAddress);
92 stage = WAIT_ACKNOWLEDGEMENT;
93 break;
94
95 case WAIT_ACKNOWLEDGEMENT:
96 if (mySDI12Recorder.isBusy()) { break; }
97 if (mySDI12Recorder.result() != SDI12Recorder::RESULT_SUCCESS) {
98 printFailure(F("Acknowledgement"), START_ACKNOWLEDGEMENT);
99 break;
100 }
101 Serial.println(F("Sensor acknowledged."));
102 stage = START_IDENTIFICATION;
103 break;
104
105 case START_IDENTIFICATION:
106 mySDI12Recorder.startIdentify(sensorAddress);
107 stage = WAIT_IDENTIFICATION;
108 break;
109
110 case WAIT_IDENTIFICATION:
111 if (mySDI12Recorder.isBusy()) { break; }
112 if (mySDI12Recorder.result() != SDI12Recorder::RESULT_SUCCESS) {
113 printFailure(F("Identification"), START_IDENTIFICATION);
114 break;
115 }
116 mySDI12Recorder.printSensorInfo(mySDI12Recorder.identification(), Serial);
117 restartStage = START_MEASUREMENT;
118 stage = START_MEASUREMENT;
119 break;
120
121 case START_MEASUREMENT:
122 mySDI12Recorder.startMeasurement(sensorAddress, 0U, useCRC);
123 stage = WAIT_MEASUREMENT;
124 break;
125
126 case WAIT_MEASUREMENT:
127 if (mySDI12Recorder.isBusy()) { break; }
128 if (mySDI12Recorder.result() != SDI12Recorder::RESULT_SUCCESS) {
129 printFailure(F("Measurement"));
130 break;
131 }
132 Serial.print(F("Sensor advertised "));
133 Serial.print(mySDI12Recorder.expectedValueCount());
134 Serial.println(F(" values."));
135 stage = mySDI12Recorder.expectedValueCount() == 0U ? START_DATA
136 : START_CAPACITY_CHECK;
137 break;
138
139 case START_CAPACITY_CHECK:
140 // This local error does not transmit and leaves the measurement context intact.
141 mySDI12Recorder.requestData(sensorAddress, values,
142 mySDI12Recorder.expectedValueCount() - 1U);
143 stage = WAIT_CAPACITY_CHECK;
144 break;
145
146 case WAIT_CAPACITY_CHECK:
147 if (mySDI12Recorder.isBusy()) { break; }
148 if (mySDI12Recorder.result() == SDI12Recorder::RESULT_DATA_BUFFER_TOO_SMALL) {
149 Serial.println(F("The intentional capacity error was reported correctly."));
150 } else {
151 printFailure(F("Capacity check"));
152 break;
153 }
154 stage = START_DATA;
155 break;
156
157 case START_DATA:
158 mySDI12Recorder.requestData(sensorAddress, values, 9U);
159 stage = WAIT_DATA;
160 break;
161
162 case WAIT_DATA:
163 if (mySDI12Recorder.isBusy()) { break; }
164 if (mySDI12Recorder.result() != SDI12Recorder::RESULT_SUCCESS) {
165 printFailure(F("Data retrieval"));
166 break;
167 }
168 for (size_t i = 0U; i < mySDI12Recorder.dataValueCount(); ++i) {
169 Serial.print(F("Value "));
170 Serial.print(i + 1U);
171 Serial.print(F(": "));
172 Serial.println(values[i], 7);
173 }
174 measurementEnd = millis();
175 stage = WAIT_NEXT_MEASUREMENT;
176 break;
177
178 case WAIT_NEXT_MEASUREMENT:
179 if (millis() - measurementEnd >= 10000UL) { stage = restartStage; }
180 break;
181 }
182}