forked from adafruit/Adafruit_Wippersnapper_Arduino
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathWippersnapper_AnalogIO.cpp
408 lines (370 loc) · 14.5 KB
/
Wippersnapper_AnalogIO.cpp
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
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
/*!
* @file Wippersnapper_AnalogIO.cpp
*
* This file provides an API for interacting with
* a board's analog IO pins.
*
* Adafruit invests time and resources providing this open source code,
* please support Adafruit and open-source hardware by purchasing
* products from Adafruit!
*
* Copyright (c) Brent Rubell 2020-2023 for Adafruit Industries.
*
* BSD license, all text here must be included in any redistribution.
*
*/
#include "Wippersnapper_AnalogIO.h"
/***********************************************************************************/
/*!
@brief Initializes Analog IO class.
@param totalAnalogInputPins
Total number of analog input pins to allocate.
@param aRef
ADC's voltage reference value, in volts.
*/
/***********************************************************************************/
Wippersnapper_AnalogIO::Wippersnapper_AnalogIO(int32_t totalAnalogInputPins,
float aRef) {
_totalAnalogInputPins = totalAnalogInputPins;
// Set aref
setAref(aRef);
// Set ADC resolution, default to 16-bit
setADCResolution(16);
// allocate analog input pins
_analog_input_pins = new analogInputPin[_totalAnalogInputPins];
// TODO: Refactor this to use list-based initialization
for (int pin = 0; pin < _totalAnalogInputPins; pin++) {
// turn sampling off
_analog_input_pins[pin].enabled = false;
}
}
/***********************************************************************************/
/*!
@brief Destructor for Analog IO class.
*/
/***********************************************************************************/
Wippersnapper_AnalogIO::~Wippersnapper_AnalogIO() {
_aRef = 0.0;
_totalAnalogInputPins = 0;
delete _analog_input_pins;
}
/***********************************************************************************/
/*!
@brief Sets the device's reference voltage.
@param refVoltage
The voltage reference to use during conversions.
*/
/***********************************************************************************/
void Wippersnapper_AnalogIO::setAref(float refVoltage) { _aRef = refVoltage; }
/***********************************************************************************/
/*!
@brief Returns the device's reference voltage.
@returns Analog reference voltage, in volts.
*/
/***********************************************************************************/
float Wippersnapper_AnalogIO::getAref() { return _aRef; }
/***********************************************************************************/
/*!
@brief Sets the device's ADC resolution, either natively via calling
Arduino API's analogReadResolution() or via scaling.
@param resolution
The desired analog resolution, in bits.
*/
/***********************************************************************************/
void Wippersnapper_AnalogIO::setADCResolution(int resolution) {
// set the resolution natively in the BSP
#if defined(ARDUINO_ARCH_SAMD)
analogReadResolution(16);
_nativeResolution = 12;
#elif defined(ARDUINO_ARCH_ESP32)
scaleAnalogRead = false; // probably should be false, handled in bsp
#if defined(ESP32S3)
_nativeResolution = 13; // S3 ADC is 13-bit, others are 12-bit
#else
_nativeResolution = 12;
#endif
#elif defined(ARDUINO_ARCH_RP2040)
scaleAnalogRead = true;
_nativeResolution = 10;
#else
scaleAnalogRead = true;
_nativeResolution = 10;
#endif
_adcResolution = resolution;
}
/***********************************************************************************/
/*!
@brief Gets the scaled ADC resolution.
@returns resolution
The scaled analog resolution, in bits.
*/
/***********************************************************************************/
int Wippersnapper_AnalogIO::getADCresolution() { return _adcResolution; }
/***********************************************************************************/
/*!
@brief Gets the device's native ADC resolution.
@returns resolution
The native analog resolution, in bits.
*/
/***********************************************************************************/
int Wippersnapper_AnalogIO::getNativeResolution() { return _nativeResolution; }
/***********************************************************************************/
/*!
@brief Initializes an analog input pin
@param pin
The analog pin to read from.
@param period
Time between measurements, in seconds.
@param pullMode
The pin's pull value.
@param analogReadMode
Defines if pin will read and return an ADC value or a voltage value.
*/
/***********************************************************************************/
void Wippersnapper_AnalogIO::initAnalogInputPin(
int pin, float period,
wippersnapper_pin_v1_ConfigurePinRequest_Pull pullMode,
wippersnapper_pin_v1_ConfigurePinRequest_AnalogReadMode analogReadMode) {
// Set analog read pull mode
if (pullMode == wippersnapper_pin_v1_ConfigurePinRequest_Pull_PULL_UP)
pinMode(pin, INPUT_PULLUP);
else
pinMode(pin, INPUT);
// Period is in seconds, cast it to long and convert it to milliseconds
long periodMs = (long)period * 1000;
// TODO: Maybe pull this out into a func. or use map() lookup instead
// attempt to allocate pin within _analog_input_pins[]
for (int i = 0; i < _totalAnalogInputPins; i++) {
if (_analog_input_pins[i].enabled == false) {
_analog_input_pins[i].pinName = pin;
_analog_input_pins[i].period = periodMs;
_analog_input_pins[i].prvPeriod = 0L;
_analog_input_pins[i].readMode = analogReadMode;
_analog_input_pins[i].enabled = true;
break;
}
}
WS_DEBUG_PRINT("Configured Analog Input pin with polling time (ms):");
WS_DEBUG_PRINTLN(periodMs);
}
/***********************************************************************************/
/*!
@brief Disables an analog input pin from sampling
@param pin
The analog input pin to disable.
*/
/***********************************************************************************/
void Wippersnapper_AnalogIO::disableAnalogInPin(int pin) {
for (int i = 0; i < _totalAnalogInputPins; i++) {
if (_analog_input_pins[i].pinName == pin) {
_analog_input_pins[i].enabled = false;
break;
}
}
}
/***********************************************************************************/
/*!
@brief Deinitializes an analog pin.
@param direction
The analog pin's direction.
@param pin
The analog pin to deinitialize.
*/
/***********************************************************************************/
void Wippersnapper_AnalogIO::deinitAnalogPin(
wippersnapper_pin_v1_ConfigurePinRequest_Direction direction, int pin) {
WS_DEBUG_PRINT("Deinitializing analog pin A");
WS_DEBUG_PRINTLN(pin);
if (direction ==
wippersnapper_pin_v1_ConfigurePinRequest_Direction_DIRECTION_INPUT) {
WS_DEBUG_PRINTLN("Deinitialized analog input pin obj.");
disableAnalogInPin(pin);
}
pinMode(pin, INPUT); // hi-z
}
/**********************************************************/
/*!
@brief Reads the raw ADC value of an analog pin.
Value is always scaled to 16-bit.
@param pin
The pin to be read.
@returns The pin's ADC value.
*/
/**********************************************************/
uint16_t Wippersnapper_AnalogIO::getPinValue(int pin) {
// get pin value
uint16_t value = analogRead(pin);
// scale by the ADC resolution manually if not implemented by BSP
if (scaleAnalogRead) {
if (getADCresolution() > getNativeResolution()) {
value = value << (getADCresolution() - getNativeResolution());
} else {
value = value >> (getNativeResolution() - getADCresolution());
}
}
return value;
}
/**********************************************************/
/*!
@brief Calculates analog pin's voltage provided
a 16-bit ADC value.
@param pin
The value from a previous ADC reading.
@returns The pin's voltage.
*/
/**********************************************************/
float Wippersnapper_AnalogIO::getPinValueVolts(int pin) {
#ifdef ARDUINO_ARCH_ESP32
return analogReadMilliVolts(pin) / 1000.0;
#else
uint16_t rawValue = getPinValue(pin);
return rawValue * getAref() / 65536;
#endif
}
/******************************************************************/
/*!
@brief Encodes an analog input pin event into a
signal message and publish it to IO.
@param pinName
Specifies the pin's name.
@param readMode
Read mode - raw ADC or voltage.
@param pinValRaw
Raw pin value, used if readmode is raw.
@param pinValVolts
Raw pin value expressed in Volts, used if readmode is
volts.
@returns True if successfully encoded a PinEvent signal
message, False otherwise.
*/
/******************************************************************/
bool Wippersnapper_AnalogIO::encodePinEvent(
uint8_t pinName,
wippersnapper_pin_v1_ConfigurePinRequest_AnalogReadMode readMode,
uint16_t pinValRaw, float pinValVolts) {
// Create new signal message
wippersnapper_signal_v1_CreateSignalRequest outgoingSignalMsg =
wippersnapper_signal_v1_CreateSignalRequest_init_zero;
// Fill payload
outgoingSignalMsg.which_payload =
wippersnapper_signal_v1_CreateSignalRequest_pin_event_tag;
sprintf(outgoingSignalMsg.payload.pin_event.pin_name, "A%d", pinName);
// Fill pinValue based on the analog read mode
char buffer[100];
if (readMode ==
wippersnapper_pin_v1_ConfigurePinRequest_AnalogReadMode_ANALOG_READ_MODE_PIN_VALUE) {
sprintf(outgoingSignalMsg.payload.pin_event.pin_value, "%u", pinValRaw);
snprintf(buffer, 100, "[Pin] A%d read: %u\n", pinName, pinValRaw);
} else {
sprintf(outgoingSignalMsg.payload.pin_event.pin_value, "%0.3f",
pinValVolts);
snprintf(buffer, 100, "[Pin] A%d read: %0.2f\n", pinName, pinValVolts);
}
// display analog pin read on terminal
#ifdef USE_DISPLAY
WS._ui_helper->add_text_to_terminal(buffer);
#endif
// Encode signal message
pb_ostream_t stream =
pb_ostream_from_buffer(WS._buffer_outgoing, sizeof(WS._buffer_outgoing));
if (!ws_pb_encode(&stream, wippersnapper_signal_v1_CreateSignalRequest_fields,
&outgoingSignalMsg)) {
WS_DEBUG_PRINTLN("ERROR: Unable to encode signal message");
return false;
}
// Publish out to IO
size_t msgSz;
pb_get_encoded_size(&msgSz,
wippersnapper_signal_v1_CreateSignalRequest_fields,
&outgoingSignalMsg);
WS_DEBUG_PRINT("Publishing pinEvent...");
WS.publish(WS._topic_signal_device, WS._buffer_outgoing, msgSz, 1);
WS_DEBUG_PRINTLN("Published!");
return true;
}
/**********************************************************/
/*!
@brief Checks if pin's period is expired.
@param currentTime
The current software timer value.
@param pin
The desired analog pin to check
@returns True if pin's period expired, False otherwise.
*/
/**********************************************************/
bool Wippersnapper_AnalogIO::timerExpired(long currentTime,
analogInputPin pin) {
if (currentTime - pin.prvPeriod > pin.period && pin.period != 0L)
return true;
return false;
}
/**********************************************************/
/*!
@brief Iterates thru analog inputs
*/
/**********************************************************/
void Wippersnapper_AnalogIO::update() {
// TODO: Globally scope these, dont have them here every time
float pinValVolts = 0.0;
uint16_t pinValRaw = 0;
// Process analog input pins
for (int i = 0; i < _totalAnalogInputPins; i++) {
// TODO: Can we collapse the conditionals below?
if (_analog_input_pins[i].enabled == true) {
// Does the pin execute on-period?
if ((long)millis() - _analog_input_pins[i].prvPeriod >
_analog_input_pins[i].period &&
_analog_input_pins[i].period != 0L) {
WS_DEBUG_PRINT("Executing periodic event on A");
WS_DEBUG_PRINTLN(_analog_input_pins[i].pinName);
// Read from analog pin
if (_analog_input_pins[i].readMode ==
wippersnapper_pin_v1_ConfigurePinRequest_AnalogReadMode_ANALOG_READ_MODE_PIN_VOLTAGE) {
pinValVolts = getPinValueVolts(_analog_input_pins[i].pinName);
} else if (
_analog_input_pins[i].readMode ==
wippersnapper_pin_v1_ConfigurePinRequest_AnalogReadMode_ANALOG_READ_MODE_PIN_VALUE) {
pinValRaw = getPinValue(_analog_input_pins[i].pinName);
} else {
WS_DEBUG_PRINTLN("ERROR: Unable to read pin value, cannot determine "
"analog read mode!");
pinValRaw = 0.0;
}
// Publish a new pin event
encodePinEvent(_analog_input_pins[i].pinName,
_analog_input_pins[i].readMode, pinValRaw, pinValVolts);
// IMPT - reset the digital pin
_analog_input_pins[i].prvPeriod = millis();
}
// Does the pin execute on_change?
else if (_analog_input_pins[i].period == 0L) {
// note: on-change requires ADC DEFAULT_HYSTERISIS to check against prv
// pin value
uint16_t pinValRaw = getPinValue(_analog_input_pins[i].pinName);
uint16_t _pinValThreshHi =
_analog_input_pins[i].prvPinVal +
(_analog_input_pins[i].prvPinVal * DEFAULT_HYSTERISIS);
uint16_t _pinValThreshLow =
_analog_input_pins[i].prvPinVal -
(_analog_input_pins[i].prvPinVal * DEFAULT_HYSTERISIS);
if (pinValRaw > _pinValThreshHi || pinValRaw < _pinValThreshLow) {
// Perform voltage conversion if we need to
if (_analog_input_pins[i].readMode ==
wippersnapper_pin_v1_ConfigurePinRequest_AnalogReadMode_ANALOG_READ_MODE_PIN_VOLTAGE) {
pinValVolts = getPinValueVolts(_analog_input_pins[i].pinName);
}
// Publish pin event to IO
encodePinEvent(_analog_input_pins[i].pinName,
_analog_input_pins[i].readMode, pinValRaw,
pinValVolts);
} else {
// WS_DEBUG_PRINTLN("ADC has not changed enough, continue...");
continue;
}
// set the pin value in the digital pin object for comparison on next
// run
_analog_input_pins[i].prvPinVal = pinValRaw;
}
}
}
}