SDI12Recorder class

High-level SDI-12 recorder transaction executor.

A transaction is a complete request-response exchange with a single sensor, including wake-up, command transmission, response collection, validation, and automatic retries according to the SDI-12 specification.

Usage:

1SDI12Recorder recorder(mySDI12);
2
3void setup() {
4 mySDI12.begin(dataPin);
5 SDI12Recorder::Identification info;
6 if (recorder.identify('0', info)) {
7 Serial.println(info.vendorID);
8 } else {
9 recorder.printRecorderResult(Serial);
10 Serial.println();
11 }
12}
13
14void loop() {}

Public types

struct Identification
Parsed response to an SDI-12 identification command.
enum RecorderResult: uint8_t { RESULT_NONE = 0x00, RESULT_SUCCESS = 0x01, RESULT_NO_RESPONSE = 0x02, RESULT_FRAMING_ERROR = 0x03, RESULT_WRONG_ADDRESS = 0x04, RESULT_CRC_ERROR = 0x05, RESULT_BUFFER_OVERFLOW = 0x06, RESULT_MALFORMED_RESPONSE = 0x07, RESULT_RESPONSE_TIMEOUT = 0x08, RESULT_TRANSACTION_TIMEOUT = 0x09, RESULT_CANCELLED = 0x0A, RESULT_INVALID_ADDRESS = 0x0B, RESULT_INVALID_COMMAND_ARGUMENT = 0x0C, RESULT_BUSY = 0x0D, RESULT_CONTEXT_CAPACITY = 0x0E, RESULT_CONTEXT_REQUIRED = 0x0F, RESULT_DATA_BUFFER_TOO_SMALL = 0x10, RESULT_MEASUREMENT_ABORTED = 0x11, RESULT_INCOMPLETE_DATA = 0x12, RESULT_RETRIES_EXHAUSTED = 0x13, RESULT_UNEXPECTED_BREAK = 0x14 }
Terminal result of a transaction.
enum MeasurementState: uint8_t { MEASUREMENT_NONE = 0x00, MEASUREMENT_RUNNING = 0x01, MEASUREMENT_READY = 0x02 }
Retained measurement state for one sensor address.
using RecorderResult = enum SDI12Recorder::RecorderResult
Terminal result of a transaction.
using MeasurementState = enum SDI12Recorder::MeasurementState
Retained measurement state for one sensor address.

Public static functions

static void printSensorInfo(const Identification& identification, Stream& printStream)
Print parsed sensor identification fields with descriptive labels.

Constructors, destructors, conversion operators

SDI12Recorder(SDI12& sdi12) explicit
Construct a recorder bound to an SDI12 transport instance.
MeasurementState measurementState(char address) const -> MeasurementState
Retrieve the retained measurement state for one sensor address.
RecorderResult result() const -> RecorderResult
Retrieve the terminal result of the completed transaction.

Public functions

