DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
ads1115.cpp
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 ads1115.cpp
6 * @brief TI ADS1115 16-bit ADC codec - implementation. See ads1115.h.
7 */
8
10#include "ServerConfig.h"
11
12#if DETWS_ENABLE_ADS1115
13
14namespace
15{
16// Config-register field values (per the ADS1115 datasheet).
17const uint16_t OS_SINGLE = 0x8000; // start a single conversion
18const uint16_t MUX_SINGLE0 = 0x4000; // single-ended AIN0; AINx = this | (channel << 12)
19const uint16_t MODE_SINGLE = 0x0100; // single-shot
20const uint16_t COMP_DISABLE = 0x0003;
21
22// PGA bits and the matching full-scale range in microvolts, indexed by gain code.
23const uint16_t PGA_BITS[6] = {0x0000, 0x0200, 0x0400, 0x0600, 0x0800, 0x0A00};
24const int32_t FSR_UV[6] = {6144000, 4096000, 2048000, 1024000, 512000, 256000};
25} // namespace
26
27uint16_t ads1115_config_single(uint8_t channel, uint8_t gain, uint8_t dr)
28{
29 if (channel > 3)
30 channel = 0;
32 gain = (uint8_t)DETWS_ADS1115_GAIN;
34 dr = (uint8_t)DETWS_ADS1115_DR;
35 uint16_t cfg = OS_SINGLE;
36#if DETWS_ADS1115_DIFFERENTIAL
37 cfg |= (uint16_t)((uint16_t)channel << 12); // differential pair: MUX 0=AIN0-1, 1=AIN0-3, 2=AIN1-3, 3=AIN2-3
38#else
39 cfg |= (uint16_t)(MUX_SINGLE0 | ((uint16_t)channel << 12)); // single-ended AINx
40#endif
41 cfg |= PGA_BITS[gain];
42 cfg |= MODE_SINGLE;
43 cfg |= (uint16_t)((uint16_t)dr << 5); // data-rate bits [7:5]
44 cfg |= COMP_DISABLE;
45 return cfg;
46}
47
48int32_t ads1115_raw_to_uv(int16_t raw, uint8_t gain)
49{
51 gain = (uint8_t)DETWS_ADS1115_GAIN;
52 return (int32_t)((int64_t)raw * FSR_UV[gain] / 32768);
53}
54
55// ---------------------------------------------------------------------------
56// I2C binding
57// ---------------------------------------------------------------------------
58
59#if defined(ARDUINO)
60
61#include "services/i2c.h"
62#include <Arduino.h>
63#include <Wire.h>
64
65namespace
66{
67// All ADS1115 I2C-binding state, owned by one instance (internal linkage): the device
68// address, so it is one named owner, unreachable from any other translation unit.
69struct Ads1115Ctx
70{
71 uint8_t addr = DETWS_ADS1115_I2C_ADDR;
72};
73Ads1115Ctx s_ads;
74
75bool wr16(uint8_t reg, uint16_t v)
76{
77 Wire.beginTransmission(s_ads.addr);
78 Wire.write(reg);
79 Wire.write((uint8_t)(v >> 8)); // ADS1115 registers are big-endian
80 Wire.write((uint8_t)(v & 0xFF));
81 return Wire.endTransmission() == 0;
82}
83
84bool rd16(uint8_t reg, uint16_t *v)
85{
86 Wire.beginTransmission(s_ads.addr);
87 Wire.write(reg);
88 if (Wire.endTransmission(false) != 0)
89 return false;
90 if (Wire.requestFrom((int)s_ads.addr, 2) != 2)
91 return false;
92 uint8_t hi = (uint8_t)Wire.read();
93 uint8_t lo = (uint8_t)Wire.read();
94 *v = (uint16_t)(((uint16_t)hi << 8) | lo);
95 return true;
96}
97} // namespace
98
99bool ads1115_begin(uint8_t addr)
100{
101 s_ads.addr = addr ? addr : (uint8_t)DETWS_ADS1115_I2C_ADDR;
103 return true;
104}
105
106bool ads1115_read_raw(uint8_t channel, uint8_t gain, int16_t *raw)
107{
108 if (!raw)
109 return false;
110 uint8_t dr = (uint8_t)DETWS_ADS1115_DR;
113 if (!wr16(ADS1115_REG_CONFIG, ads1115_config_single(channel, gain, dr)))
114 return false;
115 // Single-shot conversion time tracks the data rate (~1000/SPS ms); wait it out plus a 1 ms margin.
116 static const uint16_t ads1115_sps[8] = {8, 16, 32, 64, 128, 250, 475, 860};
117 delay(1000u / ads1115_sps[dr] + 1);
118 uint16_t v = 0;
119 if (!rd16(ADS1115_REG_CONVERSION, &v))
120 return false;
121 *raw = (int16_t)v;
122 return true;
123}
124
125bool ads1115_read_uv(uint8_t channel, uint8_t gain, int32_t *microvolts)
126{
127 int16_t raw = 0;
128 if (!ads1115_read_raw(channel, gain, &raw))
129 return false;
130 if (microvolts)
131 *microvolts = ads1115_raw_to_uv(raw, gain);
132 return true;
133}
134
135#else // host build: no I2C. The config encoder + conversion above are host-tested.
136
137bool ads1115_begin(uint8_t)
138{
139 return false;
140}
141bool ads1115_read_raw(uint8_t, uint8_t, int16_t *)
142{
143 return false;
144}
145bool ads1115_read_uv(uint8_t, uint8_t, int32_t *)
146{
147 return false;
148}
149
150#endif // ARDUINO
151
152#endif // DETWS_ENABLE_ADS1115
User-facing configuration for DeterministicESPAsyncWebServer.
#define DETWS_ADS1115_I2C_ADDR
I2C address of the ADS1115 (0x48 with ADDR to GND; 0x49/0x4A/0x4B for VDD/SDA/SCL).
#define DETWS_ADS1115_DR
Default ADS1115 data-rate code (ADS1115_DR_*): 0=8, 1=16, 2=32, 3=64, 4=128 (default),...
#define DETWS_ADS1115_GAIN
Default ADS1115 PGA gain code (ADS1115_GAIN_*): 0=+/-6.144V, 1=+/-4.096V, 2=+/-2.048V (default),...
TI ADS1115 16-bit ADC codec (DETWS_ENABLE_ADS1115).
uint16_t ads1115_config_single(uint8_t channel, uint8_t gain, uint8_t dr)
Build the 16-bit config word for a single-shot, single-ended reading of channel (0....
bool ads1115_begin(uint8_t addr)
Initialize the I2C bus for the ADS1115 at addr.
#define ADS1115_REG_CONVERSION
conversion result register
Definition ads1115.h:31
int32_t ads1115_raw_to_uv(int16_t raw, uint8_t gain)
Convert a signed 16-bit sample to microvolts for gain's full-scale range.
#define ADS1115_REG_CONFIG
configuration register
Definition ads1115.h:32
bool ads1115_read_raw(uint8_t channel, uint8_t gain, int16_t *raw)
Single-shot read of channel (0..3) at gain into raw.
bool ads1115_read_uv(uint8_t channel, uint8_t gain, int32_t *microvolts)
Single-shot read of channel at gain, converted to microvolts in microvolts.
The one owner of the shared I2C bus bring-up for the peripheral drivers.
void detws_i2c_begin()
Bring up the shared I2C bus on DETWS_I2C_SDA_PIN / DETWS_I2C_SCL_PIN (-1 = default).
Definition i2c.h:33
static constexpr uint8_t ADS1115_DR_128
128 SPS (default)
Definition ads1115.h:53
static constexpr uint8_t ADS1115_DR_860
860 SPS
Definition ads1115.h:56
static constexpr uint8_t ADS1115_GAIN_16
+/- 0.256 V
Definition ads1115.h:43