14 KiB
IR All-Sky Camera Documentation
Project Overview
This firmware implements an infrared all-sky camera system based on up to five MLX90640 thermal imaging sensors (32×24 pixels each), designed for cloud monitoring at the Special Astrophysical Observatory of the Russian Academy of Sciences. The system automates sky quality assessment for 0.5-meter telescopes of the "Astro-M" complex.
The device continuously captures thermal images from multiple sensors (Zenith, East, South, West, North), processes environmental data from a BMP280 pressure/humidity/temperature sensor, and controls PWM heaters to prevent dew formation on optics.
Repository: https://github.com/eddyem/stm32samples/tree/master/F3%3AF303/MLX90640-allsky
Hardware Requirements
Microcontroller
- STM32F303 series (tested on F303)
- 72 MHz system clock (HSE) or 48 MHz (HSI)
Sensors
| Sensor | Interface | Purpose |
|---|---|---|
| MLX90640 (up to 5) | I²C (400 kHz) | Thermal imaging (32×24 pixels, -40..+300°C) |
| BMP280 / BME280 | SPI | Ambient temperature, pressure, humidity |
| NTC thermistors (4) | ADC (12-bit) | Heater feedback / backup temperature |
Outputs
- DAC output (PA5) – Onboard heater control (0–3.3V)
- PWM channels (TIM3) – External heaters (PA6, PA7, PB0, PB1)
- UART1 – Main debug/auxiliary interface
- USB (CDC class) – Secondary interface
Pin Assignment (STM32F303)
| Pin | Function | Notes |
|---|---|---|
| PA0–PA3 | NTC1–NTC4 | ADC inputs |
| PA4 | DAC_OUT1 | Onboard heater |
| PA5 | ADC_IN2 | DAC feedback |
| PA6, PA7 | PWM CH1, CH2 | TIM3_CH1, TIM3_CH2 (external heaters) |
| PA9, PA10 | UART1 TX/RX | Controlling interface |
| PA11, PA12 | USB DP/DM | CDC virtual COM |
| PA13, PA14 | SWD | Programming/debug |
| PA15 | USB DP pullup | USB pullup management for development board |
| PB0, PB1 | PWM CH3, CH4 | TIM3_CH3, CH4 (sky-quality + T-proportional) |
| PB3, PB4, PB5 | SPI SCK, MISO, MOSI | BMP280 interface |
| PB6, PB7 | I²C1 SCL/SDA | MLX90640 bus |
| PB9 | SPI CS | BMP280 chip select |
Firmware Architecture
The firmware is event-driven with a state machine for MLX90640 data acquisition and a command parser for bidirectional communication over USB and UART.
Core Modules
| File | Responsibility |
|---|---|
main.c |
Main loop, SysTick, initialization, state machine orchestration |
adc.c/h |
ADC setup (DMA, median filtering), NTC reading, Vdd measurement |
i2c.c/h |
I²C low-level + DMA, multi-word transfers, bus scanning |
mlxproc.c/h |
MLX90640 state machine (5 sensors), parameter caching, image processing |
mlx90640.c/h |
MLX90640 calibration math (from Melexis datasheet) |
BMP280.c/h |
SPI-based BMP280/BME280 driver |
heater.c/h |
PWM heater control, dew-point avoidance logic |
commproto.cpp/h |
Command parser (USB/UART), ASCII maps, binary image export |
usb_dev.c/h |
USB CDC stack (bulk endpoints, ring buffers) |
usart.c/h |
UART driver with DMA and ring buffers |
spi.c/h |
SPI driver for BMP280 |
ringbuffer.c/h |
Circular buffers |
strfunc.c/h |
Number/string conversions, hexdumps |
Data Flow
MLX90640 sensors (I²C DMA)
↓
mlxproc.c (state machine, 5× parameter loading)
↓
mlx90640.c (pixel-by-pixel temperature calculation)
↓
user requests via USB/UART (ASCII map, binary, temperature map)
BMP280 (SPI)
↓
Temperature, Pressure, Humidity, Dew point
↓
Heater logic (auto or setpoint) → PWM outputs
ADC (DMA + median filter)
↓
NTC temperatures, MCU temperature, Vdd
MLX90640 Processing
State Machine (mlxproc.c)
The firmware manages up to 5 sensors (addresses 0x10..0x14 left-shifted for I²C). The state machine cycles through:
- NOTINIT – Read calibration parameters (832 words) from each sensor over I²C DMA.
- WAITPARAMS – Wait for DMA completion, calculate parameters via
get_parameters(). - WAITSUBPAGE – Poll
REG_STATUSforNEWDATAflag, skip subpage 0. - READSUBPAGE – Read image data (832 words) via DMA, process with
process_image().
Key features:
- Automatic sensor exclusion after
MLX_MAX_ERRORS(default 11) consecutive errors. Tlastimage[]timestamps for each sensor (used by "cartoon mode" and timeout recovery).- Supports changing sensor I²C addresses at runtime (
mlx_sethwaddr()).
Temperature Calculation (mlx90640.c)
Implementation follows Melexis MLX90640 Datasheet (Section 11):
- Supply voltage compensation (
kVdd,vdd25) - Ambient temperature (
KvPTAT,KtPTAT,vPTAT25) - Gain normalization
- Offset correction (per-pixel
offset[],kta[],kv[]) - TGC (temperature gradient compensation)
- Pixel sensitivity (
alpha[]) + KsTa, KsTo, range detection (CT3, CT4) - Extended temperature range (up to 400°C) via
alphacorr[]
Output: float array of 768 temperatures (°C) per sensor.
Environmental & Heater Control
BMP280/BME280 Readings
- Temperature (°C)
- Pressure (Pa, also converted to hPa and mmHg)
- Humidity (%)
- Dew point (°C) – calculated via Magnus formula.
Measurements are triggered every ENV_MEAS_PERIOD (10 seconds) in forced mode.
Heater Modes
| Mode | Activation | Behavior |
|---|---|---|
Auto (autoheater=1) |
Humidity > 90% | Sets holding temperature = ambient + dew_over_delta (7°C) + 0.5°C (min 5°C) |
Hold (setheater N) |
Manual command | Maintains N°C ±5°C hysteresis using PWM |
| Off | Default | All heaters disabled |
PWM channels:
ch0,ch1– External heaters (controlled identically for symmetry)ch2– Proportional to ambient temperature:(T_ambient + 20) × 2%, clamped 0–100ch3– Proportional to(35°C – T_ambient + T_sky) × 2.5%, clamped 0–100
Anti-overheating: If any NTC exceeds setpoint by >5°C, PWM is reduced to PWM_HOLD_VAL (10%).
Command Protocol
The device presents a virtual COM port (CDC) over USB and a second UART interface. Both share the same command set.
Command Syntax
<command> [parameter] [= value]
- Commands are case-sensitive (lowercase).
- Parameters are decimal (or hex with
0x, binary withb). - Use
helpfor full list. - Lines end with
\n(newline).
Core Commands
MLX90640 Image Commands
| Command | Description | Example |
|---|---|---|
ascii n |
Draw ASCII-art thermal map (16 grayscale levels) | ascii 0 |
tempmap n |
Print numeric temperature grid (°C) | tempmap 1 |
binary n |
Dump raw float array (768×4 bytes) | binary 2 |
acqtime n |
Show last image acquisition timestamp (ms) | acqtime 3 |
cartoon |
Toggle live ASCII animation over USB | cartoon |
Sensor Management
| Command | Description | Example |
|---|---|---|
listids |
List active I²C addresses of all sensors | listids |
mlxaddr n [= addr] |
Get/set I²C address for sensor n (0–4) | mlxaddr 2 = 0x15 |
mlxdump n |
Dump all calibration parameters for sensor n | mlxdump 0 |
state |
Show MLX90640 state machine status | state |
mlxpause / mlxcont / mlxstop |
Control acquisition | – |
Environment
| Command | Description | Example |
|---|---|---|
environ |
Print T, P, H, dew point, Tsky | environ |
bmereinit |
Reinitialize BMP280 | – |
ntc [n] |
Get NTC temperature (°C) for channel n (0–3) | ntc 1 |
Heater Control
| Command | Description | Example |
|---|---|---|
autoheater [= 0/1] |
Enable/disable automatic dew avoidance | autoheater = 1 |
setheater [= N] |
Set holding temperature (°C) | setheater = 25 |
clearheater |
Disable holding, turn off heaters | – |
pwm [n] [= val] |
Get/set PWM duty cycle (0–100%) for channel n | pwm 0 = 50 |
System & Debug
| Command | Description | Example |
|---|---|---|
adc [n] |
Get raw ADC value (0–4095) for channel n | adc 4 |
dac [= val] |
Get/set DAC output (0–4095) | dac = 2048 |
mcutemp |
Show MCU internal temperature (°C) | – |
mcuvdd |
Show Vdd (V) | – |
time |
Show SysTick counter (ms) | – |
reset |
Software reset | – |
sendstr = text |
Forward string to other interface | sendstr = "hello" |
Raw I²C (for debugging)
| Command | Description | Example |
|---|---|---|
iicscan |
Scan I²C bus, report found addresses | – |
iicaddr [= addr] |
Get/set current I²C target address (non-shifted) | iicaddr = 0x33 |
iicspeed [= n] |
Set I²C speed: 0=10k,1=100k,2=400k,3=1M,4=2M | – |
readreg reg [= n] |
Read n 16-bit registers | readreg 0x8000 |
writedata = v1 v2 ... |
Write 16-bit values | writedata = 0x1234 0x5678 |
hwaddr addr |
Change hardware I²C address of MLX90640 (non-shifted) | hwaddr 0x15 |
Output Formats
ASCII Thermal Map (ascii)
RANGE=12.345
MIN=-5.67
MAX=6.78
.':;+*oxX#&%B$@
.':;+*oxX#&%B$@
...
Uses 16‑character grayscale ramp: space (coldest) → @ (hottest).
Numeric Map (tempmap)
-2.3 1.2 3.4 ...
5.6 7.8 9.0 ...
...
Binary Image (binary n)
BINARY0=<768×4 bytes raw float array>ENDIMAGE\n
Little-endian IEEE 754 float values. Exact size: 3072 bytes per image.
Environment Output
TEMPERATURE=22.34
SKYTEMPERATURE=-15.67
PRESSURE_HPA=1013.25
PRESSURE_MM=759.98
HUMIDITY=65.20
TEMP_DEW=15.60
T_MEASUREMENT=12345678
Sensors location
| N | ID | Cardinal direction |
|---|---|---|
| 0 | 0x10 | Zenith |
| 1 | 0x11 | East |
| 2 | 0x12 | South |
| 3 | 0x13 | West |
| 4 | 0x14 | North |
Building & Flashing
Prerequisites
- ARM GCC toolchain (
arm-none-eabi-gcc) - Make
- st-flash, stm32flash or OpenOCD (for flashing MCU)
Build
git clone --depth=1 https://github.com/eddyem/stm32samples.git
cd stm32samples/F3:F303/MLX90640-allsky
make
Configuration
EBUGflag in Makefile enables debug output (slower, but verbose).NOCAN– define if CAN peripheral not used (increases USB buffer).BUILD_NUMBERandBUILD_DATEinversion.incare auto-generated.
Flashing
make flash # for flashing over st-link
# make boot - for flashing over USART1 bootloader
# make dfuboot - for flashing over USB-DFU
or manually (over st-link):
openocd -f interface/stlink.cfg -f target/stm32f3x.cfg -c "program build/firmware.elf verify reset exit"
udev Rule (Linux)
Create /etc/udev/rules.d/99-USBiinterface.rules:
ACTION=="add", ENV{USB_IDS}=="0483:5740", ATTRS{interface}=="?*", PROGRAM="/bin/bash -c \"ls /dev | grep $attr{interface} | wc -l \"", SYMLINK+="$attr{interface}%c", MODE="0666", GROUP="tty"
After replug, device appears as /dev/ir-allsky0.
Calibration & Tuning
NTC Thermistors
The firmware includes a lookup table (LUT) for NTC with B=3950, R25=1000Ω, covering -10..+59°C.
If a sensor reads <5 ADC → short circuit; >4090 ADC → open circuit.
To calibrate your NTCs, modify Rlut[] in adc.c.
MLX90640
Calibration data is per-sensor and stored in EEPROM. The firmware reads it automatically on startup. No user calibration is required – the factory data is used.
Vdd Measurement
Internal Vrefint (1.2V typical) and factory calibration value *VREFINT_CAL_ADDR are used:
Vdd = (VREFINT_CAL * 3.3) / ADC_VREF
MCU Temperature
Internal sensor (ADC1_IN16) uses factory calibration TEMP30_CAL_ADDR and TEMP110_CAL_ADDR.
Troubleshooting
No USB device detected
- Check
USBPU_ON()inmain.c– some boards need pull-up control, others constant. - Verify PA11/PA12 are configured as AF14.
- Try different USB cable / port.
I²C communication errors
- Reduce speed:
iicspeed = 1(100 kHz). - Check pull-up resistors (2.2k–10kΩ on SCL/SDA).
- Use
iicscanto verify sensor addresses.
BMP280 not responding
- Check SPI wiring (PB3–PB5, PB9 CS).
- Try software reset:
bmereinit. - Verify chip ID: should be 0x58 (BMP280) or 0x60 (BME280).
Heater not working
- Check
autoheaterstatus:autoheater. - Verify NTC readings:
ntc. - For manual mode:
setheater = 25, observepwm.
Cartoon mode stops updating
- Cartoon mode works only over USB, not UART.
- If no new images, check
state– may be paused or in error.
Watchdog resets
- IWDG period is 2 seconds. Long operations (e.g., full calibration dump) may trigger reset.
- Disable in debug builds (remove
IWDG_REFRESHcalls or undefineEBUG).
Performance & Limitations
| Parameter | Value |
|---|---|
| Image update rate (5 sensors) | ~1–2 Hz (depends on I²C speed) |
| RAM usage | ~39 kB (image buffers + parameters) |
| Flash usage | ~26 kB |
| Maximum sensors | 5 (limited by I²C address space 0x10–0x14) |
| Temperature range | -40..+300°C (MLX90640) |
| Accuracy | ±1°C (typical after calibration) |
License
All source files are licensed under GNU General Public License v3.0 unless stated otherwise.
The BMP280 driver includes MIT-licensed code from sheinz (2016).
Credits & References
- Author: Edward V. Emelianov edward.emelianoff@gmail.com
- MLX90640 Datasheet: Melexis (Document REV 10)
- BMP280 Datasheet: Bosch Sensortec
- STM32F303 Reference Manual: STMicroelectronics (RM0316)
For questions, bug reports, or contributions, please use the GitHub issue tracker.