This commit is contained in:
Melissa LeBlanc-Williams 2025-04-16 00:14:00 -07:00
commit 87c04e1c46
82 changed files with 2747 additions and 496 deletions

View file

@ -0,0 +1,47 @@
// SPDX-FileCopyrightText: 2025 Liz Clark for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#include <Adafruit_MCP23X17.h>
#define NOID_1 0 // MCP23XXX pin LED is attached to
#define NOID_2 4 // MCP23XXX pin LED is attached to
Adafruit_MCP23X17 mcp;
void setup() {
Serial.begin(115200);
while (!Serial);
Serial.println("8 Channel Solenoid Driver Demo");
if (!mcp.begin_I2C()) {
Serial.println("Couldn't find MCP23017..");
while (1);
}
mcp.pinMode(NOID_1, OUTPUT);
mcp.pinMode(NOID_2, OUTPUT);
Serial.println("Found MCP23017, looping...");
}
void loop() {
Serial.println("Solenoid 1!");
mcp.digitalWrite(NOID_1, HIGH);
delay(500);
mcp.digitalWrite(NOID_1, LOW);
delay(500);
Serial.println("Solenoid 2!");
mcp.digitalWrite(NOID_2, HIGH);
delay(500);
mcp.digitalWrite(NOID_2, LOW);
delay(500);
Serial.println("Together!");
mcp.digitalWrite(NOID_1, HIGH);
mcp.digitalWrite(NOID_2, HIGH);
delay(1000);
mcp.digitalWrite(NOID_1, LOW);
mcp.digitalWrite(NOID_2, LOW);
delay(2000);
Serial.println("Repeat!");
Serial.println();
delay(500);
}

View file

@ -0,0 +1,38 @@
# SPDX-FileCopyrightText: 2025 Liz Clark for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import time
import board
from adafruit_mcp230xx.mcp23017 import MCP23017
i2c = board.STEMMA_I2C()
mcp = MCP23017(i2c)
noid_1 = mcp.get_pin(0)
noid_2 = mcp.get_pin(4)
noid_1.switch_to_output(value=False)
noid_2.switch_to_output(value=False)
while True:
noid_1.value = True
print(f"Solenoid 1: {noid_1.value}, Solenoid 2: {noid_2.value}")
time.sleep(0.2)
noid_1.value = False
print(f"Solenoid 1: {noid_1.value}, Solenoid 2: {noid_2.value}")
time.sleep(0.2)
noid_2.value = True
print(f"Solenoid 1: {noid_1.value}, Solenoid 2: {noid_2.value}")
time.sleep(0.2)
noid_2.value = False
print(f"Solenoid 1: {noid_1.value}, Solenoid 2: {noid_2.value}")
time.sleep(1)
noid_1.value = True
noid_2.value = True
print(f"Solenoid 1: {noid_1.value}, Solenoid 2: {noid_2.value}")
time.sleep(1)
noid_1.value = False
noid_2.value = False
print(f"Solenoid 1: {noid_1.value}, Solenoid 2: {noid_2.value}")
time.sleep(2)

View file

@ -58,7 +58,7 @@ group.append(press_data)
display.root_group = group
# function to convert celcius to fahrenheit
# function to convert celsius to fahrenheit
def c_to_f(temp):
temp_f = (temp * 9/5) + 32
return temp_f
@ -87,12 +87,12 @@ while True:
print(servo_value)
# if metric units...
if metric_units:
# update temp & pressure text in celcius and hPa
# update temp & pressure text in celsius and hPa
temp_data.text = "%0.1f ºC" % bmp280.temperature
press_data.text = "%0.1f hPa" % bmp280.pressure
# if imperial units...
else:
# convert celcius to fahrenheit
# convert celsius to fahrenheit
temp_fahrenheit = c_to_f(bmp280.temperature)
# convert hPa to inHg
pressure_inHg = hpa_to_inHg(bmp280.pressure)

View file

@ -23,7 +23,7 @@ pixels = neopixel.NeoPixel(board.D1, # NeoPixels on pin D1
# For the Gemma M0 onboard DotStar LED
dotstar = adafruit_dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1)
def deg_f(deg_c): # Convert Celcius to Fahrenheit
def deg_f(deg_c): # Convert celsius to Fahrenheit
return(deg_c * 9 / 5) + 32.0
while True:

View file

@ -0,0 +1,251 @@
# SPDX-FileCopyrightText: 2024 Tim Cocks for Adafruit Industries
# SPDX-License-Identifier: MIT
"""
This example demonstrates basic autosave and resume functionality. There are two buttons
that can be clicked to increment respective counters. The number of clicks is stored
in a game_state dictionary and saved to a data file on the SDCard. When the code first
launches it will read the data file and load the game_state from it.
"""
import array
from io import BytesIO
import os
import board
import busio
import digitalio
import displayio
import msgpack
import storage
import supervisor
import terminalio
import usb
import adafruit_sdcard
from adafruit_display_text.bitmap_label import Label
from adafruit_button import Button
# use the default built-in display
display = supervisor.runtime.display
# button configuration
BUTTON_WIDTH = 100
BUTTON_HEIGHT = 30
BUTTON_STYLE = Button.ROUNDRECT
# game state object will get loaded from SDCard
# or a new one initialized as a dictionary
game_state = None
save_to = None
# boolean variables for possible SDCard states
sd_pins_in_use = False
# The SD_CS pin is the chip select line.
SD_CS = board.SD_CS
# try to Connect to the sdcard card and mount the filesystem.
try:
# initialze CS pin
cs = digitalio.DigitalInOut(SD_CS)
except ValueError:
# likely the SDCard was auto-initialized by the core
sd_pins_in_use = True
try:
# if sd CS pin was not in use
if not sd_pins_in_use:
# try to initialize and mount the SDCard
sdcard = adafruit_sdcard.SDCard(
busio.SPI(board.SD_SCK, board.SD_MOSI, board.SD_MISO), cs
)
vfs = storage.VfsFat(sdcard)
storage.mount(vfs, "/sd")
# check for the autosave data file
if "autosave_demo.dat" in os.listdir("/sd/"):
# if the file is found read data from it into a BytesIO buffer
buffer = BytesIO()
with open("/sd/autosave_demo.dat", "rb") as f:
buffer.write(f.read())
buffer.seek(0)
# unpack the game_state object from the read data in the buffer
game_state = msgpack.unpack(buffer)
print(game_state)
# if placeholder.txt file does not exist
if "placeholder.txt" not in os.listdir("/sd/"):
# if we made it to here then /sd/ exists and has a card
# so use it for save data
save_to = "/sd/autosave_demo.dat"
except OSError as e:
# sdcard init or mounting failed
raise OSError(
"This demo requires an SDCard. Please power off the device " +
"insert an SDCard and then plug it back in."
) from e
# if no saved game_state was loaded
if game_state is None:
# create a new game state dictionary
game_state = {"pink_count": 0, "blue_count": 0}
# Make the display context
main_group = displayio.Group()
display.root_group = main_group
# make buttons
blue_button = Button(
x=30,
y=40,
width=BUTTON_WIDTH,
height=BUTTON_HEIGHT,
style=BUTTON_STYLE,
fill_color=0x0000FF,
outline_color=0xFFFFFF,
label="BLUE",
label_font=terminalio.FONT,
label_color=0xFFFFFF,
)
pink_button = Button(
x=30,
y=80,
width=BUTTON_WIDTH,
height=BUTTON_HEIGHT,
style=BUTTON_STYLE,
fill_color=0xFF00FF,
outline_color=0xFFFFFF,
label="PINK",
label_font=terminalio.FONT,
label_color=0x000000,
)
# add them to a list for easy iteration
all_buttons = [blue_button, pink_button]
# Add buttons to the display context
main_group.append(blue_button)
main_group.append(pink_button)
# make labels for each button
blue_lbl = Label(
terminalio.FONT, text=f"Blue: {game_state['blue_count']}", color=0x3F3FFF
)
blue_lbl.anchor_point = (0, 0)
blue_lbl.anchored_position = (4, 4)
pink_lbl = Label(
terminalio.FONT, text=f"Pink: {game_state['pink_count']}", color=0xFF00FF
)
pink_lbl.anchor_point = (0, 0)
pink_lbl.anchored_position = (4, 4 + 14)
main_group.append(blue_lbl)
main_group.append(pink_lbl)
# load the mouse cursor bitmap
mouse_bmp = displayio.OnDiskBitmap("mouse_cursor.bmp")
# make the background pink pixels transparent
mouse_bmp.pixel_shader.make_transparent(0)
# create a TileGrid for the mouse, using its bitmap and pixel_shader
mouse_tg = displayio.TileGrid(mouse_bmp, pixel_shader=mouse_bmp.pixel_shader)
# move it to the center of the display
mouse_tg.x = display.width // 2
mouse_tg.y = display.height // 2
# add the mouse tilegrid to main_group
main_group.append(mouse_tg)
# scan for connected USB device and loop over any found
for device in usb.core.find(find_all=True):
# print device info
print(f"{device.idVendor:04x}:{device.idProduct:04x}")
print(device.manufacturer, device.product)
print(device.serial_number)
# assume the device is the mouse
mouse = device
# detach the kernel driver if needed
if mouse.is_kernel_driver_active(0):
mouse.detach_kernel_driver(0)
# set configuration on the mouse so we can use it
mouse.set_configuration()
# buffer to hold mouse data
# Boot mice have 4 byte reports
buf = array.array("b", [0] * 4)
def save_game_state():
"""
msgpack the game_state and save it to the autosave data file
:return:
"""
b = BytesIO()
msgpack.pack(game_state, b)
b.seek(0)
with open(save_to, "wb") as savefile:
savefile.write(b.read())
# main loop
while True:
try:
# attempt to read data from the mouse
# 10ms timeout, so we don't block long if there
# is no data
count = mouse.read(0x81, buf, timeout=10)
except usb.core.USBTimeoutError:
# skip the rest of the loop if there is no data
continue
# update the mouse tilegrid x and y coordinates
# based on the delta values read from the mouse
mouse_tg.x = max(0, min(display.width - 1, mouse_tg.x + buf[1]))
mouse_tg.y = max(0, min(display.height - 1, mouse_tg.y + buf[2]))
# if left click is pressed
if buf[0] & (1 << 0) != 0:
# get the current cursor coordinates
coords = (mouse_tg.x, mouse_tg.y, 0)
# loop over the buttons
for button in all_buttons:
# if the current button contains the mouse coords
if button.contains(coords):
# if the button isn't already in the selected state
if not button.selected:
# enter selected state
button.selected = True
# if it is the pink button
if button == pink_button:
# increment pink count
game_state["pink_count"] += 1
# update the label
pink_lbl.text = f"Pink: {game_state['pink_count']}"
# if it is the blue button
elif button == blue_button:
# increment blue count
game_state["blue_count"] += 1
# update the label
blue_lbl.text = f"Blue: {game_state['blue_count']}"
# save the new game state
save_game_state()
# if the click is not on the current button
else:
# set this button as not selected
button.selected = False
# left click is not pressed
else:
# set all buttons as not selected
for button in all_buttons:
button.selected = False

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 922 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 922 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 922 B

View file

