upgrade to support CircuitPython 8.0.0

This commit is contained in:
Cedar Grove Maker Studios 2022-11-03 18:44:08 -07:00
parent e85845c776
commit ee52aeb34d
11 changed files with 438 additions and 349 deletions

493
PyGamer_Improved_Thermal_Camera/code.py Normal file → Executable file
View file

@ -1,14 +1,18 @@
# SPDX-FileCopyrightText: 2021 Jan Goolsbey for Adafruit Industries
# SPDX-FileCopyrightText: 2022 Jan Goolsbey for Adafruit Industries
# SPDX-License-Identifier: MIT
# Thermal_Cam_v70_PyBadge_code.py
# 2021-12-21 v7.0 # CircuitPython v7.x compatible
"""
`thermalcamera`
================================================================================
PyGamer/PyBadge Thermal Camera Project
"""
import time
import board
import busio
import gc
import ulab
import board
import keypad
import busio
from ulab import numpy as np
import displayio
import neopixel
from analogio import AnalogIn
@ -18,79 +22,93 @@ 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 gamepadshift import GamePadShift
from index_to_rgb.iron_spectrum import index_to_rgb
from thermal_cam_converters import celsius_to_fahrenheit, fahrenheit_to_celsius
from thermal_cam_config import ALARM_F, MIN_RANGE_F, MAX_RANGE_F, SELFIE
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 display, joystick, speaker, and neopixels
__version__ = "0.0.0+auto.0"
__repo__ = "https://github.com/CedarGroveStudios/ThermalCamera.git"
# 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"):
has_joystick = True # PyGamer with joystick
# PyGamer with joystick
HAS_JOYSTICK = True
joystick_x = AnalogIn(board.JOYSTICK_X)
joystick_y = AnalogIn(board.JOYSTICK_Y)
else:
has_joystick = False # PyBadge with buttons
# PyBadge with buttons
HAS_JOYSTICK = False # PyBadge with buttons
speaker_enable = DigitalInOut(board.SPEAKER_ENABLE)
speaker_enable.switch_to_output(value=True)
# 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 # Set NeoPixel brightness
pixels.fill(0x000000) # Clear all NeoPixels
pixels.brightness = 0.25
pixels.fill(0x000000)
# Define and instantiate front panel buttons
BUTTON_LEFT = 0b10000000
BUTTON_UP = 0b01000000
BUTTON_DOWN = 0b00100000
BUTTON_RIGHT = 0b00010000
BUTTON_SELECT = 0b00001000
BUTTON_START = 0b00000100
BUTTON_A = 0b00000010
BUTTON_B = 0b00000001
panel = GamePadShift(
DigitalInOut(board.BUTTON_CLOCK),
DigitalInOut(board.BUTTON_OUT),
DigitalInOut(board.BUTTON_LATCH),
# 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,
)
# Establish I2C interface for the AMG8833 Thermal Camera
# 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("/thermal_cam_splash.bmp")
bitmap = displayio.OnDiskBitmap("/thermalcamera_splash.bmp")
splash.append(displayio.TileGrid(bitmap, pixel_shader=bitmap.pixel_shader))
board.DISPLAY.show(splash)
time.sleep(0.1) # Allow the splash to display
# Set up ulab arrays
n = 8 # Thermal sensor grid axis size; AMG8833 sensor is 8x8
sensor_data = ulab.numpy.array(range(n * n)).reshape((n, n)) # Color index narray
grid_data = ulab.numpy.zeros(((2 * n) - 1, (2 * n) - 1)) # 15x15 color index narray
histogram = ulab.numpy.zeros((2 * n) - 1) # Histogram accumulation narray
# 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)
# The board's integral display size
WIDTH = display.width
HEIGHT = display.height
GRID_AXIS = (2 * n) - 1 # Number of cells along the grid x or y axis
GRID_SIZE = HEIGHT # Maximum number of 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 colors in spectral palette (must be > 0)
# Default colors for temperature value sidebar
BLACK = 0x000000
RED = 0xFF0000
@ -100,81 +118,79 @@ BLUE = 0x0000FF
WHITE = 0xFFFFFF
# Text colors for setup helper's on-screen parameters
param_colors = [("ALARM", WHITE), ("RANGE", RED), ("RANGE", CYAN)]
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)
return
def flash_status(text="", duration=0.05): # Flash status message once
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 = ""
return
def spectrum(): # Load a test spectrum into the grid_data array
for row in range(0, GRID_AXIS):
for col in range(0, GRID_AXIS):
grid_data[row][col] = ((row * GRID_AXIS) + col) * 1 / 235
return
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):
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]
color_index = GRID_DATA[GRID_AXIS - 1 - _row][_col]
else:
color_index = grid_data[GRID_AXIS - 1 - row][GRID_AXIS - 1 - col]
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
return
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 histogram legend
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 = ulab.numpy.zeros(GRID_AXIS) # Clear histogram accumulation array
for row in range(0, GRID_AXIS): # Collect camera data and calculate histo
for col in range(0, GRID_AXIS):
histo_index = int(map_range(grid_data[col, row], 0, 1, 0, GRID_AXIS - 1))
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 = ulab.numpy.max(histogram) / (GRID_AXIS - 1)
histo_scale = np.max(histogram) / (GRID_AXIS - 1)
if histo_scale <= 0:
histo_scale = 1
for col in range(0, GRID_AXIS): # Display histogram
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)
# 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
return
image_group[((_row * GRID_AXIS) + _col)].fill = BLACK
def ulab_bilinear_interpolation(): # 2x bilinear interpolation
# Upscale 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
return
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
def setup_mode(): # Set alarm threshold and minimum/maximum range values
# 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-"
@ -189,68 +205,64 @@ def setup_mode(): # Set alarm threshold and minimum/maximum range values
param_index = 0 # Reset index of parameter to set
# Select parameter to set
buttons = panel.get_pressed()
while not buttons & BUTTON_START:
buttons = panel.get_pressed()
while (not buttons & BUTTON_A) and (not buttons & BUTTON_START):
up, down = move_buttons(joystick=has_joystick)
if up:
param_index = param_index - 1
if down:
param_index = param_index + 1
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 = param_colors[param_index][0]
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 = param_colors[param_index][1]
image_group[param_index + 226].color = SETUP_COLORS[param_index][1]
status_label.color = WHITE
time.sleep(0.25)
buttons = panel.get_pressed()
buttons = panel.get_pressed()
if buttons & BUTTON_A: # Hold (button A) pressed
play_tone(1319, 0.030) # E6
while buttons & BUTTON_A: # Wait for button release
buttons = panel.get_pressed()
time.sleep(0.1)
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)
buttons = panel.get_pressed()
while (not buttons & BUTTON_A) and (not buttons & BUTTON_START):
up, down = move_buttons(joystick=has_joystick)
if up:
param_value = param_value + 1
if down:
param_value = param_value - 1
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 = param_colors[param_index][1]
image_group[param_index + 230].color = SETUP_COLORS[param_index][1]
status_label.color = WHITE
time.sleep(0.2)
buttons = panel.get_pressed()
buttons = panel.get_pressed()
if buttons & BUTTON_A: # Button A pressed
play_tone(1319, 0.030) # E6
while buttons & BUTTON_A: # Wait for button release
buttons = panel.get_pressed()
time.sleep(0.1)
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
buttons = panel.get_pressed()
if buttons & BUTTON_START: # Start button pressed
play_tone(784, 0.030) # G5
while buttons & BUTTON_START: # Wait for button release
buttons = panel.get_pressed()
time.sleep(0.1)
status_label.text = "RESUME"
time.sleep(0.5)
status_label.text = ""
@ -261,27 +273,23 @@ def setup_mode(): # Set alarm threshold and minimum/maximum range values
return int(alarm_value.text), int(max_value.text), int(min_value.text)
def move_buttons(joystick=False): # Read position buttons and joystick
move_u = move_d = False
if joystick: # For PyGamer: interpret joystick as buttons
def get_joystick():
"""Read the joystick and interpret as up/down buttons (PyGamer)"""
if HAS_JOYSTICK:
if joystick_y.value < 20000:
move_u = True
elif joystick_y.value > 44000:
move_d = True
else: # For PyBadge read the buttons
buttons = panel.get_pressed()
if buttons & BUTTON_UP:
move_u = True
if buttons & BUTTON_DOWN:
move_d = True
return move_u, move_d
# Up
return 1
if joystick_y.value > 44000:
# Down
return -1
return 0
play_tone(440, 0.1) # A4
play_tone(880, 0.1) # A5
play_tone(440, 0.1) # Musical note A4
play_tone(880, 0.1) # Musical note A5
# ### Define the display group ###
t0 = time.monotonic() # Time marker: Define Display Elements
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]
@ -363,40 +371,38 @@ range_histo.anchored_position = ((WIDTH // 2) + (GRID_X_OFFSET // 2), 121)
image_group.append(range_histo) # image_group[236]
# ###--- PRIMARY PROCESS SETUP ---###
t1 = time.monotonic() # Time marker: Primary Process Setup
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
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 and play welcome tone
# Activate display, show preloaded sample spectrum, and play welcome tone
display.show(image_group)
spectrum()
update_image_frame()
flash_status("IRON", 0.75)
play_tone(880, 0.010) # A5
play_tone(880, 0.010) # Musical note A5
# ###--- PRIMARY PROCESS LOOP ---###
while True:
t2 = time.monotonic() # Time marker: Acquire Sensor Data
if display_hold:
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
sensor_data = ulab.numpy.array(sensor) # Copy to narray
t3 = time.monotonic() # Time marker: Constrain Sensor Values
for row in range(0, 8):
for col in range(0, 8):
sensor_data[col, row] = min(max(sensor_data[col, row], 0), 80)
# 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
t4 = time.monotonic() # Time marker: Display Statistics
v_max = ulab.numpy.max(sensor_data)
v_min = ulab.numpy.min(sensor_data)
v_ave = ulab.numpy.mean(sensor_data)
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))
@ -404,14 +410,14 @@ while True:
ave_value.text = str(celsius_to_fahrenheit(v_ave))
# Normalize temperature to index values and interpolate
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
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
t6 = time.monotonic() # Time marker: Display Image
if display_image:
mkr_t6 = time.monotonic() # Time marker: Display Image
if DISPLAY_IMAGE:
update_image_frame(selfie=SELFIE)
else:
update_histo_frame()
@ -419,89 +425,80 @@ while True:
# If alarm threshold is reached, flash NeoPixels and play alarm tone
if v_max >= ALARM_C:
pixels.fill(RED)
play_tone(880, 0.015) # A5
play_tone(880, 0.015) # Musical note A5
pixels.fill(BLACK)
# See if a panel button is pressed
buttons = panel.get_pressed()
if buttons & BUTTON_A: # Toggle display hold (shutter)
play_tone(1319, 0.030) # E6
display_hold = not display_hold
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
while buttons & BUTTON_A:
buttons = panel.get_pressed()
time.sleep(0.1)
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 buttons & BUTTON_B: # Toggle image/histogram mode (display image)
play_tone(659, 0.030) # E5
display_image = not display_image
while buttons & BUTTON_B:
buttons = panel.get_pressed()
time.sleep(0.1)
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 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 & BUTTON_SELECT: # Toggle focus mode (display focus)
play_tone(698, 0.030) # 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
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)
flash_status("ORIG", 0.2)
while buttons & BUTTON_SELECT:
buttons = panel.get_pressed()
time.sleep(0.1)
if buttons & BUTTON_START: # Activate setup mode
play_tone(784, 0.030) # G5
while buttons & BUTTON_START:
buttons = panel.get_pressed()
time.sleep(0.1)
# 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)
t7 = time.monotonic() # Time marker: End of Primary Process
mkr_t7 = time.monotonic() # Time marker: End of Primary Process
gc.collect()
fm7 = gc.mem_free()
mem_fm7 = gc.mem_free()
# Print frame performance report
print("*** PyBadge/Gamer Performance Stats ***")
print(f" define displayio: {(t1 - t0):6.3f} sec")
print(f" startup free memory: {fm1/1000:6.3} Kb")
print(f" define display: {(mkr_t1 - mkr_t0):6.3f} sec")
print(f" free memory: {mem_fm1 / 1000:6.3f} Kb")
print("")
print(
f" 1) data acquisition: {(t4 - t2):6.3f} rate: {(1 / (t4 - t2)):5.1f} /sec"
)
print(f" 2) display stats: {(t5 - t4):6.3f}")
print(f" 3) interpolate: {(t6 - t5):6.3f}")
print(f" 4) display image: {(t7 - t6):6.3f}")
print(f" =======")
print(
f"total frame: {(t7 - t2):6.3f} sec rate: {(1 / (t7 - t2)):5.1f} /sec"
)
print(f" free memory: {fm7/1000:6.3} Kb")
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("")

