1
+ /* !
2
+ * @file WipperSnapper_I2C_Driver_MLX90632D.h
3
+ *
4
+ * Device driver for a Melexis MLX90632-D (medical) thermal FIR sensor.
5
+ *
6
+ * Adafruit invests time and resources providing this open source code,
7
+ * please support Adafruit and open-source hardware by purchasing
8
+ * products from Adafruit!
9
+ *
10
+ * Copyright (c) Tyeth Gundry 2025 for Adafruit Industries.
11
+ *
12
+ * MIT license, all text here must be included in any redistribution.
13
+ *
14
+ */
15
+
16
+ #ifndef WipperSnapper_I2C_Driver_MLX90632D_H
17
+ #define WipperSnapper_I2C_Driver_MLX90632D_H
18
+
19
+ #include < Adafruit_MLX90632.h>
20
+
21
+ #include " WipperSnapper_I2C_Driver.h"
22
+
23
+ /* *************************************************************************/
24
+ /* !
25
+ @brief Sensor driver for the Melexis MLX90632-D temperature sensor.
26
+ */
27
+ /* *************************************************************************/
28
+ class WipperSnapper_I2C_Driver_MLX90632D : public WipperSnapper_I2C_Driver {
29
+ public:
30
+ /* ******************************************************************************/
31
+ /* !
32
+ @brief Constructor for an MLX90632 sensor.
33
+ @param i2c
34
+ The I2C interface.
35
+ @param sensorAddress
36
+ 7-bit device address.
37
+ */
38
+ /* ******************************************************************************/
39
+ WipperSnapper_I2C_Driver_MLX90632D (TwoWire *i2c, uint16_t sensorAddress)
40
+ : WipperSnapper_I2C_Driver(i2c, sensorAddress) {
41
+ _i2c = i2c;
42
+ _sensorAddress = sensorAddress;
43
+ _mlx90632 = nullptr ;
44
+ _deviceTemp = NAN;
45
+ _objectTemp = NAN;
46
+ _lastRead = 0 ;
47
+ }
48
+
49
+ /* ******************************************************************************/
50
+ /* !
51
+ @brief Destructor for an MLX90632 sensor.
52
+ */
53
+ /* ******************************************************************************/
54
+ ~WipperSnapper_I2C_Driver_MLX90632D () {
55
+ if (_mlx90632) {
56
+ delete _mlx90632;
57
+ _mlx90632 = nullptr ;
58
+ }
59
+ }
60
+
61
+ /* ******************************************************************************/
62
+ /* !
63
+ @brief Initializes the MLX90632 sensor and begins I2C.
64
+ @returns True if initialized successfully, False otherwise.
65
+ */
66
+ /* ******************************************************************************/
67
+ bool begin () {
68
+ if (_mlx90632) {
69
+ delete _mlx90632;
70
+ _mlx90632 = nullptr ;
71
+ }
72
+ _mlx90632 = new Adafruit_MLX90632 ();
73
+ // attempt to initialize MLX90632
74
+ if (!_mlx90632->begin (_sensorAddress, _i2c))
75
+ return false ;
76
+
77
+ return ConfigureAndPrintSensorInfo ();
78
+ }
79
+
80
+ /* ******************************************************************************/
81
+ /* !
82
+ @brief Configures the MLX90632 sensor and prints its information.
83
+ @param extendedInsteadOfMedicalRange
84
+ If true, configures the sensor for extended temperature
85
+ range/acc.
86
+ @returns True if configuration fetching and setting were successful.
87
+ */
88
+ /* ******************************************************************************/
89
+ bool ConfigureAndPrintSensorInfo (bool extendedInsteadOfMedicalRange = false ) {
90
+ // Reset the device
91
+ if (!_mlx90632->reset ()) {
92
+ WS_DEBUG_PRINTLN (F (" Device reset failed" ));
93
+ return false ;
94
+ }
95
+
96
+ uint16_t productCode = _mlx90632->getProductCode ();
97
+ // Decode product code bits
98
+ uint8_t fov = (productCode >> 8 ) & 0x3 ;
99
+ uint8_t package = (productCode >> 5 ) & 0x7 ;
100
+ uint8_t accuracy = productCode & 0x1F ;
101
+
102
+ if (!_mlx90632->setMode (MLX90632_MODE_CONTINUOUS)) {
103
+ WS_DEBUG_PRINTLN (F (" Failed to set mode" ));
104
+ return false ;
105
+ }
106
+
107
+ // set accuracy mode based on medical if detected
108
+ if (accuracy == 1 ) {
109
+ // Set and get measurement select (medical)
110
+ if (!extendedInsteadOfMedicalRange &&
111
+ !_mlx90632->setMeasurementSelect (MLX90632_MEAS_MEDICAL)) {
112
+ WS_DEBUG_PRINTLN (F (" Failed to set measurement select to Medical" ));
113
+ return false ;
114
+ } else if (extendedInsteadOfMedicalRange &&
115
+ !_mlx90632->setMeasurementSelect (
116
+ MLX90632_MEAS_EXTENDED_RANGE)) {
117
+ WS_DEBUG_PRINTLN (
118
+ F (" Failed to set measurement select to Extended Range" ));
119
+ return false ;
120
+ }
121
+ }
122
+
123
+ // Set and get refresh rate (default to 2Hz)
124
+ if (!_mlx90632->setRefreshRate (MLX90632_REFRESH_2HZ)) {
125
+ WS_DEBUG_PRINTLN (F (" Failed to set refresh rate to 2Hz" ));
126
+ return false ;
127
+ }
128
+
129
+ if (!_mlx90632->resetNewData ()) {
130
+ WS_DEBUG_PRINTLN (F (" Failed to reset new data flag" ));
131
+ return false ;
132
+ }
133
+ return true ;
134
+ }
135
+
136
+ /* ******************************************************************************/
137
+ /* !
138
+ @brief Checks if sensor was read within last 1s, or is the first read.
139
+ @returns True if the sensor was recently read, False otherwise.
140
+ */
141
+ /* ******************************************************************************/
142
+ bool HasBeenReadInLast200ms () {
143
+ return _lastRead != 0 && millis () - _lastRead < 200 ;
144
+ }
145
+
146
+ /* ******************************************************************************/
147
+ /* !
148
+ @brief Reads the sensor.
149
+ @returns True if the sensor was read successfully, False otherwise.
150
+ */
151
+ /* ******************************************************************************/
152
+ bool ReadSensorData () {
153
+ bool result = false ;
154
+ if (HasBeenReadInLast200ms ()) {
155
+ WS_DEBUG_PRINTLN (F (" Sensor was read recently, using cached data" ));
156
+ return true ;
157
+ }
158
+
159
+ // Check if we need to trigger a new measurement for step modes
160
+ mlx90632_mode_t currentMode = _mlx90632->getMode ();
161
+ if (currentMode == MLX90632_MODE_STEP ||
162
+ currentMode == MLX90632_MODE_SLEEPING_STEP) {
163
+ // Trigger single measurement (SOC bit) for step modes
164
+ if (!_mlx90632->startSingleMeasurement ()) {
165
+ WS_DEBUG_PRINTLN (F (" Failed to start single measurement" ));
166
+ return false ;
167
+ }
168
+ delay (510 ); // Wait for measurement to complete @ 2Hz
169
+ }
170
+
171
+ // Only check new data flag - much more efficient for continuous mode
172
+ if (_mlx90632->isNewData ()) {
173
+ _deviceTemp = _mlx90632->getAmbientTemperature ();
174
+ _objectTemp = _mlx90632->getObjectTemperature ();
175
+ if (isnan (_objectTemp)) {
176
+ WS_DEBUG_PRINTLN (F (" NaN (invalid cycle position)" ));
177
+ return false ;
178
+ }
179
+ result = true ;
180
+ _lastRead = millis ();
181
+ // Reset new data flag after reading
182
+ if (!_mlx90632->resetNewData ()) {
183
+ WS_DEBUG_PRINTLN (F (" Failed to reset new data flag" ));
184
+ }
185
+ } else {
186
+ WS_DEBUG_PRINTLN (F (" No new data available, skipping read" ));
187
+ }
188
+
189
+ return result;
190
+ }
191
+
192
+ /* ******************************************************************************/
193
+ /* !
194
+ @brief Gets the MLX90632's current temperature.
195
+ @param tempEvent
196
+ Pointer to an Adafruit_Sensor event.
197
+ @returns True if the temperature was obtained successfully, False
198
+ otherwise.
199
+ */
200
+ /* ******************************************************************************/
201
+ bool getEventAmbientTemp (sensors_event_t *tempEvent) {
202
+ if (ReadSensorData () && _deviceTemp != NAN) {
203
+ tempEvent->temperature = _deviceTemp;
204
+ return true ;
205
+ }
206
+ return false ; // sensor not read recently, return false
207
+ }
208
+
209
+ /* ******************************************************************************/
210
+ /* !
211
+ @brief Gets the MLX90632's object temperature.
212
+ @param tempEvent
213
+ Pointer to an Adafruit_Sensor event.
214
+ @returns True if the temperature was obtained successfully, False
215
+ otherwise.
216
+ */
217
+ /* ******************************************************************************/
218
+ bool getEventObjectTemp (sensors_event_t *tempEvent) {
219
+ if (ReadSensorData () && _objectTemp != NAN) {
220
+ tempEvent->temperature = _objectTemp;
221
+ return true ;
222
+ }
223
+ return false ; // sensor not read recently, return false
224
+ }
225
+
226
+ protected:
227
+ double _deviceTemp; // /< Device temperature in Celsius
228
+ double _objectTemp; // /< Object temperature in Celsius
229
+ uint32_t _lastRead; // /< Last time the sensor was read in milliseconds
230
+ Adafruit_MLX90632 *_mlx90632 = nullptr ; // /< MLX90632 object
231
+ };
232
+
233
+ #endif // WipperSnapper_I2C_Driver_MLX90632D_H
0 commit comments