@ -0,0 +1,458 @@
# SPDX-FileCopyrightText: Copyright (c) 2025 Tim Cocks for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
Match3 game inspired by the Set card game. Two players compete
to find sets of cards that share matching or mis-matching traits.
"""
import array
import atexit
import io
import os
import time
import board
import busio
import digitalio
import supervisor
import terminalio
import usb
from tilepalettemapper import TilePaletteMapper
from displayio import TileGrid, Group, Palette, OnDiskBitmap, Bitmap
from adafruit_display_text.text_box import TextBox
import adafruit_usb_host_descriptors
from adafruit_debouncer import Debouncer
import adafruit_sdcard
import msgpack
import storage
from match3_game_helpers import (
Match3Game,
STATE_GAMEOVER,
STATE_PLAYING_SETCALLED,
GameOverException,
)
original_autoreload_val = supervisor.runtime.autoreload
supervisor.runtime.autoreload = False
AUTOSAVE_FILENAME = "match3_game_autosave.dat"
main_group = Group()
display = supervisor.runtime.display
# set up scale factor of 2 for larger display resolution
scale_factor = 1
if display.width > 360:
scale_factor = 2
main_group.scale = scale_factor
save_to = None
game_state = None
try:
# check for autosave file on CPSAVES drive
if AUTOSAVE_FILENAME in os.listdir("/saves/"):
savegame_buffer = io.BytesIO()
with open(f"/saves/{AUTOSAVE_FILENAME}", "rb") as f:
savegame_buffer.write(f.read())
savegame_buffer.seek(0)
game_state = msgpack.unpack(savegame_buffer)
print(game_state)
# if we made it to here then /saves/ exist so use it for
# save data
save_to = f"/saves/{AUTOSAVE_FILENAME}"
except OSError as e:
# no /saves/ dir likely means no CPSAVES
pass
sd_pins_in_use = False
if game_state is None:
# try to use sdcard for saves
# The SD_CS pin is the chip select line.
SD_CS = board.SD_CS
# Connect to the card and mount the filesystem.
try:
cs = digitalio.DigitalInOut(SD_CS)
except ValueError:
sd_pins_in_use = True
print(f"sd pins in use: {sd_pins_in_use}")
try:
if not sd_pins_in_use:
sdcard = adafruit_sdcard.SDCard(
busio.SPI(board.SD_SCK, board.SD_MOSI, board.SD_MISO), cs
)
vfs = storage.VfsFat(sdcard)
storage.mount(vfs, "/sd")
if "set_game_autosave.dat" in os.listdir("/sd/"):
savegame_buffer = io.BytesIO()
with open("/sd/set_game_autosave.dat", "rb") as f:
savegame_buffer.write(f.read())
savegame_buffer.seek(0)
game_state = msgpack.unpack(savegame_buffer)
print(game_state)
if "placeholder.txt" not in os.listdir("/sd/"):
# if we made it to here then /sd/ exists and has a card
# so use it for save data
save_to = "/sd/set_game_autosave.dat"
except OSError:
# no SDcard
pass
# background color
bg_bmp = Bitmap(
display.width // scale_factor // 10, display.height // scale_factor // 10, 1
)
bg_palette = Palette(1)
bg_palette[0] = 0x888888
bg_tg = TileGrid(bg_bmp, pixel_shader=bg_palette)
bg_group = Group(scale=10)
bg_group.append(bg_tg)
main_group.append(bg_group)
# create Game helper object
match3_game = Match3Game(
game_state=game_state,
display_size=(display.width // scale_factor, display.height // scale_factor),
save_location=save_to,
)
main_group.append(match3_game)
# create a group to hold the game over elements
game_over_group = Group()
# create a TextBox to hold the game over message
game_over_label = TextBox(
terminalio.FONT,
text="",
color=0xFFFFFF,
background_color=0x111111,
width=display.width // scale_factor // 2,
height=110,
align=TextBox.ALIGN_CENTER,
)
# move it to the center top of the display
game_over_label.anchor_point = (0, 0)
game_over_label.anchored_position = (
display.width // scale_factor // 2 - (game_over_label.width) // 2,
40,
)
# make it hidden, we'll show it when the game is over.
game_over_group.hidden = True
# add the game over lable to the game over group
game_over_group.append(game_over_label)
# load the play again, and exit button bitmaps
play_again_btn_bmp = OnDiskBitmap("btn_play_again.bmp")
exit_btn_bmp = OnDiskBitmap("btn_exit.bmp")
# create TileGrid for the play again button
play_again_btn = TileGrid(
bitmap=play_again_btn_bmp, pixel_shader=play_again_btn_bmp.pixel_shader
)
# transparent pixels in the corners for the rounded corner effect
play_again_btn_bmp.pixel_shader.make_transparent(0)
# centered within the display, offset to the left
play_again_btn.x = (
display.width // scale_factor // 2 - (play_again_btn_bmp.width) // 2 - 30
)
# inside the bounds of the game over label, so it looks like a dialog visually
play_again_btn.y = 100
# create TileGrid for the exit button
exit_btn = TileGrid(bitmap=exit_btn_bmp, pixel_shader=exit_btn_bmp.pixel_shader)
# transparent pixels in the corners for the rounded corner effect
exit_btn_bmp.pixel_shader.make_transparent(0)
# centered within the display, offset to the right
exit_btn.x = display.width // scale_factor // 2 - (exit_btn_bmp.width) // 2 + 30
# inside the bounds of the game over label, so it looks like a dialog visually
exit_btn.y = 100
# add the play again and exit buttons to the game over group
game_over_group.append(play_again_btn)
game_over_group.append(exit_btn)
main_group.append(game_over_group)
# wait a second for USB devices to be ready
time.sleep(1)
# load the mouse bitmap
mouse_bmp = OnDiskBitmap("mouse_cursor.bmp")
# make the background pink pixels transparent
mouse_bmp.pixel_shader.make_transparent(0)
# list for mouse tilegrids
mouse_tgs = []
# list for palette mappers, one for each mouse
palette_mappers = []
# list for mouse colors
colors = [0x2244FF, 0xFFFF00]
# remap palette will have the 3 colors from mouse bitmap
# and the two colors from the mouse colors list
remap_palette = Palette(3 + len(colors))
# index 0 is transparent
remap_palette.make_transparent(0)
# copy the 3 colors from the mouse bitmap palette
for i in range(3):
remap_palette[i] = mouse_bmp.pixel_shader[i]
# copy the 2 colors from the mouse colors list
for i in range(2):
remap_palette[i + 3] = colors[i]
# create tile palette mappers
for i in range(2):
palette_mapper = TilePaletteMapper(remap_palette, 3, 1, 1)
# remap index 2 to each of the colors in mouse colors list
palette_mapper[0] = [0, 1, i + 3]
palette_mappers.append(palette_mapper)
# create tilegrid for each mouse
mouse_tg = TileGrid(mouse_bmp, pixel_shader=palette_mapper)
mouse_tg.x = display.width // scale_factor // 2 - (i * 12)
mouse_tg.y = display.height // scale_factor // 2
mouse_tgs.append(mouse_tg)
# USB info lists
mouse_interface_indexes = []
mouse_endpoint_addresses = []
kernel_driver_active_flags = []
# USB device object instance list
mice = []
# buffers list for mouse packet data
mouse_bufs = []
# debouncers list for debouncing mouse left clicks
mouse_debouncers = []
# scan for connected USB devices
for device in usb.core.find(find_all=True):
# check if current device is has a boot mouse endpoint
mouse_interface_index, mouse_endpoint_address = (
adafruit_usb_host_descriptors.find_boot_mouse_endpoint(device)
)
if mouse_interface_index is not None and mouse_endpoint_address is not None:
# if it does have a boot mouse endpoint then add information to the
# usb info lists
mouse_interface_indexes.append(mouse_interface_index)
mouse_endpoint_addresses.append(mouse_endpoint_address)
# add the mouse device instance to list
mice.append(device)
print(
f"mouse interface: {mouse_interface_index} "
+ f"endpoint_address: {hex(mouse_endpoint_address)}"
)
# detach kernel driver if needed
kernel_driver_active_flags.append(device.is_kernel_driver_active(0))
if device.is_kernel_driver_active(0):
device.detach_kernel_driver(0)
# set the mouse configuration so it can be used
device.set_configuration()
def is_mouse1_left_clicked():
"""
Check if mouse 1 left click is pressed
:return: True if mouse 1 left click is pressed
"""
return is_left_mouse_clicked(mouse_bufs[0])
def is_mouse2_left_clicked():
"""
Check if mouse 2 left click is pressed
:return: True if mouse 2 left click is pressed
"""
return is_left_mouse_clicked(mouse_bufs[1])
def is_left_mouse_clicked(buf):
"""
Check if a mouse is pressed given its packet buffer
filled with read data
:param buf: the buffer containing the packet data
:return: True if mouse left click is pressed
"""
val = buf[0] & (1 << 0) != 0
return val
def is_right_mouse_clicked(buf):
"""
check if a mouse right click is pressed given its packet buffer
:param buf: the buffer containing the packet data
:return: True if mouse right click is pressed
"""
val = buf[0] & (1 << 1) != 0
return val
# print(f"addresses: {mouse_endpoint_addresses}")
# print(f"indexes: {mouse_interface_indexes}")
for mouse_tg in mouse_tgs:
# add the mouse to the main group
main_group.append(mouse_tg)
# Buffer to hold data read from the mouse
# Boot mice have 4 byte reports
mouse_bufs.append(array.array("b", [0] * 8))
# create debouncer objects for left click functions
mouse_debouncers.append(Debouncer(is_mouse1_left_clicked))
mouse_debouncers.append(Debouncer(is_mouse2_left_clicked))
# set main_group as root_group, so it is visible on the display
display.root_group = main_group
# variable to hold winning player
winner = None
def get_mouse_deltas(buffer, read_count):
"""
Given a mouse packet buffer and a read count of number of bytes read,
return the delta x and y values of the mouse.
:param buffer: the buffer containing the packet data
:param read_count: the number of bytes read from the mouse
:return: tuple containing x and y delta values
"""
if read_count == 4:
delta_x = buffer[1]
delta_y = buffer[2]
elif read_count == 8:
delta_x = buffer[2]
delta_y = buffer[4]
else:
raise ValueError(f"Unsupported mouse packet size: {read_count}, must be 4 or 8")
return delta_x, delta_y
def atexit_callback():
"""
re-attach USB devices to kernel if needed, and set
autoreload back to the original state.
:return:
"""
for _i, _mouse in enumerate(mice):
if kernel_driver_active_flags[_i]:
if not _mouse.is_kernel_driver_active(0):
_mouse.attach_kernel_driver(0)
supervisor.runtime.autoreload = original_autoreload_val
atexit.register(atexit_callback)
# main loop
while True:
# if set has been called
if match3_game.cur_state == STATE_PLAYING_SETCALLED:
# update the progress bar ticking down
match3_game.update_active_turn_progress()
# loop over the mice objects
for i, mouse in enumerate(mice):
mouse_tg = mouse_tgs[i]
# attempt mouse read
try:
# read data from the mouse, small timeout so we move on
# quickly if there is no data
data_len = mouse.read(
mouse_endpoint_addresses[i], mouse_bufs[i], timeout=10
)
mouse_deltas = get_mouse_deltas(mouse_bufs[i], data_len)
# if we got data, then update the mouse cursor on the display
# using min and max to keep it within the bounds of the display
mouse_tg.x = max(
0,
min(
display.width // scale_factor - 1, mouse_tg.x + mouse_deltas[0] // 2
),
)
mouse_tg.y = max(
0,
min(
display.height // scale_factor - 1,
mouse_tg.y + mouse_deltas[1] // 2,
),
)
# timeout error is raised if no data was read within the allotted timeout
except usb.core.USBTimeoutError:
pass
# update the mouse debouncers
mouse_debouncers[i].update()
try:
# if the current mouse is right-clicking
if is_right_mouse_clicked(mouse_bufs[i]):
# let the game object handle the right-click
match3_game.handle_right_click(i)
# if the current mouse left-clicked
if mouse_debouncers[i].rose:
# get the current mouse coordinates
coords = (mouse_tg.x, mouse_tg.y, 0)
# if the current state is GAMEOVER
if match3_game.cur_state != STATE_GAMEOVER:
# let the game object handle the click event
match3_game.handle_left_click(i, coords)
else:
# if the mouse point is within the play again
# button bounding box
if play_again_btn.contains(coords):
# set next code file to this one
supervisor.set_next_code_file(__file__)
# reload
supervisor.reload()
# if the mouse point is within the exit
# button bounding box
if exit_btn.contains(coords):
supervisor.reload()
# if the game is over
except GameOverException:
# check for a winner
winner = None
if match3_game.scores[0] > match3_game.scores[1]:
winner = 0
elif match3_game.scores[0] < match3_game.scores[1]:
winner = 1
# if there was a winner
if winner is not None:
# show a message with the winning player
message = f"\nGame Over\nPlayer{winner + 1} Wins!"
game_over_label.color = colors[winner]
game_over_label.text = message
else: # there wasn't a winner
# show a tie game message
message = "\nGame Over\nTie Game Everyone Wins!"
# make the gameover group visible
game_over_group.hidden = False
# delete the autosave file.
os.remove(save_to)

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View file

@ -0,0 +1,779 @@
# SPDX-FileCopyrightText: Copyright (c) 2025 Tim Cocks for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import os
import random
import time
from io import BytesIO
import terminalio
import adafruit_imageload
from displayio import Group, TileGrid, OnDiskBitmap, Palette, Bitmap
import bitmaptools
import msgpack
from tilepalettemapper import TilePaletteMapper
import ulab.numpy as np
from adafruit_display_text.bitmap_label import Label
from adafruit_displayio_layout.layouts.grid_layout import GridLayout
from adafruit_button import Button
from adafruit_progressbar.horizontalprogressbar import (
HorizontalProgressBar,
HorizontalFillDirection,
)
# pylint: disable=too-many-locals, too-many-nested-blocks, too-many-branches, too-many-statements
colors = [0x2244FF, 0xFFFF00]
STATE_TITLE = 0
STATE_PLAYING_OPEN = 1
STATE_PLAYING_SETCALLED = 2
STATE_GAMEOVER = 3
ACTIVE_TURN_TIME_LIMIT = 10.0
def random_selection(lst, count):
"""
Select items randomly from a list of items.
returns a list of length count containing the selected items.
"""
if len(lst) < count:
raise ValueError("Count must be less than or equal to length of list")
selection = []
while len(selection) < count:
selection.append(lst.pop(random.randrange(len(lst))))
return selection
def validate_set(card_1, card_2, card_3):
"""
Check if a set of 3 cards is valid
:param card_1: the first card
:param card_2: the second card
:param card_3: the third card
:return: True if they are a valid set, False otherwise
"""
matrix_sums = card_1.tuple + card_2.tuple + card_3.tuple
for val in matrix_sums:
if val % 3 != 0:
return False
return True
class Match3Card(Group):
"""
Class representing a Match3 Card. Keeps track of shape, count, color, and fill.
tuple value mappings:
color, shape, fill, count
0 , 1 , 2 , 1
colors
purple: 0
red: 1
green: 2
shapes
rectangle: 0
triangle: 1
circle: 2
fill
outline: 0
filled: 1
striped: 2
count
one: 0
two: 1
three: 2
"""
TUPLE_VALUE_TO_TILE_INDEX_LUT = {
# rectangle filled
(0, 1, 0): 0,
(0, 1, 1): 1,
(0, 1, 2): 2,
# triangle filled
(1, 1, 0): 3,
(1, 1, 1): 4,
(1, 1, 2): 5,
# circle filled
(2, 1, 0): 6,
(2, 1, 1): 13,
(2, 1, 2): 20,
# rectangle outline
(0, 0, 0): 7,
(0, 0, 1): 8,
(0, 0, 2): 9,
# triangle outline
(1, 0, 0): 10,
(1, 0, 1): 11,
(1, 0, 2): 12,
# circle outline
(2, 0, 0): 21,
(2, 0, 1): 22,
(2, 0, 2): 23,
# rectangle striped
(0, 2, 0): 14,
(0, 2, 1): 15,
(0, 2, 2): 16,
# triangle striped
(1, 2, 0): 17,
(1, 2, 1): 18,
(1, 2, 2): 19,
# circle striped
(2, 2, 0): 24,
(2, 2, 1): 25,
(2, 2, 2): 26,
}
def __init__(self, card_tuple, **kwargs):
# tile palette mapper to color the card
self._mapper = TilePaletteMapper(kwargs["pixel_shader"], 5, 1, 1)
kwargs["pixel_shader"] = self._mapper
# tile grid to for the visible sprite
self._tilegrid = TileGrid(**kwargs)
self._tilegrid.x = 4
self._tilegrid.y = 4
# initialize super class Group
super().__init__()
# add tilegrid to self instance Group
self.append(self._tilegrid)
# numpy array of the card tuple values
self._tuple = np.array(list(card_tuple), dtype=np.uint8)
# set the sprite and color based on card attributes
self._update_card_attributes()
def _update_card_attributes(self):
"""
set the sprite and color based on card attributes
:return: None
"""
# set color
color_tuple_val = self._tuple[0]
self._mapper[0] = [0, color_tuple_val + 2, 2, 3, 4]
# set tile
self._tilegrid[0] = Match3Card.TUPLE_VALUE_TO_TILE_INDEX_LUT[
(self._tuple[1], self._tuple[2], self._tuple[3])
]
def __str__(self):
return self.tuple
def __repr__(self):
return self.tuple
@property
def tuple(self):
"""
The tuple containing attributes values for this card.
"""
return self._tuple
def contains(self, coordinates):
"""
Check if the cards bounding box contains the given coordinates.
:param coordinates: the coordinates to check
:return: True if the bounding box contains the given coordinates, False otherwise
"""
return (
self.x <= coordinates[0] <= self.x + self._tilegrid.tile_width
and self.y <= coordinates[1] <= self.y + self._tilegrid.tile_height
)
class Match3Game(Group):
"""
Match3 Game helper class
Holds visual elements, manages state machine.
"""
def __init__(self, game_state=None, display_size=None, save_location=None):
# initialize super Group instance
super().__init__()
self.game_state = game_state
self.display_size = display_size
# list of Match3Card instances representing the current deck
self.play_deck = []
# load the spritesheet
self.card_spritesheet, self.card_palette = adafruit_imageload.load(
"match3_cards_spritesheet.bmp"
)
# start in the TITLE state
self.cur_state = STATE_TITLE
# create a grid layout to help place cards neatly
# into a grid on the display
grid_size = (6, 3)
self.card_grid = GridLayout(
x=10, y=10, width=260, height=200, grid_size=grid_size
)
# no set button in the bottom right
self.no_set_btn_bmp = OnDiskBitmap("btn_no_set.bmp")
self.no_set_btn_bmp.pixel_shader.make_transparent(0)
self.no_set_btn = TileGrid(
bitmap=self.no_set_btn_bmp, pixel_shader=self.no_set_btn_bmp.pixel_shader
)
self.no_set_btn.x = display_size[0] - self.no_set_btn.tile_width
self.no_set_btn.y = display_size[1] - self.no_set_btn.tile_height
self.append(self.no_set_btn)
# list to hold score labels, one for each player
self.score_lbls = []
# player scores start at 0
self.scores = [0, 0]
self.save_location = save_location
# initialize and position the score labels
for i in range(2):
score_lbl = Label(terminalio.FONT, text=f"P{i + 1}: 0", color=colors[i])
self.score_lbls.append(score_lbl)
score_lbl.anchor_point = (1.0, 0.0)
score_lbl.anchored_position = (display_size[0] - 2, 2 + i * 12)
self.append(score_lbl)
# deck count label in the bottom left
self.deck_count_lbl = Label(
terminalio.FONT, text=f"Deck: {len(self.play_deck)}"
)
self.deck_count_lbl.anchor_point = (0.0, 1.0)
self.deck_count_lbl.anchored_position = (2, display_size[1] - 2)
self.append(self.deck_count_lbl)
# will hold active player index
self.active_player = None
# list of player index who have called no set
self.no_set_called_player_indexes = []
# active turn countdown progress bar
# below the score labels
self.active_turn_countdown = HorizontalProgressBar(
(display_size[0] - 64, 30),
(60, 6),
direction=HorizontalFillDirection.LEFT_TO_RIGHT,
min_value=0,
max_value=ACTIVE_TURN_TIME_LIMIT * 10,
)
self.active_turn_countdown.hidden = True
self.append(self.active_turn_countdown)
# will hold the timestamp when active turn began
self.active_turn_start_time = None
# add the card grid to self instance Group
self.append(self.card_grid)
# list of card objects that have been clicked
self.clicked_cards = []
# list of coordinates that have been clicked
self.clicked_coordinates = []
# initialize title screen
self.title_screen = Match3TitleScreen(display_size)
self.append(self.title_screen)
# set up the clicked card indicator borders
self.clicked_card_indicator_palette = Palette(2)
self.clicked_card_indicator_palette[0] = 0x000000
self.clicked_card_indicator_palette.make_transparent(0)
self.clicked_card_indicator_palette[1] = colors[0]
self.clicked_card_indicator_bmp = Bitmap(24 + 8, 32 + 8, 2)
self.clicked_card_indicator_bmp.fill(1)
bitmaptools.fill_region(
self.clicked_card_indicator_bmp,
2,
2,
self.clicked_card_indicator_bmp.width - 2,
self.clicked_card_indicator_bmp.height - 2,
value=0,
)
self.clicked_card_indicators = []
for _ in range(3):
self.clicked_card_indicators.append(
TileGrid(
bitmap=self.clicked_card_indicator_bmp,
pixel_shader=self.clicked_card_indicator_palette,
)
)
def update_scores(self):
"""
Update the score labels to reflect the current player scores
:return: None
"""
for player_index in range(2):
prefix = ""
if player_index == self.active_player:
prefix = ">"
if player_index in self.no_set_called_player_indexes:
prefix = "*"
self.score_lbls[player_index].text = (
f"{prefix}P{player_index + 1}: {self.scores[player_index]}"
)
def save_game_state(self):
"""
Save the game state to a file
:return: None
"""
# if there is a valid save location
if self.save_location is not None:
# create a dictionary object to store the game state
game_state = {"scores": self.scores, "board": {}, "deck": []}
# read the current board state into the dictionary object
for _y in range(3):
for _x in range(6):
try:
content = self.card_grid.get_content((_x, _y))
game_state["board"][f"{_x},{_y}"] = tuple(content.tuple)
except KeyError:
pass
# read the current deck state into the dictionary object
for card in self.play_deck:
game_state["deck"].append(tuple(card.tuple))
# msgpack the object and write it to a file
b = BytesIO()
msgpack.pack(game_state, b)
b.seek(0)
with open(self.save_location, "wb") as f:
f.write(b.read())
def load_from_game_state(self, game_state):
"""
Load game state from a dictionary.
:param game_state: The dictionary of game state to load
:return: None
"""
# loop over cards in the deck
for card_tuple in game_state["deck"]:
# create a card instance and add it to the deck
self.play_deck.append(
Match3Card(
card_tuple,
bitmap=self.card_spritesheet,
pixel_shader=self.card_palette,
tile_width=24,
tile_height=32,
)
)
# loop over grid cells
for y in range(3):
for x in range(6):
# if the current cell is in the board state of the save game
if f"{x},{y}" in game_state["board"]:
# create a card instance and put it in the grid here
card_tuple = game_state["board"][f"{x},{y}"]
self.card_grid.add_content(
Match3Card(
card_tuple,
bitmap=self.card_spritesheet,
pixel_shader=self.card_palette,
tile_width=24,
tile_height=32,
),
(x, y),
(1, 1),
)
# set the scores from the game state
self.scores = game_state["scores"]
# update the visible score labels
self.update_scores()
# update the deck count label
self.deck_count_lbl.text = f"Deck: {len(self.play_deck)}"
def init_new_game(self):
"""
Initialize a new game state.
:return: None
"""
self.play_deck = []
# loop over the 3 possibilities in each of the 4 attributes
for _color in range(0, 3):
for _shape in range(0, 3):
for _fill in range(0, 3):
for _count in range(0, 3):
# create a card instance with the current attributes
self.play_deck.append(
Match3Card(
(_color, _shape, _fill, _count),
bitmap=self.card_spritesheet,
pixel_shader=self.card_palette,
tile_width=24,
tile_height=32,
)
)
# draw the starting cards at random
starting_pool = random_selection(self.play_deck, 12)
# put the starting cards into the grid layout
for y in range(3):
for x in range(4):
self.card_grid.add_content(starting_pool[y * 4 + x], (x, y), (1, 1))
# update the deck count label
self.deck_count_lbl.text = f"Deck: {len(self.play_deck)}"
def handle_right_click(self, player_index):
"""
Handle right click event
:param player_index: the index of the player who clicked
:return: None
"""
# if the current state is open play
if self.cur_state == STATE_PLAYING_OPEN:
# if there is no active player
if self.active_player is None:
# if the player who right clicked is in the no set called list
if player_index in self.no_set_called_player_indexes:
# remove them from the no set called list
self.no_set_called_player_indexes.remove(player_index)
# set the active player to the player that clicked
self.active_player = player_index
# set the clicked card indicators to the active player's color
self.clicked_card_indicator_palette[1] = colors[player_index]
# set the current state to the set called state
self.cur_state = STATE_PLAYING_SETCALLED
# store timestamp of when the active turn began
self.active_turn_start_time = time.monotonic()
# make the countdown progress bar visible
self.active_turn_countdown.hidden = False
# set the value to the maximum of the progress bar
self.active_turn_countdown.value = 60
# update the score labels to show the active player indicator
self.update_scores()
def handle_left_click(self, player_index, coords):
"""
Handle left click events
:param player_index: the index of the player who clicked
:param coords: the coordinates where the mouse clicked
:return: None
"""
# if the current state is open playing
if self.cur_state == STATE_PLAYING_OPEN:
# if the click is on the no set button
if self.no_set_btn.contains(coords):
# if the player that clicked is not in the net set called list
if player_index not in self.no_set_called_player_indexes:
# add them to the no set called list
self.no_set_called_player_indexes.append(player_index)
# if both players have called no set
if len(self.no_set_called_player_indexes) == 2:
# if there are no cards left in the deck
if len(self.play_deck) == 0:
# set the state to game over
self.cur_state = STATE_GAMEOVER
raise GameOverException()
# empty the no set called list
self.no_set_called_player_indexes = []
# find the empty cells in the card grid
empty_cells = self.find_empty_cells()
# if there are more than 3 empty cells
if len(empty_cells) >= 3:
# draw 3 new cards
_new_cards = random_selection(self.play_deck, 3)
# place them in 3 of the empty cells
for i, _new_card in enumerate(_new_cards):
self.card_grid.add_content(
_new_card, empty_cells[i], (1, 1)
)
else: # there are no 3 empty cells
# redraw the original grid with 12 new cards
# remove existing cards from the grid and
# return them to the deck.
for _y in range(3):
for _x in range(6):
try:
_remove_card = self.card_grid.pop_content(
(_x, _y)
)
print(f"remove_card: {_remove_card}")
self.play_deck.append(_remove_card)
except KeyError:
continue
# draw 12 new cards from the deck
starting_pool = random_selection(self.play_deck, 12)
# place them into the grid
for y in range(3):
for x in range(4):
self.card_grid.add_content(
starting_pool[y * 4 + x], (x + 1, y), (1, 1)
)
# update the deck count label
self.deck_count_lbl.text = f"Deck: {len(self.play_deck)}"
# save the game state
self.save_game_state()
# update the score labels to show the no set called indicator(s)
self.update_scores()
# if the current state is set called
elif self.cur_state == STATE_PLAYING_SETCALLED:
# if the player that clicked is the active player
if player_index == self.active_player:
# get the coordinates that were clicked adjusting for the card_grid position
adjusted_coords = (
coords[0] - self.card_grid.x,
coords[1] - self.card_grid.y,
0,
)
# check which cell contains the clicked coordinates
clicked_grid_cell_coordinates = self.card_grid.which_cell_contains(
coords
)
# print(clicked_grid_cell_coordinates)
# if a cell in the grid was clicked
if clicked_grid_cell_coordinates is not None:
# try to get the content of the clicked cell, a Card instance potentially
try:
clicked_cell_content = self.card_grid.get_content(
clicked_grid_cell_coordinates
)
except KeyError:
# if no content is in the cell just return
return
# check if the Card instance was clicked, and if the card
# isn't already in the list of clicked cards
if (
clicked_cell_content.contains(adjusted_coords)
and clicked_cell_content not in self.clicked_cards
):
clicked_card = clicked_cell_content
# show the clicked card indicator in this cell
clicked_cell_content.insert(
0, self.clicked_card_indicators[len(self.clicked_cards)]
)
# add the card instance to the clicked cards list
self.clicked_cards.append(clicked_card)
# add the coordinates to the clicked coordinates list
self.clicked_coordinates.append(clicked_grid_cell_coordinates)
# if 3 cards have been clicked
if len(self.clicked_cards) == 3:
# check if the 3 cards make a valid set
valid_set = validate_set(self.clicked_cards[0],
self.clicked_cards[1],
self.clicked_cards[2])
# if they are a valid set
if valid_set:
# award a point to the active player
self.scores[self.active_player] += 1
# loop over the clicked coordinates
for coord in self.clicked_coordinates:
# remove the old card from this cell
_remove_card = self.card_grid.pop_content(coord)
# remove border from Match3Card group
_remove_card.pop(0)
# find empty cells in the grid
empty_cells = self.find_empty_cells()
# if there are at least 3 cards in the deck and
# at least 6 empty cells in the grid
if len(self.play_deck) >= 3 and len(empty_cells) > 6:
# deal 3 new cards to empty spots in the grid
for i in range(3):
_new_card = random_selection(self.play_deck, 1)[
0
]
self.card_grid.add_content(
_new_card, empty_cells[i], (1, 1)
)
# update the deck count label
self.deck_count_lbl.text = (
f"Deck: {len(self.play_deck)}"
)
# there are not at least 3 cards in the deck
# and at least 6 empty cells
else:
# if there are no empty cells
if len(self.find_empty_cells()) == 0:
# set the current state to game over
self.cur_state = STATE_GAMEOVER
raise GameOverException()
else: # the 3 clicked cards are not a valid set
# remove the clicked card indicators
for _ in range(3):
coords = self.clicked_coordinates.pop()
self.card_grid.get_content(coords).pop(0)
# subtract a point from the active player
self.scores[self.active_player] -= 1
# save the game state
self.save_game_state()
# reset the clicked cards and coordinates lists
self.clicked_cards = []
self.clicked_coordinates = []
# set the current state to open play
self.cur_state = STATE_PLAYING_OPEN
# set active player and active turn vars
self.active_player = None
self.active_turn_start_time = None
self.active_turn_countdown.hidden = True
# update the score labels
self.update_scores()
# if the current state is title state
elif self.cur_state == STATE_TITLE:
# if the resume button is visible and was clicked
if (
not self.title_screen.resume_btn.hidden
and self.title_screen.resume_btn.contains(coords)
):
# load the game from the given game state
self.load_from_game_state(self.game_state)
# hide the title screen
self.title_screen.hidden = True # pylint: disable=attribute-defined-outside-init
# set the current state to open play
self.cur_state = STATE_PLAYING_OPEN
# if the new game button was clicked
elif self.title_screen.new_game_btn.contains(coords):
self.game_state = None
# delete the autosave file
try:
os.remove(self.save_location)
print("removed old game save file")
except OSError:
pass
# initialize a new game
self.init_new_game()
# hide the title screen
self.title_screen.hidden = True # pylint: disable=attribute-defined-outside-init
# set the current state to open play
self.cur_state = STATE_PLAYING_OPEN
def find_empty_cells(self):
"""
find the cells within the card grid that are empty
:return: list of empty cell coordinate tuples.
"""
empty_cells = []
for x in range(6):
for y in range(3):
try:
_content = self.card_grid.get_content((x, y))
except KeyError:
empty_cells.append((x, y))
return empty_cells
def update_active_turn_progress(self):
"""
update the active turn progress bar countdown
:return:
"""
if self.cur_state == STATE_PLAYING_SETCALLED:
time_diff = time.monotonic() - self.active_turn_start_time
if time_diff > ACTIVE_TURN_TIME_LIMIT:
self.scores[self.active_player] -= 1
self.active_player = None
self.update_scores()
self.cur_state = STATE_PLAYING_OPEN
self.active_turn_countdown.hidden = True
else:
self.active_turn_countdown.value = int(
(ACTIVE_TURN_TIME_LIMIT - time_diff) * 10
)
class GameOverException(Exception):
"""
Exception that indicates the game is over.
Message will contain the reason.
"""
class Match3TitleScreen(Group):
"""
Title screen for the Match3 game.
"""
def __init__(self, display_size):
super().__init__()
self.display_size = display_size
# background bitmap color
bg_bmp = Bitmap(display_size[0] // 10, display_size[1] // 10, 1)
bg_palette = Palette(1)
bg_palette[0] = 0xFFFFFF
bg_tg = TileGrid(bg_bmp, pixel_shader=bg_palette)
bg_group = Group(scale=10)
bg_group.append(bg_tg)
self.append(bg_group)
# load title card bitmap
title_card_bmp = OnDiskBitmap("title_card_match3.bmp")
title_card_tg = TileGrid(
title_card_bmp, pixel_shader=title_card_bmp.pixel_shader
)
title_card_tg.x = 2
if display_size[1] > 200:
title_card_tg.y = 20
self.append(title_card_tg)
# make resume and new game buttons
BUTTON_X = display_size[0] - 90
BUTTON_WIDTH = 70
BUTTON_HEIGHT = 20
self.resume_btn = Button(
x=BUTTON_X,
y=40,
width=BUTTON_WIDTH,
height=BUTTON_HEIGHT,
style=Button.ROUNDRECT,
fill_color=0x6D2EDC,
outline_color=0x888888,
label="Resume",
label_font=terminalio.FONT,
label_color=0xFFFFFF,
)
self.append(self.resume_btn)
self.new_game_btn = Button(
x=BUTTON_X,
y=40 + BUTTON_HEIGHT + 10,
width=BUTTON_WIDTH,
height=BUTTON_HEIGHT,
style=Button.RECT,
fill_color=0x0C9F0C,
outline_color=0x111111,
label="New Game",
label_font=terminalio.FONT,
label_color=0xFFFFFF,
)
self.append(self.new_game_btn)

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View file

@ -0,0 +1,50 @@
# SPDX-FileCopyrightText: Copyright (c) 2025 Tim Cocks for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import supervisor
from displayio import Group, OnDiskBitmap, TileGrid
from tilepalettemapper import TilePaletteMapper
# use the default built-in display,
# the HSTX / PicoDVI display for the Metro RP2350
display = supervisor.runtime.display
# a group to hold all other visual elements
main_group = Group(scale=4, x=30, y=30)
# set the main group to show on the display
display.root_group = main_group
# load the sprite sheet bitmap
spritesheet_bmp = OnDiskBitmap("match3_cards_spritesheet.bmp")
# create a TilePaletteMapper
tile_palette_mapper = TilePaletteMapper(
spritesheet_bmp.pixel_shader, # input pixel_shader
5, # input color count
3, # grid width
1 # grid height
)
# create a TileGrid to show some cards
cards_tilegrid = TileGrid(spritesheet_bmp, pixel_shader=tile_palette_mapper,
width=3, height=1, tile_width=24, tile_height=32)
# set each tile in the grid to a different sprite index
cards_tilegrid[0, 0] = 10
cards_tilegrid[1, 0] = 25
cards_tilegrid[2, 0] = 2
# re-map each tile in the grid to use a different color for index 1
# all other indexes remain their default values
tile_palette_mapper[0, 0] = [0, 2, 2, 3, 4]
tile_palette_mapper[1, 0] = [0, 3, 2, 3, 4]
tile_palette_mapper[2, 0] = [0, 4, 2, 3, 4]
# add the tilegrid to the main group
main_group.append(cards_tilegrid)
# wait forever so it remains visible on the display
while True:
pass

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View file

@ -0,0 +1,184 @@
# SPDX-FileCopyrightText: Copyright (c) 2025 Tim Cocks for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import array
import supervisor
import terminalio
import usb.core
from adafruit_display_text.bitmap_label import Label
from displayio import Group, OnDiskBitmap, TileGrid, Palette
import adafruit_usb_host_descriptors
# use the default built-in display,
# the HSTX / PicoDVI display for the Metro RP2350
display = supervisor.runtime.display
# a group to hold all other visual elements
main_group = Group()
# set the main group to show on the display
display.root_group = main_group
# load the cursor bitmap file
mouse_bmp = OnDiskBitmap("mouse_cursor.bmp")
# lists for labels, mouse tilegrids, and palettes.
# each mouse will get 1 of each item. All lists
# will end up with length 2.
output_lbls = []
mouse_tgs = []
palettes = []
# the different colors to use for each mouse cursor
# and labels
colors = [0xFF00FF, 0x00FF00]
for i in range(2):
# create a palette for this mouse
mouse_palette = Palette(3)
# index zero is used for transparency
mouse_palette.make_transparent(0)
# add the palette to the list of palettes
palettes.append(mouse_palette)
# copy the first two colors from mouse palette
for palette_color_index in range(2):
mouse_palette[palette_color_index] = mouse_bmp.pixel_shader[palette_color_index]
# replace the last color with different color for each mouse
mouse_palette[2] = colors[i]
# create a TileGrid for this mouse cursor.
# use the palette created above
mouse_tg = TileGrid(mouse_bmp, pixel_shader=mouse_palette)
# move the mouse tilegrid to near the center of the display
mouse_tg.x = display.width // 2 - (i * 12)
mouse_tg.y = display.height // 2
# add this mouse tilegrid to the list of mouse tilegrids
mouse_tgs.append(mouse_tg)
# add this mouse tilegrid to the main group so it will show
# on the display
main_group.append(mouse_tg)
# create a label for this mouse
output_lbl = Label(terminalio.FONT, text=f"{mouse_tg.x},{mouse_tg.y}", color=colors[i], scale=1)
# anchored to the top left corner of the label
output_lbl.anchor_point = (0, 0)
# move to op left corner of the display, moving
# down by a static amount to static the two labels
# one below the other
output_lbl.anchored_position = (1, 1 + i * 13)
# add the label to the list of labels
output_lbls.append(output_lbl)
# add the label to the main group so it will show
# on the display
main_group.append(output_lbl)
# lists for mouse interface indexes, endpoint addresses, and USB Device instances
# each of these will end up with length 2 once we find both mice
mouse_interface_indexes = []
mouse_endpoint_addresses = []
mice = []
# scan for connected USB devices
for device in usb.core.find(find_all=True):
# check for boot mouse endpoints on this device
mouse_interface_index, mouse_endpoint_address = (
adafruit_usb_host_descriptors.find_boot_mouse_endpoint(device)
)
# if a boot mouse interface index and endpoint address were found
if mouse_interface_index is not None and mouse_endpoint_address is not None:
# add the interface index to the list of indexes
mouse_interface_indexes.append(mouse_interface_index)
# add the endpoint address to the list of addresses
mouse_endpoint_addresses.append(mouse_endpoint_address)
# add the device instance to the list of mice
mice.append(device)
# print details to the console
print(f"mouse interface: {mouse_interface_index} ", end="")
print(f"endpoint_address: {hex(mouse_endpoint_address)}")
# detach device from kernel if needed
if device.is_kernel_driver_active(0):
device.detach_kernel_driver(0)
# set the mouse configuration so it can be used
device.set_configuration()
# This is ordered by bit position.
BUTTONS = ["left", "right", "middle"]
# list of buffers, will hold one buffer for each mouse
mouse_bufs = []
for i in range(2):
# Buffer to hold data read from the mouse
mouse_bufs.append(array.array("b", [0] * 8))
def get_mouse_deltas(buffer, read_count):
"""
Given a buffer and read_count return the x and y delta values
:param buffer: A buffer containing data read from the mouse
:param read_count: How many bytes of data were read from the mouse
:return: tuple x,y delta values
"""
if read_count == 4:
delta_x = buffer[1]
delta_y = buffer[2]
elif read_count == 8:
delta_x = buffer[2]
delta_y = buffer[4]
else:
raise ValueError(f"Unsupported mouse packet size: {read_count}, must be 4 or 8")
return delta_x, delta_y
# main loop
while True:
# for each mouse instance
for mouse_index, mouse in enumerate(mice):
# try to read data from the mouse
try:
count = mouse.read(
mouse_endpoint_addresses[mouse_index], mouse_bufs[mouse_index], timeout=10
)
# if there is no data it will raise USBTimeoutError
except usb.core.USBTimeoutError:
# Nothing to do if there is no data for this mouse
continue
# there was mouse data, so get the delta x and y values from it
mouse_deltas = get_mouse_deltas(mouse_bufs[mouse_index], count)
# update the x position of this mouse cursor using the delta value
# clamped to the display size
mouse_tgs[mouse_index].x = max(
0, min(display.width - 1, mouse_tgs[mouse_index].x + mouse_deltas[0])
)
# update the y position of this mouse cursor using the delta value
# clamped to the display size
mouse_tgs[mouse_index].y = max(
0, min(display.height - 1, mouse_tgs[mouse_index].y + mouse_deltas[1])
)
# output string with the new cursor position
out_str = f"{mouse_tgs[mouse_index].x},{mouse_tgs[mouse_index].y}"
# loop over possible button bit indexes
for i, button in enumerate(BUTTONS):
# check each bit index to determin if the button was pressed
if mouse_bufs[mouse_index][0] & (1 << i) != 0:
# if it was pressed, add the button to the output string
out_str += f" {button}"
# set the output string into text of the label
# to show it on the display
output_lbls[mouse_index].text = out_str

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 B

View file

@ -76,8 +76,27 @@ def open_next_image():
return open(filename, "wb")
cam.colorspace = adafruit_ov5640.OV5640_COLOR_JPEG
cam.quality = 3
b = bytearray(cam.capture_buffer_size)
# Different platforms have different amounts of memory available.
# Typically a Pico 2 can handle quality = 2 and a Pico can handle quality = 5.
# Rather than detect and select sizes, let's try to detect the best dynamically
# for broader platform support.
# Start with the highest quality setting and attempt to allocate a buffer
# of the necessary size. If it fails, try the next lowest.
b = None
for quality in range(2,55): #valid range is 2 to 54 inclusive
try:
cam.quality = quality
print(f"Attempting to use quality {quality}.")
b = bytearray(cam.capture_buffer_size)
print(f"Quality {quality} successfully selected.")
break
except MemoryError:
print(f"Quality {quality} was too big. Trying next lowest.")
if b is None:
print("There wasn't enough system memory to allocate the lowest quality buffer.")
jpeg = cam.capture(b)
while True:

View file

@ -2,6 +2,7 @@
#
# SPDX-License-Identifier: MIT
from os import getenv
import time
import ssl
import wifi
@ -14,6 +15,21 @@ import adafruit_requests
from digitalio import DigitalInOut, Direction, Pull
from adafruit_debouncer import Debouncer
# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml
# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.)
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
aio_username = getenv("ADAFRUIT_AIO_USERNAME")
aio_key = getenv("ADAFRUIT_AIO_KEY")
if None in [ssid, password, aio_username, aio_key]:
raise RuntimeError(
"WiFi and Adafruit IO settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"'ADAFRUIT_AIO_USERNAME' and 'ADAFRUIT_AIO_KEY' at a minimum."
)
alarm_out = DigitalInOut(board.A1)
alarm_out.direction = Direction.OUTPUT
alarm_out.value = False
@ -23,37 +39,28 @@ button_in.pull = Pull.UP
button = Debouncer(button_in)
# Get wifi details and more from a secrets.py file
try:
from secrets import secrets
except ImportError:
print("WiFi secrets are kept in secrets.py, please add them there!")
raise
print("Adafruit Raspberry Pi In Stock Tweet Listener")
# import your bearer token
bear = secrets['bearer_token']
bearer_token = getenv('bearer_token')
# query URL for tweets. looking for hashtag partyparrot sent to a specific username
# disabling line-too-long because queries for tweet_query & TIME_URL cannot have line breaks
# pylint: disable=line-too-long
tweet_query = 'https://api.twitter.com/2/tweets/search/recent?query=In Stock at Adafruit from:rpilocator&tweet.fields=created_at'
headers = {'Authorization': 'Bearer ' + bear}
headers = {'Authorization': 'Bearer ' + bearer_token}
print("Connecting to %s"%secrets["ssid"])
wifi.radio.connect(secrets["ssid"], secrets["password"])
print("Connected to %s!"%secrets["ssid"])
print("My IP address is", wifi.radio.ipv4_address)
print(f"Connecting to {ssid}")
wifi.radio.connect(ssid, password)
print(f"Connected to {ssid}!")
print(f"My IP address is {wifi.radio.ipv4_address}")
pool = socketpool.SocketPool(wifi.radio)
requests = adafruit_requests.Session(pool, ssl.create_default_context())
# gets and formats time from adafruit.io
aio_username = secrets["aio_username"]
aio_key = secrets["aio_key"]
location = secrets.get("timezone", None)
location = getenv("timezone", None)
TIME_URL = "https://io.adafruit.com/api/v2/%s/integrations/time/strftime?x-aio-key=%s" % (aio_username, aio_key)
TIME_URL += "&fmt=%25Y-%25m-%25dT%25H%3A%25M%3A%25S.%25L%25j%25u%25z%25Z"
@ -132,7 +139,7 @@ while True:
else:
# if it's not new, then the wait continues
no_tweet_text = ("No stock in last hour :( Last stock: %s" % (timestamp))
no_tweet_text = "No stock in last hour :( Last stock: %s" % (timestamp)
text_area.text = "\n".join(wrap_text_to_lines(no_tweet_text, 21))
print("no new in stock notifications :(")
# updates tweet ID
@ -140,6 +147,6 @@ while True:
# if the tweet wasn't today
else:
# if it's not new, then the wait continues
no_tweet_text = ("No stock in last hour :( Last stock: %s" % (timestamp))
no_tweet_text = "No stock in last hour :( Last stock: %s" % (timestamp)
text_area.text = "\n".join(wrap_text_to_lines(no_tweet_text, 21))
print("no new in stock notifications :(")

View file

@ -37,7 +37,7 @@ ow_bus = OneWireBus(board.GP6)
# scan for temp sensor
ds18 = DS18X20(ow_bus, ow_bus.scan()[0])
# function to convert celcius to fahrenheit
# function to convert celsius to fahrenheit
def c_to_f(temp):
temp_f = (temp * 9/5) + 32
return temp_f

View file

@ -2,17 +2,29 @@
#
# SPDX-License-Identifier: MIT
import os
from os import getenv
import ipaddress
import wifi
import socketpool
# Get WiFi details, ensure these are setup in settings.toml
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
if None in [ssid, password]:
raise RuntimeError(
"WiFi settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"at a minimum."
)
print()
print("Connecting to WiFi")
# connect to your SSID
try:
wifi.radio.connect(os.getenv('CIRCUITPY_WIFI_SSID'), os.getenv('CIRCUITPY_WIFI_PASSWORD'))
wifi.radio.connect(ssid, password)
except TypeError:
print("Could not find WiFi info. Check your settings.toml file!")
raise
@ -25,7 +37,7 @@ pool = socketpool.SocketPool(wifi.radio)
print("My MAC addr:", [hex(i) for i in wifi.radio.mac_address])
# prints IP address to REPL
print("My IP address is", wifi.radio.ipv4_address)
print(f"My IP address is {wifi.radio.ipv4_address}")
# pings Google
ipv4 = ipaddress.ip_address("8.8.4.4")

View file

@ -7,17 +7,23 @@
# or Matrix Portal
# and 64 x 32 RGB LED Matrix
from os import getenv
import time
import board
import terminalio
from adafruit_matrixportal.matrixportal import MatrixPortal
# Get wifi details and more from a secrets.py file
try:
from secrets import secrets
except ImportError:
print("WiFi secrets are kept in secrets.py, please add them there!")
raise
# Get WiFi details, ensure these are setup in settings.toml
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
if None in [ssid, password]:
raise RuntimeError(
"WiFi settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"at a minimum."
)
def aqi_transform(val):
aqi = pm_to_aqi(val) # derive Air Quality Index from Particulate Matter 2.5 value
@ -49,7 +55,7 @@ matrixportal = MatrixPortal(
status_neopixel=board.NEOPIXEL,
debug=True,
url=DATA_SOURCE,
headers={"X-API-Key": secrets["purple_air_api_key"], # purpleair.com
headers={"X-API-Key": getenv("purple_air_api_key"), # purpleair.com
"Accept": "application/json"
},
json_path=(DATA_LOCATION, DATA_LOCATION),

View file

@ -10,7 +10,8 @@ notifications when it needs watering with your PyPortal.
Author: Brent Rubell for Adafruit Industries, 2019
"""
import os
from os import getenv
import time
import json
import board
@ -28,10 +29,17 @@ import aws_gfx_helper
# Time between polling the STEMMA, in minutes
SENSOR_DELAY = 15
secrets = {
"ssid" : os.getenv("CIRCUITPY_WIFI_SSID"),
"password" : os.getenv("CIRCUITPY_WIFI_PASSWORD"),
}
# Get WiFi details, ensure these are setup in settings.toml
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
if None in [ssid, password]:
raise RuntimeError(
"WiFi settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"at a minimum."
)
# Get device certificate
try:
@ -65,9 +73,10 @@ esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
# Verify nina-fw version >= 1.4.0
assert int(bytes(esp.firmware_version).decode("utf-8")[2]) >= 4, "Please update nina-fw to >=1.4.0."
status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)
wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(
esp, secrets, status_light)
status_pixel = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)
wifi = adafruit_esp32spi_wifimanager.WiFiManager(
esp, ssid, password, status_pixel=status_pixel
)
# Initialize the graphics helper
print("Loading AWS IoT Graphics...")
@ -126,8 +135,8 @@ def message(client, topic, msg):
print("Message from {}: {}".format(topic, msg))
# Set up a new MiniMQTT Client
client = MQTT.MQTT(broker = os.getenv("BROKER"),
client_id = os.getenv("CLIENT_ID"),
client = MQTT.MQTT(broker = getenv("BROKER"),
client_id = getenv("CLIENT_ID"),
socket_pool=pool,
ssl_context=ssl_context)

View file

@ -7,19 +7,29 @@ This example will access the adafruit.io API, grab a number like active users
and io plus subscribers... and display it on a screen
If you can find something that spits out JSON data, we can display it!
"""
from os import getenv
import time
import board
from adafruit_pyportal import PyPortal
# Get wifi details and more from a secrets.py file
try:
from secrets import secrets
except ImportError:
print("WiFi secrets are kept in secrets.py, please add them there!")
raise
# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml
# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.)
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
aio_username = getenv("ADAFRUIT_AIO_USERNAME")
aio_key = getenv("ADAFRUIT_AIO_KEY")
if None in [ssid, password, aio_username, aio_key]:
raise RuntimeError(
"WiFi and Adafruit IO settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"'ADAFRUIT_AIO_USERNAME' and 'ADAFRUIT_AIO_KEY' at a minimum."
)
# Set up where we'll be fetching data from
DATA_SOURCE = "https://io.adafruit.com/api/v2/stats?x-aio-key="+secrets['aio_key']
DATA_SOURCE = f"https://io.adafruit.com/api/v2/stats?x-aio-key={aio_key}"
DATA_LOCATION1 = ["io_plus", "io_plus_subscriptions"]
DATA_LOCATION2 = ["users", "users_active_30_days"]

View file

@ -12,6 +12,8 @@ Dependencies:
* CircuitPython_AdafruitIO
https://github.com/adafruit/Adafruit_CircuitPython_AdafruitIO
"""
from os import getenv
import time
import board
import busio
@ -33,12 +35,20 @@ import adafruit_adt7410
# Timeout between sending data to Adafruit IO, in seconds
IO_DELAY = 30
# Get wifi details and more from a secrets.py file
try:
from secrets import secrets
except ImportError:
print("WiFi secrets are kept in secrets.py, please add them there!")
raise
# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml
# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.)
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
aio_username = getenv("ADAFRUIT_AIO_USERNAME")
aio_key = getenv("ADAFRUIT_AIO_KEY")
if None in [ssid, password, aio_username, aio_key]:
raise RuntimeError(
"WiFi and Adafruit IO settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"'ADAFRUIT_AIO_USERNAME' and 'ADAFRUIT_AIO_KEY' at a minimum."
)
# PyPortal ESP32 Setup
esp32_cs = DigitalInOut(board.ESP_CS)
@ -46,17 +56,11 @@ esp32_ready = DigitalInOut(board.ESP_BUSY)
esp32_reset = DigitalInOut(board.ESP_RESET)
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)
wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light)
# Set your Adafruit IO Username and Key in secrets.py
# (visit io.adafruit.com if you need to create an account,
# or if you need your Adafruit IO key.)
ADAFRUIT_IO_USER = secrets['aio_username']
ADAFRUIT_IO_KEY = secrets['aio_key']
status_pixel = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)
wifi = adafruit_esp32spi_wifimanager.WiFiManager(esp, ssid, password, status_pixel=status_pixel)
# Create an instance of the Adafruit IO HTTP client
io = IO_HTTP(ADAFRUIT_IO_USER, ADAFRUIT_IO_KEY, wifi)
io = IO_HTTP(aio_username, aio_key, wifi)
try:
# Get the 'temperature' feed from Adafruit IO

View file

@ -20,9 +20,9 @@ All text above must be included in any redistribution.
#pylint:disable=no-self-use,too-many-branches,too-many-statements
#pylint:disable=useless-super-delegation, too-many-locals
from os import getenv
import time
import json
from secrets import secrets
import board
from adafruit_pyportal import PyPortal
from adafruit_bitmap_font import bitmap_font
@ -32,9 +32,21 @@ import analogio
import displayio
import adafruit_logging as logging
# Get WiFi details, ensure these are setup in settings.toml
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
if None in [ssid, password]:
raise RuntimeError(
"WiFi settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"at a minimum."
)
# Set up where we'll be fetching data from
DATA_SOURCE = 'http://api.openweathermap.org/data/2.5/weather?id='+secrets['city_id']
DATA_SOURCE += '&appid='+secrets['openweather_token']
DATA_SOURCE = 'http://api.openweathermap.org/data/2.5/weather?id='+getenv('city_id')
DATA_SOURCE += '&appid='+getenv('openweather_token')
# You'll need to get a token from openweather.org, looks like 'b6907d289e10d714a6e88b30761fae22'
DATA_LOCATION = []
@ -73,7 +85,7 @@ mugsy_background = 'mugsy_background.bmp'
icon_file = None
icon_sprite = None
celcius = secrets['celcius']
celsius = getenv('celsius')
# display/data refresh timers
@ -243,7 +255,7 @@ class Time_State(State):
if (not self.refresh_time) or ((now - self.refresh_time) > 3600):
logger.debug('Fetching time')
try:
pyportal.get_local_time(location=secrets['timezone'])
pyportal.get_local_time(location=getenv('timezone'))
self.refresh_time = now
except RuntimeError as e:
self.refresh_time = now - 3000 # delay 10 minutes before retrying
@ -282,7 +294,7 @@ class Time_State(State):
self.weather_icon.append(icon_sprite)
temperature = weather['main']['temp'] - 273.15 # its...in kelvin
if celcius:
if celsius:
temperature_text = '%3d C' % round(temperature)
else:
temperature_text = '%3d F' % round(((temperature * 9 / 5) + 32))