char queryAddress() -> char
Query the address of the only sensor attached to the bus, blocking until completion.
bool requestAcknowledgement(char address) -> bool
Request acknowledgement from one sensor, blocking until completion.
bool identify(char address, Identification& identification) -> bool
Request and parse identification information, blocking until completion.
bool changeAddress(char oldAddress, char newAddress) -> bool
Change a sensor address, blocking until completion.
void startQueryAddress()
Start a non-blocking address query.
void startRequestAcknowledgement(char address)
Start a non-blocking acknowledgement request.
void startIdentify(char address)
Start a non-blocking identification request.
void startChangeAddress(char oldAddress, char newAddress)
Start a non-blocking address-change request.
void startMeasurement(char address, uint8_t measurementNumber = 0U, bool requestCRC = false)
Start a non-blocking normal measurement request.
void startConcurrentMeasurement(char address, uint8_t measurementNumber = 0U, bool requestCRC = false)
Start a non-blocking concurrent measurement request.
size_t requestContinuousData(char address, float* values, size_t capacity, uint8_t dataOption, bool requestCRC) -> size_t
Request one continuous measurement and retrieve its values, blocking until completion.
void startRequestContinuousData(char address, float* values, size_t capacity, uint8_t dataOption, bool requestCRC)
Start a non-blocking continuous measurement data request.
void requestData(char address, float* values, size_t capacity)
Retrieve all values from a retained measurement context.
void requestDataImmediately(char address, float* values, size_t capacity)
Attempt to retrieve all retained measurement values immediately.
size_t measure(char address, float* values, size_t capacity, uint8_t measurementNumber = 0U, bool requestCRC = false) -> size_t
Perform a normal measurement and retrieve all of its values, blocking until completion.
bool isMeasurementReady(char address) const -> bool
Check whether retained measurement data are ready for one sensor.
void startRawCommand(char address, const String& command)
Start a low-level raw command transaction.
void update()
Advance the transaction state machine.
bool isBusy() const -> bool
Check if a transaction is currently active.
bool isOperationComplete() const -> bool
Check if the current executor operation has completed.
void printRecorderResult(Stream& printStream) const
Print a human-readable description of the current recorder result.
void printPhase(Stream& printStream) const
Print a human-readable description of the current executor phase.
char queriedAddress() const -> char
Retrieve the address returned by a successful address query.
char acknowledgedAddress() const -> char
Retrieve the address returned by a successful acknowledgement.
const Identification& identification() const -> const Identification&
Retrieve the most recently parsed identification fields.
uint16_t measurementSeconds() const -> uint16_t
Retrieve the advertised time for the most recent measurement request.
bool measurementServiceRequestReceived() const -> bool
Check whether the most recent normal measurement completed by service request.
uint32_t measurementServiceRequestMillis() const -> uint32_t
Retrieve when the most recent normal-measurement service request began.
uint32_t measurementAcknowledgementMillis() const -> uint32_t
Retrieve when the most recent measurement acknowledgement was validated.
uint8_t expectedValueCount() const -> uint8_t
Retrieve the advertised value count for the most recent measurement.
size_t dataValueCount() const -> size_t
Retrieve the number of values accepted by the current data workflow.
const char* response() const -> const char*
Retrieve the collected response, if successful.
size_t responseLength() const -> size_t
Retrieve the number of payload characters in response().
size_t copyResponse(char* destination, size_t capacity) const -> size_t
Copy the response payload into an application-owned buffer.
void cancel()
Cancel the current transaction and return to idle.
void clearResponse()
Clear the collected response payload.

Enum documentation

enum SDI12Recorder::RecorderResult: uint8_t

Terminal result of a transaction.

Each result code corresponds to a specific terminal condition. The recorder publishes these results to allow the application to decide on retry strategy (e.g., try a different sensor, abandon this measurement, etc.).

Enumerators
RESULT_NONE

Transaction is in progress or has not been started.

RESULT_SUCCESS

Transaction completed successfully; response is valid and available.

RESULT_NO_RESPONSE

No response was received within the maximum allowed time.

RESULT_FRAMING_ERROR

Response arrived but had invalid framing or parity.

RESULT_WRONG_ADDRESS

Response address does not match the command address.

RESULT_CRC_ERROR

CRC validation failed (if CRC was requested).

RESULT_BUFFER_OVERFLOW

Response buffer overflowed before response completed.

RESULT_MALFORMED_RESPONSE

Response-specific format validation failed.

RESULT_RESPONSE_TIMEOUT

Response deadline (command-specific max time) was exceeded.

RESULT_TRANSACTION_TIMEOUT

Overall transaction deadline was exceeded (retries not attempted).

RESULT_CANCELLED

Transaction was cancelled by the application.

RESULT_INVALID_ADDRESS

The supplied sensor address is not valid for the requested operation.

RESULT_INVALID_COMMAND_ARGUMENT

A command-specific argument is outside its permitted range.

RESULT_BUSY

A new operation was rejected because another operation is active.

RESULT_CONTEXT_CAPACITY

No tracking slot is available for a new pending sensor transaction.

RESULT_CONTEXT_REQUIRED

The requested operation requires pending measurement context.

RESULT_DATA_BUFFER_TOO_SMALL

The caller-provided data buffer cannot hold all returned values.

RESULT_MEASUREMENT_ABORTED

The sensor reported that its pending measurement was aborted.

RESULT_INCOMPLETE_DATA

The advertised value count was not received by the end of D9.

RESULT_RETRIES_EXHAUSTED

All protocol-required retry attempts were exhausted.

RESULT_UNEXPECTED_BREAK

Another recorder break interrupted a normal measurement workflow.


enum SDI12Recorder::MeasurementState: uint8_t

Retained measurement state for one sensor address.

Enumerators
MEASUREMENT_NONE

No pending measurement context is retained for the address.

MEASUREMENT_RUNNING

A concurrent measurement is still within its advertised measurement time.

MEASUREMENT_READY

Measurement data are ready to retrieve.



Typedef documentation

typedef enum SDI12Recorder::RecorderResult SDI12Recorder::RecorderResult

Terminal result of a transaction.

