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