PyGamer Thermal Camera Source Code
Download the project's source files and copy them to the PyGamer's CIRCUITPY root directory, including the fonts and index_to_rgb folders.
In the code window below, click the link Download Project Bundle. This will download a zip file containing the code (4 .py files), the index_to_rgb folder, needed library files and the font folder.
The zip folder contains the following folders and files:
- code.py main thermal camera code
-
fonts folder
- OpenSans-9.bdf font file
-
index_to_rgb folder
- iron_spectrum.py color converter method file
-
A lib folder containing these required libraries:
- adafruit_amg88xx
- adafruit_bitmap_font
- adafruit_bus_device
- adafruit_display_shapes
- adafruit_display_text
- adafruit_pixelbuf
- adafruit_register
- neopixel
- simpleio
- thermalcamera_config.py start-up default settings
- thermalcamera_converters.py temperature converter helpers
- thermalcamera_splash.bmp startup screen graphic
Here's the main CircuitPython code for the Thermal Camera. It's contained in the project zip folder as code.py. Copy this to the main (root) folder of the CIRCUITPY drive that appears when your PyGamer is connected to your computer via a known good USB cable.
# SPDX-FileCopyrightText: 2022 Jan Goolsbey for Adafruit Industries
# SPDX-License-Identifier: MIT
"""
`thermalcamera`
================================================================================
PyGamer/PyBadge Thermal Camera Project
"""
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
# 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)
# 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
# Initiate the AMG8833 Thermal Camera
i2c = busio.I2C(board.SCL, board.SDA, frequency=400000)
amg8833 = adafruit_amg88xx.AMG88XX(i2c)
# 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
# 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)
# Convert default alarm and min/max range values from config file
ALARM_C = fahrenheit_to_celsius(ALARM_F)
MIN_RANGE_C = fahrenheit_to_celsius(MIN_RANGE_F)
MAX_RANGE_C = fahrenheit_to_celsius(MAX_RANGE_F)
# Default colors for temperature value sidebar
BLACK = 0x000000
RED = 0xFF0000
YELLOW = 0xFFFF00
CYAN = 0x00FFFF
BLUE = 0x0000FF
WHITE = 0xFFFFFF
# Text colors for setup helper's on-screen parameters
SETUP_COLORS = [("ALARM", WHITE), ("RANGE", RED), ("RANGE", CYAN)]
# ### Helpers ###
def play_tone(freq=440, duration=0.01):
"""Play a tone over the speaker"""
tone(board.A0, freq, duration)
def flash_status(text="", duration=0.05):
"""Flash status message once"""
status_label.color = WHITE
status_label.text = text
time.sleep(duration)
status_label.color = BLACK
time.sleep(duration)
status_label.text = ""
def update_image_frame(selfie=False):
"""Get camera data and update display"""
for _row in range(0, GRID_AXIS):
for _col in range(0, GRID_AXIS):
if selfie:
color_index = GRID_DATA[GRID_AXIS - 1 - _row][_col]
else:
color_index = GRID_DATA[GRID_AXIS - 1 - _row][GRID_AXIS - 1 - _col]
color = index_to_rgb(round(color_index * PALETTE_SIZE, 0) / PALETTE_SIZE)
if color != image_group[((_row * GRID_AXIS) + _col)].fill:
image_group[((_row * GRID_AXIS) + _col)].fill = color
def update_histo_frame():
"""Calculate and display histogram"""
min_histo.text = str(MIN_RANGE_F) # Display the legend
max_histo.text = str(MAX_RANGE_F)
histogram = np.zeros(GRID_AXIS) # Clear histogram accumulation array
# Collect camera data and calculate the histogram
for _row in range(0, GRID_AXIS):
for _col in range(0, GRID_AXIS):
histo_index = int(map_range(GRID_DATA[_col, _row], 0, 1, 0, GRID_AXIS - 1))
histogram[histo_index] = histogram[histo_index] + 1
histo_scale = np.max(histogram) / (GRID_AXIS - 1)
if histo_scale <= 0:
histo_scale = 1
# Display the histogram
for _col in range(0, GRID_AXIS):
for _row in range(0, GRID_AXIS):
if histogram[_col] / histo_scale > GRID_AXIS - 1 - _row:
image_group[((_row * GRID_AXIS) + _col)].fill = index_to_rgb(
round((_col / GRID_AXIS), 3)
)
else:
image_group[((_row * GRID_AXIS) + _col)].fill = BLACK
def ulab_bilinear_interpolation():
"""2x bilinear interpolation to upscale the sensor data array; by @v923z
and @David.Glaude."""
GRID_DATA[1::2, ::2] = SENSOR_DATA[:-1, :]
GRID_DATA[1::2, ::2] += SENSOR_DATA[1:, :]
GRID_DATA[1::2, ::2] /= 2
GRID_DATA[::, 1::2] = GRID_DATA[::, :-1:2]
GRID_DATA[::, 1::2] += GRID_DATA[::, 2::2]
GRID_DATA[::, 1::2] /= 2
# pylint: disable=too-many-branches
# pylint: disable=too-many-statements
def setup_mode():
"""Change alarm threshold and minimum/maximum range values"""
status_label.color = WHITE
status_label.text = "-SET-"
ave_label.color = BLACK # Turn off average label and value display
ave_value.color = BLACK
max_value.text = str(MAX_RANGE_F) # Display maximum range value
min_value.text = str(MIN_RANGE_F) # Display minimum range value
time.sleep(0.8) # Show SET status text before setting parameters
status_label.text = "" # Clear status text
param_index = 0 # Reset index of parameter to set
setup_state = "SETUP" # Set initial state
while setup_state == "SETUP":
# Select parameter to set
setup_state = "SELECT_PARAM" # Parameter selection state
while setup_state == "SELECT_PARAM":
param_index = max(0, min(2, param_index))
status_label.text = SETUP_COLORS[param_index][0]
image_group[param_index + 226].color = BLACK
status_label.color = BLACK
time.sleep(0.25)
image_group[param_index + 226].color = SETUP_COLORS[param_index][1]
status_label.color = WHITE
time.sleep(0.25)
param_index -= get_joystick()
_buttons = panel.events.get()
if _buttons and _buttons.pressed:
if _buttons.key_number == BUTTON_UP: # HOLD button pressed
param_index = param_index - 1
if _buttons.key_number == BUTTON_DOWN: # SET button pressed
param_index = param_index + 1
if _buttons.key_number == BUTTON_HOLD: # HOLD button pressed
play_tone(1319, 0.030) # Musical note E6
setup_state = "ADJUST_VALUE" # Next state
if _buttons.key_number == BUTTON_SET: # SET button pressed
play_tone(1319, 0.030) # Musical note E6
setup_state = "EXIT" # Next state
# Adjust parameter value
param_value = int(image_group[param_index + 230].text)
while setup_state == "ADJUST_VALUE":
param_value = max(32, min(157, param_value))
image_group[param_index + 230].text = str(param_value)
image_group[param_index + 230].color = BLACK
status_label.color = BLACK
time.sleep(0.05)
image_group[param_index + 230].color = SETUP_COLORS[param_index][1]
status_label.color = WHITE
time.sleep(0.2)
param_value += get_joystick()
_buttons = panel.events.get()
if _buttons and _buttons.pressed:
if _buttons.key_number == BUTTON_UP: # HOLD button pressed
param_value = param_value + 1
if _buttons.key_number == BUTTON_DOWN: # SET button pressed
param_value = param_value - 1
if _buttons.key_number == BUTTON_HOLD: # HOLD button pressed
play_tone(1319, 0.030) # Musical note E6
setup_state = "SETUP" # Next state
if _buttons.key_number == BUTTON_SET: # SET button pressed
play_tone(1319, 0.030) # Musical note E6
setup_state = "EXIT" # Next state
# Exit setup process
status_label.text = "RESUME"
time.sleep(0.5)
status_label.text = ""
# Display average label and value
ave_label.color = YELLOW
ave_value.color = YELLOW
return int(alarm_value.text), int(max_value.text), int(min_value.text)
def get_joystick():
"""Read the joystick and interpret as up/down buttons (PyGamer)"""
if HAS_JOYSTICK:
if joystick_y.value < 20000:
# Up
return 1
if joystick_y.value > 44000:
# Down
return -1
return 0
play_tone(440, 0.1) # Musical note A4
play_tone(880, 0.1) # Musical note A5
# ### Define the display group ###
mkr_t0 = time.monotonic() # Time marker: Define Display Elements
image_group = displayio.Group(scale=1)
# Define the foundational thermal image grid cells; image_group[0:224]
# image_group[#] = image_group[ (row * GRID_AXIS) + column ]
for row in range(0, GRID_AXIS):
for col in range(0, GRID_AXIS):
cell_x = (col * CELL_SIZE) + GRID_X_OFFSET
cell_y = row * CELL_SIZE
cell = Rect(
x=cell_x,
y=cell_y,
width=CELL_SIZE,
height=CELL_SIZE,
fill=None,
outline=None,
stroke=0,
)
image_group.append(cell)
# Define labels and values
status_label = Label(font_0, text="", color=None)
status_label.anchor_point = (0.5, 0.5)
status_label.anchored_position = ((WIDTH // 2) + (GRID_X_OFFSET // 2), HEIGHT // 2)
image_group.append(status_label) # image_group[225]
alarm_label = Label(font_0, text="alm", color=WHITE)
alarm_label.anchor_point = (0, 0)
alarm_label.anchored_position = (1, 16)
image_group.append(alarm_label) # image_group[226]
max_label = Label(font_0, text="max", color=RED)
max_label.anchor_point = (0, 0)
max_label.anchored_position = (1, 46)
image_group.append(max_label) # image_group[227]
min_label = Label(font_0, text="min", color=CYAN)
min_label.anchor_point = (0, 0)
min_label.anchored_position = (1, 106)
image_group.append(min_label) # image_group[228]
ave_label = Label(font_0, text="ave", color=YELLOW)
ave_label.anchor_point = (0, 0)
ave_label.anchored_position = (1, 76)
image_group.append(ave_label) # image_group[229]
alarm_value = Label(font_0, text=str(ALARM_F), color=WHITE)
alarm_value.anchor_point = (0, 0)
alarm_value.anchored_position = (1, 5)
image_group.append(alarm_value) # image_group[230]
max_value = Label(font_0, text=str(MAX_RANGE_F), color=RED)
max_value.anchor_point = (0, 0)
max_value.anchored_position = (1, 35)
image_group.append(max_value) # image_group[231]
min_value = Label(font_0, text=str(MIN_RANGE_F), color=CYAN)
min_value.anchor_point = (0, 0)
min_value.anchored_position = (1, 95)
image_group.append(min_value) # image_group[232]
ave_value = Label(font_0, text="---", color=YELLOW)
ave_value.anchor_point = (0, 0)
ave_value.anchored_position = (1, 65)
image_group.append(ave_value) # image_group[233]
min_histo = Label(font_0, text="", color=None)
min_histo.anchor_point = (0, 0.5)
min_histo.anchored_position = (GRID_X_OFFSET, 121)
image_group.append(min_histo) # image_group[234]
max_histo = Label(font_0, text="", color=None)
max_histo.anchor_point = (1, 0.5)
max_histo.anchored_position = (WIDTH - 2, 121)
image_group.append(max_histo) # image_group[235]
range_histo = Label(font_0, text="-RANGE-", color=None)
range_histo.anchor_point = (0.5, 0.5)
range_histo.anchored_position = ((WIDTH // 2) + (GRID_X_OFFSET // 2), 121)
image_group.append(range_histo) # image_group[236]
# ###--- PRIMARY PROCESS SETUP ---###
mkr_t1 = time.monotonic() # Time marker: Primary Process Setup
# pylint: disable=no-member
mem_fm1 = gc.mem_free() # Monitor free memory
DISPLAY_IMAGE = True # Image display mode; False for histogram
DISPLAY_HOLD = False # Active display mode; True to hold display
DISPLAY_FOCUS = False # Standard display range; True to focus display range
# pylint: disable=invalid-name
orig_max_range_f = 0 # Establish temporary range variables
orig_min_range_f = 0
# Activate display, show preloaded sample spectrum, and play welcome tone
display.root_group = image_group
update_image_frame()
flash_status("IRON", 0.75)
play_tone(880, 0.010) # Musical note A5
# ###--- PRIMARY PROCESS LOOP ---###
while True:
mkr_t2 = time.monotonic() # Time marker: Acquire Sensor Data
if DISPLAY_HOLD:
flash_status("-HOLD-", 0.25)
else:
sensor = amg8833.pixels # Get sensor_data data
# Put sensor data in array; limit to the range of 0, 80
SENSOR_DATA = np.clip(np.array(sensor), 0, 80)
# Update and display alarm setting and max, min, and ave stats
mkr_t4 = time.monotonic() # Time marker: Display Statistics
v_max = np.max(SENSOR_DATA)
v_min = np.min(SENSOR_DATA)
v_ave = np.mean(SENSOR_DATA)
alarm_value.text = str(ALARM_F)
max_value.text = str(celsius_to_fahrenheit(v_max))
min_value.text = str(celsius_to_fahrenheit(v_min))
ave_value.text = str(celsius_to_fahrenheit(v_ave))
# Normalize temperature to index values and interpolate
mkr_t5 = time.monotonic() # Time marker: Normalize and Interpolate
SENSOR_DATA = (SENSOR_DATA - MIN_RANGE_C) / (MAX_RANGE_C - MIN_RANGE_C)
GRID_DATA[::2, ::2] = SENSOR_DATA # Copy sensor data to the grid array
ulab_bilinear_interpolation() # Interpolate to produce 15x15 result
# Display image or histogram
mkr_t6 = time.monotonic() # Time marker: Display Image
if DISPLAY_IMAGE:
update_image_frame(selfie=SELFIE)
else:
update_histo_frame()
# If alarm threshold is reached, flash NeoPixels and play alarm tone
if v_max >= ALARM_C:
pixels.fill(RED)
play_tone(880, 0.015) # Musical note A5
pixels.fill(BLACK)
# See if a panel button is pressed
buttons = panel.events.get()
if buttons and buttons.pressed:
if buttons.key_number == BUTTON_HOLD:
# Toggle display hold (shutter)
play_tone(1319, 0.030) # Musical note E6
DISPLAY_HOLD = not DISPLAY_HOLD
if buttons.key_number == BUTTON_IMAGE:
# Toggle image/histogram mode (display image)
play_tone(659, 0.030) # Musical note E5
DISPLAY_IMAGE = not DISPLAY_IMAGE
if DISPLAY_IMAGE:
min_histo.color = None
max_histo.color = None
range_histo.color = None
else:
min_histo.color = CYAN
max_histo.color = RED
range_histo.color = BLUE
if buttons.key_number == BUTTON_FOCUS: # Toggle display focus mode
play_tone(698, 0.030) # Musical note F5
DISPLAY_FOCUS = not DISPLAY_FOCUS
if DISPLAY_FOCUS:
# Set range values to image min/max for focused image display
orig_min_range_f = MIN_RANGE_F
orig_max_range_f = MAX_RANGE_F
MIN_RANGE_F = celsius_to_fahrenheit(v_min)
MAX_RANGE_F = celsius_to_fahrenheit(v_max)
# Update range min and max values in Celsius
MIN_RANGE_C = v_min
MAX_RANGE_C = v_max
flash_status("FOCUS", 0.2)
else:
# Restore previous (original) range values for image display
MIN_RANGE_F = orig_min_range_f
MAX_RANGE_F = orig_max_range_f
# Update range min and max values in Celsius
MIN_RANGE_C = fahrenheit_to_celsius(MIN_RANGE_F)
MAX_RANGE_C = fahrenheit_to_celsius(MAX_RANGE_F)
flash_status("ORIG", 0.2)
if buttons.key_number == BUTTON_SET:
# Activate setup mode
play_tone(784, 0.030) # Musical note G5
# Invoke startup helper; update alarm and range values
ALARM_F, MAX_RANGE_F, MIN_RANGE_F = setup_mode()
ALARM_C = fahrenheit_to_celsius(ALARM_F)
MIN_RANGE_C = fahrenheit_to_celsius(MIN_RANGE_F)
MAX_RANGE_C = fahrenheit_to_celsius(MAX_RANGE_F)
mkr_t7 = time.monotonic() # Time marker: End of Primary Process
gc.collect()
mem_fm7 = gc.mem_free()
# Print frame performance report
print("*** PyBadge/Gamer Performance Stats ***")
print(f" define display: {(mkr_t1 - mkr_t0):6.3f} sec")
print(f" free memory: {mem_fm1 / 1000:6.3f} Kb")
print("")
print(" rate")
print(f" 1) acquire: {(mkr_t4 - mkr_t2):6.3f} sec ", end="")
print(f"{(1 / (mkr_t4 - mkr_t2)):5.1f} /sec")
print(f" 2) stats: {(mkr_t5 - mkr_t4):6.3f} sec")
print(f" 3) convert: {(mkr_t6 - mkr_t5):6.3f} sec")
print(f" 4) display: {(mkr_t7 - mkr_t6):6.3f} sec")
print(" =======")
print(f"total frame: {(mkr_t7 - mkr_t2):6.3f} sec ", end="")
print(f"{(1 / (mkr_t7 - mkr_t2)):5.1f} /sec")
print(f" free memory: {mem_fm7 / 1000:6.3f} Kb")
print("")
The Thermal Camera needs some helpers to convert back and forth between Celsius and Fahrenheit units. This file is contained in the project zip folder as thermalcamera_converters.py.
# SPDX-FileCopyrightText: 2022 Jan Goolsbey for Adafruit Industries
# SPDX-License-Identifier: MIT
"""
`thermalcamera_converters`
================================================================================
Celsius-to-Fahrenheit and Fahrenheit-to-Celsius converter helpers.
"""
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))
The color spectrum is calculated by the iron.py helper file within the index_to_rgb folder. The helper calculates a 24-bit red, green, and blue (RGB) color value from an input value of 0 to 1.0.
# SPDX-FileCopyrightText: Copyright (c) 2022 JG for Cedar Grove Maker Studios
#
# SPDX-License-Identifier: MIT
"""
`cedargrove_rgb_spectrumtools.iron`
================================================================================
Temperature Index to Iron Pseudocolor Spectrum RGB Converter Helper
* Author(s): JG
Implementation Notes
--------------------
**Hardware:**
**Software and Dependencies:**
* Adafruit CircuitPython firmware for the supported boards:
https://circuitpython.org/downloads
"""
__version__ = "0.0.0+auto.0"
__repo__ = "https://github.com/CedarGroveStudios/CircuitPython_RGB_SpectrumTools.git"
def map_range(x, in_min, in_max, out_min, out_max):
"""
Maps and constrains an input value from one range of values to another.
(from adafruit_simpleio)
:param float x: The value to be mapped. No default.
:param float in_min: The beginning of the input range. No default.
:param float in_max: The end of the input range. No default.
:param float out_min: The beginning of the output range. No default.
:param float out_max: The end of the output range. No default.
:return: Returns value mapped to new range
:rtype: float
"""
in_range = in_max - in_min
in_delta = x - in_min
if in_range != 0:
mapped = in_delta / in_range
elif in_delta != 0:
mapped = in_delta
else:
mapped = 0.5
mapped *= out_max - out_min
mapped += out_min
if out_min <= out_max:
return max(min(mapped, out_max), out_min)
return min(max(mapped, out_max), out_min)
def index_to_rgb(index=0, gamma=0.5):
"""
Converts a temperature index to an iron thermographic pseudocolor spectrum
RGB value. Temperature index in range of 0.0 to 1.0. Gamma in range of
0.0 to 1.0 (1.0=linear), default 0.5 for color TFT displays.
:param float index: The normalized index value, range 0 to 1.0. Defaults to 0.
:param float gamma: The gamma color perception value. Defaults to 0.5.
:return: Returns a 24-bit RGB value
:rtype: integer
"""
band = index * 600 # an arbitrary spectrum band index; 0 to 600
if band < 70: # dark gray to blue
red = 0.1
grn = 0.1
blu = (0.2 + (0.8 * map_range(band, 0, 70, 0.0, 1.0))) ** gamma
if 70 <= band < 200: # blue to violet
red = map_range(band, 70, 200, 0.0, 0.6) ** gamma
grn = 0.0
blu = 1.0**gamma
if 200 <= band < 300: # violet to red
red = map_range(band, 200, 300, 0.6, 1.0) ** gamma
grn = 0.0
blu = map_range(band, 200, 300, 1.0, 0.0) ** gamma
if 300 <= band < 400: # red to orange
red = 1.0**gamma
grn = map_range(band, 300, 400, 0.0, 0.5) ** gamma
blu = 0.0
if 400 <= band < 500: # orange to yellow
red = 1.0**gamma
grn = map_range(band, 400, 500, 0.5, 1.0) ** gamma
blu = 0.0
if band >= 500: # yellow to white
red = 1.0**gamma
grn = 1.0**gamma
blu = map_range(band, 500, 580, 0.0, 1.0) ** gamma
return (int(red * 255) << 16) + (int(grn * 255) << 8) + int(blu * 255)
Finally, the power-up alarm threshold, temperature display range settings, and camera orientation are contained in the thermalcamera_config.py file. All values are in degrees Fahrenheit. A SELFIE value of True adjusts the image for a front-facing camera orientation; False is used for cameras facing away from the viewer.
# SPDX-FileCopyrightText: 2022 Jan Goolsbey for Adafruit Industries # SPDX-License-Identifier: MIT """ `thermalcamera_config` ================================================================================ Thermal Camera configuration parameters. """ # ### Alarm and range default values in Farenheit ### ALARM_F = 120 MIN_RANGE_F = 60 MAX_RANGE_F = 120 # ### Display characteristics SELFIE = False # Rear camera view; True for front view
After copying all the project files to the PyGamer, you'll see the camera's splash graphics and a sample of the iron color spectrum. After a couple of beeps, the thermal image will appear.
The next section shows the features of the camera and how it operates.
Page last edited January 22, 2025
Text editor powered by tinymce.