DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
sdi12.h
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file sdi12.h
6 * @brief SDI-12 sensor-bus command / response codec (DETWS_ENABLE_SDI12).
7 *
8 * SDI-12 is the 1200-baud single-wire ASCII bus used by environmental / agricultural sensors
9 * (soil moisture, water level, weather). A recorder addresses a sensor by a single character
10 * (0-9, A-Z, a-z) and sends `<addr><command>!`; the sensor replies `<addr>...<CR><LF>`. This
11 * codec builds the standard commands, parses the measurement response (`atttn`: seconds until
12 * ready + value count), splits the data values, and does the SDI-12 CRC (the `aMC!` / `aCC!`
13 * CRC-protected variants).
14 *
15 * The wire is a single 1200-baud 7E1 line with a 5 V break / marking convention; on an ESP32
16 * it needs a small level / direction circuit, and the UART transport is the application's.
17 * Pure codec, host-tested. Bridge a sensor string onto Wi-Fi by polling `aM!` / `aD0!` and
18 * publishing the values.
19 *
20 * @author Douglas Quigg (dstroy0)
21 * @date 2026
22 */
23
24#ifndef DETERMINISTICESPASYNCWEBSERVER_SDI12_H
25#define DETERMINISTICESPASYNCWEBSERVER_SDI12_H
26
27#include "ServerConfig.h"
28
29#if DETWS_ENABLE_SDI12
30
31#include <stddef.h>
32#include <stdint.h>
33
34#define SDI12_CRC_POLY 0xA001u ///< CRC-16 polynomial (reflected 0x8005), init 0x0000
35#define SDI12_CRC_CHARS 3 ///< the CRC is appended as 3 printable ASCII octets
36
37// --- command builders: write a NUL-terminated command into @p buf, return its length or 0 ---
38
39/** @brief Generic `<addr><body>!` command (body is the command letters, e.g. "M" or "D0"). */
40size_t sdi12_build(char *buf, size_t cap, char addr, const char *body);
41
42/** @brief Acknowledge-active command `a!`. */
43size_t sdi12_build_ack(char *buf, size_t cap, char addr);
44
45/** @brief Send-identification command `aI!`. */
46size_t sdi12_build_identify(char *buf, size_t cap, char addr);
47
48/** @brief Start-measurement command `aM!` (or `aMC!` when @p with_crc). */
49size_t sdi12_build_measure(char *buf, size_t cap, char addr, bool with_crc);
50
51/** @brief Concurrent-measurement command `aC!` (or `aCC!` when @p with_crc). */
52size_t sdi12_build_concurrent(char *buf, size_t cap, char addr, bool with_crc);
53
54/** @brief Send-data command `aD<n>!` (@p d_index 0..9). */
55size_t sdi12_build_data(char *buf, size_t cap, char addr, uint8_t d_index);
56
57/** @brief Change-address command `aA<b>!` (@p new_addr is the new sensor address). */
58size_t sdi12_build_change_address(char *buf, size_t cap, char addr, char new_addr);
59
60/** @brief Address-query command `?!` (asks the single connected sensor for its address). */
61size_t sdi12_build_query_address(char *buf, size_t cap);
62
63// --- response parsing ---
64
65/**
66 * @brief Parse a measurement response `atttn<CR><LF>`: @p ready_sec = seconds until the data is
67 * ready, @p num_values = how many values will be available. Works for both the 1-digit (`aM!`)
68 * and 2-digit (`aC!`) value-count forms. @p addr (optional) receives the echoed address.
69 */
70bool sdi12_parse_measure(const char *resp, size_t len, char *addr, uint16_t *ready_sec, uint8_t *num_values);
71
72/**
73 * @brief Split a data response `a<+/-value...><CR><LF>` into floats. Skips the leading address,
74 * decodes each sign-prefixed number, and stops at CR/LF or the buffer end. Returns the count
75 * via @p n (capped at @p max).
76 */
77bool sdi12_parse_values(const char *resp, size_t len, float *out, size_t max, size_t *n);
78
79// --- CRC (RFC-free SDI-12 16-bit CRC) ---
80
81/** @brief Compute the SDI-12 CRC-16 over @p data. */
82uint16_t sdi12_crc16(const uint8_t *data, size_t len);
83
84/** @brief Encode a CRC into its 3 printable ASCII octets (out[0..2]). */
85void sdi12_crc_encode(uint16_t crc, char out[SDI12_CRC_CHARS]);
86
87/**
88 * @brief Verify a CRC-protected response: the 3 octets before the trailing `<CR><LF>` must be
89 * the CRC of everything before them. @p len should include the data and the 3 CRC octets (the
90 * `<CR><LF>` may or may not be included).
91 */
92bool sdi12_check_crc(const char *resp, size_t len);
93
94#endif // DETWS_ENABLE_SDI12
95#endif // DETERMINISTICESPASYNCWEBSERVER_SDI12_H
User-facing configuration for DeterministicESPAsyncWebServer.