diff --git a/Adafruit_MLX90632.cpp b/Adafruit_MLX90632.cpp index 1daef48..546f82f 100644 --- a/Adafruit_MLX90632.cpp +++ b/Adafruit_MLX90632.cpp @@ -15,10 +15,15 @@ #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 +} /*! * @brief Cleans up the MLX90632 @@ -376,30 +381,32 @@ bool Adafruit_MLX90632::getCalibrations() { 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("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); + 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; } @@ -427,18 +434,141 @@ double Adafruit_MLX90632::getAmbientTemperature() { 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("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); + Serial.print(F(" RAM_6 = ")); Serial.println(ram6); + Serial.print(F(" RAM_9 = ")); Serial.println(ram9); + 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() { + // Read cycle position to determine which RAM registers to use + uint8_t cycle_pos = readCyclePosition(); + + // Read raw data from RAM registers + 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(); + + // Calculate S based on cycle position + // S = (RAM_4 + RAM_5) / 2 if cycle_pos = 2 + // S = (RAM_7 + RAM_8) / 2 if cycle_pos = 1 + double S; + 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; + } + + // Pre-calculations for object temperature + // VRTO = RAM_9 + Ka * (RAM_6 / 12) + // Ka = EE_Ka * 2^-10 (already calculated in getCalibrations()) + double VRTO = (double)ram9 + Ka * ((double)ram6 / 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)ram9 + Gb * ((double)ram6 / 12.0); + double AMB = ((double)ram6 / 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(" Cycle Position = ")); Serial.println(cycle_pos); + Serial.print(F(" RAM_4 = ")); Serial.println(ram4); + Serial.print(F(" RAM_5 = ")); Serial.println(ram5); + Serial.print(F(" RAM_6 = ")); Serial.println(ram6); + Serial.print(F(" RAM_7 = ")); Serial.println(ram7); + Serial.print(F(" RAM_8 = ")); Serial.println(ram8); + Serial.print(F(" RAM_9 = ")); Serial.println(ram9); + 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 diff --git a/Adafruit_MLX90632.h b/Adafruit_MLX90632.h index 030d8e1..abf2a31 100644 --- a/Adafruit_MLX90632.h +++ b/Adafruit_MLX90632.h @@ -185,6 +185,7 @@ public: mlx90632_refresh_rate_t getRefreshRate(); bool getCalibrations(); double getAmbientTemperature(); + double getObjectTemperature(); private: Adafruit_I2CDevice *i2c_dev; ///< Pointer to I2C bus interface @@ -214,6 +215,10 @@ private: 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 \ No newline at end of file diff --git a/examples/test_MLX90632/test_MLX90632.ino b/examples/test_MLX90632/test_MLX90632.ino index 4735b91..3939d2a 100644 --- a/examples/test_MLX90632/test_MLX90632.ino +++ b/examples/test_MLX90632/test_MLX90632.ino @@ -8,32 +8,32 @@ 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("EEPROM Version: 0x"); + Serial.print(F("EEPROM Version: 0x")); Serial.println(eepromVersion, HEX); // Decode product code bits @@ -41,129 +41,139 @@ void setup() { 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 ---"); + Serial.println(F("\n--- Mode Settings ---")); if (!mlx.setMode(MLX90632_MODE_CONTINUOUS)) { - Serial.println("Failed to set mode to Continuous"); + Serial.println(F("Failed to set mode to Continuous")); 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("\n--- Refresh Rate Settings ---"); + Serial.println(F("\n--- Refresh Rate Settings ---")); if (!mlx.setRefreshRate(MLX90632_REFRESH_2HZ)) { - Serial.println("Failed to set refresh rate to 2Hz"); + Serial.println(F("Failed to set refresh rate to 2Hz")); while (1) { delay(10); } } mlx90632_refresh_rate_t currentRefreshRate = mlx.getRefreshRate(); - Serial.print("Current refresh rate: "); + Serial.print(F("Current refresh rate: ")); switch (currentRefreshRate) { case MLX90632_REFRESH_0_5HZ: - Serial.println("0.5 Hz"); + Serial.println(F("0.5 Hz")); break; case MLX90632_REFRESH_1HZ: - Serial.println("1 Hz"); + Serial.println(F("1 Hz")); break; case MLX90632_REFRESH_2HZ: - Serial.println("2 Hz"); + Serial.println(F("2 Hz")); break; case MLX90632_REFRESH_4HZ: - Serial.println("4 Hz"); + Serial.println(F("4 Hz")); break; case MLX90632_REFRESH_8HZ: - Serial.println("8 Hz"); + Serial.println(F("8 Hz")); break; case MLX90632_REFRESH_16HZ: - Serial.println("16 Hz"); + Serial.println(F("16 Hz")); break; case MLX90632_REFRESH_32HZ: - Serial.println("32 Hz"); + Serial.println(F("32 Hz")); break; case MLX90632_REFRESH_64HZ: - Serial.println("64 Hz"); + Serial.println(F("64 Hz")); break; default: - Serial.println("Unknown"); + Serial.println(F("Unknown")); } // Load calibration constants - Serial.println("\n--- Calibration Constants ---"); + Serial.println(F("\n--- Calibration Constants ---")); if (!mlx.getCalibrations()) { - Serial.println("Failed to load calibration constants"); + Serial.println(F("Failed to load calibration constants")); while (1) { delay(10); } } - Serial.println("Calibration constants loaded successfully"); + Serial.println(F("Calibration constants loaded successfully")); } 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: "); + Serial.print(F("Device Busy: ")); + Serial.print(mlx.isBusy() ? F("YES") : F("NO")); + Serial.print(F(" EEPROM Busy: ")); + Serial.print(mlx.isEEPROMBusy() ? F("YES") : F("NO")); + Serial.print(F(" New Data: ")); + Serial.print(mlx.isNewData() ? F("YES") : F("NO")); + Serial.print(F(" Cycle Position: ")); Serial.println(mlx.readCyclePosition()); // Read ambient temperature double ambientTemp = mlx.getAmbientTemperature(); - Serial.print("Ambient Temperature: "); + Serial.print(F("Ambient Temperature: ")); Serial.print(ambientTemp, 4); - Serial.println(" °C"); + 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")); + } delay(500); } \ No newline at end of file