View file

@ -12,6 +12,8 @@ your PyPortal
Authors: Brent Rubell for Adafruit Industries, 2019
: Jim Bennett for Microsoft, 2020
"""
from os import getenv
import time
import json
import board
@ -30,12 +32,17 @@ import azure_gfx_helper
# init. graphics helper
gfx = azure_gfx_helper.Azure_GFX(is_celsius=True)
# Get wifi details and more from a secrets.py file
try:
from secrets import secrets
except ImportError:
print("WiFi secrets are kept in secrets.py, please add them there!")
raise
# Get WiFi details, ensure these are setup in settings.toml
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
if None in [ssid, password]:
raise RuntimeError(
"WiFi settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"at a minimum."
)
# PyPortal ESP32 Setup
esp32_cs = DigitalInOut(board.ESP_CS)
@ -45,8 +52,8 @@ spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
# Set up the WiFi manager with a status light to show the WiFi connection status
status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)
wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light)
status_pixel = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)
wifi = adafruit_esp32spi_wifimanager.WiFiManager(esp, ssid, password, status_pixel=status_pixel)
print("WiFi connecting...")
wifi.connect()
print("WiFi connected!")
@ -68,7 +75,7 @@ ss = Seesaw(i2c_bus, addr=0x36)
# Create an instance of the Azure IoT Central device
device = IoTCentralDevice(
socket, esp, secrets["id_scope"], secrets["device_id"], secrets["key"]
socket, esp, getenv("id_scope"), getenv("device_id"), getenv("key")
)
# Connect to Azure IoT Central

View file

@ -2,6 +2,7 @@
#
# SPDX-License-Identifier: MIT
from os import getenv
import time
import random
import board
@ -11,6 +12,21 @@ from adafruit_display_shapes.circle import Circle
WIDTH = board.DISPLAY.width
HEIGHT = board.DISPLAY.height
# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml
# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.)
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
aio_username = getenv("ADAFRUIT_AIO_USERNAME")
aio_key = getenv("ADAFRUIT_AIO_KEY")
if None in [ssid, password, aio_username, aio_key]:
raise RuntimeError(
"WiFi and Adafruit IO settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"'ADAFRUIT_AIO_USERNAME' and 'ADAFRUIT_AIO_KEY' at a minimum."
)
#pylint: disable=line-too-long
# these lines show the entire collection

View file

@ -1,13 +0,0 @@
# SPDX-FileCopyrightText: 2019 Kattni Rembor for Adafruit Industries
#
# SPDX-License-Identifier: MIT
# This file is where you keep secret settings, passwords, and tokens!
# If you put them in the code you risk committing that info or sharing it
secrets = {
'ssid' : 'CHANGE ME',
'password' : 'CHANGE ME',
'aio_username' : 'CHANGE ME',
'aio_key' : 'CHANGE ME',
}

View file

@ -0,0 +1,12 @@
# SPDX-FileCopyrightText: 2019 Kattni Rembor for Adafruit Industries
#
# SPDX-License-Identifier: MIT
# This file is where you keep private settings, passwords, and tokens!
# If you put them in the code you risk committing that info or sharing it
CIRCUITPY_WIFI_SSID="your-wifi-ssid"
CIRCUITPY_WIFI_PASSWORD="your-wifi-password"
ADAFRUIT_AIO_USERNAME="my_username"
ADAFRUIT_AIO_KEY="my_key"
CIRCUITPY_PYSTACK_SIZE=2048

View file

@ -1,7 +0,0 @@
PyPortal_Electioncal_US/electioncal_graphics.py 73: Line too long (120/100) (line-too-long)
PyPortal_Electioncal_US/electioncal_graphics.py 75: Line too long (108/100) (line-too-long)
PyPortal_Electioncal_US/electioncal_graphics.py 86: Line too long (141/100) (line-too-long)
PyPortal_Electioncal_US/electioncal_graphics.py 107: Unused variable 'time_str' (unused-variable)
PyPortal_Electioncal_US/electioncal_graphics.py 111: Method could be a function (no-self-use)
PyPortal_Electioncal_US/electioncal_graphics.py 72: Attribute 'electioncal' defined outside __init__ (attribute-defined-outside-init)
PyPortal_Electioncal_US/electioncal.py 11: Unused secrets imported from secrets (unused-import)

View file

@ -2,6 +2,7 @@
#
# SPDX-License-Identifier: MIT
from os import getenv
import sys
import time
import board
@ -10,12 +11,17 @@ cwd = ("/"+__file__).rsplit('/', 1)[0] # the current working directory (where th
sys.path.append(cwd)
import electioncal_graphics # pylint: disable=wrong-import-position
# Get wifi details and more from a secrets.py file
try:
from secrets import secrets # pylint: disable=unused-import
except ImportError:
print("WiFi secrets are kept in secrets.py, please add them there!")
raise
# Get WiFi details, ensure these are setup in settings.toml
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
if None in [ssid, password]:
raise RuntimeError(
"WiFi settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"at a minimum."
)
# Change this to your state and county, replacing spaces for underscores and in lowercase
STATE="new_york"

View file

@ -112,7 +112,8 @@ class Electioncal_Graphics(displayio.Group):
date_str = date_format_str % (year, month, day)
self.date_text.text = "Today is: " + date_str
def paragrapher(self, text, cut): # pylint: disable=no-self-use
@staticmethod
def paragrapher(text, cut):
""" Cuts a long line into two, having spaces in mind.
Note we return line2 first as it looks better to clear the line2
before printing a line1 with empty line2

View file

@ -7,25 +7,30 @@ PyPortal Adafruit IO Feed Display
Displays an Adafruit IO Feed on a PyPortal.
"""
from os import getenv
import time
import board
from adafruit_pyportal import PyPortal
# Get Adafruit IO details and more from a secrets.py file
try:
from secrets import secrets
except ImportError:
print("Adafruit IO secrets are kept in secrets.py, please add them there!")
raise
# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.)
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
aio_username = getenv("ADAFRUIT_AIO_USERNAME")
aio_key = getenv("ADAFRUIT_AIO_KEY")
if None in [ssid, password, aio_username, aio_key]:
raise RuntimeError(
"WiFi and Adafruit IO settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"'ADAFRUIT_AIO_USERNAME' and 'ADAFRUIT_AIO_KEY' at a minimum."
)
# Adafruit IO Account
IO_USER = secrets['aio_username']
IO_KEY = secrets['aio_key']
# Adafruit IO Feed
IO_FEED = 'zapemail'
io_feed = 'zapemail'
DATA_SOURCE = "https://io.adafruit.com/api/v2/{0}/feeds/{1}?X-AIO-Key={2}".format(IO_USER,
IO_FEED, IO_KEY)
DATA_SOURCE = f"https://io.adafruit.com/api/v2/{aio_username}/feeds/{io_feed}?X-AIO-Key={aio_key}"
FEED_VALUE_LOCATION = ['last_value']
cwd = ("/"+__file__).rsplit('/', 1)[0]

View file

@ -10,6 +10,8 @@ Cloud IoT Core with your PyPortal.
Author: Brent Rubell for Adafruit Industries, 2019
"""
from os import getenv
import time
import json
import board
@ -22,26 +24,33 @@ from adafruit_gc_iot_core import MQTT_API, Cloud_Core
import adafruit_minimqtt.adafruit_minimqtt as MQTT
from adafruit_seesaw.seesaw import Seesaw
import digitalio
from rsa_private_key import private_key
# Get WiFi details, ensure these are setup in settings.toml
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
if None in [ssid, password]:
raise RuntimeError(
"WiFi settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"at a minimum."
)
# Delay before reading the sensors, in minutes
SENSOR_DELAY = 10
# Get wifi details and more from a secrets.py file
try:
from secrets import secrets
except ImportError:
print("WiFi secrets are kept in secrets.py, please add them there!")
raise
# PyPortal ESP32 Setup
esp32_cs = digitalio.DigitalInOut(board.ESP_CS)
esp32_ready = digitalio.DigitalInOut(board.ESP_BUSY)
esp32_reset = digitalio.DigitalInOut(board.ESP_RESET)
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)
wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(
esp, secrets, status_light)
status_pixel = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)
wifi = adafruit_esp32spi_wifimanager.WiFiManager(
esp, ssid, password, status_pixel=status_pixel
)
# Connect to WiFi
print("Connecting to WiFi...")
@ -137,7 +146,14 @@ def handle_pump(command):
# Initialize Google Cloud IoT Core interface
google_iot = Cloud_Core(esp, secrets)
settings = {
"cloud_region": getenv("cloud_region"),
"device_id": getenv("device_id"),
"private_key": private_key,
"project_id": getenv("project_id"),
"registry_id": getenv("registry_id"),
}
google_iot = Cloud_Core(esp, settings)
# JSON-Web-Token (JWT) Generation
print("Generating JWT...")

View file

@ -59,7 +59,7 @@ class Google_GFX(displayio.Group):
temp_group.append(temp_label)
self.temp_data_label = Label(font, text="75 F")
self.temp_data_label.x = (self.display.width//3)
self.temp_data_label.x = self.display.width//3
self.temp_data_label.y = 55
temp_group.append(self.temp_data_label)
self._text_group.append(temp_group)
@ -72,7 +72,7 @@ class Google_GFX(displayio.Group):
water_group.append(self.water_level)
self.water_lvl_label = Label(font, text="350")
self.water_lvl_label.x = (self.display.width//3)
self.water_lvl_label.x = self.display.width//3
self.water_lvl_label.y = 75
temp_group.append(self.water_lvl_label)
self._text_group.append(water_group)

View file

@ -0,0 +1,6 @@
# SPDX-FileCopyrightText: 2025 Tim Cocks for Adafruit Industries
#
# SPDX-License-Identifier: MIT
# Replace the value with your generated RSA Private Key
private_key = (0, 0, 0, 0, 0)

View file

@ -7,23 +7,31 @@ This example will access the github API, grab a number like
the number of stars for a repository... and display it on a screen!
if you can find something that spits out JSON data, we can display it
"""
from os import getenv
import time
import board
from adafruit_pyportal import PyPortal
# Get wifi details and more from a secrets.py file
try:
from secrets import secrets
except ImportError:
print("WiFi secrets are kept in secrets.py, please add them there!")
raise
# Get WiFi details, ensure these are setup in settings.toml
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
if None in [ssid, password]:
raise RuntimeError(
"WiFi settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"at a minimum."
)
# Set up where we'll be fetching data from
DATA_SOURCE = "https://api.github.com/repos/adafruit/circuitpython"
CAPTION = "www.github.com/adafruit/circuitpython"
# If we have an access token, we can query more often
if 'github_token' in secrets:
DATA_SOURCE += "?access_token="+secrets['github_token']
github_token = getenv("github_token")
if github_token:
DATA_SOURCE += f"?access_token={github_token}"
# The data we want to display
DATA_LOCATION = ["stargazers_count"]

View file

@ -1,12 +0,0 @@
# SPDX-FileCopyrightText: 2020 Liz Clark for Adafruit Industries
#
# SPDX-License-Identifier: MIT
secrets = {
'ssid' : 'your-ssid-here',
'password' : 'your-password-here',
'openweather_token' : 'your-openweather-token-here',
'aio_username' : "your-aio-username-here",
'aio_key' : 'your-aio-key-here',
'location' : 'New York, US'
}

View file

@ -8,20 +8,27 @@ and display it on a screen
If you can find something that spits out JSON data, we can display it!
Note that you need a hackaday API key to access the API!
"""
from os import getenv
import time
import board
from adafruit_pyportal import PyPortal
# Get wifi details and more from a secrets.py file
try:
from secrets import secrets
except ImportError:
print("WiFi secrets are kept in secrets.py, please add them there!")
raise
# Get WiFi details, ensure these are setup in settings.toml
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
if None in [ssid, password]:
raise RuntimeError(
"WiFi settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"at a minimum."
)
# Some data sources and JSON locations to try out
CAPTION="hackaday.io/project/163309-circuitpython-hackaday"
DATA_SOURCE = "https://api.hackaday.io/v1/projects/163309?api_key="+secrets['hackaday_token']
DATA_SOURCE = "https://api.hackaday.io/v1/projects/163309?api_key="+getenv('hackaday_token')
DATA_LOCATION = ["skulls"]
# the current working directory (where this file is)

View file

@ -2,23 +2,29 @@
#
# SPDX-License-Identifier: MIT
from os import getenv
import time
import board
from adafruit_pyportal import PyPortal
# Get wifi details and more from a secrets.py file
try:
from secrets import secrets
except ImportError:
print("WiFi secrets are kept in secrets.py, please add them there!")
raise
# Get WiFi details, ensure these are setup in settings.toml
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
if None in [ssid, password]:
raise RuntimeError(
"WiFi settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"at a minimum."
)
PROJECT_NAME = "3c92f0"
# Set up where we'll be fetching data from
DATA_SOURCE = "https://api.hackster.io/v2/projects/"+PROJECT_NAME+"?"
DATA_SOURCE += "client_id="+secrets['hackster_clientid']
DATA_SOURCE += "&client_secret="+secrets['hackster_secret']
DATA_SOURCE += "client_id="+getenv('hackster_clientid')
DATA_SOURCE += "&client_secret="+getenv('hackster_secret')
VIEWS_LOCATION = ['stats', 'views']
LIKES_LOCATION = ['stats', 'respects']
CAPTION = "http://www.hackster.com/project/"+PROJECT_NAME

View file

@ -2,22 +2,28 @@
#
# SPDX-License-Identifier: MIT
from os import getenv
import time
import random
import board
import adafruit_pyportal
# Get wifi details and more from a settings.py file
try:
from secrets import secrets
except ImportError:
print("WiFi secrets are kept in secrets.py, please add them there!")
raise
# Get WiFi details, ensure these are setup in settings.toml
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
if None in [ssid, password]:
raise RuntimeError(
"WiFi settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"at a minimum."
)
# Set up where we'll be fetching data from
DATA_SOURCE = "https://api.hackster.io/v2/projects?"
DATA_SOURCE += "client_id="+secrets['hackster_clientid']
DATA_SOURCE += "&client_secret="+secrets['hackster_secret']
DATA_SOURCE += "client_id="+getenv('hackster_clientid')
DATA_SOURCE += "&client_secret="+getenv('hackster_secret')
IMAGE_LOCATION = ['records', 0, "cover_image_url"]
TITLE_LOCATION = ['records',0, "name"]
HID_LOCATION = ['records', 0, "hid"]

View file

@ -16,8 +16,7 @@ Licensed under the MIT license.
All text above must be included in any redistribution.
"""
#pylint:disable=invalid-name
import os
from os import getenv
import time
import random
import board
@ -25,6 +24,19 @@ from adafruit_pyportal import PyPortal
from adafruit_bitmap_font import bitmap_font
from adafruit_display_text.label import Label
# Get WiFi details, ensure these are setup in settings.toml
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
if None in [ssid, password]:
raise RuntimeError(
"WiFi settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"at a minimum."
)
# The time of the thing!
EVENT_YEAR = 2019
EVENT_MONTH = 10
@ -83,7 +95,7 @@ while True:
if (not refresh_time) or (time.monotonic() - refresh_time) > 3600:
try:
print("Getting time from internet!")
pyportal.get_local_time(location=os.getenv("TIMEZONE"))
pyportal.get_local_time(location=getenv("timezone"))
refresh_time = time.monotonic()
except RuntimeError as e:
print("Some error occured, retrying! -", e)

View file

@ -1,14 +0,0 @@
# SPDX-FileCopyrightText: 2019 Kattni Rembor for Adafruit Industries
#
# SPDX-License-Identifier: MIT
# This file is where you keep secret settings, passwords, and tokens!
# If you put them in the code you risk committing that info or sharing it
secrets = {
'ssid' : 'CHANGE ME',
'password' : 'CHANGE ME',
'timezone' : 'CHANGE ME',
'aio_username' : 'CHANGE ME',
'aio_key' : 'CHANGE ME',
}

View file

@ -0,0 +1,12 @@
# SPDX-FileCopyrightText: 2019 Kattni Rembor for Adafruit Industries
#
# SPDX-License-Identifier: MIT
# This file is where you keep private settings, passwords, and tokens!
# If you put them in the code you risk committing that info or sharing it
CIRCUITPY_WIFI_SSID="your-wifi-ssid"
CIRCUITPY_WIFI_PASSWORD="your-wifi-password"
ADAFRUIT_AIO_USERNAME="my_username"
ADAFRUIT_AIO_KEY="my_key"
timezone="America/New_York" # http://worldtimeapi.org/timezones

View file

@ -8,6 +8,8 @@ an internet of things smart-scale for Adafruit IO
Brent Rubell for Adafruit Industries, 2019
"""
from os import getenv
import time
import board
import adafruit_dymoscale
@ -22,12 +24,20 @@ from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager
import neopixel
from adafruit_io.adafruit_io import IO_HTTP
# Get wifi details and more from a secrets.py file
try:
from secrets import secrets
except ImportError:
print("WiFi secrets are kept in secrets.py, please add them there!")
raise
# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml
# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.)
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
aio_username = getenv("ADAFRUIT_AIO_USERNAME")
aio_key = getenv("ADAFRUIT_AIO_KEY")
if None in [ssid, password, aio_username, aio_key]:
raise RuntimeError(
"WiFi and Adafruit IO settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"'ADAFRUIT_AIO_USERNAME' and 'ADAFRUIT_AIO_KEY' at a minimum."
)
# the current working directory (where this file is)
cwd = ("/"+__file__).rsplit('/', 1)[0]
@ -69,14 +79,8 @@ esp32_ready = digitalio.DigitalInOut(board.ESP_BUSY)
esp32_reset = digitalio.DigitalInOut(board.ESP_RESET)
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)
wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light)
# Set your Adafruit IO Username and Key in secrets.py
# (visit io.adafruit.com if you need to create an account,
# or if you need your Adafruit IO key.)
aio_username = secrets['aio_username']
aio_key = secrets['aio_key']
status_pixel = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)
wifi = adafruit_esp32spi_wifimanager.WiFiManager(esp, ssid, password, status_pixel=status_pixel)
# Create an instance of the IO_HTTP client
io = IO_HTTP(aio_username, aio_key, wifi)

View file

@ -9,7 +9,8 @@ https://learn.adafruit.com/pyportal-smart-lighting-controller
Brent Rubell for Adafruit Industries, 2019
"""
import os
from os import getenv
import board
import displayio
@ -26,10 +27,17 @@ from adafruit_esp32spi import adafruit_esp32spi_wifimanager
# import lifx library
import adafruit_lifx
secrets = {
"ssid" : os.getenv("CIRCUITPY_WIFI_SSID"),
"password" : os.getenv("CIRCUITPY_WIFI_PASSWORD"),
}
# Get WiFi details, ensure these are setup in settings.toml
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
if None in [ssid, password]:
raise RuntimeError(
"WiFi settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"at a minimum."
)
# ESP32 SPI
esp32_cs = DigitalInOut(board.ESP_CS)
@ -37,8 +45,8 @@ esp32_ready = DigitalInOut(board.ESP_BUSY)
esp32_reset = DigitalInOut(board.ESP_RESET)
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)
wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light)
status_pixel = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)
wifi = adafruit_esp32spi_wifimanager.WiFiManager(esp, ssid, password, status_pixel=status_pixel)
# These pins are used as both analog and digital! XL, XR and YU must be analog
# and digital capable. YD just need to be digital
@ -49,7 +57,7 @@ ts = adafruit_touchscreen.Touchscreen(board.TOUCH_XL, board.TOUCH_XR,
# Set this to your LIFX personal access token in settings.toml
# (to obtain a token, visit: https://cloud.lifx.com/settings)
lifx_token = os.getenv("LIFX_TOKEN")
lifx_token = getenv("LIFX_TOKEN")
# Initialize the LIFX API Helper
lifx = adafruit_lifx.LIFX(wifi, lifx_token)

View file

@ -7,26 +7,35 @@ This example will access the lastFM API, grab a number like subreddit followers
and display it on a screen
If you can find something that spits out JSON data, we can display it!
"""
from os import getenv
import time
import board
from adafruit_pyportal import PyPortal
# Get wifi details and more from a secrets.py file
try:
from secrets import secrets
except ImportError:
print("WiFi secrets are kept in secrets.py, please add them there!")
raise
# Get WiFi details, ensure these are setup in settings.toml
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
if None in [ssid, password]:
raise RuntimeError(
"WiFi settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"at a minimum."
)
# Set up where we'll be fetching data from
DATA_SOURCE = "http://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&limit=1&format=json"
CAPTION = "www.last.fm/user"
# If we have an access token, we can query more often
if 'lfm_username' in secrets:
DATA_SOURCE += "&user="+secrets['lfm_username']
CAPTION += "/"+secrets['lfm_username']
if 'lfm_key' in secrets:
DATA_SOURCE += "&api_key="+secrets['lfm_key']
lfm_username = getenv("lfm_username")
lfm_key = getenv("lfm_key")
if lfm_username:
DATA_SOURCE += "&user=" + lfm_username
CAPTION += "/" + lfm_username
if lfm_key:
DATA_SOURCE += "&api_key=" + lfm_key
print(DATA_SOURCE)
# Total number of plays

View file

@ -5,20 +5,33 @@
"""
This project will access the League of Legends API, grab a Summoner's Level
and display it on a screen.
You'll need a Riot API key in your secrets.py file
You'll need a Riot API key in your settings.toml file
If you can find something that spits out JSON data, we can display it!
"""
import os
from os import getenv
import time
import board
from adafruit_pyportal import PyPortal
# Get WiFi details, ensure these are setup in settings.toml
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
if None in [ssid, password]:
raise RuntimeError(
"WiFi settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"at a minimum."
)
#Choose a valid Summoner name
SUMMONER_NAME = "RiotSchmick"
# Set up where we'll be fetching data from
DATA_SOURCE = "https://na1.api.riotgames.com/lol/summoner/v4/summoners/by-name/"+SUMMONER_NAME
DATA_SOURCE += "?api_key=" + os.getenv("LEAGUE_TOKEN")
DATA_SOURCE += "?api_key=" + getenv("LEAGUE_TOKEN")
DATA_LOCATION = ["summonerLevel"]
CAPTION = "SUMMONER "+SUMMONER_NAME

View file

@ -2,6 +2,7 @@
#
# SPDX-License-Identifier: MIT
from os import getenv
import board
import displayio
import busio
@ -18,14 +19,19 @@ from adafruit_button import Button
import adafruit_touchscreen
import adafruit_minimqtt.adafruit_minimqtt as MQTT
# ------------- WiFi ------------- #
# Get WiFi details, ensure these are setup in settings.toml
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
# Get wifi details and more from a secrets.py file
try:
from secrets import secrets
except ImportError:
print("WiFi secrets are kept in secrets.py, please add them there!")
raise
if None in [ssid, password]:
raise RuntimeError(
"WiFi settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"at a minimum."
)
# ------------- WiFi ------------- #
# If you are using a board with pre-defined ESP32 Pins:
esp32_cs = DigitalInOut(board.ESP_CS)
@ -34,8 +40,8 @@ esp32_reset = DigitalInOut(board.ESP_RESET)
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)
wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light)
status_pixel = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)
wifi = adafruit_esp32spi_wifimanager.WiFiManager(esp, ssid, password, status_pixel=status_pixel)
# ------- Sensor Setup ------- #
# init. the temperature sensor
@ -234,10 +240,10 @@ ssl_context = adafruit_connection_manager.get_radio_ssl_context(esp)
# Set up a MiniMQTT Client
client = MQTT.MQTT(
broker=secrets["broker"],
broker=getenv("mqtt_broker"),
port=1883,
username=secrets["user"],
password=secrets["pass"],
username=getenv("mqtt_username"),
password=getenv("mqtt_password"),
socket_pool=pool,
ssl_context=ssl_context,
)

View file

@ -1,11 +0,0 @@
# SPDX-FileCopyrightText: 2020 Anne Barela for Adafruit Industries
#
# SPDX-License-Identifier: MIT
secrets = {
'ssid' : '_your_wifi_ssid_',
'password' : '_your_wifi_password_',
'broker' : '_your_mqtt_broker_url_or_ip',
'user' : '_your_mqtt_broker_username_',
'pass' : '_your_mqtt_broker_password_'
}

View file

@ -0,0 +1,12 @@
# SPDX-FileCopyrightText: 2020 Anne Barela for Adafruit Industries
#
# SPDX-License-Identifier: MIT
# This file is where you keep private settings, passwords, and tokens!
# If you put them in the code you risk committing that info or sharing it
CIRCUITPY_WIFI_SSID="your-wifi-ssid"
CIRCUITPY_WIFI_PASSWORD="your-wifi-password"
mqtt_broker="your-mqtt-broker-url-or-ip"
mqtt_username="your-mqtt-broker-username"
mqtt_password="your-mqtt-broker-password"

View file

@ -2,6 +2,7 @@
#
# SPDX-License-Identifier: MIT
from os import getenv
import sys
import time
import board
@ -11,12 +12,17 @@ cwd = ("/"+__file__).rsplit('/', 1)[0] # the current working directory (where th
sys.path.append(cwd)
import openweather_graphics # pylint: disable=wrong-import-position
# Get wifi details and more from a secrets.py file
try:
from secrets import secrets
except ImportError:
print("WiFi secrets are kept in secrets.py, please add them there!")
raise
# Get WiFi details, ensure these are setup in settings.toml
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
if None in [ssid, password]:
raise RuntimeError(
"WiFi settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"at a minimum."
)
# Use cityname, country code where countrycode is ISO3166 format.
# E.g. "New York, US" or "London, GB"
@ -24,7 +30,7 @@ LOCATION = "New York, US"
# Set up where we'll be fetching data from
DATA_SOURCE = "http://api.openweathermap.org/data/2.5/weather?q="+LOCATION
DATA_SOURCE += "&appid="+secrets['openweather_token']
DATA_SOURCE += "&appid=" + getenv('openweather_token')
# You'll need to get a token from openweather.org, looks like 'b6907d289e10d714a6e88b30761fae22'
DATA_LOCATION = []

View file

@ -7,6 +7,8 @@ This example queries the Open Weather Maps site API to find out the current
weather for your location... and display it on a screen!
if you can find something that spits out JSON data, we can display it
"""
from os import getenv
import sys
import time
import board
@ -15,12 +17,17 @@ cwd = ("/"+__file__).rsplit('/', 1)[0] # the current working directory (where th
sys.path.append(cwd)
import openweather_graphics # pylint: disable=wrong-import-position
# Get wifi details and more from a secrets.py file
try:
from secrets import secrets
except ImportError:
print("WiFi secrets are kept in secrets.py, please add them there!")
raise
# Get WiFi details, ensure these are setup in settings.toml
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
if None in [ssid, password]:
raise RuntimeError(
"WiFi settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"at a minimum."
)
# Use cityname, country code where countrycode is ISO3166 format.
# E.g. "New York, US" or "London, GB"
@ -28,7 +35,7 @@ LOCATION = "Manhattan, US"
# Set up where we'll be fetching data from
DATA_SOURCE = "http://api.openweathermap.org/data/2.5/weather?q="+LOCATION
DATA_SOURCE += "&appid="+secrets['openweather_token']
DATA_SOURCE += "&appid=" + getenv('openweather_token')
# You'll need to get a token from openweather.org, looks like 'b6907d289e10d714a6e88b30761fae22'
DATA_LOCATION = []

View file

@ -7,7 +7,8 @@ PyPortal Philips Hue Lighting Controller
Brent Rubell for Adafruit Industries, 2019
"""
import os
from os import getenv
import board
import displayio
@ -23,9 +24,17 @@ from adafruit_esp32spi import adafruit_esp32spi_wifimanager
# Import Philips Hue Bridge
from adafruit_hue import Bridge
secrets = dict()
secrets["ssid"] = os.getenv("CIRCUITPY_WIFI_SSID")
secrets["password"] = os.getenv("CIRCUITPY_WIFI_PASSWORD")
# Get WiFi details, ensure these are setup in settings.toml
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
if None in [ssid, password]:
raise RuntimeError(
"WiFi settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"at a minimum."
)
# ESP32 SPI
esp32_cs = DigitalInOut(board.ESP_CS)
@ -33,13 +42,13 @@ esp32_ready = DigitalInOut(board.ESP_BUSY)
esp32_reset = DigitalInOut(board.ESP_RESET)
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)
wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light)
status_pixel = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)
wifi = adafruit_esp32spi_wifimanager.WiFiManager(esp, ssid, password, status_pixel=status_pixel)
# Attempt to load bridge username and IP address from secrets.py
# Attempt to load bridge username and IP address from settings.toml
try:
username = os.getenv("HUE_USERNAME")
bridge_ip = os.getenv("BRIDGE_IP")
username = getenv("HUE_USERNAME")
bridge_ip = getenv("BRIDGE_IP")
my_bridge = Bridge(wifi, bridge_ip, username)
except:
# Perform first-time bridge setup

View file

@ -2,6 +2,7 @@
#
# SPDX-License-Identifier: MIT
from os import getenv
import time
import board
import busio
@ -13,12 +14,17 @@ from adafruit_pyportal import PyPortal
from adafruit_bitmap_font import bitmap_font
from adafruit_display_text import label
try:
from secrets import secrets
except ImportError:
print("""WiFi settings are kept in secrets.py, please add them there!
the secrets dictionary must contain 'ssid' and 'password' at a minimum""")
raise
# Get WiFi details, ensure these are setup in settings.toml
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
if None in [ssid, password]:
raise RuntimeError(
"WiFi settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"at a minimum."
)
# Label colors
LABEL_DAY_COLOR = 0xFFFFFF
@ -88,7 +94,7 @@ if esp.status == adafruit_esp32spi.WL_IDLE_STATUS:
print("Connecting to AP...")
while not esp.is_connected:
try:
esp.connect_AP(secrets['ssid'], secrets['password'])
esp.connect_AP(ssid, password)
except RuntimeError as e:
print("could not connect to AP, retrying: ", e)
continue
@ -117,7 +123,7 @@ while True:
if (not refresh_time) or (time.monotonic() - refresh_time) > 3600:
try:
print("Getting new time from internet...")
pyportal.get_local_time(secrets['timezone'])
pyportal.get_local_time(getenv('timezone'))
refresh_time = time.monotonic()
# set the_time
the_time = time.localtime()

View file

@ -1,6 +1,7 @@
# SPDX-FileCopyrightText: 2019 ladyada for Adafruit Industries
# SPDX-License-Identifier: MIT
from os import getenv
import time
import math
import board
@ -13,6 +14,18 @@ import displayio
import adafruit_touchscreen
import adafruit_imageload
# Get WiFi details, ensure these are setup in settings.toml
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
if None in [ssid, password]:
raise RuntimeError(
"WiFi settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"at a minimum."
)
# Set up the touchscreen
ts = adafruit_touchscreen.Touchscreen(
board.TOUCH_XL,
@ -23,13 +36,6 @@ ts = adafruit_touchscreen.Touchscreen(
size=(320, 240),
)
# Get wifi details and more from a secrets.py file
try:
from secrets import secrets
except ImportError:
print("WiFi secrets are kept in secrets.py, please add them there!")
raise
# If you are using a board with pre-defined ESP32 Pins:
esp32_cs = DigitalInOut(board.ESP_CS)
esp32_ready = DigitalInOut(board.ESP_BUSY)
@ -38,9 +44,9 @@ esp32_reset = DigitalInOut(board.ESP_RESET)
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)
status_pixel = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)
wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light)
wifi = adafruit_esp32spi_wifimanager.WiFiManager(esp, ssid, password, status_pixel=status_pixel)
# Set the ip of your Roku here
ip = "192.168.1.3"

View file

@ -2,6 +2,7 @@
#
# SPDX-License-Identifier: MIT
from os import getenv
import time
import gc
import board
@ -20,6 +21,21 @@ import adafruit_touchscreen
from adafruit_minimqtt import MQTT
# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml
# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.)
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
aio_username = getenv("ADAFRUIT_AIO_USERNAME")
aio_key = getenv("ADAFRUIT_AIO_KEY")
if None in [ssid, password, aio_username, aio_key]:
raise RuntimeError(
"WiFi and Adafruit IO settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"'ADAFRUIT_AIO_USERNAME' and 'ADAFRUIT_AIO_KEY' at a minimum."
)
DISPLAY_COLOR = 0x006600
SWITCH_COLOR = 0x008800
SWITCH_FILL_COLOR = 0xffffff
@ -39,15 +55,8 @@ def get_local_timestamp(location=None):
"""Fetch and "set" the local time of this microcontroller to the local time at the location, using an internet time API.
:param str location: Your city and country, e.g. ``"New York, US"``.
"""
# pylint: enable=line-too-long
api_url = None
try:
aio_username = secrets['aio_username']
aio_key = secrets['aio_key']
except KeyError:
raise KeyError("\n\nOur time service requires a login/password to rate-limit. Please register for a free adafruit.io account and place the user/key in your secrets file under 'aio_username' and 'aio_key'")# pylint: disable=line-too-long
location = secrets.get('timezone', location)
location = getenv('timezone', location)
if location:
print("Getting time for timezone", location)
api_url = (TIME_SERVICE + "&tz=%s") % (aio_username, aio_key, location)
@ -70,7 +79,7 @@ def get_local_timestamp(location=None):
tzseconds += tzminutes * 60
print(seconds + tzseconds, tzoffset, tzhours, tzminutes)
except KeyError:
raise KeyError("Was unable to lookup the time, try setting secrets['timezone'] according to http://worldtimeapi.org/timezones") # pylint: disable=line-too-long
raise KeyError("Was unable to lookup the time, try setting timezone in your settings.toml according to http://worldtimeapi.org/timezones") # pylint: disable=line-too-long
# now clean up
response.close()
@ -157,7 +166,7 @@ class Clock(object):
# Update the time
print("update the time")
self.update_time = int(now)
self.snapshot_time = get_local_timestamp(secrets['timezone'])
self.snapshot_time = get_local_timestamp(getenv("timezone"))
self.current_time = time.localtime(self.snapshot_time)
else:
self.current_time = time.localtime(int(now) - self.update_time + self.snapshot_time)
@ -185,8 +194,8 @@ class Clock(object):
def connected(client, userdata, flags, rc):
# This function will be called when the client is connected
# successfully to the broker.
onoff_feed = secrets['aio_username'] + '/feeds/' + FEED_NAME
print('Connected to Adafruit IO! Listening for topic changes on %s' % onoff_feed)
onoff_feed = f"{aio_username}/feeds/{FEED_NAME}"
print(f"Connected to Adafruit IO! Listening for topic changes on {onoff_feed}")
# Subscribe to all changes on the onoff_feed.
client.subscribe(onoff_feed)
@ -205,13 +214,6 @@ def message(client, topic, message):
############################################
try:
from secrets import secrets
except ImportError:
print("""WiFi settings are kept in secrets.py, please add them there!
the secrets dictionary must contain 'ssid' and 'password' at a minimum""")
raise
esp32_cs = digitalio.DigitalInOut(board.ESP_CS)
esp32_ready = digitalio.DigitalInOut(board.ESP_BUSY)
esp32_reset = digitalio.DigitalInOut(board.ESP_RESET)
@ -257,7 +259,7 @@ for ap in esp.scan_networks():
print("Connecting to AP...")
while not esp.is_connected:
try:
esp.connect_AP(secrets['ssid'], secrets['password'])
esp.connect_AP(ssid, password)
except RuntimeError as e:
print("could not connect to AP, retrying: ",e)
continue
@ -265,14 +267,15 @@ print("Connected to", str(esp.ssid, 'utf-8'), "\tRSSI:", esp.rssi)
print("My IP address is", esp.pretty_ip(esp.ip_address))
wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(
esp, secrets, debug = True)
wifi = adafruit_esp32spi_wifimanager.WiFiManager(
esp, ssid, password, debug=True
)
# Set up a MiniMQTT Client
mqtt_client = MQTT(broker='io.adafruit.com',
username=secrets['aio_username'],
password=secrets['aio_key'],
username=aio_username,
password=aio_key,
network_manager=wifi,
socket_pool=pool,
ssl_context=ssl_context)

View file

@ -1,15 +0,0 @@
# SPDX-FileCopyrightText: 2019 Kattni Rembor for Adafruit Industries
#
# SPDX-License-Identifier: MIT
# This file is where you keep secret settings, passwords, and tokens!
# If you put them in the code you risk committing that info or sharing it
secrets = {
'ssid' : 'CHANGE ME',
'password' : 'CHANGE ME',
# leave blank or use timezone from # http://worldtimeapi.org/timezones
'timezone' : '',
'aio_username' : 'CHANGE ME',
'aio_key' : 'CHANGE ME',
}

View file

@ -0,0 +1,12 @@
# SPDX-FileCopyrightText: 2019 Kattni Rembor for Adafruit Industries
#
# SPDX-License-Identifier: MIT
# This file is where you keep private settings, passwords, and tokens!
# If you put them in the code you risk committing that info or sharing it
CIRCUITPY_WIFI_SSID="your-wifi-ssid"
CIRCUITPY_WIFI_PASSWORD="your-wifi-password"
ADAFRUIT_AIO_USERNAME="my_username"
ADAFRUIT_AIO_KEY="my_key"
timezone="" # leave blank or use timezone from # http://worldtimeapi.org/timezones

View file

@ -10,6 +10,8 @@ thermometer with Adafruit IO
Author: Brent Rubell for Adafruit Industries, 2019
"""
from os import getenv
import time
import board
import neopixel
@ -27,12 +29,20 @@ import thermometer_helper
# rate at which to refresh the pyportal screen, in seconds
PYPORTAL_REFRESH = 2
# Get wifi details and more from a secrets.py file
try:
from secrets import secrets
except ImportError:
print("WiFi secrets are kept in secrets.py, please add them there!")
raise
# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml
# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.)
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
aio_username = getenv("ADAFRUIT_AIO_USERNAME")
aio_key = getenv("ADAFRUIT_AIO_KEY")
if None in [ssid, password, aio_username, aio_key]:
raise RuntimeError(
"WiFi and Adafruit IO settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"'ADAFRUIT_AIO_USERNAME' and 'ADAFRUIT_AIO_KEY' at a minimum."
)
# PyPortal ESP32 Setup
esp32_cs = DigitalInOut(board.ESP_CS)
@ -40,21 +50,11 @@ esp32_ready = DigitalInOut(board.ESP_BUSY)
esp32_reset = DigitalInOut(board.ESP_RESET)
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)
wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light)
# Set your Adafruit IO Username and Key in secrets.py
# (visit io.adafruit.com if you need to create an account,
# or if you need your Adafruit IO key.)
try:
ADAFRUIT_IO_USER = secrets['aio_username']
ADAFRUIT_IO_KEY = secrets['aio_key']
except KeyError:
raise KeyError('To use this code, you need to include your Adafruit IO username \
and password in a secrets.py file on the CIRCUITPY drive.')
status_pixel = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)
wifi = adafruit_esp32spi_wifimanager.WiFiManager(esp, ssid, password, status_pixel=status_pixel)
# Create an instance of the IO_HTTP client
io = IO_HTTP(ADAFRUIT_IO_USER, ADAFRUIT_IO_KEY, wifi)
io = IO_HTTP(aio_username, aio_key, wifi)
# Get the temperature feed from Adafruit IO
temperature_feed = io.get_feed('temperature')

View file

@ -36,12 +36,11 @@ ALWAYS_ON = True
# How long to stay on if not in always_on mode
ON_SECONDS = 60
# Get totp keys from a secrets.py file
# Get totp_keys from a totp_keys.py file
try:
from secrets import secrets
from totp_keys import totp_keys
except ImportError:
print("TOTP keys are kept in secrets.py, please add them there!")
raise
print("TOTP info not found in totp_keys.py, please add them there!")
# Initialize PyPortal Display
display = board.DISPLAY
@ -228,13 +227,13 @@ mono_time = int(time.monotonic())
print("Monotonic time", mono_time)
# Add buttons to the interface
assert len(secrets['totp_keys']) < 6, "This code can only display 5 keys at a time"
assert len(totp_keys) < 6, "This code can only display 5 keys at a time"
# generate buttons
buttons = []
btn_x = 5
for i in secrets['totp_keys']:
for i in totp_keys:
button = Button(name=i[0], x=btn_x,
y=175, width=60,
height=60, label=i[0].strip(" "),
@ -264,7 +263,7 @@ splash.append(progress_bar)
countdown = ON_SECONDS
# current button state, defaults to first item in totp_keys
current_button = secrets['totp_keys'][0][0]
current_button = totp_keys[0][0]
buttons[0].selected = True
while ALWAYS_ON or (countdown > 0):
@ -295,7 +294,7 @@ while ALWAYS_ON or (countdown > 0):
for i, b in enumerate(buttons):
if b.contains(p):
b.selected = True
for name, secret in secrets['totp_keys']:
for name, secret in totp_keys:
# check if button name is the same as a key name
if b.name == name:
current_button = name
@ -305,7 +304,7 @@ while ALWAYS_ON or (countdown > 0):
else:
b.selected = False
else:
for name, secret in secrets['totp_keys']:
for name, secret in totp_keys:
if current_button == name:
# Generate OTP
otp = generate_otp(unix_time // 30, secret)

View file

@ -1,14 +0,0 @@
# SPDX-FileCopyrightText: 2019 Brent Rubell for Adafruit Industries
#
# SPDX-License-Identifier: MIT
# This file is where you keep secret settings, passwords, and tokens!
# If you put them in the code you risk committing that info or sharing it
secrets = {
'totp_keys' : [("Discord ", "JBSWY3DPEHPK3PXP"),
("Gmail", "JBSWY3DPEHPK3PZP"),
("GitHub", "JBSWY5DZEHPK3PXP"),
("Adafruit", "JBSWY6DZEHPK3PXP"),
("Outlook", "JBSWY7DZEHPK3PXP")]
}

View file

@ -0,0 +1,14 @@
# SPDX-FileCopyrightText: 2019 Brent Rubell for Adafruit Industries
#
# SPDX-License-Identifier: MIT
# This file contains totp codes!
# If you put them in the code you risk committing that info or sharing it
totp_keys = [
("Discord ", "JBSWY3DPEHPK3PXP"),
("Gmail", "JBSWY3DPEHPK3PZP"),
("GitHub", "JBSWY5DZEHPK3PXP"),
("Adafruit", "JBSWY6DZEHPK3PXP"),
("Outlook", "JBSWY7DZEHPK3PXP"),
]

View file

@ -2,6 +2,7 @@
#
# SPDX-License-Identifier: MIT
from os import getenv
import time
import json
import board
@ -9,12 +10,17 @@ from adafruit_pyportal import PyPortal
from adafruit_bitmap_font import bitmap_font
from adafruit_display_text.label import Label
# Get wifi details and more from a secrets.py file
try:
from secrets import secrets
except ImportError:
print("WiFi secrets are kept in secrets.py, please add them there!")
raise
# Get WiFi details, ensure these are setup in settings.toml
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
if None in [ssid, password]:
raise RuntimeError(
"WiFi settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"at a minimum."
)
#--| USER CONFIG |--------------------------
STATION_ID = "0245" # tide location, find yours from admiralty website/
@ -30,7 +36,7 @@ DATA_LOCATION = []
# determine the current working directory needed so we know where to find files
cwd = ("/"+__file__).rsplit('/', 1)[0]
pyportal = PyPortal(url=DATA_SOURCE,
headers={"Ocp-Apim-Subscription-Key":secrets['Ocp-Apim-Subscription-Key']},
headers={"Ocp-Apim-Subscription-Key": getenv('Ocp-Apim-Subscription-Key')},
json_path=DATA_LOCATION,
status_neopixel=board.NEOPIXEL,
default_bg=cwd+"/images/tides_bg.bmp")

View file

@ -2,6 +2,7 @@
#
# SPDX-License-Identifier: MIT
from os import getenv
import time
import json
import board
@ -10,12 +11,17 @@ from adafruit_pyportal import PyPortal
from adafruit_bitmap_font import bitmap_font
from adafruit_display_text.label import Label
# Get wifi details and more from a secrets.py file
try:
from secrets import secrets
except ImportError:
print("WiFi secrets are kept in secrets.py, please add them there!")
raise
# Get WiFi details, ensure these are setup in settings.toml
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
if None in [ssid, password]:
raise RuntimeError(
"WiFi settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"at a minimum."
)
#--| USER CONFIG |--------------------------
STATION_ID = "0245" # tide location, find yours from admiralty website
@ -44,7 +50,7 @@ else:
bg_image_path = "/images/tides_bg_graph.bmp"
pyportal = PyPortal(url=DATA_SOURCE,
headers={"Ocp-Apim-Subscription-Key":secrets['Ocp-Apim-Subscription-Key']},
headers={"Ocp-Apim-Subscription-Key": getenv('Ocp-Apim-Subscription-Key')},
json_path=DATA_LOCATION,
status_neopixel=board.NEOPIXEL,
default_bg=cwd+bg_image_path)

View file

@ -2,6 +2,7 @@
#
# SPDX-License-Identifier: MIT
from os import getenv
import time
from calendar import alarms
from calendar import timers
@ -12,20 +13,25 @@ from adafruit_button import Button
from adafruit_pyportal import PyPortal
import openweather_graphics # pylint: disable=wrong-import-position
# Get wifi details and more from a secrets.py file
try:
from secrets import secrets
except ImportError:
print("WiFi secrets are kept in secrets.py, please add them there!")
raise
# Get WiFi details, ensure these are setup in settings.toml
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
if None in [ssid, password]:
raise RuntimeError(
"WiFi settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"at a minimum."
)
# Use cityname, country code where countrycode is ISO3166 format.
# E.g. "New York, US" or "London, GB"
LOCATION = secrets['location']
LOCATION = getenv('location')
# Set up where we'll be fetching data from
DATA_SOURCE = "http://api.openweathermap.org/data/2.5/weather?q="+LOCATION
DATA_SOURCE += "&appid="+secrets['openweather_token']
DATA_SOURCE = "http://api.openweathermap.org/data/2.5/weather?q=" + LOCATION
DATA_SOURCE += "&appid=" + getenv('openweather_token')
# You'll need to get a token from openweather.org, looks like 'b6907d289e10d714a6e88b30761fae22'
DATA_LOCATION = []

View file

@ -1,15 +0,0 @@
# SPDX-FileCopyrightText: 2020 Liz Clark for Adafruit Industries
#
# SPDX-License-Identifier: MIT
# This file is where you keep secret settings, passwords, and tokens!
# If you put them in the code you risk committing that info or sharing it
secrets = {
'ssid' : 'your-ssid-here',
'password' : 'your-password-here',
'openweather_token' : 'your-openweather-token-here',
'aio_username' : "your-aio-username-here",
'aio_key' : 'your-aio-key-here',
'location' : 'New York, US'
}

View file

@ -0,0 +1,14 @@
# SPDX-FileCopyrightText: 2020 Liz Clark for Adafruit Industries
#
# SPDX-License-Identifier: MIT
# This file is where you keep private settings, passwords, and tokens!
# If you put them in the code you risk committing that info or sharing it
CIRCUITPY_WIFI_SSID="your-wifi-ssid"
CIRCUITPY_WIFI_PASSWORD="your-wifi-password"
ADAFRUIT_AIO_USERNAME="my_username"
ADAFRUIT_AIO_KEY="my_key"
timezone="America/New_York" # http://worldtimeapi.org/timezones
openweather_token="my_openweather_token"
location="New York, US"

View file

@ -14,11 +14,11 @@ analogin = AnalogIn(board.LIGHT)
cwd = ("/"+__file__).rsplit('/', 1)[0]
laura = (cwd+"/laura.bmp")
laura = cwd+"/laura.bmp"
woodsman = (cwd+"/woodsman.bmp")
woodsman = cwd+"/woodsman.bmp"
gottaLight = (cwd+"/gottaLight.wav")
gottaLight = cwd+"/gottaLight.wav"
pyportal = PyPortal(default_bg=laura)

View file

@ -16,6 +16,7 @@ Licensed under the MIT license.
All text above must be included in any redistribution.
"""
from os import getenv
import time
import json
import board
@ -25,12 +26,17 @@ from adafruit_display_shapes.rect import Rect
from adafruit_display_text.Label import Label
from adafruit_bitmap_font import bitmap_font
try:
from secrets import secrets
except ImportError:
print("""WiFi settings are kept in secrets.py, please add them there!
the secrets dictionary must contain 'ssid' and 'password' at a minimum""")
raise
# Get WiFi details, ensure these are setup in settings.toml
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
if None in [ssid, password]:
raise RuntimeError(
"WiFi settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"at a minimum."
)
MAX_BAR_HEIGHT = 160
MARGIN = 10
@ -48,7 +54,7 @@ CAPTION_FONT_FILE = cwd+'/fonts/Helvetica-Bold-16.bdf'
BAR_FONT_FILE = cwd+'/fonts/Arial-Bold-12.bdf'
#pylint:disable=line-too-long
url = 'https://enviro.epa.gov/enviro/efservice/getEnvirofactsUVHOURLY/ZIP/{0}/JSON'.format(secrets['zip'])
url = f"https://enviro.epa.gov/enviro/efservice/getEnvirofactsUVHOURLY/ZIP/{getenv('zip')}/JSON"
#pylint:enable=line-too-long
def extract_hour(date_time):