Each result code corresponds to a specific terminal condition. The recorder publishes these results to allow the application to decide on retry strategy (e.g., try a different sensor, abandon this measurement, etc.).



Function documentation

static void SDI12Recorder::printSensorInfo(const Identification& identification, Stream& printStream)

Print parsed sensor identification fields with descriptive labels.

Parameters
identification Parsed identification information to print.
printStream Stream that will receive the identification information.

A trailing newline is printed after the sensor-information field.


SDI12Recorder::SDI12Recorder(SDI12& sdi12) explicit

Construct a recorder bound to an SDI12 transport instance.

Parameters
sdi12 Reference to the SDI12 object that owns the data line.

MeasurementState SDI12Recorder::measurementState(char address) const

Retrieve the retained measurement state for one sensor address.

Parameters
address Sensor address to query.
Returns MeasurementState No context, concurrent measurement running, or data ready.

Concurrent readiness is evaluated with rollover-safe elapsed-time arithmetic. This query does not start a bus operation or change executor completion state.


RecorderResult SDI12Recorder::result() const

Retrieve the terminal result of the completed transaction.

Returns RecorderResult The result code. Returns RESULT_BUSY immediately after an operation was rejected while another transaction is active. Otherwise returns RESULT_NONE while the transaction is in progress.

char SDI12Recorder::queryAddress()

Query the address of the only sensor attached to the bus, blocking until completion.

Returns char The sensor address, or the null character on failure. Use result() for the detailed terminal reason.

Sends ?! and accepts exactly one valid sensor address followed by CR/LF.


bool SDI12Recorder::requestAcknowledgement(char address)

Request acknowledgement from one sensor, blocking until completion.

Parameters
address Sensor address from 0-9, a-z, or A-Z.
Returns bool true only when the correctly addressed acknowledgement is validated.

Sends a! and requires the addressed sensor to return exactly a followed by CR/LF.


bool SDI12Recorder::identify(char address, Identification& identification)

Request and parse identification information, blocking until completion.

Parameters
address Sensor address from 0-9, a-z, or A-Z.
identification Destination updated only after a validated response.
Returns bool true when identification was updated; otherwise false.

Sends aI! and validates the fixed identification fields plus the optional 0-13 character sensor-information field.


bool SDI12Recorder::changeAddress(char oldAddress, char newAddress)

Change a sensor address, blocking until completion.

Parameters
oldAddress Current sensor address.
newAddress New sensor address.
Returns bool true only when the new address is acknowledged.

Sends aAb! and requires the sensor to acknowledge with the new address followed by CR/LF. After the acknowledgement, this function waits one second for the sensor to finish processing the address change.


void SDI12Recorder::startQueryAddress()

Start a non-blocking address query.

Call update() until isOperationComplete() is true, then read queriedAddress() after a successful result.


void SDI12Recorder::startRequestAcknowledgement(char address)

Start a non-blocking acknowledgement request.

Parameters
address Sensor address from 0-9, a-z, or A-Z.

void SDI12Recorder::startIdentify(char address)

Start a non-blocking identification request.

Parameters
address Sensor address from 0-9, a-z, or A-Z.

Call update() until isOperationComplete() is true, then read identification() after a successful result.


void SDI12Recorder::startChangeAddress(char oldAddress, char newAddress)

Start a non-blocking address-change request.

Parameters
oldAddress Current sensor address.
newAddress New sensor address.

The operation remains busy for one second after a valid acknowledgement so the sensor can finish processing the address change.


void SDI12Recorder::startMeasurement(char address, uint8_t measurementNumber = 0U, bool requestCRC = false)

Start a non-blocking normal measurement request.

Parameters
address Sensor address from 0-9, a-z, or A-Z.
measurementNumber Zero for the unnumbered command, or 1 through 9.
requestCRC true to request the MC command family and require CRC on the later data response.

Sends aM!, aM1! through aM9!, aMC!, or aMC1! through aMC9!. After validating the acknowledgement, the recorder remains busy until the sensor sends its service request or the advertised measurement time expires. On success, the pending data context is retained for a later data request.


void SDI12Recorder::startConcurrentMeasurement(char address, uint8_t measurementNumber = 0U, bool requestCRC = false)

Start a non-blocking concurrent measurement request.

Parameters
address Sensor address from 0-9, a-z, or A-Z.
measurementNumber Zero for the unnumbered command, or 1 through 9.
requestCRC true to request the CC command family and require CRC on the later data response.