View file

@ -1,8 +0,0 @@
PyGamer_Improved_Thermal_Camera/index_to_rgb/iron_spectrum.py 42: Simplify chained comparison between the operands (chained-comparison)
PyGamer_Improved_Thermal_Camera/index_to_rgb/iron_spectrum.py 46: Simplify chained comparison between the operands (chained-comparison)
PyGamer_Improved_Thermal_Camera/index_to_rgb/iron_spectrum.py 50: Simplify chained comparison between the operands (chained-comparison)
PyGamer_Improved_Thermal_Camera/index_to_rgb/iron_spectrum.py 54: Simplify chained comparison between the operands (chained-comparison)
PyGamer_Improved_Thermal_Camera/index_to_rgb/visible_spectrum.py 25: Simplify chained comparison between the operands (chained-comparison)
PyGamer_Improved_Thermal_Camera/index_to_rgb/visible_spectrum.py 29: Simplify chained comparison between the operands (chained-comparison)
PyGamer_Improved_Thermal_Camera/index_to_rgb/visible_spectrum.py 33: Simplify chained comparison between the operands (chained-comparison)
PyGamer_Improved_Thermal_Camera/index_to_rgb/visible_spectrum.py 37: Simplify chained comparison between the operands (chained-comparison)

View file

@ -1,16 +1,41 @@
# SPDX-FileCopyrightText: 2021 Cedar Grove Studios for Adafruit Industries
# SPDX-FileCopyrightText: Copyright (c) 2022 JG for Cedar Grove Maker Studios
#
# SPDX-License-Identifier: MIT
"""
`cedargrove_rgb_spectrumtools.grayscale`
================================================================================
Spectral Index to Grayscale 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"
# grayscale_spectrum.py
# 2021-05-19 version 1.1
# Copyright 2021 Cedar Grove Studios
# Spectral Index to Grayscale RGB Converter Helper
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
"""
@ -28,11 +53,16 @@ def map_range(x, in_min, in_max, 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.8):
"""
Converts a spectral index to a grayscale RGB value. Spectral index in
range of 0.0 to 1.0. Gamma in range of 0.0 to 1.0 (1.0=linear),
default 0.8 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.8.
:return: Returns a 24-bit RGB value
:rtype: integer
"""

