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>
This commit is contained in:
ladyada 2025-08-01 20:38:53 -04:00
parent dfa2379739
commit 374dbc0494
3 changed files with 290 additions and 0 deletions

View file

@ -87,6 +87,16 @@ uint16_t Adafruit_MLX90632::getProductCode() {
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
@ -249,6 +259,186 @@ bool Adafruit_MLX90632::isNewData() {
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
// Debug: Print calibration constants
Serial.println("Calibration constants:");
Serial.print("P_R = "); Serial.println(P_R, 8);
Serial.print("P_G = "); Serial.println(P_G, 8);
Serial.print("P_T = "); Serial.println(P_T, 12);
Serial.print("P_O = "); Serial.println(P_O, 8);
Serial.print("Aa = "); Serial.println(Aa, 8);
Serial.print("Ab = "); Serial.println(Ab, 8);
Serial.print("Ba = "); Serial.println(Ba, 8);
Serial.print("Bb = "); Serial.println(Bb, 8);
Serial.print("Ca = "); Serial.println(Ca, 8);
Serial.print("Cb = "); Serial.println(Cb, 8);
Serial.print("Da = "); Serial.println(Da, 8);
Serial.print("Db = "); Serial.println(Db, 8);
Serial.print("Ea = "); Serial.println(Ea, 8);
Serial.print("Eb = "); Serial.println(Eb, 8);
Serial.print("Fa = "); Serial.println(Fa, 12);
Serial.print("Fb = "); Serial.println(Fb, 10);
Serial.print("Ga = "); Serial.println(Ga, 10);
Serial.print("Gb = "); Serial.println(Gb, 8);
Serial.print("Ka = "); Serial.println(Ka, 8);
Serial.print("Kb = "); Serial.println(Kb);
Serial.print("Ha = "); Serial.println(Ha, 8);
Serial.print("Hb = "); Serial.println(Hb, 8);
return true;
}
/*!
* @brief Calculate ambient temperature
* @return Ambient temperature in degrees Celsius
*/
double Adafruit_MLX90632::getAmbientTemperature() {
// Read raw data from RAM registers
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);
int16_t ram6 = (int16_t)ram6_reg.read();
int16_t ram9 = (int16_t)ram9_reg.read();
// Pre-calculations for ambient temperature
// Gb = EE_Gb * 2^-10 (already calculated in getCalibrations())
double VRTA = (double)ram9 + Gb * ((double)ram6 / 12.0);
double AMB = ((double)ram6 / 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);
// Debug output
Serial.print("RAM_6 = "); Serial.println(ram6);
Serial.print("RAM_9 = "); Serial.println(ram9);
Serial.print("Gb = "); Serial.println(Gb, 8);
Serial.print("VRTA = "); Serial.println(VRTA, 8);
Serial.print("AMB = "); Serial.println(AMB, 8);
Serial.print("AMB - P_R = "); Serial.println(amb_diff, 8);
Serial.print("Ambient Temp = "); Serial.println(ambient_temp, 8);
return ambient_temp;
}
/*!
* @brief Byte swap helper for register addresses
* @param value 16-bit value to swap

View file

@ -141,6 +141,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;
/*=========================================================================*/
/*!
@ -154,6 +168,7 @@ public:
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 +181,39 @@ 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();
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
};
#endif

View file

@ -32,6 +32,10 @@ void setup() {
Serial.print("Product Code: 0x");
Serial.println(productCode, HEX);
uint16_t eepromVersion = mlx.getEEPROMVersion();
Serial.print("EEPROM Version: 0x");
Serial.println(eepromVersion, HEX);
// Decode product code bits
uint8_t fov = (productCode >> 8) & 0x3;
uint8_t package = (productCode >> 5) & 0x7;
@ -97,6 +101,52 @@ void setup() {
default:
Serial.println("Unknown");
}
// Set and get refresh rate (default to 2Hz)
Serial.println("\n--- Refresh Rate Settings ---");
if (!mlx.setRefreshRate(MLX90632_REFRESH_2HZ)) {
Serial.println("Failed to set refresh rate to 2Hz");
while (1) { delay(10); }
}
mlx90632_refresh_rate_t currentRefreshRate = mlx.getRefreshRate();
Serial.print("Current refresh rate: ");
switch (currentRefreshRate) {
case MLX90632_REFRESH_0_5HZ:
Serial.println("0.5 Hz");
break;
case MLX90632_REFRESH_1HZ:
Serial.println("1 Hz");
break;
case MLX90632_REFRESH_2HZ:
Serial.println("2 Hz");
break;
case MLX90632_REFRESH_4HZ:
Serial.println("4 Hz");
break;
case MLX90632_REFRESH_8HZ:
Serial.println("8 Hz");
break;
case MLX90632_REFRESH_16HZ:
Serial.println("16 Hz");
break;
case MLX90632_REFRESH_32HZ:
Serial.println("32 Hz");
break;
case MLX90632_REFRESH_64HZ:
Serial.println("64 Hz");
break;
default:
Serial.println("Unknown");
}
// Load calibration constants
Serial.println("\n--- Calibration Constants ---");
if (!mlx.getCalibrations()) {
Serial.println("Failed to load calibration constants");
while (1) { delay(10); }
}
Serial.println("Calibration constants loaded successfully");
}
void loop() {
@ -109,5 +159,11 @@ void loop() {
Serial.print(" Cycle Position: ");
Serial.println(mlx.readCyclePosition());
// Read ambient temperature
double ambientTemp = mlx.getAmbientTemperature();
Serial.print("Ambient Temperature: ");
Serial.print(ambientTemp, 4);
Serial.println(" °C");
delay(500);
}