Sends aC!, aC1! through aC9!, aCC!, or aCC1! through aCC9!. The executor operation completes after a valid atttnn acknowledgement while the per-address measurement context remains running or ready independently.


size_t SDI12Recorder::requestContinuousData(char address, float* values, size_t capacity, uint8_t dataOption, bool requestCRC)

Request one continuous measurement and retrieve its values, blocking until completion.

Parameters
address Sensor address from 0-9, a-z, or A-Z.
values Caller-owned destination for parsed values. May be null only when capacity is zero.
capacity Number of float elements available in values.
dataOption Continuous measurement data option from 0 through 9.
requestCRC true to use the RC command family and validate the response CRC.
Returns size_t Number of values written on success, or zero on failure or a valid zero-value response. Use result() to distinguish those outcomes.

Sends aR0! through aR9! or aRC0! through aRC9!. Each data option is an independent measurement; options do not need to be requested in sequence. An address-only response is a successful zero-value result indicating that the sensor cannot provide the requested continuous measurement.


void SDI12Recorder::startRequestContinuousData(char address, float* values, size_t capacity, uint8_t dataOption, bool requestCRC)

Start a non-blocking continuous measurement data request.

Parameters
address Sensor address from 0-9, a-z, or A-Z.
values Caller-owned destination for parsed values. May be null only when capacity is zero.
capacity Number of float elements available in values.
dataOption Continuous measurement data option from 0 through 9.
requestCRC true to use the RC command family and validate the response CRC.

Sends aR0! through aR9! or aRC0! through aRC9! and strictly validates the immediate data response. Call update() until isOperationComplete() is true. The caller-owned buffer must remain valid and must not be modified until the operation completes or is cancelled.


void SDI12Recorder::requestData(char address, float* values, size_t capacity)

Retrieve all values from a retained measurement context.

Parameters
address Sensor address with retained measurement context.
values Caller-owned destination for the parsed values. May be null only when the advertised value count is zero.
capacity Number of float elements available in values.

The recorder waits for a concurrent measurement to become ready, then generates D0 through D9 as needed. Each response is strictly validated and an optional CRC is checked according to the measurement that established the context.

Call update() until isOperationComplete() is true. The caller-owned buffer must remain valid and must not be modified until the operation completes or is cancelled. Values from each accepted data block are appended exactly once.


void SDI12Recorder::requestDataImmediately(char address, float* values, size_t capacity)

Attempt to retrieve all retained measurement values immediately.

Parameters
address Sensor address with retained measurement context.
values Caller-owned destination for the parsed values. May be null only when the advertised value count is zero.
capacity Number of float elements available in values.

This diagnostic variant starts with D0 immediately, even when a retained concurrent measurement has not reached its advertised ready time. It uses the same typed response parsing, CRC validation, and D0 through D9 workflow as requestData(), but transmits each data command only once. Disabling retries keeps a later successful response from being attributed to the original probe time.

Call update() until isOperationComplete() is true. The caller-owned buffer must remain valid and must not be modified until the operation completes or is cancelled.


size_t SDI12Recorder::measure(char address, float* values, size_t capacity, uint8_t measurementNumber = 0U, bool requestCRC = false)

Perform a normal measurement and retrieve all of its values, blocking until completion.

Parameters
address Sensor address from 0-9, a-z, or A-Z.
values Caller-owned destination for parsed values.
capacity Number of float elements available in values.
measurementNumber Zero for the unnumbered command, or 1 through 9.
requestCRC true to use the MC command family and validate response CRCs.
Returns size_t Number of values written on success, or zero on failure. Use result() to distinguish failure from a successful zero-value result.

This convenience method drives startMeasurement(), update(), and requestData() without duplicating their command, validation, retry, or context logic.


bool SDI12Recorder::isMeasurementReady(char address) const

Check whether retained measurement data are ready for one sensor.

Parameters
address Sensor address to query.
Returns bool true when measurementState() is MEASUREMENT_READY.

void SDI12Recorder::startRawCommand(char address, const String& command)

Start a low-level raw command transaction.

Parameters
address The SDI-12 address (0-9, a-z, A-Z) of the sensor to query.
command The command body to transmit, without the address or terminating exclamation point. For example, "X" becomes "[address]X!" on the wire.

This secondary interface supports manufacturer-specific extended commands and commands that do not yet have a typed recorder operation. The recorder transmits address, the unchanged command body, and a terminating exclamation point. It applies the normal break and retry policy, but only requires a non-empty response whose first character matches address. The remainder of the response is retained without command-specific validation or CRC checking.

Call update() until isOperationComplete() is true, then inspect result() and response(). Prefer a typed recorder method whenever one represents the desired standard command because typed methods provide command-specific validation and automatic multi-command workflows.


void SDI12Recorder::update()

Advance the transaction state machine.

Call this method repeatedly (typically in loop()) to drive the transaction forward through its phases: break, mark, transmit, wait, receive, validate, and retry-decision phases. The method performs all work for the current phase and may move to the next phase if guard conditions are satisfied.

The method returns immediately if no work is needed in the current phase (e.g., waiting for a deadline). It is safe to call update() frequently without penalty.


bool SDI12Recorder::isBusy() const

Check if a transaction is currently active.

Returns bool true if an operation has been started and has not yet completed or been cancelled.

bool SDI12Recorder::isOperationComplete() const

Check if the current executor operation has completed.

Returns bool true if the active operation has reached a terminal state (success or failure) and result() may be queried.

void SDI12Recorder::printRecorderResult(Stream& printStream) const

Print a human-readable description of the current recorder result.

Parameters
printStream Stream that will receive the description.

The text describes the same RecorderResult returned by result(). No trailing newline is printed.


void SDI12Recorder::printPhase(Stream& printStream) const

Print a human-readable description of the current executor phase.

Parameters
printStream Stream that will receive the description.

No trailing newline is printed.


char SDI12Recorder::queriedAddress() const

Retrieve the address returned by a successful address query.

Returns char The queried sensor address, or the null character when no successful address query is available.

char SDI12Recorder::acknowledgedAddress() const

Retrieve the address returned by a successful acknowledgement.

Returns char The acknowledged address, or the null character when unavailable.

This accessor applies to requestAcknowledgement() and changeAddress().


const Identification& SDI12Recorder::identification() const

Retrieve the most recently parsed identification fields.

Returns const Identification& Recorder-owned identification storage. Its fields are empty unless the most recent identification operation completed successfully.

uint16_t SDI12Recorder::measurementSeconds() const

Retrieve the advertised time for the most recent measurement request.

Returns uint16_t Measurement time in seconds from a validated atttn or atttnn response, or zero when unavailable or when the sensor reported immediate readiness.

bool SDI12Recorder::measurementServiceRequestReceived() const

Check whether the most recent normal measurement completed by service request.

Returns bool true when a validated service request ended the measurement wait; false when the advertised time elapsed or no valid service request is available.

uint32_t SDI12Recorder::measurementServiceRequestMillis() const

Retrieve when the most recent normal-measurement service request began.

Returns uint32_t Milliseconds from completion of the validated measurement acknowledgement to observation of the service-request start bit. The value is valid only when measurementServiceRequestReceived() returns true.

uint32_t SDI12Recorder::measurementAcknowledgementMillis() const

Retrieve when the most recent measurement acknowledgement was validated.

Returns uint32_t The millis() timestamp captured after validating the acknowledgement for a normal or concurrent measurement. The value is valid after a successful measurement-start operation and until another operation is started.

uint8_t SDI12Recorder::expectedValueCount() const

Retrieve the advertised value count for the most recent measurement.

Returns uint8_t Expected number of values from a validated atttn response, or zero when unavailable.

size_t SDI12Recorder::dataValueCount() const

Retrieve the number of values accepted by the current data workflow.

Returns size_t Number of values written to the caller-owned data buffer.

const char* SDI12Recorder::response() const

Retrieve the collected response, if successful.

Returns const char* The null-terminated response payload. Protocol envelope characters that have already been validated, including the address, CRC, and CR+LF terminator, are excluded.

size_t SDI12Recorder::responseLength() const

Retrieve the number of payload characters in response().

Returns size_t The response payload length, excluding the null terminator.

size_t SDI12Recorder::copyResponse(char* destination, size_t capacity) const

Copy the response payload into an application-owned buffer.

Parameters
destination Buffer that will receive the response payload. May be null only when capacity is zero.
capacity Total destination capacity, including its null terminator.
Returns size_t The complete response payload length, excluding the null terminator.

Copies at most capacity minus one characters and null-terminates the destination whenever destination is not null and capacity is greater than zero. Compare the return value with capacity to detect truncation.


void SDI12Recorder::cancel()

Cancel the current transaction and return to idle.

Stops all retry logic, releases the SDI-12 transport to listening, and resets the transaction executor to PHASE_IDLE. The result will be set to RESULT_CANCELLED.


void SDI12Recorder::clearResponse()

Clear the collected response payload.

This does not change the current transaction phase or SDI-12 line state.