View file

@ -1,16 +1,40 @@
# SPDX-FileCopyrightText: 2021 Cedar Grove Studios for Adafruit Industries
# 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"
# iron_spectrum.py
# 2021-05-27 version 1.2
# Copyright 2021 Cedar Grove Studios
# Temperature Index to Iron Pseudocolor Spectrum RGB Converter Helper
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
"""
@ -28,11 +52,16 @@ def map_range(x, in_min, in_max, 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
"""
@ -43,25 +72,29 @@ def index_to_rgb(index=0, gamma=0.5):
red = 0.1
grn = 0.1
blu = (0.2 + (0.8 * map_range(band, 0, 70, 0.0, 1.0))) ** gamma
if band >= 70 and band < 200: # blue to violet
# if band >= 70 and band < 200: # blue to violet
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 band >= 200 and band < 300: # violet to red
blu = 1.0**gamma
# if band >= 200 and band < 300: # violet to red
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 band >= 300 and band < 400: # red to orange
red = 1.0 ** gamma
# if band >= 300 and band < 400: # red to orange
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 band >= 400 and band < 500: # orange to yellow
red = 1.0 ** gamma
# if band >= 400 and band < 500: # orange to yellow
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
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)

View file

@ -0,0 +1,77 @@
# SPDX-FileCopyrightText: Copyright (c) 2022 JG for Cedar Grove Maker Studios
#
# SPDX-License-Identifier: MIT
"""
`cedargrove_rgb_spectrumtools.visible`
================================================================================
A Spectral Index to Visible (Rainbow) Spectrum RGB Converter Helper
Based on original 1996 Fortran code by Dan Bruton:
physics.sfasu.edu/astro/color/spectra.html
* 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 index_to_rgb(index=0, gamma=0.5):
"""
Converts a spectral index to rainbow (visible light wavelength)
spectrum to an RGB value. Spectral index in range of 0.0 to 1.0
(violet --> white). 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
"""
wavelength = (index * 320) + 380
if wavelength < 440:
intensity = 0.1 + (0.9 * (wavelength - 380) / (440 - 380))
red = ((-1.0 * (wavelength - 440) / (440 - 380)) * intensity) ** gamma
grn = 0.0
blu = (1.0 * intensity) ** gamma
# if wavelength >= 440 and wavelength < 490:
if 440 <= wavelength < 490:
red = 0.0
grn = (1.0 * (wavelength - 440) / (490 - 440)) ** gamma
blu = 1.0**gamma
# if wavelength >= 490 and wavelength < 510:
if 490 <= wavelength < 510:
red = 0.0
grn = 1.0**gamma
blu = (-1.0 * (wavelength - 510) / (510 - 490)) ** gamma
# if wavelength >= 510 and wavelength < 580:
if 510 <= wavelength < 580:
red = (1.0 * (wavelength - 510) / (580 - 510)) ** gamma
grn = 1.0**gamma
blu = 0.0
# if wavelength >= 580 and wavelength < 645:
if 580 <= wavelength < 645:
red = 1.0**gamma
grn = (-1.0 * (wavelength - 645) / (645 - 580)) ** gamma
blu = 0.0
if wavelength >= 645:
intensity = 0.3 + (0.7 * (700 - wavelength) / (700 - 645))
red = (1.0) ** gamma
grn = (1.0 - intensity) ** gamma
blu = (1.0 - intensity) ** gamma
return (int(red * 255) << 16) + (int(grn * 255) << 8) + int(blu * 255)

View file

@ -1,51 +0,0 @@
# SPDX-FileCopyrightText: 2021 Cedar Grove Studios for Adafruit Industries
#
# SPDX-License-Identifier: MIT
# visible_spectrum.py
# 2021-05-27 version 1.2
# Copyright 2021 Cedar Grove Studios
# Spectral Index to Visible (Rainbow) Spectrum RGB Converter Helper
# Based on original 1996 Fortran code by Dan Bruton:
# physics.sfasu.edu/astro/color/spectra.html
def index_to_rgb(index=0, gamma=0.5):
"""
Converts a spectral index to rainbow (visible light wavelength)
spectrum to an RGB value. Spectral index in range of 0.0 to 1.0
(violet --> white). Gamma in range of 0.0 to 1.0 (1.0=linear),
default 0.5 for color TFT displays.
:return: Returns a 24-bit RGB value
:rtype: integer
"""
wl = (index * 320) + 380
if wl < 440:
intensity = 0.1 + (0.9 * (wl - 380) / (440 - 380))
red = ((-1.0 * (wl - 440) / (440 - 380)) * intensity) ** gamma
grn = 0.0
blu = (1.0 * intensity) ** gamma
if wl >= 440 and wl < 490:
red = 0.0
grn = (1.0 * (wl - 440) / (490 - 440)) ** gamma
blu = 1.0 ** gamma
if wl >= 490 and wl < 510:
red = 0.0
grn = 1.0 ** gamma
blu = (-1.0 * (wl - 510) / (510 - 490)) ** gamma
if wl >= 510 and wl < 580:
red = (1.0 * (wl - 510) / (580 - 510)) ** gamma
grn = 1.0 ** gamma
blu = 0.0
if wl >= 580 and wl < 645:
red = 1.0 ** gamma
grn = (-1.0 * (wl - 645) / (645 - 580)) ** gamma
blu = 0.0
if wl >= 645:
intensity = 0.3 + (0.7 * (700 - wl) / (700 - 645))
red = (1.0) ** gamma
grn = (1.0 - intensity) ** gamma
blu = (1.0 - intensity) ** gamma
return (int(red * 255) << 16) + (int(grn * 255) << 8) + int(blu * 255)

View file

@ -1,12 +0,0 @@
# SPDX-FileCopyrightText: 2021 Anne Barela for Adafruit Industries
#
# SPDX-License-Identifier: MIT
# thermal_cam_config.py
# ### 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

View file

@ -1,11 +0,0 @@
# SPDX-FileCopyrightText: 2020 CedarGroveStudios for Adafruit Industries
#
# SPDX-License-Identifier: MIT
# thermal_cam_converters.py
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))

View file

@ -0,0 +1,16 @@
# 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

View file

@ -0,0 +1,18 @@
# 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))

View file

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB