Compare commits

...

10 commits

Author SHA1 Message Date
Tyeth Gundry
1d237a6695
Bump version to 1.0.2 in library.properties
Some checks failed
Arduino Library CI / build (push) Has been cancelled
2025-08-22 21:44:39 +01:00
Limor "Ladyada" Fried
1846a7318a
Merge pull request #1 from adafruit/init-as-nullptr
Initialize i2c_dev to nullptr in constructor
2025-08-22 15:18:11 -04:00
Tyeth Gundry
dd363596d7
Initialize i2c_dev to nullptr in constructor
This alleviates the issue on initialisation and particularly on destruction and reinitialization of the driver.
2025-08-22 12:30:28 +01:00
Tyeth Gundry
25ae7b4997
Update library.properties - bump version to 1.0.1
Some checks failed
Arduino Library CI / build (push) Has been cancelled
2025-08-05 13:38:54 +01:00
ladyada
39dd3c54fe Update license to MIT and add proper attribution
- Change from BSD to MIT license across all files
- Add MIT LICENSE file with standard text
- Update author attribution to Limor "Ladyada" Fried with assistance from Claude Code
- Update file headers to reference MIT license and LICENSE file
- Enhance README with comprehensive feature list and proper attribution
- Update README to reference LICENSE file instead of license.txt

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-03 12:51:32 -04:00
ladyada
1da8a7438c Add library.properties with Adafruit_BusIO dependency
Some checks failed
Arduino Library CI / build (push) Has been cancelled
- Define library metadata for Arduino Library Manager
- Add Adafruit BusIO as required dependency
- Set initial version to 1.0.0
- Include comprehensive library description
- Support all architectures

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-02 16:32:45 -04:00
ladyada
c93ea16dd6 Integrate calibration loading and add comprehensive mode support
- Automatically load calibration constants in begin() method
- Remove separate getCalibrations() call from example sketch
- Add selectable measurement modes in example with commented options
- Implement proper SOC (Start of Conversion) triggering for step modes
- Support all measurement modes: Continuous, Step, and Sleeping Step
- Optimize loop for efficient new data flag handling
- Add automatic single measurement triggering for step-based modes
- Tested and verified both continuous and step mode operation
- Apply clang-format code formatting to all C++ files

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-02 16:29:51 -04:00
ladyada
195681dc69 Add extended range mode support and optimize continuous measurements
- Implement extended range measurement mode for both ambient and object temperature
- Support automatic mode detection (Medical vs Extended Range)
- Extended range uses RAM_52-59 with complex S calculation formula
- Medical mode continues using cycle position-based calculations
- Unify variable names (ram_ambient, ram_ref) for cleaner code
- Update debug output to show measurement mode and generic variables
- Optimize test sketch for efficient continuous monitoring:
  - Reset new data flag before starting measurements
  - Only check isNewData() flag instead of busy flags
  - Reset flag after each reading for proper data flow
  - Reduce I2C bus traffic with minimal delay

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-02 15:56:29 -04:00
ladyada
4552553eb3 Add complete object temperature calculation with debug controls
- Implement full getObjectTemperature() with Stefan-Boltzmann law calculation
- Add complete object temperature formula: TO = pow( STO / (emiss * Fa * Ha * (1 + Ga * (TODUT - TO0) + Fb * (TADUT - TA0))) + TAK^4, 0.25) - 273.15 - Hb
- Add TO0/TA0 member variables for iterative calculation state tracking
- Add scientific notation display for large values (TAK^4, TO_K^4) using custom formatting
- Add MLX90632_DEBUG define with ifdef controls for all debug output
- Add proper indentation (2 spaces) for all debug prints for better readability
- Convert all string literals to F() macro for SRAM optimization (1150 bytes saved)
- Complete temperature measurement system working: ambient ~24°C, object detection functional
- All intermediate calculations visible with proper formatting in debug mode

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-02 15:15:02 -04:00
ladyada
374dbc0494 Add calibration constants and ambient temperature calculation
- Implement getCalibrations() to read all EEPROM calibration constants with proper double precision scaling using multiplication (2^-n) instead of division
- Add getAmbientTemperature() with complete calculation: VRTA, AMB pre-calculations and final temperature formula P_O + (AMB - P_R)/P_G + P_T * (AMB - P_R)^2
- Add refresh rate control with setRefreshRate()/getRefreshRate() functions
- Add getEEPROMVersion() for version information
- Add RAM_6 register definition for ambient temperature calculations
- Update test sketch to display calibration constants and continuous ambient temperature readings
- All functions tested successfully on hardware with MLX90632 sensor showing stable ~23.36°C readings

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-01 20:38:53 -04:00
6 changed files with 825 additions and 170 deletions

View file

@ -10,15 +10,23 @@
* please support Adafruit and open-source hardware by purchasing products from
* Adafruit!
*
* BSD license (see license.txt)
* Written by Limor "Ladyada" Fried with assistance from Claude Code.
*
* MIT license, see LICENSE for more information
*/
#include "Adafruit_MLX90632.h"
#define MLX90632_DEBUG
/*!
* @brief Instantiates a new MLX90632 class
*/
Adafruit_MLX90632::Adafruit_MLX90632() {}
Adafruit_MLX90632::Adafruit_MLX90632() {
TO0 = 25.0; // Initialize previous object temperature
TA0 = 25.0; // Initialize previous ambient temperature
i2c_dev = nullptr;
}
/*!
* @brief Cleans up the MLX90632
@ -37,7 +45,7 @@ Adafruit_MLX90632::~Adafruit_MLX90632() {
* The Wire object to be used for I2C connections.
* @return True if initialization was successful, otherwise false.
*/
bool Adafruit_MLX90632::begin(uint8_t i2c_address, TwoWire *wire) {
bool Adafruit_MLX90632::begin(uint8_t i2c_address, TwoWire* wire) {
if (i2c_dev) {
delete i2c_dev;
}
@ -47,14 +55,19 @@ bool Adafruit_MLX90632::begin(uint8_t i2c_address, TwoWire *wire) {
return false;
}
Adafruit_BusIO_Register product_code_reg =
Adafruit_BusIO_Register(i2c_dev, swapBytes(MLX90632_REG_EE_PRODUCT_CODE), 2, MSBFIRST, 2);
Adafruit_BusIO_Register product_code_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_EE_PRODUCT_CODE), 2, MSBFIRST, 2);
uint16_t product_code = product_code_reg.read();
if (product_code == 0xFFFF || product_code == 0x0000) {
return false;
}
// Load calibration constants automatically
if (!getCalibrations()) {
return false;
}
return true;
}
@ -63,12 +76,12 @@ bool Adafruit_MLX90632::begin(uint8_t i2c_address, TwoWire *wire) {
* @return Product ID (48-bit value in uint64_t)
*/
uint64_t Adafruit_MLX90632::getProductID() {
Adafruit_BusIO_Register id0_reg =
Adafruit_BusIO_Register(i2c_dev, swapBytes(MLX90632_REG_ID0), 2, MSBFIRST, 2);
Adafruit_BusIO_Register id1_reg =
Adafruit_BusIO_Register(i2c_dev, swapBytes(MLX90632_REG_ID1), 2, MSBFIRST, 2);
Adafruit_BusIO_Register id2_reg =
Adafruit_BusIO_Register(i2c_dev, swapBytes(MLX90632_REG_ID2), 2, MSBFIRST, 2);
Adafruit_BusIO_Register id0_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_ID0), 2, MSBFIRST, 2);
Adafruit_BusIO_Register id1_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_ID1), 2, MSBFIRST, 2);
Adafruit_BusIO_Register id2_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_ID2), 2, MSBFIRST, 2);
uint16_t id0 = id0_reg.read();
uint16_t id1 = id1_reg.read();
@ -82,18 +95,28 @@ uint64_t Adafruit_MLX90632::getProductID() {
* @return Product code (16-bit value)
*/
uint16_t Adafruit_MLX90632::getProductCode() {
Adafruit_BusIO_Register product_code_reg =
Adafruit_BusIO_Register(i2c_dev, swapBytes(MLX90632_REG_EE_PRODUCT_CODE), 2, MSBFIRST, 2);
Adafruit_BusIO_Register product_code_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_EE_PRODUCT_CODE), 2, MSBFIRST, 2);
return product_code_reg.read();
}
/*!
* @brief Read the EEPROM version
* @return EEPROM version (16-bit value)
*/
uint16_t Adafruit_MLX90632::getEEPROMVersion() {
Adafruit_BusIO_Register version_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_EE_VERSION), 2, MSBFIRST, 2);
return version_reg.read();
}
/*!
* @brief Start a single measurement (SOC)
* @return True if write succeeded, false otherwise
*/
bool Adafruit_MLX90632::startSingleMeasurement() {
Adafruit_BusIO_Register control_reg =
Adafruit_BusIO_Register(i2c_dev, swapBytes(MLX90632_REG_CONTROL), 2, MSBFIRST, 2);
Adafruit_BusIO_Register control_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_CONTROL), 2, MSBFIRST, 2);
Adafruit_BusIO_RegisterBits soc_bit =
Adafruit_BusIO_RegisterBits(&control_reg, 1, 3);
@ -105,8 +128,8 @@ bool Adafruit_MLX90632::startSingleMeasurement() {
* @return True if write succeeded, false otherwise
*/
bool Adafruit_MLX90632::startFullMeasurement() {
Adafruit_BusIO_Register control_reg =
Adafruit_BusIO_Register(i2c_dev, swapBytes(MLX90632_REG_CONTROL), 2, MSBFIRST, 2);
Adafruit_BusIO_Register control_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_CONTROL), 2, MSBFIRST, 2);
Adafruit_BusIO_RegisterBits sob_bit =
Adafruit_BusIO_RegisterBits(&control_reg, 1, 11);
@ -119,8 +142,8 @@ bool Adafruit_MLX90632::startFullMeasurement() {
* @return True if write succeeded, false otherwise
*/
bool Adafruit_MLX90632::setMode(mlx90632_mode_t mode) {
Adafruit_BusIO_Register control_reg =
Adafruit_BusIO_Register(i2c_dev, swapBytes(MLX90632_REG_CONTROL), 2, MSBFIRST, 2);
Adafruit_BusIO_Register control_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_CONTROL), 2, MSBFIRST, 2);
Adafruit_BusIO_RegisterBits mode_bits =
Adafruit_BusIO_RegisterBits(&control_reg, 2, 1);
@ -132,8 +155,8 @@ bool Adafruit_MLX90632::setMode(mlx90632_mode_t mode) {
* @return The current measurement mode
*/
mlx90632_mode_t Adafruit_MLX90632::getMode() {
Adafruit_BusIO_Register control_reg =
Adafruit_BusIO_Register(i2c_dev, swapBytes(MLX90632_REG_CONTROL), 2, MSBFIRST, 2);
Adafruit_BusIO_Register control_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_CONTROL), 2, MSBFIRST, 2);
Adafruit_BusIO_RegisterBits mode_bits =
Adafruit_BusIO_RegisterBits(&control_reg, 2, 1);
@ -145,9 +168,10 @@ mlx90632_mode_t Adafruit_MLX90632::getMode() {
* @param meas_select The measurement select type to set
* @return True if write succeeded, false otherwise
*/
bool Adafruit_MLX90632::setMeasurementSelect(mlx90632_meas_select_t meas_select) {
Adafruit_BusIO_Register control_reg =
Adafruit_BusIO_Register(i2c_dev, swapBytes(MLX90632_REG_CONTROL), 2, MSBFIRST, 2);
bool Adafruit_MLX90632::setMeasurementSelect(
mlx90632_meas_select_t meas_select) {
Adafruit_BusIO_Register control_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_CONTROL), 2, MSBFIRST, 2);
Adafruit_BusIO_RegisterBits meas_select_bits =
Adafruit_BusIO_RegisterBits(&control_reg, 5, 4);
@ -159,8 +183,8 @@ bool Adafruit_MLX90632::setMeasurementSelect(mlx90632_meas_select_t meas_select)
* @return The current measurement select type
*/
mlx90632_meas_select_t Adafruit_MLX90632::getMeasurementSelect() {
Adafruit_BusIO_Register control_reg =
Adafruit_BusIO_Register(i2c_dev, swapBytes(MLX90632_REG_CONTROL), 2, MSBFIRST, 2);
Adafruit_BusIO_Register control_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_CONTROL), 2, MSBFIRST, 2);
Adafruit_BusIO_RegisterBits meas_select_bits =
Adafruit_BusIO_RegisterBits(&control_reg, 5, 4);
@ -172,8 +196,8 @@ mlx90632_meas_select_t Adafruit_MLX90632::getMeasurementSelect() {
* @return True if device is busy, false otherwise
*/
bool Adafruit_MLX90632::isBusy() {
Adafruit_BusIO_Register status_reg =
Adafruit_BusIO_Register(i2c_dev, swapBytes(MLX90632_REG_STATUS), 2, MSBFIRST, 2);
Adafruit_BusIO_Register status_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_STATUS), 2, MSBFIRST, 2);
Adafruit_BusIO_RegisterBits device_busy_bit =
Adafruit_BusIO_RegisterBits(&status_reg, 1, 10);
@ -185,8 +209,8 @@ bool Adafruit_MLX90632::isBusy() {
* @return True if EEPROM is busy, false otherwise
*/
bool Adafruit_MLX90632::isEEPROMBusy() {
Adafruit_BusIO_Register status_reg =
Adafruit_BusIO_Register(i2c_dev, swapBytes(MLX90632_REG_STATUS), 2, MSBFIRST, 2);
Adafruit_BusIO_Register status_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_STATUS), 2, MSBFIRST, 2);
Adafruit_BusIO_RegisterBits eeprom_busy_bit =
Adafruit_BusIO_RegisterBits(&status_reg, 1, 9);
@ -215,8 +239,8 @@ bool Adafruit_MLX90632::reset() {
* @return Current cycle position (0-31)
*/
uint8_t Adafruit_MLX90632::readCyclePosition() {
Adafruit_BusIO_Register status_reg =
Adafruit_BusIO_Register(i2c_dev, swapBytes(MLX90632_REG_STATUS), 2, MSBFIRST, 2);
Adafruit_BusIO_Register status_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_STATUS), 2, MSBFIRST, 2);
Adafruit_BusIO_RegisterBits cycle_position_bits =
Adafruit_BusIO_RegisterBits(&status_reg, 5, 2);
@ -228,8 +252,8 @@ uint8_t Adafruit_MLX90632::readCyclePosition() {
* @return True if write succeeded, false otherwise
*/
bool Adafruit_MLX90632::resetNewData() {
Adafruit_BusIO_Register status_reg =
Adafruit_BusIO_Register(i2c_dev, swapBytes(MLX90632_REG_STATUS), 2, MSBFIRST, 2);
Adafruit_BusIO_Register status_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_STATUS), 2, MSBFIRST, 2);
Adafruit_BusIO_RegisterBits new_data_bit =
Adafruit_BusIO_RegisterBits(&status_reg, 1, 0);
@ -241,14 +265,429 @@ bool Adafruit_MLX90632::resetNewData() {
* @return True if new data is available, false otherwise
*/
bool Adafruit_MLX90632::isNewData() {
Adafruit_BusIO_Register status_reg =
Adafruit_BusIO_Register(i2c_dev, swapBytes(MLX90632_REG_STATUS), 2, MSBFIRST, 2);
Adafruit_BusIO_Register status_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_STATUS), 2, MSBFIRST, 2);
Adafruit_BusIO_RegisterBits new_data_bit =
Adafruit_BusIO_RegisterBits(&status_reg, 1, 0);
return new_data_bit.read();
}
/*!
* @brief Set the refresh rate for both measurement registers
* @param refresh_rate The refresh rate to set
* @return True if both writes succeeded, false otherwise
*/
bool Adafruit_MLX90632::setRefreshRate(mlx90632_refresh_rate_t refresh_rate) {
// Set refresh rate in EE_MEAS_1 register (bits 10:8)
Adafruit_BusIO_Register meas1_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_EE_MEAS_1), 2, MSBFIRST, 2);
Adafruit_BusIO_RegisterBits meas1_refresh_bits =
Adafruit_BusIO_RegisterBits(&meas1_reg, 3, 8);
if (!meas1_refresh_bits.write(refresh_rate)) {
return false;
}
// Set refresh rate in EE_MEAS_2 register (bits 10:8)
Adafruit_BusIO_Register meas2_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_EE_MEAS_2), 2, MSBFIRST, 2);
Adafruit_BusIO_RegisterBits meas2_refresh_bits =
Adafruit_BusIO_RegisterBits(&meas2_reg, 3, 8);
return meas2_refresh_bits.write(refresh_rate);
}
/*!
* @brief Get the refresh rate from EE_MEAS_1 register
* @return The current refresh rate
*/
mlx90632_refresh_rate_t Adafruit_MLX90632::getRefreshRate() {
Adafruit_BusIO_Register meas1_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_EE_MEAS_1), 2, MSBFIRST, 2);
Adafruit_BusIO_RegisterBits meas1_refresh_bits =
Adafruit_BusIO_RegisterBits(&meas1_reg, 3, 8);
return (mlx90632_refresh_rate_t)meas1_refresh_bits.read();
}
/*!
* @brief Helper function to read 32-bit values from consecutive registers
* @param lsw_addr Address of the least significant word register
* @return 32-bit value (LSW + MSW)
*/
uint32_t Adafruit_MLX90632::read32BitRegister(uint16_t lsw_addr) {
Adafruit_BusIO_Register lsw_reg =
Adafruit_BusIO_Register(i2c_dev, swapBytes(lsw_addr), 2, MSBFIRST, 2);
Adafruit_BusIO_Register msw_reg =
Adafruit_BusIO_Register(i2c_dev, swapBytes(lsw_addr + 1), 2, MSBFIRST, 2);
uint16_t lsw = lsw_reg.read();
uint16_t msw = msw_reg.read();
return ((uint32_t)msw << 16) | lsw;
}
/*!
* @brief Read all calibration constants from EEPROM
* @return True if all reads succeeded, false otherwise
*/
bool Adafruit_MLX90632::getCalibrations() {
// Read 32-bit calibration constants
uint32_t ee_p_r = read32BitRegister(MLX90632_REG_EE_P_R_LSW);
uint32_t ee_p_g = read32BitRegister(MLX90632_REG_EE_P_G_LSW);
uint32_t ee_p_t = read32BitRegister(MLX90632_REG_EE_P_T_LSW);
uint32_t ee_p_o = read32BitRegister(MLX90632_REG_EE_P_O_LSW);
uint32_t ee_aa = read32BitRegister(MLX90632_REG_EE_AA_LSW);
uint32_t ee_ab = read32BitRegister(MLX90632_REG_EE_AB_LSW);
uint32_t ee_ba = read32BitRegister(MLX90632_REG_EE_BA_LSW);
uint32_t ee_bb = read32BitRegister(MLX90632_REG_EE_BB_LSW);
uint32_t ee_ca = read32BitRegister(MLX90632_REG_EE_CA_LSW);
uint32_t ee_cb = read32BitRegister(MLX90632_REG_EE_CB_LSW);
uint32_t ee_da = read32BitRegister(MLX90632_REG_EE_DA_LSW);
uint32_t ee_db = read32BitRegister(MLX90632_REG_EE_DB_LSW);
uint32_t ee_ea = read32BitRegister(MLX90632_REG_EE_EA_LSW);
uint32_t ee_eb = read32BitRegister(MLX90632_REG_EE_EB_LSW);
uint32_t ee_fa = read32BitRegister(MLX90632_REG_EE_FA_LSW);
uint32_t ee_fb = read32BitRegister(MLX90632_REG_EE_FB_LSW);
uint32_t ee_ga = read32BitRegister(MLX90632_REG_EE_GA_LSW);
// Read 16-bit calibration constants
Adafruit_BusIO_Register gb_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_EE_GB), 2, MSBFIRST, 2);
Adafruit_BusIO_Register ka_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_EE_KA), 2, MSBFIRST, 2);
Adafruit_BusIO_Register kb_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_EE_KB), 2, MSBFIRST, 2);
Adafruit_BusIO_Register ha_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_EE_HA), 2, MSBFIRST, 2);
Adafruit_BusIO_Register hb_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_EE_HB), 2, MSBFIRST, 2);
// Convert to proper double values with scaling factors from datasheet
P_R = (double)(int32_t)ee_p_r * (double)pow(2, -8); // 2^-8
P_G = (double)(int32_t)ee_p_g * (double)pow(2, -20); // 2^-20
P_T = (double)(int32_t)ee_p_t * (double)pow(2, -44); // 2^-44
P_O = (double)(int32_t)ee_p_o * (double)pow(2, -8); // 2^-8
Aa = (double)(int32_t)ee_aa * (double)pow(2, -16); // 2^-16
Ab = (double)(int32_t)ee_ab * (double)pow(2, -8); // 2^-8
Ba = (double)(int32_t)ee_ba * (double)pow(2, -16); // 2^-16
Bb = (double)(int32_t)ee_bb * (double)pow(2, -8); // 2^-8
Ca = (double)(int32_t)ee_ca * (double)pow(2, -16); // 2^-16
Cb = (double)(int32_t)ee_cb * (double)pow(2, -8); // 2^-8
Da = (double)(int32_t)ee_da * (double)pow(2, -16); // 2^-16
Db = (double)(int32_t)ee_db * (double)pow(2, -8); // 2^-8
Ea = (double)(int32_t)ee_ea * (double)pow(2, -16); // 2^-16
Eb = (double)(int32_t)ee_eb * (double)pow(2, -8); // 2^-8
Fa = (double)(int32_t)ee_fa * (double)pow(2, -46); // 2^-46
Fb = (double)(int32_t)ee_fb * (double)pow(2, -36); // 2^-36
Ga = (double)(int32_t)ee_ga * (double)pow(2, -36); // 2^-36
// 16-bit signed values with scaling
Gb = (double)(int16_t)gb_reg.read() * (double)pow(2, -10); // 2^-10
Ka = (double)(int16_t)ka_reg.read() * (double)pow(2, -10); // 2^-10
Kb = (int16_t)kb_reg.read(); // No scaling
Ha = (double)(int16_t)ha_reg.read() * (double)pow(2, -14); // 2^-14
Hb = (double)(int16_t)hb_reg.read() * (double)pow(2, -10); // 2^-10
#ifdef MLX90632_DEBUG
// Debug: Print calibration constants
Serial.println(F("Calibration constants:"));
Serial.print(F(" P_R = "));
Serial.println(P_R, 8);
Serial.print(F(" P_G = "));
Serial.println(P_G, 8);
Serial.print(F(" P_T = "));
Serial.println(P_T, 12);
Serial.print(F(" P_O = "));
Serial.println(P_O, 8);
Serial.print(F(" Aa = "));
Serial.println(Aa, 8);
Serial.print(F(" Ab = "));
Serial.println(Ab, 8);
Serial.print(F(" Ba = "));
Serial.println(Ba, 8);
Serial.print(F(" Bb = "));
Serial.println(Bb, 8);
Serial.print(F(" Ca = "));
Serial.println(Ca, 8);
Serial.print(F(" Cb = "));
Serial.println(Cb, 8);
Serial.print(F(" Da = "));
Serial.println(Da, 8);
Serial.print(F(" Db = "));
Serial.println(Db, 8);
Serial.print(F(" Ea = "));
Serial.println(Ea, 8);
Serial.print(F(" Eb = "));
Serial.println(Eb, 8);
Serial.print(F(" Fa = "));
Serial.println(Fa, 12);
Serial.print(F(" Fb = "));
Serial.println(Fb, 10);
Serial.print(F(" Ga = "));
Serial.println(Ga, 10);
Serial.print(F(" Gb = "));
Serial.println(Gb, 8);
Serial.print(F(" Ka = "));
Serial.println(Ka, 8);
Serial.print(F(" Kb = "));
Serial.println(Kb);
Serial.print(F(" Ha = "));
Serial.println(Ha, 8);
Serial.print(F(" Hb = "));
Serial.println(Hb, 8);
#endif
return true;
}
/*!
* @brief Calculate ambient temperature
* @return Ambient temperature in degrees Celsius
*/
double Adafruit_MLX90632::getAmbientTemperature() {
// Check measurement mode to determine which RAM registers to use
mlx90632_meas_select_t meas_mode = getMeasurementSelect();
int16_t ram_ambient, ram_ref;
if (meas_mode == MLX90632_MEAS_EXTENDED_RANGE) {
// Extended range mode: use RAM_54 and RAM_57
Adafruit_BusIO_Register ram54_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_RAM_54), 2, MSBFIRST, 2);
Adafruit_BusIO_Register ram57_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_RAM_57), 2, MSBFIRST, 2);
ram_ambient = (int16_t)ram54_reg.read();
ram_ref = (int16_t)ram57_reg.read();
} else {
// Medical mode: use RAM_6 and RAM_9 (default)
Adafruit_BusIO_Register ram6_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_RAM_6), 2, MSBFIRST, 2);
Adafruit_BusIO_Register ram9_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_RAM_9), 2, MSBFIRST, 2);
ram_ambient = (int16_t)ram6_reg.read();
ram_ref = (int16_t)ram9_reg.read();
}
// Pre-calculations for ambient temperature (same for both modes)
// Gb = EE_Gb * 2^-10 (already calculated in getCalibrations())
double VRTA = (double)ram_ref + Gb * ((double)ram_ambient / 12.0);
double AMB = ((double)ram_ambient / 12.0) / VRTA * (double)pow(2, 19);
// Calculate ambient temperature: P_O + (AMB - P_R)/P_G + P_T * (AMB - P_R)^2
double amb_diff = AMB - P_R;
double ambient_temp = P_O + (amb_diff / P_G) + P_T * (amb_diff * amb_diff);
#ifdef MLX90632_DEBUG
// Debug output
Serial.print(F(" Mode = "));
Serial.println(meas_mode == MLX90632_MEAS_EXTENDED_RANGE ? F("Extended")
: F("Medical"));
Serial.print(F(" RAM_ambient = "));
Serial.println(ram_ambient);
Serial.print(F(" RAM_ref = "));
Serial.println(ram_ref);
Serial.print(F(" Gb = "));
Serial.println(Gb, 8);
Serial.print(F(" VRTA = "));
Serial.println(VRTA, 8);
Serial.print(F(" AMB = "));
Serial.println(AMB, 8);
Serial.print(F(" AMB - P_R = "));
Serial.println(amb_diff, 8);
Serial.print(F(" Ambient Temp = "));
Serial.println(ambient_temp, 8);
#endif
return ambient_temp;
}
/*!
* @brief Calculate object temperature
* @return Object temperature in degrees Celsius or NaN if invalid cycle
* position
*/
double Adafruit_MLX90632::getObjectTemperature() {
// Check measurement mode to determine which calculation to use
mlx90632_meas_select_t meas_mode = getMeasurementSelect();
double S;
int16_t ram_ambient, ram_ref;
if (meas_mode == MLX90632_MEAS_EXTENDED_RANGE) {
// Extended range mode: use RAM_52-59
Adafruit_BusIO_Register ram52_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_RAM_52), 2, MSBFIRST, 2);
Adafruit_BusIO_Register ram53_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_RAM_53), 2, MSBFIRST, 2);
Adafruit_BusIO_Register ram54_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_RAM_54), 2, MSBFIRST, 2);
Adafruit_BusIO_Register ram55_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_RAM_55), 2, MSBFIRST, 2);
Adafruit_BusIO_Register ram56_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_RAM_56), 2, MSBFIRST, 2);
Adafruit_BusIO_Register ram57_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_RAM_57), 2, MSBFIRST, 2);
Adafruit_BusIO_Register ram58_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_RAM_58), 2, MSBFIRST, 2);
Adafruit_BusIO_Register ram59_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_RAM_59), 2, MSBFIRST, 2);
int16_t ram52 = (int16_t)ram52_reg.read();
int16_t ram53 = (int16_t)ram53_reg.read();
int16_t ram54 = (int16_t)ram54_reg.read();
int16_t ram55 = (int16_t)ram55_reg.read();
int16_t ram56 = (int16_t)ram56_reg.read();
int16_t ram57 = (int16_t)ram57_reg.read();
int16_t ram58 = (int16_t)ram58_reg.read();
int16_t ram59 = (int16_t)ram59_reg.read();
// Extended range S calculation
S = ((double)ram52 - (double)ram53 - (double)ram55 + (double)ram56) / 2.0 +
(double)ram58 + (double)ram59;
ram_ambient = ram54;
ram_ref = ram57;
} else {
// Medical mode: use cycle position and RAM_4-9
uint8_t cycle_pos = readCyclePosition();
Adafruit_BusIO_Register ram4_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_RAM_4), 2, MSBFIRST, 2);
Adafruit_BusIO_Register ram5_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_RAM_5), 2, MSBFIRST, 2);
Adafruit_BusIO_Register ram6_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_RAM_6), 2, MSBFIRST, 2);
Adafruit_BusIO_Register ram7_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_RAM_7), 2, MSBFIRST, 2);
Adafruit_BusIO_Register ram8_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_RAM_8), 2, MSBFIRST, 2);
Adafruit_BusIO_Register ram9_reg = Adafruit_BusIO_Register(
i2c_dev, swapBytes(MLX90632_REG_RAM_9), 2, MSBFIRST, 2);
int16_t ram4 = (int16_t)ram4_reg.read();
int16_t ram5 = (int16_t)ram5_reg.read();
int16_t ram6 = (int16_t)ram6_reg.read();
int16_t ram7 = (int16_t)ram7_reg.read();
int16_t ram8 = (int16_t)ram8_reg.read();
int16_t ram9 = (int16_t)ram9_reg.read();
// Medical mode S calculation based on cycle position
if (cycle_pos == 2) {
S = ((double)ram4 + (double)ram5) / 2.0;
} else if (cycle_pos == 1) {
S = ((double)ram7 + (double)ram8) / 2.0;
} else {
// Invalid cycle position - return NaN
return NAN;
}
ram_ambient = ram6;
ram_ref = ram9;
}
// Pre-calculations for object temperature (same for both modes)
// VRTO = ram_ref + Ka * (ram_ambient / 12)
// Ka = EE_Ka * 2^-10 (already calculated in getCalibrations())
double VRTO = (double)ram_ref + Ka * ((double)ram_ambient / 12.0);
// STO = [S/12]/VRTO * 2^19
double STO = ((S / 12.0) / VRTO) * (double)pow(2, 19);
// Calculate AMB for ambient temperature (needed for TADUT)
double VRTA = (double)ram_ref + Gb * ((double)ram_ambient / 12.0);
double AMB = ((double)ram_ambient / 12.0) / VRTA * (double)pow(2, 19);
// Additional temperature calculations
double TADUT = (AMB - Eb) / Ea + 25.0;
double TAK = TADUT + 273.15;
double emissivity = 1.0;
// For the first iteration, use current TADUT as TODUT approximation
double TODUT = TADUT;
// Calculate final object temperature:
// TO = pow( STO / (emiss * Fa * Ha * (1 + Ga * (TODUT - TO0) + Fb * (TADUT -
// TA0))) + TAK^4, 0.25) - 273.15 - Hb
double denominator =
emissivity * Fa * Ha * (1.0 + Ga * (TODUT - TO0) + Fb * (TADUT - TA0));
double TAK4 = pow(TAK, 4);
double TO_K4 = (STO / denominator) + TAK4;
double TO = pow(TO_K4, 0.25) - 273.15 - Hb;
#ifdef MLX90632_DEBUG
// Debug output
Serial.print(F(" Mode = "));
Serial.println(meas_mode == MLX90632_MEAS_EXTENDED_RANGE ? F("Extended")
: F("Medical"));
if (meas_mode == MLX90632_MEAS_MEDICAL) {
Serial.print(F(" Cycle Position = "));
Serial.println(readCyclePosition());
}
Serial.print(F(" RAM_ambient = "));
Serial.println(ram_ambient);
Serial.print(F(" RAM_ref = "));
Serial.println(ram_ref);
Serial.print(F(" S = "));
Serial.println(S, 8);
Serial.print(F(" Ka = "));
Serial.println(Ka, 8);
Serial.print(F(" VRTO = "));
Serial.println(VRTO, 8);
Serial.print(F(" STO = "));
Serial.println(STO, 8);
Serial.print(F(" VRTA = "));
Serial.println(VRTA, 8);
Serial.print(F(" AMB = "));
Serial.println(AMB, 8);
Serial.print(F(" TADUT = "));
Serial.println(TADUT, 8);
Serial.print(F(" TODUT = "));
Serial.println(TODUT, 8);
Serial.print(F(" TAK = "));
Serial.println(TAK, 8);
Serial.print(F(" TAK^4 = "));
if (TAK4 >= 1e9) {
Serial.print(TAK4 / 1e9, 2);
Serial.println(F("e+09"));
} else if (TAK4 >= 1e6) {
Serial.print(TAK4 / 1e6, 2);
Serial.println(F("e+06"));
} else {
Serial.println(TAK4, 2);
}
Serial.print(F(" TO0 = "));
Serial.println(TO0, 8);
Serial.print(F(" TA0 = "));
Serial.println(TA0, 8);
Serial.print(F(" Emissivity = "));
Serial.println(emissivity, 8);
Serial.print(F(" Denominator = "));
Serial.println(denominator, 8);
Serial.print(F(" TO_K^4 = "));
if (TO_K4 >= 1e9) {
Serial.print(TO_K4 / 1e9, 2);
Serial.println(F("e+09"));
} else if (TO_K4 >= 1e6) {
Serial.print(TO_K4 / 1e6, 2);
Serial.println(F("e+06"));
} else {
Serial.println(TO_K4, 2);
}
Serial.print(F(" TO = "));
Serial.println(TO, 8);
#endif
// Update TO0 and TA0 with current measurements for next calculation
TO0 = TO; // Use calculated object temperature
TA0 = TADUT; // Update with current ambient temperature calculation
return TO;
}
/*!
* @brief Byte swap helper for register addresses
* @param value 16-bit value to swap

View file

@ -10,17 +10,20 @@
* please support Adafruit and open-source hardware by purchasing products from
* Adafruit!
*
* BSD license (see license.txt)
* Written by Limor "Ladyada" Fried with assistance from Claude Code.
*
* MIT license, see LICENSE for more information
*/
#ifndef _ADAFRUIT_MLX90632_H
#define _ADAFRUIT_MLX90632_H
#include "Arduino.h"
#include <Adafruit_BusIO_Register.h>
#include <Adafruit_I2CDevice.h>
#include <Wire.h>
#include "Arduino.h"
/*=========================================================================
I2C ADDRESS/BITS
-----------------------------------------------------------------------*/
@ -43,40 +46,74 @@
#define MLX90632_REG_EE_PRODUCT_CODE 0x2409 ///< Sensor information
#define MLX90632_REG_MELEXIS_RESERVED10 0x240A ///< Melexis reserved
#define MLX90632_REG_EE_VERSION 0x240B ///< EEPROM version
#define MLX90632_REG_EE_P_R_LSW 0x240C ///< P_R calibration constant (16-bit, Least Significant Word)
#define MLX90632_REG_EE_P_R_MSW 0x240D ///< P_R calibration constant (16-bit, Most Significant Word)
#define MLX90632_REG_EE_P_G_LSW 0x240E ///< P_G calibration constant (16-bit, Least Significant Word)
#define MLX90632_REG_EE_P_G_MSW 0x240F ///< P_G calibration constant (16-bit, Most Significant Word)
#define MLX90632_REG_EE_P_T_LSW 0x2410 ///< P_T calibration constant (16-bit, Least Significant Word)
#define MLX90632_REG_EE_P_T_MSW 0x2411 ///< P_T calibration constant (16-bit, Most Significant Word)
#define MLX90632_REG_EE_P_O_LSW 0x2412 ///< P_O calibration constant (16-bit, Least Significant Word)
#define MLX90632_REG_EE_P_O_MSW 0x2413 ///< P_O calibration constant (16-bit, Most Significant Word)
#define MLX90632_REG_EE_AA_LSW 0x2414 ///< Aa calibration constant (16-bit, Least Significant Word)
#define MLX90632_REG_EE_AA_MSW 0x2415 ///< Aa calibration constant (16-bit, Most Significant Word)
#define MLX90632_REG_EE_AB_LSW 0x2416 ///< Ab calibration constant (16-bit, Least Significant Word)
#define MLX90632_REG_EE_AB_MSW 0x2417 ///< Ab calibration constant (16-bit, Most Significant Word)
#define MLX90632_REG_EE_BA_LSW 0x2418 ///< Ba calibration constant (16-bit, Least Significant Word)
#define MLX90632_REG_EE_BA_MSW 0x2419 ///< Ba calibration constant (16-bit, Most Significant Word)
#define MLX90632_REG_EE_BB_LSW 0x241A ///< Bb calibration constant (16-bit, Least Significant Word)
#define MLX90632_REG_EE_BB_MSW 0x241B ///< Bb calibration constant (16-bit, Most Significant Word)
#define MLX90632_REG_EE_CA_LSW 0x241C ///< Ca calibration constant (16-bit, Least Significant Word)
#define MLX90632_REG_EE_CA_MSW 0x241D ///< Ca calibration constant (16-bit, Most Significant Word)
#define MLX90632_REG_EE_CB_LSW 0x241E ///< Cb calibration constant (16-bit, Least Significant Word)
#define MLX90632_REG_EE_CB_MSW 0x241F ///< Cb calibration constant (16-bit, Most Significant Word)
#define MLX90632_REG_EE_DA_LSW 0x2420 ///< Da calibration constant (16-bit, Least Significant Word)
#define MLX90632_REG_EE_DA_MSW 0x2421 ///< Da calibration constant (16-bit, Most Significant Word)
#define MLX90632_REG_EE_DB_LSW 0x2422 ///< Db calibration constant (16-bit, Least Significant Word)
#define MLX90632_REG_EE_DB_MSW 0x2423 ///< Db calibration constant (16-bit, Most Significant Word)
#define MLX90632_REG_EE_EA_LSW 0x2424 ///< Ea calibration constant (16-bit, Least Significant Word)
#define MLX90632_REG_EE_EA_MSW 0x2425 ///< Ea calibration constant (16-bit, Most Significant Word)
#define MLX90632_REG_EE_EB_LSW 0x2426 ///< Eb calibration constant (16-bit, Least Significant Word)
#define MLX90632_REG_EE_EB_MSW 0x2427 ///< Eb calibration constant (16-bit, Most Significant Word)
#define MLX90632_REG_EE_FA_LSW 0x2428 ///< Fa calibration constant (16-bit, Least Significant Word)
#define MLX90632_REG_EE_FA_MSW 0x2429 ///< Fa calibration constant (16-bit, Most Significant Word)
#define MLX90632_REG_EE_FB_LSW 0x242A ///< Fb calibration constant (16-bit, Least Significant Word)
#define MLX90632_REG_EE_FB_MSW 0x242B ///< Fb calibration constant (16-bit, Most Significant Word)
#define MLX90632_REG_EE_GA_LSW 0x242C ///< Ga calibration constant (16-bit, Least Significant Word)
#define MLX90632_REG_EE_GA_MSW 0x242D ///< Ga calibration constant (16-bit, Most Significant Word)
#define MLX90632_REG_EE_P_R_LSW \
0x240C ///< P_R calibration constant (16-bit, Least Significant Word)
#define MLX90632_REG_EE_P_R_MSW \
0x240D ///< P_R calibration constant (16-bit, Most Significant Word)
#define MLX90632_REG_EE_P_G_LSW \
0x240E ///< P_G calibration constant (16-bit, Least Significant Word)
#define MLX90632_REG_EE_P_G_MSW \
0x240F ///< P_G calibration constant (16-bit, Most Significant Word)
#define MLX90632_REG_EE_P_T_LSW \
0x2410 ///< P_T calibration constant (16-bit, Least Significant Word)
#define MLX90632_REG_EE_P_T_MSW \
0x2411 ///< P_T calibration constant (16-bit, Most Significant Word)
#define MLX90632_REG_EE_P_O_LSW \
0x2412 ///< P_O calibration constant (16-bit, Least Significant Word)
#define MLX90632_REG_EE_P_O_MSW \
0x2413 ///< P_O calibration constant (16-bit, Most Significant Word)
#define MLX90632_REG_EE_AA_LSW \
0x2414 ///< Aa calibration constant (16-bit, Least Significant Word)
#define MLX90632_REG_EE_AA_MSW \
0x2415 ///< Aa calibration constant (16-bit, Most Significant Word)
#define MLX90632_REG_EE_AB_LSW \
0x2416 ///< Ab calibration constant (16-bit, Least Significant Word)
#define MLX90632_REG_EE_AB_MSW \
0x2417 ///< Ab calibration constant (16-bit, Most Significant Word)
#define MLX90632_REG_EE_BA_LSW \
0x2418 ///< Ba calibration constant (16-bit, Least Significant Word)
#define MLX90632_REG_EE_BA_MSW \
0x2419 ///< Ba calibration constant (16-bit, Most Significant Word)
#define MLX90632_REG_EE_BB_LSW \
0x241A ///< Bb calibration constant (16-bit, Least Significant Word)
#define MLX90632_REG_EE_BB_MSW \
0x241B ///< Bb calibration constant (16-bit, Most Significant Word)
#define MLX90632_REG_EE_CA_LSW \
0x241C ///< Ca calibration constant (16-bit, Least Significant Word)
#define MLX90632_REG_EE_CA_MSW \
0x241D ///< Ca calibration constant (16-bit, Most Significant Word)
#define MLX90632_REG_EE_CB_LSW \
0x241E ///< Cb calibration constant (16-bit, Least Significant Word)
#define MLX90632_REG_EE_CB_MSW \
0x241F ///< Cb calibration constant (16-bit, Most Significant Word)
#define MLX90632_REG_EE_DA_LSW \
0x2420 ///< Da calibration constant (16-bit, Least Significant Word)
#define MLX90632_REG_EE_DA_MSW \
0x2421 ///< Da calibration constant (16-bit, Most Significant Word)
#define MLX90632_REG_EE_DB_LSW \
0x2422 ///< Db calibration constant (16-bit, Least Significant Word)
#define MLX90632_REG_EE_DB_MSW \
0x2423 ///< Db calibration constant (16-bit, Most Significant Word)
#define MLX90632_REG_EE_EA_LSW \
0x2424 ///< Ea calibration constant (16-bit, Least Significant Word)
#define MLX90632_REG_EE_EA_MSW \
0x2425 ///< Ea calibration constant (16-bit, Most Significant Word)
#define MLX90632_REG_EE_EB_LSW \
0x2426 ///< Eb calibration constant (16-bit, Least Significant Word)
#define MLX90632_REG_EE_EB_MSW \
0x2427 ///< Eb calibration constant (16-bit, Most Significant Word)
#define MLX90632_REG_EE_FA_LSW \
0x2428 ///< Fa calibration constant (16-bit, Least Significant Word)
#define MLX90632_REG_EE_FA_MSW \
0x2429 ///< Fa calibration constant (16-bit, Most Significant Word)
#define MLX90632_REG_EE_FB_LSW \
0x242A ///< Fb calibration constant (16-bit, Least Significant Word)
#define MLX90632_REG_EE_FB_MSW \
0x242B ///< Fb calibration constant (16-bit, Most Significant Word)
#define MLX90632_REG_EE_GA_LSW \
0x242C ///< Ga calibration constant (16-bit, Least Significant Word)
#define MLX90632_REG_EE_GA_MSW \
0x242D ///< Ga calibration constant (16-bit, Most Significant Word)
#define MLX90632_REG_EE_GB 0x242E ///< Gb calibration constant (16-bit)
#define MLX90632_REG_EE_KA 0x242F ///< Ka calibration constant (16-bit)
#define MLX90632_REG_EE_KB 0x2430 ///< Kb calibration constant (16-bit)
@ -89,7 +126,8 @@
#define MLX90632_REG_CUSTOMER_DATA_START 0x24C0 ///< Customer data start
#define MLX90632_REG_CUSTOMER_DATA_END 0x24CF ///< Customer data end
#define MLX90632_REG_MELEXIS_RESERVED208 0x24D0 ///< Melexis reserved
#define MLX90632_REG_EE_CONTROL 0x24D4 ///< EEPROM Control register, measurement control
#define MLX90632_REG_EE_CONTROL \
0x24D4 ///< EEPROM Control register, measurement control
#define MLX90632_REG_EE_I2C_ADDRESS 0x24D5 ///< I2C slave address >> 1
#define MLX90632_REG_MELEXIS_RESERVED214 0x24D6 ///< Melexis reserved
#define MLX90632_REG_EE_MEAS_1 0x24E1 ///< Measurement settings 1
@ -141,6 +179,20 @@ typedef enum {
MLX90632_MEAS_MEDICAL = 0x00, ///< Medical measurement
MLX90632_MEAS_EXTENDED_RANGE = 0x11 ///< Extended range measurement
} mlx90632_meas_select_t;
/*!
* @brief MLX90632 refresh rates
*/
typedef enum {
MLX90632_REFRESH_0_5HZ = 0, ///< 0.5 Hz (2000ms)
MLX90632_REFRESH_1HZ = 1, ///< 1 Hz (1000ms)
MLX90632_REFRESH_2HZ = 2, ///< 2 Hz (500ms)
MLX90632_REFRESH_4HZ = 3, ///< 4 Hz (250ms)
MLX90632_REFRESH_8HZ = 4, ///< 8 Hz (125ms)
MLX90632_REFRESH_16HZ = 5, ///< 16 Hz (62.5ms)
MLX90632_REFRESH_32HZ = 6, ///< 32 Hz (31.25ms)
MLX90632_REFRESH_64HZ = 7 ///< 64 Hz (15.625ms)
} mlx90632_refresh_rate_t;
/*=========================================================================*/
/*!
@ -148,12 +200,13 @@ typedef enum {
* MLX90632 Far Infrared Temperature Sensor
*/
class Adafruit_MLX90632 {
public:
public:
Adafruit_MLX90632();
~Adafruit_MLX90632();
bool begin(uint8_t i2c_addr = MLX90632_DEFAULT_ADDR, TwoWire *wire = &Wire);
bool begin(uint8_t i2c_addr = MLX90632_DEFAULT_ADDR, TwoWire* wire = &Wire);
uint64_t getProductID();
uint16_t getProductCode();
uint16_t getEEPROMVersion();
bool startSingleMeasurement();
bool startFullMeasurement();
bool setMode(mlx90632_mode_t mode);
@ -166,10 +219,46 @@ public:
uint8_t readCyclePosition();
bool resetNewData();
bool isNewData();
bool setRefreshRate(mlx90632_refresh_rate_t refresh_rate);
mlx90632_refresh_rate_t getRefreshRate();
bool getCalibrations();
double getAmbientTemperature();
double getObjectTemperature();
private:
Adafruit_I2CDevice *i2c_dev; ///< Pointer to I2C bus interface
uint16_t swapBytes(uint16_t value); ///< Byte swap helper for register addresses
private:
Adafruit_I2CDevice* i2c_dev; ///< Pointer to I2C bus interface
uint16_t swapBytes(
uint16_t value); ///< Byte swap helper for register addresses
uint32_t read32BitRegister(
uint16_t lsw_addr); ///< Helper to read 32-bit values
// Calibration constants
double P_R; ///< P_R calibration constant
double P_G; ///< P_G calibration constant
double P_T; ///< P_T calibration constant
double P_O; ///< P_O calibration constant
double Aa; ///< Aa calibration constant
double Ab; ///< Ab calibration constant
double Ba; ///< Ba calibration constant
double Bb; ///< Bb calibration constant
double Ca; ///< Ca calibration constant
double Cb; ///< Cb calibration constant
double Da; ///< Da calibration constant
double Db; ///< Db calibration constant
double Ea; ///< Ea calibration constant
double Eb; ///< Eb calibration constant
double Fa; ///< Fa calibration constant
double Fb; ///< Fb calibration constant
double Ga; ///< Ga calibration constant
double Gb; ///< Gb calibration constant
double Ka; ///< Ka calibration constant
int16_t Kb; ///< Kb calibration constant (16-bit signed)
double Ha; ///< Ha calibration constant
double Hb; ///< Hb calibration constant
// Temperature calculation variables
double TO0; ///< Previous object temperature (starts at 25.0)
double TA0; ///< Previous ambient temperature (starts at 25.0)
};
#endif

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 Adafruit Industries
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -2,6 +2,20 @@
Arduino library for the MLX90632 Far Infrared Temperature Sensor
This library provides a complete interface for the MLX90632 Far Infrared Temperature Sensor, supporting both medical and extended range measurement modes with automatic calibration loading and comprehensive temperature calculations using the Stefan-Boltzmann law.
## Features
- Complete MLX90632 register control and status monitoring
- Support for all measurement modes: Continuous, Step, and Sleeping Step
- Automatic calibration constant loading from EEPROM
- Both medical and extended range measurement support
- Stefan-Boltzmann law temperature calculations for accurate object temperature
- Double precision calibration with proper scaling factors
- Efficient new data flag handling for optimal performance
- Debug output control with preprocessor directives
- Hardware tested and verified functionality
## Dependencies
* [Adafruit BusIO](https://github.com/adafruit/Adafruit_BusIO)
@ -27,7 +41,7 @@ Note that the formatting output by `clang-format` is what the automated formatti
## About this Driver
Written by Adafruit Industries.
Written by Limor "Ladyada" Fried for Adafruit Industries with assistance from Claude Code.
MIT license, check license.txt for more information.
MIT license, check LICENSE for more information.
All text above must be included in any redistribution.

View file

@ -8,106 +8,188 @@ void setup() {
Serial.begin(115200);
while (!Serial) delay(10);
Serial.println("Adafruit MLX90632 test");
Serial.println(F("Adafruit MLX90632 test"));
if (!mlx.begin()) {
Serial.println("Failed to find MLX90632 chip");
Serial.println(F("Failed to find MLX90632 chip"));
while (1) { delay(10); }
}
Serial.println("MLX90632 Found!");
Serial.println(F("MLX90632 Found!"));
// Reset the device
if (!mlx.reset()) {
Serial.println("Device reset failed");
Serial.println(F("Device reset failed"));
while (1) { delay(10); }
}
Serial.println("Device reset: SUCCESS");
Serial.println(F("Device reset: SUCCESS"));
uint64_t productID = mlx.getProductID();
Serial.print("Product ID: 0x");
Serial.print(F("Product ID: 0x"));
Serial.print((uint32_t)(productID >> 32), HEX);
Serial.println((uint32_t)(productID & 0xFFFFFFFF), HEX);
uint16_t productCode = mlx.getProductCode();
Serial.print("Product Code: 0x");
Serial.print(F("Product Code: 0x"));
Serial.println(productCode, HEX);
uint16_t eepromVersion = mlx.getEEPROMVersion();
Serial.print(F("EEPROM Version: 0x"));
Serial.println(eepromVersion, HEX);
// Decode product code bits
uint8_t fov = (productCode >> 8) & 0x3;
uint8_t package = (productCode >> 5) & 0x7;
uint8_t accuracy = productCode & 0x1F;
Serial.print("FOV: ");
Serial.println(fov == 0 ? "50°" : "Unknown");
Serial.print(F("FOV: "));
Serial.println(fov == 0 ? F("50°") : F("Unknown"));
Serial.print("Package: ");
Serial.println(package == 1 ? "SFN 3x3" : "Unknown");
Serial.print(F("Package: "));
Serial.println(package == 1 ? F("SFN 3x3") : F("Unknown"));
Serial.print("Accuracy: ");
Serial.print(F("Accuracy: "));
if (accuracy == 1) {
Serial.println("Medical");
Serial.println(F("Medical"));
} else if (accuracy == 2) {
Serial.println("Standard");
Serial.println(F("Standard"));
} else {
Serial.println("Unknown");
Serial.println(F("Unknown"));
}
// Set and get mode (continuous)
Serial.println("\n--- Mode Settings ---");
// Set and get mode - choose one:
Serial.println(F("\n--- Mode Settings ---"));
if (!mlx.setMode(MLX90632_MODE_CONTINUOUS)) {
Serial.println("Failed to set mode to Continuous");
// if (!mlx.setMode(MLX90632_MODE_STEP)) { // Uncomment for step mode testing
// if (!mlx.setMode(MLX90632_MODE_SLEEPING_STEP)) { // Uncomment for sleeping step mode testing
Serial.println(F("Failed to set mode"));
while (1) { delay(10); }
}
mlx90632_mode_t currentMode = mlx.getMode();
Serial.print("Current mode: ");
Serial.print(F("Current mode: "));
switch (currentMode) {
case MLX90632_MODE_HALT:
Serial.println("Halt");
Serial.println(F("Halt"));
break;
case MLX90632_MODE_SLEEPING_STEP:
Serial.println("Sleeping Step");
Serial.println(F("Sleeping Step"));
break;
case MLX90632_MODE_STEP:
Serial.println("Step");
Serial.println(F("Step"));
break;
case MLX90632_MODE_CONTINUOUS:
Serial.println("Continuous");
Serial.println(F("Continuous"));
break;
default:
Serial.println("Unknown");
Serial.println(F("Unknown"));
}
// Set and get measurement select (medical)
Serial.println("\n--- Measurement Select Settings ---");
Serial.println(F("\n--- Measurement Select Settings ---"));
if (!mlx.setMeasurementSelect(MLX90632_MEAS_MEDICAL)) {
Serial.println("Failed to set measurement select to Medical");
Serial.println(F("Failed to set measurement select to Medical"));
while (1) { delay(10); }
}
mlx90632_meas_select_t currentMeasSelect = mlx.getMeasurementSelect();
Serial.print("Current measurement select: ");
Serial.print(F("Current measurement select: "));
switch (currentMeasSelect) {
case MLX90632_MEAS_MEDICAL:
Serial.println("Medical");
Serial.println(F("Medical"));
break;
case MLX90632_MEAS_EXTENDED_RANGE:
Serial.println("Extended Range");
Serial.println(F("Extended Range"));
break;
default:
Serial.println("Unknown");
Serial.println(F("Unknown"));
}
// Set and get refresh rate (default to 2Hz)
Serial.println(F("\n--- Refresh Rate Settings ---"));
if (!mlx.setRefreshRate(MLX90632_REFRESH_2HZ)) {
Serial.println(F("Failed to set refresh rate to 2Hz"));
while (1) { delay(10); }
}
mlx90632_refresh_rate_t currentRefreshRate = mlx.getRefreshRate();
Serial.print(F("Current refresh rate: "));
switch (currentRefreshRate) {
case MLX90632_REFRESH_0_5HZ:
Serial.println(F("0.5 Hz"));
break;
case MLX90632_REFRESH_1HZ:
Serial.println(F("1 Hz"));
break;
case MLX90632_REFRESH_2HZ:
Serial.println(F("2 Hz"));
break;
case MLX90632_REFRESH_4HZ:
Serial.println(F("4 Hz"));
break;
case MLX90632_REFRESH_8HZ:
Serial.println(F("8 Hz"));
break;
case MLX90632_REFRESH_16HZ:
Serial.println(F("16 Hz"));
break;
case MLX90632_REFRESH_32HZ:
Serial.println(F("32 Hz"));
break;
case MLX90632_REFRESH_64HZ:
Serial.println(F("64 Hz"));
break;
default:
Serial.println(F("Unknown"));
}
// Clear new data flag before starting continuous measurements
Serial.println(F("\\n--- Starting Continuous Measurements ---"));
if (!mlx.resetNewData()) {
Serial.println(F("Failed to reset new data flag"));
while (1) { delay(10); }
}
Serial.println(F("New data flag reset - starting measurements"));
}
void loop() {
Serial.print("Device Busy: ");
Serial.print(mlx.isBusy() ? "YES" : "NO");
Serial.print(" EEPROM Busy: ");
Serial.print(mlx.isEEPROMBusy() ? "YES" : "NO");
Serial.print(" New Data: ");
Serial.print(mlx.isNewData() ? "YES" : "NO");
Serial.print(" Cycle Position: ");
// Only check new data flag - much more efficient for continuous mode
if (mlx.isNewData()) {
Serial.print(F("New Data Available - Cycle Position: "));
Serial.println(mlx.readCyclePosition());
delay(500);
// Read ambient temperature
double ambientTemp = mlx.getAmbientTemperature();
Serial.print(F("Ambient Temperature: "));
Serial.print(ambientTemp, 4);
Serial.println(F(" °C"));
// Read object temperature
double objectTemp = mlx.getObjectTemperature();
Serial.print(F("Object Temperature: "));
if (isnan(objectTemp)) {
Serial.println(F("NaN (invalid cycle position)"));
} else {
Serial.print(objectTemp, 4);
Serial.println(F(" °C"));
}
// Reset new data flag after reading
if (!mlx.resetNewData()) {
Serial.println(F("Failed to reset new data flag"));
}
Serial.println(); // Add blank line between readings
}
// Check if we need to trigger a new measurement for step modes
mlx90632_mode_t currentMode = mlx.getMode();
if (currentMode == MLX90632_MODE_STEP || currentMode == MLX90632_MODE_SLEEPING_STEP) {
// Trigger single measurement (SOC bit) for step modes
if (!mlx.startSingleMeasurement()) {
Serial.println(F("Failed to start single measurement"));
}
}
// Small delay to prevent overwhelming the I2C bus
delay(10);
}

10
library.properties Normal file
View file

@ -0,0 +1,10 @@
name=Adafruit MLX90632 Library
version=1.0.2
author=Adafruit
maintainer=Adafruit <info@adafruit.com>
sentence=Arduino library for MLX90632 Far Infrared Temperature Sensor
paragraph=This library provides a complete interface for the MLX90632 Far Infrared Temperature Sensor. Supports both medical and extended range measurement modes, automatic calibration loading, and comprehensive temperature calculations using the Stefan-Boltzmann law.
category=Sensors
url=https://github.com/adafruit/Adafruit_MLX90632
architectures=*
depends=Adafruit BusIO