nicebadge/examples/thermal-camera/main.go

103 lines
2.4 KiB
Go
Raw Normal View History

2026-04-18 11:01:06 +00:00
package main
// Thermal camera example using AMG88xx 8x8 IR sensor.
// Connect the sensor to I2C1: SDA=P0_17, SCL=P0_20 (3.3V power).
//
// The 8x8 sensor data is scaled to 24x24 with bilinear interpolation and
// rendered with an iron-palette color map on the 240x135 display.
// Each scaled pixel is drawn as a 10x5 px block → 240x120 total, centered.
import (
"image"
"machine"
draw2 "golang.org/x/image/draw"
"tinygo.org/x/drivers/amg88xx"
"tinygo.org/x/drivers/st7789"
)
const (
sensorSize = 8 // AMG88xx is 8x8
scaledSize = 24 // intermediate bilinear-scaled image
blockW = 10 // px per scaled pixel (horizontal): 24*10 = 240
blockH = 5 // px per scaled pixel (vertical): 24*5 = 120
yOffset = 7 // center 120px vertically in 135px display: (135-120)/2
)
var (
display st7789.Device
data [sensorSize * sensorSize]int16
)
func main() {
machine.SPI0.Configure(machine.SPIConfig{
SCK: machine.P1_01,
SDO: machine.P1_02,
Frequency: 8000000,
Mode: 0,
})
machine.I2C1.Configure(machine.I2CConfig{
SDA: machine.P0_17,
SCL: machine.P0_20,
Frequency: 400000,
})
display = st7789.New(machine.SPI0,
machine.P1_15, // TFT_RESET
machine.P1_13, // TFT_DC
machine.P0_10, // TFT_CS
machine.P0_09) // TFT_LITE
display.Configure(st7789.Config{
Rotation: st7789.ROTATION_90,
Width: 135,
Height: 240,
RowOffset: 40,
ColumnOffset: 53,
})
camera := amg88xx.New(machine.I2C1)
camera.Configure(amg88xx.Config{})
src := image.NewRGBA(image.Rect(0, 0, sensorSize, sensorSize))
dst := image.NewRGBA(image.Rect(0, 0, scaledSize, scaledSize))
for {
camera.ReadPixels(&data)
// map each sensor pixel to a palette color
for j := 0; j < sensorSize; j++ {
for i := 0; i < sensorSize; i++ {
v := data[63-(i+j*sensorSize)]
// clamp to 18°C33°C range → index 0432
if v < 18000 {
v = 0
} else {
v = (v - 18000) / 36
if v > 432 {
v = 432
}
}
src.Set(i, j, colors[v])
}
}
// bilinear upscale 8x8 → 24x24
draw2.BiLinear.Scale(dst, dst.Bounds(), src, src.Bounds(), draw2.Over, nil)
// draw horizontally mirrored (acts as a selfie mirror)
for j := 0; j < scaledSize; j++ {
for i := 0; i < scaledSize; i++ {
display.FillRectangle(
int16((scaledSize-1-i)*blockW),
yOffset+int16(j)*blockH,
blockW, blockH,
dst.RGBAAt(i, j),
)
}
}
}
}