When the PyGamer's power is turned on, the code.py module first imports all the required libraries. That includes the thermalcamera_converters and index_to_rgb helper files that we'll review later.
import time import gc import board import keypad import busio from ulab import numpy as np import displayio import neopixel from analogio import AnalogIn from digitalio import DigitalInOut from simpleio import map_range, tone from adafruit_display_text.label import Label from adafruit_bitmap_font import bitmap_font from adafruit_display_shapes.rect import Rect import adafruit_amg88xx from index_to_rgb.iron import index_to_rgb from thermalcamera_converters import celsius_to_fahrenheit, fahrenheit_to_celsius from thermalcamera_config import ALARM_F, MIN_RANGE_F, MAX_RANGE_F, SELFIE
After importing libraries, the display and default font are instantiated, the speaker is enabled, and the on-board NeoPixels are defined.
If the PyGamer's joystick is present, the HAS_JOYSTICK
flag is set to True
. If not, then the host device is probably a PyBadge or EdgeBadge. This allows the code to work for those devices in addition to the PyGamer, interpreting the Badge D-Pad buttons like the Gamer's joystick.
# Instantiate the integral display and define its size display = board.DISPLAY display.brightness = 1.0 WIDTH = display.width HEIGHT = display.height # Load the text font from the fonts folder font_0 = bitmap_font.load_font("/fonts/OpenSans-9.bdf") # Instantiate the joystick if available if hasattr(board, "JOYSTICK_X"): # PyGamer with joystick HAS_JOYSTICK = True joystick_x = AnalogIn(board.JOYSTICK_X) joystick_y = AnalogIn(board.JOYSTICK_Y) else: # PyBadge with buttons HAS_JOYSTICK = False # PyBadge with buttons # Enable the speaker DigitalInOut(board.SPEAKER_ENABLE).switch_to_output(value=True) # Instantiate and clear the NeoPixels pixels = neopixel.NeoPixel(board.NEOPIXEL, 5, pixel_order=neopixel.GRB) pixels.brightness = 0.25 pixels.fill(0x000000)
The PyGamer and PyBadge control buttons are connected to a hardware shift register chip that is controlled by the keypad.ShiftRegisterKeys
class. This section of the code defines each button's bit position within the shift register. The local keypad.ShiftRegisterKeys
class, panel
, will be used to read the buttons in the primary process loop and setup helper.
# Initialize ShiftRegisterKeys to read PyGamer/PyBadge buttons panel = keypad.ShiftRegisterKeys( clock=board.BUTTON_CLOCK, data=board.BUTTON_OUT, latch=board.BUTTON_LATCH, key_count=8, value_when_pressed=True, ) # Define front panel button event values BUTTON_LEFT = 7 # LEFT button BUTTON_UP = 6 # UP button BUTTON_DOWN = 5 # DOWN button BUTTON_RIGHT = 4 # RIGHT button BUTTON_FOCUS = 3 # SELECT button BUTTON_SET = 2 # START button BUTTON_HOLD = 1 # button A BUTTON_IMAGE = 0 # button B
Now it's time to connect to and instantiate the AMG8833 thermal camera FeatherWing using the I2C bus connection. The I2C serial bus speed is increased from the default 100K to 400K bits per second to improve data acquisition speed and ultimately the display frame rate.
This section of the code will also work if the AMG8833 thermal camera STEMMA breakout is used in place of the FeatherWing version. STEMMA cable length may impact sensor performance, so if you encounter issues with the breakout version, try reducing the I2C bus speed to the default 100K bits per second rate.
# Initiate the AMG8833 Thermal Camera i2c = busio.I2C(board.SCL, board.SDA, frequency=400000) amg8833 = adafruit_amg88xx.AMG88XX(i2c)
Next, the welcome graphics screen, thermalcamera_splash.bmp is displayed. The size of the image is scaled to fit the size of the PyGamer's display.
# Display splash graphics splash = displayio.Group(scale=display.width // 160) bitmap = displayio.OnDiskBitmap("/thermalcamera_splash.bmp") splash.append(displayio.TileGrid(bitmap, pixel_shader=bitmap.pixel_shader)) board.DISPLAY.root_group = splash
Finally, the ulab (micro lab) arrays needed to hold the normalized 8x8 sensor index and the transformed 15x15 display grid index are defined. A sample color spectrum is placed in the display grid index array. In addition, an array to hold histogram statistical data is established.
An array defined for ulab use is an narray
type; a format different than other CircuitPython arrays. The special narray
array type (named after a close cousin, the numpy array) is designed to support rapid array calculation and transformation.
PALETTE_SIZE
is used to select the maximum number of display colors across the iron spectrum to map to temperature values. The palette size of 100
colors was selected empirically as a value that balanced the sensor resolution of 0.5°C with the ability to visually discern objects. Increasing the number of colors beyond 160 does not improve readability and can slow the display frame rate. Fewer than 80
palette colors significantly decreases visual object detection.
# Thermal sensor grid axis size; AMG8833 sensor is 8x8 SENSOR_AXIS = 8 # Display grid parameters GRID_AXIS = (2 * SENSOR_AXIS) - 1 # Number of cells per axis GRID_SIZE = HEIGHT # Axis size (pixels) for a square grid GRID_X_OFFSET = WIDTH - GRID_SIZE # Right-align grid with display boundary CELL_SIZE = GRID_SIZE // GRID_AXIS # Size of a grid cell in pixels PALETTE_SIZE = 100 # Number of display colors in spectral palette (must be > 0) # Set up the 2-D sensor data narray SENSOR_DATA = np.array(range(SENSOR_AXIS**2)).reshape((SENSOR_AXIS, SENSOR_AXIS)) # Set up and load the 2-D display color index narray with a spectrum GRID_DATA = np.array(range(GRID_AXIS**2)).reshape((GRID_AXIS, GRID_AXIS)) / ( GRID_AXIS**2 ) # Set up the histogram accumulation narray # HISTOGRAM = np.zeros(GRID_AXIS)
A series of numerical constants are needed to set boundaries and limits for thermal camera calculations. We'll talk about those next.
Page last edited March 08, 2024
Text editor powered by tinymce.