forked from arendst/Tasmota
-
Notifications
You must be signed in to change notification settings - Fork 20
Expand file tree
/
Copy pathads1115.tc
More file actions
222 lines (196 loc) · 6.56 KB
/
Copy pathads1115.tc
File metadata and controls
222 lines (196 loc) · 6.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
// ADS1115 16-bit 4-Channel ADC Driver
// I2C addresses: 0x48 (ADDR=GND), 0x49 (ADDR=VDD), 0x4A (ADDR=SDA), 0x4B (ADDR=SCL)
// Single-ended mode: reads AIN0..AIN3 vs GND
// Configurable PGA gain, single-shot conversion
// Displays voltage on web UI + JSON teleperiod + 6h chart
#define ADS_ADDR1 0x48
#define ADS_ADDR2 0x4B
// PGA gain settings (config bits 11-9)
// Change ADS_PGA to match your voltage range
#define PGA_6144 0 // ±6.144V (LSB = 187.5µV)
#define PGA_4096 1 // ±4.096V (LSB = 125µV)
#define PGA_2048 2 // ±2.048V (LSB = 62.5µV) — default
#define PGA_1024 3 // ±1.024V (LSB = 31.25µV)
#define PGA_0512 4 // ±0.512V (LSB = 15.625µV)
#define PGA_0256 5 // ±0.256V (LSB = 7.8125µV)
// *** Configuration — adjust to your needs ***
#define ADS_PGA PGA_4096 // gain setting
#define ADS_CHANNELS 4 // number of channels to read (1..4)
// LSB voltage in µV for each PGA setting (index = PGA value)
// We store as int µV to avoid float array init issues
// PGA_6144=187500, PGA_4096=125000, PGA_2048=62500, PGA_1024=31250, PGA_0512=15625, PGA_0256=7813
int ads_addr = 0;
int ads_bus = 0;
int ads_ok = 0;
// Raw ADC values and voltages per channel
int ads_raw[4];
float ads_volt[4];
// I2C buffer for 2-byte reads/writes
char ads_buf[4];
// Chart history (6h at 1/min = 360)
#define CHART_LEN 360
float hist_ch0[CHART_LEN];
float hist_ch1[CHART_LEN];
float hist_ch2[CHART_LEN];
float hist_ch3[CHART_LEN];
int hist_pos;
int hist_tick;
// Get LSB size in µV for current PGA setting
float ads_get_lsb() {
if (ADS_PGA == 0) return 187.5;
if (ADS_PGA == 1) return 125.0;
if (ADS_PGA == 2) return 62.5;
if (ADS_PGA == 3) return 31.25;
if (ADS_PGA == 4) return 15.625;
return 7.8125;
}
// Sign-extend 16-bit to signed int
int sign16(int val) {
if (val >= 32768) return val - 65536;
return val;
}
// Scan for ADS1115 on both buses
int ads_scan() {
int bus = 0;
while (bus < 2) {
int addr = ADS_ADDR1;
while (addr <= ADS_ADDR2) {
if (i2cSetDevice(addr, bus)) {
// Read config register — default after reset is 0x8583
if (i2cRead(addr, 0x01, ads_buf, 2, bus)) {
int cfg = (ads_buf[0] << 8) | ads_buf[1];
// Check bits that should be default: DR=100 (128SPS), MODE=1
// Default config = 0x8583, but just check device responds
if (cfg != 0 && cfg != 0xFFFF) {
ads_addr = addr;
ads_bus = bus;
i2cSetActiveFound(ads_addr, "ADS1115", ads_bus);
return 1;
}
}
}
addr++;
}
bus++;
}
return 0;
}
// Read one channel (0-3) in single-shot mode, returns raw signed value
int ads_read_channel(int ch) {
// Config register: OS=1 (start), MUX=1xx (single-ended AINx), PGA, MODE=1 (single-shot)
// DR=100 (128SPS), COMP_QUE=11 (disable comparator)
// Byte 0 (high): OS[15] MUX[14:12] PGA[11:9] MODE[8]
// OS=1, MUX = 100 + ch, PGA = ADS_PGA, MODE = 1
// Byte 1 (low): DR[7:5] COMP_MODE[4] COMP_POL[3] COMP_LAT[2] COMP_QUE[1:0]
// DR=100 (128SPS), rest=0, COMP_QUE=11
// = 10000011 = 0x83
int mux = 4 + ch; // 100=AIN0, 101=AIN1, 110=AIN2, 111=AIN3
int hi = 0x80 | (mux << 4) | (ADS_PGA << 1) | 0x01;
int lo = 0x83; // 128 SPS, comparator disabled
// Write config register
ads_buf[0] = hi;
ads_buf[1] = lo;
if (!i2cWrite(ads_addr, 0x01, ads_buf, 2, ads_bus)) return 0;
// Wait for conversion (128 SPS = ~8ms, use 10ms margin)
delay(10);
// Read conversion register (2 bytes, MSB first)
if (!i2cRead(ads_addr, 0x00, ads_buf, 2, ads_bus)) return 0;
int raw = (ads_buf[0] << 8) | ads_buf[1];
return sign16(raw);
}
void EverySecond() {
if (!ads_addr) {
if (!ads_scan()) { ads_ok = 0; return; }
}
float lsb = ads_get_lsb();
int ch = 0;
while (ch < ADS_CHANNELS) {
ads_raw[ch] = ads_read_channel(ch);
// Convert to volts: raw * lsb_µV / 1000000
ads_volt[ch] = (float)ads_raw[ch] * lsb / 1000000.0;
ch++;
}
ads_ok = 1;
// Chart history every minute
hist_tick++;
if (hist_tick >= 60) {
hist_tick = 0;
int p = hist_pos % CHART_LEN;
hist_ch0[p] = ads_volt[0];
if (ADS_CHANNELS > 1) hist_ch1[p] = ads_volt[1];
if (ADS_CHANNELS > 2) hist_ch2[p] = ads_volt[2];
if (ADS_CHANNELS > 3) hist_ch3[p] = ads_volt[3];
hist_pos++;
}
}
void WebPage() {
int n = hist_pos;
if (n > CHART_LEN) n = CHART_LEN;
if (n > 0) {
// ottelo's chart-centering compensation (margin-left:-30px wrapper)
webSend("<div style='margin-left:-30px'>");
WebChart('l', "ADS1115 Voltages", "AIN0 (V)", 0xe74c3c, hist_pos, CHART_LEN, hist_ch0, 1, 0, 0, 0);
if (ADS_CHANNELS > 1) {
WebChart('l', "", "AIN1 (V)", 0x3498db, hist_pos, CHART_LEN, hist_ch1, 1, 0, 0, 0);
}
if (ADS_CHANNELS > 2) {
WebChart('l', "", "AIN2 (V)", 0x27ae60, hist_pos, CHART_LEN, hist_ch2, 1, 0, 0, 0);
}
if (ADS_CHANNELS > 3) {
WebChart('l', "", "AIN3 (V)", 0x9b59b6, hist_pos, CHART_LEN, hist_ch3, 1, 0, 0, 0);
}
webSend("</div>");
}
}
void WebCall() {
char buf[80];
if (ads_ok) {
int ch = 0;
while (ch < ADS_CHANNELS) {
sprintf(buf, "{s}ADS1115 AIN%d{m}", ch);
webSend(buf);
sprintf(buf, "%.4f V{e}", ads_volt[ch]);
webSend(buf);
ch++;
}
} else {
webSend("{s}ADS1115{m}not found{e}");
}
}
void JsonCall() {
if (!ads_ok) return;
char buf[64];
responseAppend(",\"ADS1115\":{");
int ch = 0;
while (ch < ADS_CHANNELS) {
if (ch > 0) responseAppend(",");
sprintf(buf, "\"A%d\":", ch);
responseAppend(buf);
sprintf(buf, "%.4f", ads_volt[ch]);
responseAppend(buf);
ch++;
}
responseAppend("}");
}
void OnExit() {
if (ads_addr) {
I2cResetActive(ads_addr, ads_bus);
ads_addr = 0;
}
}
int main() {
char buf[64];
ads_ok = 0;
ads_addr = 0;
hist_pos = 0;
hist_tick = 0;
if (ads_scan()) {
sprintf(buf, "ADS1115 found at 0x%x on bus %d", ads_addr, ads_bus);
addLog(buf);
sprintf(buf, "PGA gain: %d, channels: %d", ADS_PGA, ADS_CHANNELS);
addLog(buf);
} else {
addLog("ADS1115 not found on any bus");
}
return 0;
}