View file

@ -1,12 +0,0 @@
# SPDX-FileCopyrightText: 2019 Kattni Rembor for Adafruit Industries
#
# SPDX-License-Identifier: MIT
# This file is where you keep secret settings, passwords, and tokens!
# If you put them in the code you risk committing that info or sharing it
secrets = {
'ssid' : 'CHANGE ME',
'password' : 'CHANGE ME',
'zip' : 'CHANGE ME',
}

View file

@ -2,8 +2,9 @@
#
# SPDX-License-Identifier: MIT
# This file is where you keep secret settings, passwords, and tokens!
# This file is where you keep private settings, passwords, and tokens!
# If you put them in the code you risk committing that info or sharing it
secrets = {
}
CIRCUITPY_WIFI_SSID="your-wifi-ssid"
CIRCUITPY_WIFI_PASSWORD="your-wifi-password"
zip="CHANGE ME"

View file

@ -1 +0,0 @@
PyPortal_User_Interface/code.py 128: Line too long (117/100) (line-too-long)

View file

@ -1,11 +0,0 @@
# SPDX-FileCopyrightText: 2020 Anne Barela for Adafruit Industries
#
# SPDX-License-Identifier: MIT
secrets = {
'ssid' : '_your_wifi_ssid_',
'password' : '_your_wifi_password_',
'broker' : '_your_mqtt_broker_url_or_ip',
'user' : '_your_mqtt_broker_username_',
'pass' : '_your_mqtt_broker_password_'
}

View file

@ -2,7 +2,7 @@
#
# SPDX-License-Identifier: MIT
import os
from os import getenv
import time
import board
@ -21,6 +21,21 @@ from adafruit_pyportal import PyPortal
from adafruit_seesaw.seesaw import Seesaw
from simpleio import map_range
# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml
# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.)
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
aio_username = getenv("ADAFRUIT_AIO_USERNAME")
aio_key = getenv("ADAFRUIT_AIO_KEY")
if None in [ssid, password, aio_username, aio_key]:
raise RuntimeError(
"WiFi and Adafruit IO settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"'ADAFRUIT_AIO_USERNAME' and 'ADAFRUIT_AIO_KEY' at a minimum."
)
#---| User Config |---------------
# How often to poll the soil sensor, in seconds
@ -52,11 +67,6 @@ wav_water_low = "/sounds/water-low.wav"
# the current working directory (where this file is)
cwd = ("/"+__file__).rsplit('/', 1)[0]
secrets = {
"ssid" : os.getenv("CIRCUITPY_WIFI_SSID"),
"password" : os.getenv("CIRCUITPY_WIFI_PASSWORD"),
}
# Set up i2c bus
i2c_bus = busio.I2C(board.SCL, board.SDA)
@ -70,8 +80,8 @@ esp32_reset = DigitalInOut(board.ESP_RESET)
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)
wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light)
status_pixel = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)
wifi = adafruit_esp32spi_wifimanager.WiFiManager(esp, ssid, password, status_pixel=status_pixel)
# Initialize PyPortal Display
display = board.DISPLAY
@ -190,8 +200,8 @@ ssl_context = adafruit_connection_manager.get_radio_ssl_context(esp)
# Initialize a new MQTT Client object
mqtt_client = MQTT.MQTT(broker="io.adafruit.com",
username=os.getenv("AIO_USERNAME"),
password=os.getenv("AIO_KEY"),
username=aio_username,
password=aio_key,
socket_pool=pool,
ssl_context=ssl_context)

View file

@ -10,6 +10,8 @@ Adafruit IO
Author: Brent Rubell for Adafruit Industries, 2019
"""
from os import getenv
import time
import board
import neopixel
@ -38,12 +40,20 @@ anemometer_max_volts = 2.0
min_wind_speed = 0.0
max_wind_speed = 32.4
# Get wifi details and more from a secrets.py file
try:
from secrets import secrets
except ImportError:
print("WiFi secrets are kept in secrets.py, please add them there!")
raise
# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml
# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.)
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
aio_username = getenv("ADAFRUIT_AIO_USERNAME")
aio_key = getenv("ADAFRUIT_AIO_KEY")
if None in [ssid, password, aio_username, aio_key]:
raise RuntimeError(
"WiFi and Adafruit IO settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"'ADAFRUIT_AIO_USERNAME' and 'ADAFRUIT_AIO_KEY' at a minimum."
)
# PyPortal ESP32 Setup
esp32_cs = DigitalInOut(board.ESP_CS)
@ -51,14 +61,8 @@ esp32_ready = DigitalInOut(board.ESP_BUSY)
esp32_reset = DigitalInOut(board.ESP_RESET)
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)
wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light)
# Set your Adafruit IO Username and Key in secrets.py
# (visit io.adafruit.com if you need to create an account,
# or if you need your Adafruit IO key.)
ADAFRUIT_IO_USER = secrets['aio_username']
ADAFRUIT_IO_KEY = secrets['aio_key']
status_pixel = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)
wifi = adafruit_esp32spi_wifimanager.WiFiManager(esp, ssid, password, status_pixel=status_pixel)
# Create an instance of the Adafruit IO HTTP client
io = IO_HTTP(ADAFRUIT_IO_USER, ADAFRUIT_IO_KEY, wifi)

View file

@ -29,6 +29,7 @@ requests = adafruit_requests.Session(pool, ssl.create_default_context())
# neopixels, 49 total
OFF = (0, 0, 0)
ON = (255, 255, 255)
RED = (255,0,0)
pixel_pin = board.A3
num_pixels = 49
pixels = neopixel.NeoPixel(pixel_pin, num_pixels, brightness=0.1, auto_write=False)
@ -43,9 +44,11 @@ FULL_MOON = 4
WANING_GIBBOUS = 5
THIRD_QUARTER = 6
WANING_CRESCENT = 7
DARK_MOON = 8
RED_MOON = 9
# strings that match return from API
phase_names = ["New Moon", "Waxing Crescent", "First Quarter", "Waxing Gibbous",
"Full Moon", "Waning Gibbous", "Third Quarter", "Waning Crescent"]
"Full Moon", "Waning Gibbous", "Third Quarter", "Waning Crescent","Dark Moon","Red Moon"]
# functions for each moon phase to light up based on neopixel orientation
def set_new_moon():
@ -96,6 +99,16 @@ def set_waning_crescent():
pixels[i] = ON
pixels.show()
def set_dark_moon():
pixels.fill(OFF)
for i in range(9,14):
pixels[i] = ON
pixels.show()
def set_red_moon():
pixels.fill(RED)
pixels.show()
# match functions with phases
phase_functions = {
NEW_MOON: set_new_moon,
@ -105,12 +118,14 @@ phase_functions = {
FULL_MOON: set_full_moon,
WANING_GIBBOUS: set_waning_gibbous,
THIRD_QUARTER: set_third_quarter,
WANING_CRESCENT: set_waning_crescent
WANING_CRESCENT: set_waning_crescent,
DARK_MOON: set_dark_moon,
RED_MOON: set_red_moon
}
# test function, runs through all 8 in order
def demo_all_phases(delay=1):
for phase in range(8):
for phase in range(9):
print(f"Setting phase: {phase_names[phase]}")
phase_functions[phase]()
time.sleep(delay)
@ -119,10 +134,15 @@ demo_all_phases()
# takes response from API, matches to function, runs function
def set_moon_phase(phase):
phase_lower = phase.lower()
error_check = 0
for i, name in enumerate(phase_names):
if phase_lower == name.lower():
error_check = 1
phase_functions[i]()
print(f"Moon phase set to: {name}")
if error_check == 0:
print("ERROR")
set_red_moon() #error indicator if API responce is unexpected
# time keeping, fetches API every 6 hours
timer_clock = ticks_ms()

View file

@ -0,0 +1,172 @@
# SPDX-FileCopyrightText: Copyright (c) 2025 Tim Cocks for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import array
import time
import usb.core
import adafruit_usb_host_descriptors
# Set to true to print detailed information about all devices found
VERBOSE_SCAN = True
BTN_DPAD_UPDOWN_INDEX = 1
BTN_DPAD_RIGHTLEFT_INDEX = 0
BTN_ABXY_INDEX = 5
BTN_OTHER_INDEX = 6
DIR_IN = 0x80
controller = None
if VERBOSE_SCAN:
for device in usb.core.find(find_all=True):
controller = device
print("pid", hex(device.idProduct))
print("vid", hex(device.idVendor))
print("man", device.manufacturer)
print("product", device.product)
print("serial", device.serial_number)
print("config[0]:")
config_descriptor = adafruit_usb_host_descriptors.get_configuration_descriptor(
device, 0
)
i = 0
while i < len(config_descriptor):
descriptor_len = config_descriptor[i]
descriptor_type = config_descriptor[i + 1]
if descriptor_type == adafruit_usb_host_descriptors.DESC_CONFIGURATION:
config_value = config_descriptor[i + 5]
print(f" value {config_value:d}")
elif descriptor_type == adafruit_usb_host_descriptors.DESC_INTERFACE:
interface_number = config_descriptor[i + 2]
interface_class = config_descriptor[i + 5]
interface_subclass = config_descriptor[i + 6]
print(f" interface[{interface_number:d}]")
print(
f" class {interface_class:02x} subclass {interface_subclass:02x}"
)
elif descriptor_type == adafruit_usb_host_descriptors.DESC_ENDPOINT:
endpoint_address = config_descriptor[i + 2]
if endpoint_address & DIR_IN:
print(f" IN {endpoint_address:02x}")
else:
print(f" OUT {endpoint_address:02x}")
i += descriptor_len
# get the first device found
device = None
while device is None:
for d in usb.core.find(find_all=True):
device = d
break
time.sleep(0.1)
# set configuration so we can read data from it
device.set_configuration()
print(
f"configuration set for {device.manufacturer}, {device.product}, {device.serial_number}"
)
# Test to see if the kernel is using the device and detach it.
if device.is_kernel_driver_active(0):
device.detach_kernel_driver(0)
# buffer to hold 64 bytes
buf = array.array("B", [0] * 64)
def print_array(arr, max_index=None, fmt="hex"):
"""
Print the values of an array
:param arr: The array to print
:param max_index: The maximum index to print. None means print all.
:param fmt: The format to use, either "hex" or "bin"
:return: None
"""
out_str = ""
if max_index is None or max_index >= len(arr):
length = len(arr)
else:
length = max_index
for _ in range(length):
if fmt == "hex":
out_str += f"{int(arr[_]):02x} "
elif fmt == "bin":
out_str += f"{int(arr[_]):08b} "
print(out_str)
def reports_equal(report_a, report_b, check_length=None):
"""
Test if two reports are equal. If check_length is provided then
check for equality in only the first check_length number of bytes.
:param report_a: First report data
:param report_b: Second report data
:param check_length: How many bytes to check
:return: True if the reports are equal, otherwise False.
"""
if (
report_a is None
and report_b is not None
or report_b is None
and report_a is not None
):
return False
length = len(report_a) if check_length is None else check_length
for _ in range(length):
if report_a[_] != report_b[_]:
return False
return True
idle_state = None
prev_state = None
while True:
try:
count = device.read(0x81, buf)
# print(f"read size: {count}")
except usb.core.USBTimeoutError:
continue
if idle_state is None:
idle_state = buf[:]
print("Idle state:")
print_array(idle_state[:8], max_index=count)
print()
if not reports_equal(buf, prev_state, 8) and not reports_equal(buf, idle_state, 8):
if buf[BTN_DPAD_UPDOWN_INDEX] == 0x0:
print("D-Pad UP pressed")
elif buf[BTN_DPAD_UPDOWN_INDEX] == 0xFF:
print("D-Pad DOWN pressed")
if buf[BTN_DPAD_RIGHTLEFT_INDEX] == 0:
print("D-Pad LEFT pressed")
elif buf[BTN_DPAD_RIGHTLEFT_INDEX] == 0xFF:
print("D-Pad RIGHT pressed")
if buf[BTN_ABXY_INDEX] == 0x2F:
print("A pressed")
elif buf[BTN_ABXY_INDEX] == 0x4F:
print("B pressed")
elif buf[BTN_ABXY_INDEX] == 0x1F:
print("X pressed")
elif buf[BTN_ABXY_INDEX] == 0x8F:
print("Y pressed")
if buf[BTN_OTHER_INDEX] == 0x01:
print("L shoulder pressed")
elif buf[BTN_OTHER_INDEX] == 0x02:
print("R shoulder pressed")
elif buf[BTN_OTHER_INDEX] == 0x10:
print("SELECT pressed")
elif buf[BTN_OTHER_INDEX] == 0x20:
print("START pressed")
# print_array(buf[:8])
prev_state = buf[:]

View file

@ -1,25 +1,36 @@
{
"exportedFromDevice": {
"referenceVoltage": 3.3,
"totalGPIOPins": 18,
"totalAnalogPins": 4,
"sd_cs_pin": 23,
"statusLEDBrightness": 0.5
"sd_cs_pin": 23,
"referenceVoltage": 0,
"totalGPIOPins": 0,
"totalAnalogPins": 0,
"statusLEDBrightness": 0.3
},
"components": [
{
"name": "BME280 Sensor",
"componentAPI": "i2c",
"i2cDeviceName": "bme280",
"period": 15,
"i2cDeviceAddress": "0x77",
"i2cDeviceSensorTypes": [
{"type": "relative-humidity"},
{"type": "ambient-temp"},
{"type": "ambient-temp-fahrenheit"},
{"type": "pressure"},
{"type": "altitude"}
]
}
{
"name": "bme280",
"componentAPI": "i2c",
"i2cDeviceName": "bme280",
"period": 30,
"autoConfig": "true",
"i2cDeviceAddress": "0x77",
"i2cDeviceSensorTypes": [
{
"type": "ambient-temp"
},
{
"type": "ambient-temp-fahrenheit"
},
{
"type": "relative-humidity"
},
{
"type": "pressure"
},
{
"type": "altitude"
}
]
}
]
}
}