Skip to content

Instantly share code, notes, and snippets.

@recantha
Created March 21, 2021 18:42
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save recantha/a4a893e1e5b7ec48d234f2409b1996b8 to your computer and use it in GitHub Desktop.
Save recantha/a4a893e1e5b7ec48d234f2409b1996b8 to your computer and use it in GitHub Desktop.
# AMG8833 thermal camera on an ILI9341 320x240 screen
# Michael Horne, based on Jan Goolsbey's code for Adafruit
# https://learn.adafruit.com/pygamer-thermal-camera-amg8833?view=all
import time
import board
import displayio
import busio
from simpleio import map_range
from adafruit_display_text.label import Label
from adafruit_display_shapes.rect import Rect
import adafruit_amg88xx
import adafruit_ili9341
# Helper functions
def celsius_to_fahrenheit(deg_c=None): # convert C to F; round to 1 degree C
return round(((9 / 5) * deg_c) + 32)
def fahrenheit_to_celsius(deg_f=None): # convert F to C; round to 1 degree F
return round((deg_f - 32) * (5 / 9))
def element_grid(col0, row0): # Determine display coordinates for column, row
x = int(ELEMENT_SIZE * col0 + 30) # x coord + margin
y = int(ELEMENT_SIZE * row0 + 1) # y coord + margin
return x, y # Return display coordinates
def update_image_frame(): # Get camera data and display
minimum = MAX_SENSOR_C # Set minimum to sensor's maximum C value
maximum = MIN_SENSOR_C # Set maximum to sensor's minimum C value
sum_bucket = 0 # Clear bucket for building average value
for row1 in range(0, 8): # Parse camera data list and update display
for col1 in range(0, 8):
value = map_range(image[7 - row1][7 - col1],
MIN_SENSOR_C, MAX_SENSOR_C,
MIN_SENSOR_C, MAX_SENSOR_C)
color_index = int(map_range(value, MIN_RANGE_C, MAX_RANGE_C, 0, 7))
image_group[((row1 * 8) + col1) + 1].fill = element_color[color_index]
sum_bucket = sum_bucket + value # Calculate sum for average
minimum = min(value, minimum)
maximum = max(value, maximum)
return minimum, maximum, sum_bucket
# Establish I2C interface for the AMG8833 Thermal Camera
i2c_scl = board.GP1
i2c_sda = board.GP0
i2c = busio.I2C(i2c_scl, i2c_sda)
amg8833 = adafruit_amg88xx.AMG88XX(i2c)
# The image sensor's design-limited temperature range
MIN_SENSOR_C = 0
MAX_SENSOR_C = 80
MIN_SENSOR_F = celsius_to_fahrenheit(MIN_SENSOR_C)
MAX_SENSOR_F = celsius_to_fahrenheit(MAX_SENSOR_C)
# Convert default alarm and min/max range values from config file
MIN_RANGE_F = 60
MAX_RANGE_F = 120
MIN_RANGE_C = fahrenheit_to_celsius(MIN_RANGE_F)
MAX_RANGE_C = fahrenheit_to_celsius(MAX_RANGE_F)
# The board's integral display size (as informed by product page adafru.it/1770)
WIDTH = 320
HEIGHT = 240
ELEMENT_SIZE = WIDTH // 10 # Size of element_grid blocks in pixels
# Define colors
BLACK = 0x000000
RED = 0xFF0000
ORANGE = 0xFF8811
YELLOW = 0xFFFF00
GREEN = 0x00FF00
CYAN = 0x00FFFF
BLUE = 0x0000FF
VIOLET = 0x9900FF
WHITE = 0xFFFFFF
GRAY = 0x444455
# Block colors for the thermal image grid
element_color = [GRAY, BLUE, GREEN, YELLOW, ORANGE, RED, VIOLET, WHITE]
# Tell displayio to clean up after any previous usage
displayio.release_displays()
# Define ILI9341 screen as an SPI-interfaced display
tft_cs = board.GP17
tft_dc = board.GP21
tft_res = board.GP20
spi_mosi = board.GP19
spi_clk = board.GP18
spi_miso = board.GP16
spi = busio.SPI(clock=spi_clk, MOSI=spi_mosi, MISO=spi_miso)
display_bus = displayio.FourWire(spi, command=tft_dc, chip_select=tft_cs)
display = adafruit_ili9341.ILI9341(display_bus, width=WIDTH, height=HEIGHT)
# Initially, just show a bright green background to prove the display is working
splash = displayio.Group(max_size=10)
display.show(splash)
color_bitmap = displayio.Bitmap(320, 240, 1)
color_palette = displayio.Palette(1)
color_palette[0] = 0x00FF00 # Bright Green
bg_sprite = displayio.TileGrid(color_bitmap, pixel_shader=color_palette, x=0, y=0)
splash.append(bg_sprite)
# Draw a smaller inner rectangle
inner_bitmap = displayio.Bitmap(280, 200, 1)
inner_palette = displayio.Palette(1)
inner_palette[0] = 0xAA0088 # Purple
inner_sprite = displayio.TileGrid(inner_bitmap, pixel_shader=inner_palette, x=20, y=20)
splash.append(inner_sprite)
# Wait so it doesn't immediately get overwritten
time.sleep(1)
# Define the group for the thermal display
image_group = displayio.Group(max_size=77)
display.show(image_group)
# Create a black background color fill layer; image_group[0]
color_bitmap = displayio.Bitmap(WIDTH, HEIGHT, 1)
color_palette = displayio.Palette(1)
color_palette[0] = BLACK
background = displayio.TileGrid(color_bitmap, pixel_shader=color_palette, x=0, y=0)
image_group.append(background)
# Define the foundational thermal image element layers; image_group[1:64]
# image_group[#]=(row * 8) + column
for row in range(0, 8):
for col in range(0, 8):
pos_x, pos_y = element_grid(col, row)
# outline is normally None and stroke is 0
element = Rect(x=pos_x, y=pos_y, width=ELEMENT_SIZE, height=ELEMENT_SIZE, fill=None, outline=None, stroke=0)
image_group.append(element)
# Main loop
while True:
image = amg8833.pixels # Get camera data list
v_min, v_max, v_sum = update_image_frame()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment