ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
ads1115.h
Go to the documentation of this file.
1// ProtoCore v1.0.16 - Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file ads1115.h
6 * @brief TI ADS1115 16-bit ADC codec (PROTOCORE_ENABLE_ADS1115).
7 *
8 * The ADS1115 is a 4-channel 16-bit analog-to-digital converter on the I2C bus with a
9 * programmable-gain amplifier - far more resolution and range control than the ESP32's own ADC.
10 * A reading is a 16-bit config-register write (start, channel, gain, mode, data rate) followed
11 * by a 16-bit read of the conversion register; the signed result scales to a voltage by the
12 * selected gain's full-scale range.
13 *
14 * This codec is pure and host-tested: ::protocore_ads1115_config_single builds the config word for a
15 * single-shot single-ended reading, and ::protocore_ads1115_raw_to_uv converts the signed sample to
16 * microvolts. On an ESP32 the binding writes the config, waits for the conversion, and reads it
17 * back over I2C (Wire); only that touches hardware.
18 *
19 * A cheap solder-and-bench-test breakout: measure a battery, a potentiometer, or an analog
20 * sensor and bridge the reading onto the network.
21 *
22 * @author Douglas Quigg (dstroy0)
23 * @date 2026
24 */
25
26#ifndef PROTOCORE_ADS1115_H
27#define PROTOCORE_ADS1115_H
28
29#include "protocore_config.h" // the entry point: protocore_types.h for the widths
30
31#if PROTOCORE_ENABLE_ADS1115
32
34
35// PROTOCORE_I2C_DEVICE_BORROW - the bytes this module runs out of - is stated in protocore_config.h, which sums
36// it into its arena. A caller takes them once and passes the pointer to every call. How they
37// are carved is this module's and is never named here.
38
39#define ADS1115_REG_CONVERSION 0x00 ///< conversion result register
40
41#define ADS1115_REG_CONFIG 0x01 ///< configuration register
42
43#define ADS1115_GAIN_TWOTHIRDS 0 ///< +/- 6.144 V
44
45#define ADS1115_GAIN_1 1 ///< +/- 4.096 V
46
47#define ADS1115_GAIN_2 2 ///< +/- 2.048 V (default)
48
49#define ADS1115_GAIN_4 3 ///< +/- 1.024 V
50
51#define ADS1115_GAIN_8 4 ///< +/- 0.512 V
52
53#define ADS1115_GAIN_16 5 ///< +/- 0.256 V
54
55#define ADS1115_DR_8 0 ///< 8 SPS
56
57#define ADS1115_DR_16 1 ///< 16 SPS
58
59#define ADS1115_DR_32 2 ///< 32 SPS
60
61#define ADS1115_DR_64 3 ///< 64 SPS
62
63#define ADS1115_DR_128 4 ///< 128 SPS (default)
64
65#define ADS1115_DR_250 5 ///< 250 SPS
66
67#define ADS1115_DR_475 6 ///< 475 SPS
68
69#define ADS1115_DR_860 7 ///< 860 SPS
70
71/** @brief What config_single takes: channel, gain, dr. */
72typedef struct
73{
74 uint8_t channel;
75 uint8_t gain;
76 uint8_t dr;
77} Ads1115ConfigSingleArgs;
78
79/** @brief What raw_to_uv takes: raw, gain. */
80typedef struct
81{
82 int16_t raw;
83 uint8_t gain;
84} Ads1115RawToUvArgs;
85
86/** @brief What begin takes: addr. */
87typedef struct
88{
89 uint8_t addr;
90} Ads1115BeginArgs;
91
92/** @brief What read_raw takes: channel, gain, raw. */
93typedef struct
94{
95 uint8_t channel;
96 uint8_t gain;
97 int16_t *raw;
98} Ads1115ReadRawArgs;
99
100/** @brief What read_uv takes: channel, gain, microvolts. */
101typedef struct
102{
103 uint8_t channel;
104 uint8_t gain;
105 int32_t *microvolts;
106} Ads1115ReadUvArgs;
107
108/**
109 * @brief TI ADS1115 16-bit ADC codec (PROTOCORE_ENABLE_ADS1115). The ADS1115 is a 4-channel 16-bit analog-to-digital
110 * ...
111 *
112 * A caller sets the members a call takes, invokes it through ::Ads1115 with the bytes it runs
113 * out of, and reads the outcome off the same handle.
114 *
115 * Ads1115.config_single_args.channel = ...;
116 * Ads1115.config_single_args.gain = ...;
117 * Ads1115.config_single_args.dr = ...;
118 * Ads1115.config_single(work);
119 * // Ads1115.word is what the call reports
120 *
121 * @var Ads1115Ns::config_single_args what config_single takes: channel, gain, dr
122 * @var Ads1115Ns::raw_to_uv_args what raw_to_uv takes: raw, gain
123 * @var Ads1115Ns::begin_args what begin takes: addr
124 * @var Ads1115Ns::read_raw_args what read_raw takes: channel, gain, raw
125 * @var Ads1115Ns::read_uv_args what read_uv takes: channel, gain, microvolts
126 * @var Ads1115Ns::ok a call's true/false outcome
127 * @var Ads1115Ns::word what a call reports
128 * @var Ads1115Ns::uv what a call reports
129 * @var Ads1115Ns::config_single build the 16-bit config word for a single-shot, single-ended ...
130 * @var Ads1115Ns::raw_to_uv convert a signed 16-bit sample to microvolts for gain's full-scale ...
131 * @var Ads1115Ns::begin initialize the I2C bus for the ADS1115 at addr. true on ESP32
132 * @var Ads1115Ns::read_raw single-shot read of channel (0..3) at gain into raw. false on error
133 * @var Ads1115Ns::read_uv single-shot read of channel at gain, converted to microvolts in ...
134 *
135 * @c work is PROTOCORE_I2C_DEVICE_BORROW bytes the CALLER took, at an address it knows. It is not held past the call,
136 * so nothing here aliases it. How those bytes are carved is this module's and is never named here.
137 */
138typedef struct
139{
140 Ads1115ConfigSingleArgs config_single_args;
141 Ads1115RawToUvArgs raw_to_uv_args;
142 Ads1115BeginArgs begin_args;
143 Ads1115ReadRawArgs read_raw_args;
144 Ads1115ReadUvArgs read_uv_args;
145 proto_bool ok;
146 uint16_t word;
147 int32_t uv;
148} Ads1115Vars;
149
150/** @brief The operands and the outcome. */
151extern Ads1115Vars Ads1115V;
152
153/** @brief The entries. */
154typedef struct
155{
156 void (*const config_single)(uint8_t *work);
157 void (*const raw_to_uv)(uint8_t *work);
158 void (*const begin)(uint8_t *work);
159 void (*const read_raw)(uint8_t *work);
160 void (*const read_uv)(uint8_t *work);
161} Ads1115Ns;
162
163// What the table binds, defined once in the .c and taking one parameter each: everything
164// else an entry needs is an operand in Ads1115V or a region of the borrow at a fixed offset.
165void protocore_ads1115_config_single(uint8_t *work);
166void protocore_ads1115_raw_to_uv(uint8_t *work);
167void protocore_ads1115_begin(uint8_t *work);
168void protocore_ads1115_read_raw(uint8_t *work);
169void protocore_ads1115_read_uv(uint8_t *work);
170
171// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
172// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
173// `Ads1115.config_single(work)` resolves to a named function and becomes a DIRECT call. An extern table
174// leaves the call indirect and the symbol live at every level, -O2 -flto included.
175static const Ads1115Ns Ads1115 __attribute__((unused)) = {
176 .config_single = protocore_ads1115_config_single,
177 .raw_to_uv = protocore_ads1115_raw_to_uv,
178 .begin = protocore_ads1115_begin,
179 .read_raw = protocore_ads1115_read_raw,
180 .read_uv = protocore_ads1115_read_uv,
181};
182
183/**
184 * @brief The PROTOCORE_I2C_DEVICE_BORROW bytes this module's state lives in.
185 *
186 * Stated beside the namespace rather than on it: an entry takes a borrow, and this is where
187 * that borrow comes from. Taken once from the end of the pool, which no mark and no release
188 * walks, so the state lasts the life of the program.
189 *
190 * @return the span.
191 */
192uint8_t *protocore_ads1115_span(void);
193
195
196#endif // PROTOCORE_ENABLE_ADS1115
197
198#endif // PROTOCORE_ADS1115_H
#define PROTOCORE_BEGIN_DECLS
Give a header's declarations C linkage, so their symbol names carry no parameter types.
Definition types.h:96
_Bool proto_bool
The truth value.
Definition types.h:64
#define PROTOCORE_END_DECLS
Definition types.h:97