From d38f88cb806fd5df209b63eddf7a5e5788827eff Mon Sep 17 00:00:00 2001 From: Limor Fried Date: Fri, 18 Jul 2025 19:25:34 -0400 Subject: [PATCH] Initial commit: MLX90632 Far Infrared Temperature Sensor library MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Basic I2C communication with byte-swapped register addresses - Chip detection via product code register - getProductID() function for 48-bit unique product ID - Test example sketch - GitHub CI workflow and clang-format configuration 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .clang-format | 13 +++ .github/workflows/githubci.yml | 32 ++++++ .gitignore | 3 + Adafruit_MLX90632.cpp | 87 ++++++++++++++ Adafruit_MLX90632.h | 140 +++++++++++++++++++++++ README.md | 33 ++++++ examples/test_MLX90632/test_MLX90632.ino | 27 +++++ 7 files changed, 335 insertions(+) create mode 100644 .clang-format create mode 100644 .github/workflows/githubci.yml create mode 100644 .gitignore create mode 100644 Adafruit_MLX90632.cpp create mode 100644 Adafruit_MLX90632.h create mode 100644 README.md create mode 100644 examples/test_MLX90632/test_MLX90632.ino diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..ed70f56 --- /dev/null +++ b/.clang-format @@ -0,0 +1,13 @@ +Language: Cpp +BasedOnStyle: Google +IndentWidth: 2 +ColumnLimit: 80 +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +BinPackArguments: true +BinPackParameters: true +BreakBeforeBraces: Attach +DerivePointerAlignment: false +PointerAlignment: Left +SpacesBeforeTrailingComments: 1 \ No newline at end of file diff --git a/.github/workflows/githubci.yml b/.github/workflows/githubci.yml new file mode 100644 index 0000000..d871708 --- /dev/null +++ b/.github/workflows/githubci.yml @@ -0,0 +1,32 @@ +name: Arduino Library CI + +on: [pull_request, push, repository_dispatch] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/setup-python@v1 + with: + python-version: '3.x' + - uses: actions/checkout@v2 + - uses: actions/checkout@v2 + with: + repository: adafruit/ci-arduino + path: ci + + - name: pre-install + run: bash ci/actions_install.sh + + - name: clang + run: python3 ci/run-clang-format.py -e "ci/*" -e "bin/*" -r . + + - name: test platforms + run: python3 ci/build_platform.py main_platforms + + - name: doxygen + env: + GH_REPO_TOKEN: ${{ secrets.GH_REPO_TOKEN }} + PRETTYNAME : "Adafruit MLX90632 Arduino Library" + run: bash ci/doxy_gen_and_deploy.sh \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..971b67e --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.pdf +*.txt +.DS_Store \ No newline at end of file diff --git a/Adafruit_MLX90632.cpp b/Adafruit_MLX90632.cpp new file mode 100644 index 0000000..248cfb5 --- /dev/null +++ b/Adafruit_MLX90632.cpp @@ -0,0 +1,87 @@ +/*! + * @file Adafruit_MLX90632.cpp + * + * I2C Driver for MLX90632 Far Infrared Temperature Sensor + * + * This is a library for the Adafruit MLX90632 breakout: + * http://www.adafruit.com/products + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing products from + * Adafruit! + * + * BSD license (see license.txt) + */ + +#include "Adafruit_MLX90632.h" + +/*! + * @brief Instantiates a new MLX90632 class + */ +Adafruit_MLX90632::Adafruit_MLX90632() {} + +/*! + * @brief Cleans up the MLX90632 + */ +Adafruit_MLX90632::~Adafruit_MLX90632() { + if (i2c_dev) { + delete i2c_dev; + } +} + +/*! + * @brief Sets up the hardware and initializes I2C + * @param i2c_address + * The I2C address to be used. + * @param wire + * 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) { + if (i2c_dev) { + delete i2c_dev; + } + i2c_dev = new Adafruit_I2CDevice(i2c_address, wire); + + if (!i2c_dev->begin()) { + return false; + } + + 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; + } + + return true; +} + +/*! + * @brief Read the 48-bit product ID + * @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); + + uint16_t id0 = id0_reg.read(); + uint16_t id1 = id1_reg.read(); + uint16_t id2 = id2_reg.read(); + + return ((uint64_t)id2 << 32) | ((uint64_t)id1 << 16) | id0; +} + +/*! + * @brief Byte swap helper for register addresses + * @param value 16-bit value to swap + * @return Byte-swapped value + */ +uint16_t Adafruit_MLX90632::swapBytes(uint16_t value) { + return (value << 8) | (value >> 8); +} \ No newline at end of file diff --git a/Adafruit_MLX90632.h b/Adafruit_MLX90632.h new file mode 100644 index 0000000..2a6d87b --- /dev/null +++ b/Adafruit_MLX90632.h @@ -0,0 +1,140 @@ +/*! + * @file Adafruit_MLX90632.h + * + * I2C Driver for MLX90632 Far Infrared Temperature Sensor + * + * This is a library for the Adafruit MLX90632 breakout: + * http://www.adafruit.com/products + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing products from + * Adafruit! + * + * BSD license (see license.txt) + */ + +#ifndef _ADAFRUIT_MLX90632_H +#define _ADAFRUIT_MLX90632_H + +#include "Arduino.h" +#include +#include +#include + +/*========================================================================= + I2C ADDRESS/BITS + -----------------------------------------------------------------------*/ +#define MLX90632_DEFAULT_ADDR 0x3A ///< MLX90632 default i2c address +/*=========================================================================*/ + +/*========================================================================= + REGISTERS + -----------------------------------------------------------------------*/ +// EEPROM addresses +#define MLX90632_REG_MELEXIS_RESERVED0 0x2400 ///< Melexis reserved +#define MLX90632_REG_MELEXIS_RESERVED1 0x2401 ///< Melexis reserved +#define MLX90632_REG_MELEXIS_RESERVED2 0x2402 ///< Melexis reserved +#define MLX90632_REG_MELEXIS_RESERVED3 0x2403 ///< Melexis reserved +#define MLX90632_REG_MELEXIS_RESERVED4 0x2404 ///< Melexis reserved +#define MLX90632_REG_ID0 0x2405 ///< Chip ID +#define MLX90632_REG_ID1 0x2406 ///< Chip ID +#define MLX90632_REG_ID2 0x2407 ///< Chip ID +#define MLX90632_REG_ID_CRC16 0x2408 ///< CRC +#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_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) +#define MLX90632_REG_MELEXIS_RESERVED49 0x2431 ///< Melexis reserved +#define MLX90632_REG_MELEXIS_RESERVED127 0x247F ///< Melexis reserved +#define MLX90632_REG_MELEXIS_RESERVED128 0x2480 ///< Melexis reserved +#define MLX90632_REG_EE_HA 0x2481 ///< Ha Customer calibration constant (16 bit) +#define MLX90632_REG_EE_HB 0x2482 ///< Hb Customer calibration constant (16 bit) +#define MLX90632_REG_MELEXIS_RESERVED131 0x2483 ///< Melexis reserved +#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_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 +#define MLX90632_REG_EE_MEAS_2 0x24E2 ///< Measurement settings 2 + +// Control and Status registers +#define MLX90632_REG_I2C_ADDRESS 0x3000 ///< I2C slave address >> 1 +#define MLX90632_REG_CONTROL 0x3001 ///< Control register, measurement mode +#define MLX90632_REG_STATUS 0x3FFF ///< Status register: data available + +// RAM addresses +#define MLX90632_REG_RAM_1 0x4000 ///< Raw data 1 +#define MLX90632_REG_RAM_2 0x4001 ///< Raw data 2 +#define MLX90632_REG_RAM_3 0x4002 ///< Raw data 3 +#define MLX90632_REG_RAM_4 0x4003 ///< Raw data 4 +#define MLX90632_REG_RAM_5 0x4004 ///< Raw data 5 +#define MLX90632_REG_RAM_6 0x4005 ///< Raw data 6 +#define MLX90632_REG_RAM_7 0x4006 ///< Raw data 7 +#define MLX90632_REG_RAM_8 0x4007 ///< Raw data 8 +#define MLX90632_REG_RAM_9 0x4008 ///< Raw data 9 +#define MLX90632_REG_RAM_52 0x4033 ///< Raw data 52 +#define MLX90632_REG_RAM_53 0x4034 ///< Raw data 53 +#define MLX90632_REG_RAM_54 0x4035 ///< Raw data 54 +#define MLX90632_REG_RAM_55 0x4036 ///< Raw data 55 +#define MLX90632_REG_RAM_56 0x4037 ///< Raw data 56 +#define MLX90632_REG_RAM_57 0x4038 ///< Raw data 57 +#define MLX90632_REG_RAM_58 0x4039 ///< Raw data 58 +#define MLX90632_REG_RAM_59 0x403A ///< Raw data 59 +#define MLX90632_REG_RAM_60 0x403B ///< Raw data 60 +/*=========================================================================*/ + +/*! + * @brief Class that stores state and functions for interacting with + * MLX90632 Far Infrared Temperature Sensor + */ +class Adafruit_MLX90632 { +public: + Adafruit_MLX90632(); + ~Adafruit_MLX90632(); + bool begin(uint8_t i2c_addr = MLX90632_DEFAULT_ADDR, TwoWire *wire = &Wire); + uint64_t getProductID(); + +private: + Adafruit_I2CDevice *i2c_dev; ///< Pointer to I2C bus interface + uint16_t swapBytes(uint16_t value); ///< Byte swap helper for register addresses +}; + +#endif \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..f247c27 --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +# Adafruit MLX90632 [![Build Status](https://github.com/adafruit/Adafruit_MLX90632/workflows/Arduino%20Library%20CI/badge.svg)](https://github.com/adafruit/Adafruit_MLX90632/actions) + +Arduino library for the MLX90632 Far Infrared Temperature Sensor + +## Dependencies + * [Adafruit BusIO](https://github.com/adafruit/Adafruit_BusIO) + +## Contributing + +Contributions are welcome! Please read our [Code of Conduct](https://github.com/adafruit/Adafruit_MLX90632/blob/main/CODE_OF_CONDUCT.md) +before contributing to help this project stay welcoming. + +## Documentation and doxygen +Documentation is produced by doxygen. Contributions should include documentation for any new features. + +## Formatting and clang-format +This library uses [`clang-format`](https://releases.llvm.org/download.html) to standardize the formatting of `.cpp` and `.h` files. +Contributions should be formatted using `clang-format`: + +The `-i` flag will make the changes to the file. +```bash +clang-format -i *.cpp *.h +``` +If you prefer to make the changes yourself, running `clang-format` without the `-i` flag will print out a formatted version of the file. You can save this to a file and diff it against the original to see the changes. + +Note that the formatting output by `clang-format` is what the automated formatting checker will expect. Any pull requests that don't conform to the formatting style will not pass the automated checks. + +## About this Driver + +Written by Adafruit Industries. + +MIT license, check license.txt for more information. +All text above must be included in any redistribution. \ No newline at end of file diff --git a/examples/test_MLX90632/test_MLX90632.ino b/examples/test_MLX90632/test_MLX90632.ino new file mode 100644 index 0000000..027bdb9 --- /dev/null +++ b/examples/test_MLX90632/test_MLX90632.ino @@ -0,0 +1,27 @@ +// Basic test sketch for Adafruit MLX90632 Far Infrared Temperature Sensor + +#include "Adafruit_MLX90632.h" + +Adafruit_MLX90632 mlx = Adafruit_MLX90632(); + +void setup() { + Serial.begin(115200); + while (!Serial) delay(10); + + Serial.println("Adafruit MLX90632 test"); + + if (!mlx.begin()) { + Serial.println("Failed to find MLX90632 chip"); + while (1) { delay(10); } + } + Serial.println("MLX90632 Found!"); + + uint64_t productID = mlx.getProductID(); + Serial.print("Product ID: 0x"); + Serial.print((uint32_t)(productID >> 32), HEX); + Serial.println((uint32_t)(productID & 0xFFFFFFFF), HEX); +} + +void loop() { + delay(500); +} \ No newline at end of file