Merge branch 'refs/heads/main' into displayio_api_9x

This commit is contained in:
foamyguy 2025-02-14 15:07:28 -06:00
commit f1cf3ce157
870 changed files with 152449 additions and 817 deletions

View file

@ -39,7 +39,8 @@ jobs:
[[ $changedfile == *.cpp ]] ||
[[ $changedfile == *.h ]] ||
[[ $changedfile == *.hpp ]] ||
[[ $changedfile == *.ino ]]; then
[[ $changedfile == *.ino ]] ||
[[ $changedfile == *.yml ]]; then
ischanged=true
break
fi
@ -60,7 +61,7 @@ jobs:
strategy:
fail-fast: false
matrix:
arduino-platform: ["cpb", "cpc", "cpx_ada", "esp32", "esp8266", "feather32u4", "feather_m0_express", "feather_m4_express", "feather_rp2040", "flora", "funhouse", "gemma", "gemma_m0", "hallowing_m0", "hallowing_m4_tinyusb", "magtag", "metro_m0", "metro_m0_tinyusb", "metro_m4", "metro_m4_tinyusb", "monster_m4sk", "monster_m4sk_tinyusb", "neokeytrinkey_m0", "neotrellis_m4", "nrf52832", "nrf52840", "pycamera_s3", "protrinket_5v", "proxlighttrinkey_m0", "pybadge", "pygamer", "pyportal", "qualia_s3_rgb666", "qt2040_trinkey", "qtpy_m0", "qtpy_esp32s2", "rotarytrinkey_m0", "slidetrinkey_m0", "trinket_m0", "uno", "trinket_5v", "ledglasses_nrf52840" ]
arduino-platform: ["cpb", "cpc", "cpx_ada", "esp32", "esp8266", "feather32u4", "feather_esp32c6", "feather_m0_express", "feather_m4_express", "feather_rp2040", "feather_rp2040_adalogger", "flora", "funhouse", "gemma", "gemma_m0", "hallowing_m0", "hallowing_m4_tinyusb", "ledglasses_nrf52840", "magtag", "metro_m0", "metro_m0_tinyusb", "metro_m4", "metro_m4_tinyusb", "monster_m4sk", "monster_m4sk_tinyusb", "neokeytrinkey_m0", "neotrellis_m4", "nrf52832", "nrf52840", "pixeltrinkey_m0", "protrinket_5v", "proxlighttrinkey_m0", "pybadge", "pycamera_s3", "pygamer", "pyportal", "qualia_s3_rgb666", "qt2040_trinkey", "qtpy_m0", "qtpy_esp32s2", "rotarytrinkey_m0", "sht4xtrinkey_m0", "slidetrinkey_m0", "trinket_5v", "trinket_m0", "uno"]
runs-on: ubuntu-latest
if: needs.check-if-needed.outputs.answer == 'true'
needs: check-if-needed
@ -68,6 +69,11 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: "3.x"
# Checkout the learn repo itself
- uses: actions/checkout@v4
# Checkout the CI scripts
- uses: actions/checkout@v4
with:
repository: adafruit/ci-arduino

View file

@ -0,0 +1,31 @@
// SPDX-FileCopyrightText: 2024 Liz Clark for Adafruit Industries
//
// SPDX-License-Identifier: MIT
const int DIR = 5;
const int STEP = 6;
const int microMode = 16; // microstep mode, default is 1/16 so 16; ex: 1/4 would be 4
// full rotation * microstep divider
const int steps = 200 * microMode;
void setup()
{
// setup step and dir pins as outputs
pinMode(STEP, OUTPUT);
pinMode(DIR, OUTPUT);
}
void loop()
{
// change direction every loop
digitalWrite(DIR, !digitalRead(DIR));
// toggle STEP to move
for(int x = 0; x < steps; x++)
{
digitalWrite(STEP, HIGH);
delay(2);
digitalWrite(STEP, LOW);
delay(2);
}
delay(1000); // 1 second delay
}

View file

@ -0,0 +1,32 @@
# SPDX-FileCopyrightText: 2024 Liz Clark for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import time
import board
from digitalio import DigitalInOut, Direction
# direction and step pins as outputs
DIR = DigitalInOut(board.D5)
DIR.direction = Direction.OUTPUT
STEP = DigitalInOut(board.D6)
STEP.direction = Direction.OUTPUT
# microstep mode, default is 1/16 so 16
# another ex: 1/4 microstep would be 4
microMode = 16
# full rotation multiplied by the microstep divider
steps = 200 * microMode
while True:
# change direction every loop
DIR.value = not DIR.value
# toggle STEP pin to move the motor
for i in range(steps):
STEP.value = True
time.sleep(0.001)
STEP.value = False
time.sleep(0.001)
print("rotated! now reverse")
# 1 second delay before starting again
time.sleep(1)

View file

@ -0,0 +1,65 @@
// SPDX-FileCopyrightText: 2024 Liz Clark for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#include <Adafruit_ADG72x.h>
Adafruit_ADG72x adg72x;
bool isADG728 = false; // which chip are we connected to?
int analogIn = A0;
int analogValue = 0;
unsigned long switchTimer = 1000; // 1000 ms = 1 second for channel switch
unsigned long readTimer = 10; // 10 ms for analog read
unsigned long lastSwitchTime = 0; // Last time the channels were switched
unsigned long lastReadTime = 0; // Last time the analog was read
uint8_t currentChannel = 0; // Current channel being selected
void setup() {
Serial.begin(115200);
// Wait for serial port to open
while (!Serial) {
delay(1);
}
// Try with the ADG728 default address first...
if (adg72x.begin(ADG728_DEFAULT_ADDR, &Wire)) {
Serial.println("ADG728 found!");
isADG728 = true;
}
// Maybe they have an ADG729?
else if (adg72x.begin(ADG729_DEFAULT_ADDR, &Wire)) {
Serial.println("ADG729 found!");
isADG728 = false;
}
else {
Serial.println("No ADG device found? Check wiring!");
while (1); // Stop here if no device was found
}
}
void loop() {
unsigned long currentTime = millis();
// read and print analog value every 10ms
if ((currentTime - lastReadTime) >= readTimer) {
analogValue = analogRead(analogIn);
Serial.println(analogValue);
lastReadTime = currentTime;
}
// switch channels every 1 second
if ((currentTime - lastSwitchTime) >= switchTimer) {
uint8_t bits = 1 << currentChannel; // Shift a '1' from LSB to MSB
if (!adg72x.selectChannels(bits)) {
Serial.println("Failed to set channels...");
}
/*Serial.print((currentChannel % 4) + 1);
if (currentChannel < 4) Serial.println("A");
else Serial.println("B");*/
currentChannel = (currentChannel + 1) % 8; // Move to the next channel, wrap around at 8
lastSwitchTime = currentTime;
}
}

View file

@ -0,0 +1,66 @@
// SPDX-FileCopyrightText: 2024 Liz Clark for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#include <Adafruit_ADG72x.h>
Adafruit_ADG72x adg72x;
int analogInA0 = A0;
int analogInA1 = A1;
int analogValueDA = 0;
int analogValueDB = 0;
unsigned long switchTimer = 1000; // 1000 ms = 1 second for channel switch
unsigned long readTimer = 10; // 10 ms for analog read
unsigned long lastSwitchTime = 0; // Last time the channels were switched
unsigned long lastReadTime = 0; // Last time the analog was read
uint8_t currentChannel = 0; // Current channel being selected
void setup() {
Serial.begin(115200);
// Wait for serial port to open
while (!Serial) {
delay(1);
}
// Try with the ADG728 default address first...
if (adg72x.begin(ADG728_DEFAULT_ADDR, &Wire)) {
//Serial.println("ADG728 found!");
}
// Maybe they have an ADG729?
else if (adg72x.begin(ADG729_DEFAULT_ADDR, &Wire)) {
//Serial.println("ADG729 found!");
}
else {
Serial.println("No ADG72x device found? Check wiring!");
while (1); // Stop here if no device was found
}
}
void loop() {
unsigned long currentTime = millis();
// read and print analog value every 10ms
if ((currentTime - lastReadTime) >= readTimer) {
analogValueDA = analogRead(analogInA0);
analogValueDB = analogRead(analogInA1);
Serial.print(analogValueDA);
Serial.print(",");
Serial.println(analogValueDB);
lastReadTime = currentTime;
}
// switch channels every 1 second
if ((currentTime - lastSwitchTime) >= switchTimer) {
uint8_t bits = 1 << currentChannel; // Shift a '1' from LSB to MSB
if (!adg72x.selectChannels(bits)) {
Serial.println("Failed to set channels...");
}
/*Serial.print((currentChannel % 4) + 1);
if (currentChannel < 4) Serial.println("A");
else Serial.println("B");*/
currentChannel = (currentChannel + 1) % 8; // Move to the next channel, wrap around at 8
lastSwitchTime = currentTime;
}
}

View file

@ -0,0 +1,26 @@
# SPDX-FileCopyrightText: Copyright (c) 2024 Liz Clark for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import time
import board
import adafruit_adg72x
from analogio import AnalogIn
analog_in = AnalogIn(board.A0)
i2c = board.I2C()
switch = adafruit_adg72x.ADG72x(i2c)
c = 0
switch_time = 2
channels = [0, 4]
clock = time.monotonic()
while True:
if (time.monotonic() - clock) > switch_time:
print(f"Selecting channel {channels[c] + 1}")
switch.channel = channels[c]
c = (c + 1) % 2
clock = time.monotonic()
print((analog_in.value,))
time.sleep(0.1)

View file

@ -0,0 +1,31 @@
# SPDX-FileCopyrightText: Copyright (c) 2024 Liz Clark for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import time
import board
import adafruit_adg72x
from analogio import AnalogIn
analog_in_DA = AnalogIn(board.A0)
analog_in_DB = AnalogIn(board.A1)
i2c = board.I2C()
switch = adafruit_adg72x.ADG72x(i2c, 0x44)
c = 0
switch_time = 3
clock = time.monotonic()
while True:
if (time.monotonic() - clock) > switch_time:
if c < 4:
channels = "A"
else:
channels = "B"
print(f"Selecting channel {(c % 4) + 1}{channels}")
switch.channel = c
c = (c + 1) % 8
clock = time.monotonic()
print((analog_in_DA.value, analog_in_DB.value,))
time.sleep(0.1)

View file

@ -93,7 +93,7 @@ void disableInternalPower() {
#endif
#if defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2)
// turn on the I2C power by setting pin to rest state (off)
// turn off the I2C power by setting pin to rest state (off)
pinMode(PIN_I2C_POWER, INPUT);
pinMode(NEOPIXEL_POWER, OUTPUT);
digitalWrite(NEOPIXEL_POWER, LOW);

View file

@ -2,6 +2,7 @@
#
# SPDX-License-Identifier: MIT
import os
import time
import board
import busio
@ -15,12 +16,10 @@ import adafruit_minimqtt.adafruit_minimqtt as MQTT
### WiFi ###
# 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
secrets = {
"ssid" : os.getenv("CIRCUITPY_WIFI_SSID"),
"password" : os.getenv("CIRCUITPY_WIFI_PASSWORD"),
}
# If you are using a board with pre-defined ESP32 Pins:
esp32_cs = DigitalInOut(board.ESP_CS)
@ -55,7 +54,7 @@ power_pin.switch_to_output()
### Feeds ###
# Set up a feed named Relay for subscribing to the relay feed on Adafruit IO
feed_relay = secrets["aio_username"] + "/feeds/relay"
feed_relay = os.getenv("AIO_USERNAME") + "/feeds/relay"
### Code ###
@ -107,8 +106,8 @@ ssl_context = adafruit_connection_manager.get_radio_ssl_context(esp)
# Set up a MiniMQTT Client
client = MQTT.MQTT(
broker="io.adafruit.com",
username=secrets["aio_username"],
password=secrets["aio_key"],
username=os.getenv("AIO_USERNAME"),
password=os.getenv("AIO_KEY"),
socket_pool=pool,
ssl_context=ssl_context,
)

View file

@ -46,7 +46,7 @@ print("Firmware vers.", esp.firmware_version)
print("MAC addr:", [hex(i) for i in esp.MAC_address])
for ap in esp.scan_networks():
print("\t%s\t\tRSSI: %d" % (str(ap['ssid'], 'utf-8'), ap['rssi']))
print("\t%s\t\tRSSI: %d" % (str(ap.ssid, 'utf-8'), ap.rssi))
print("Connecting to AP...")
while not esp.is_connected:
@ -55,7 +55,7 @@ while not esp.is_connected:
except RuntimeError as e:
print("could not connect to AP, retrying: ", e)
continue
print("Connected to", str(esp.ssid, "utf-8"), "\tRSSI:", esp.rssi)
print("Connected to", str(esp.ap_info.ssid, "utf-8"), "\tRSSI:", esp.ap_info.rssi)
print("My IP address is", esp.pretty_ip(esp.ip_address))
print(

View file

@ -0,0 +1,50 @@
// SPDX-FileCopyrightText: 2024 ladyada for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#include "Adafruit_VCNL4200.h"
Adafruit_VCNL4200 vcnl4200;
void setup() {
Serial.begin(115200);
while (!Serial) {
delay(10); // wait for native USB
}
Serial.println("Adafruit VCNL4200 ALS simple test");
if (!vcnl4200.begin()) {
Serial.println("Could not find a valid VCNL4200 sensor, check wiring!");
while (1) {
delay(10);
}
}
Serial.println("VCNL4200 found!");
vcnl4200.setALSshutdown(false);
vcnl4200.setALSIntegrationTime(VCNL4200_ALS_IT_100MS);
vcnl4200.setALSPersistence(VCNL4200_ALS_PERS_2);
vcnl4200.setProxShutdown(false);
vcnl4200.setProxHD(false);
vcnl4200.setProxLEDCurrent(VCNL4200_LED_I_200MA);
vcnl4200.setProxIntegrationTime(VCNL4200_PS_IT_8T);
}
void loop() {
// Read the proximity sensor data
uint16_t proxData = vcnl4200.readProxData();
Serial.print("Prox Data: ");
Serial.println(proxData);
// Read the ambient light sensor (ALS) data
uint16_t alsData = vcnl4200.readALSdata();
Serial.print("ALS Data: ");
Serial.print(alsData);
// Read the raw white sensor data
uint16_t whiteData = vcnl4200.readWhiteData();
Serial.print(", White Data: ");
Serial.println(whiteData);
delay(100);
}

View file

@ -0,0 +1,113 @@
# SPDX-FileCopyrightText: 2024 Liz Clark for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import time
import microcontroller
import board
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keycode import Keycode
import adafruit_ble
from adafruit_ble.advertising import Advertisement
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
from adafruit_ble.services.standard.hid import HIDService
from adafruit_seesaw import seesaw, rotaryio, digitalio
# ios modifier
mod = Keycode.CAPS_LOCK
# keycodes
KBD_CODES = [
[Keycode.SPACE], # center
[mod], # up
[Keycode.LEFT_ARROW], # left
[Keycode.DOWN_ARROW], # down
[Keycode.RIGHT_ARROW], # right
]
i2c = board.STEMMA_I2C()
seesaw = seesaw.Seesaw(i2c, addr=0x49)
seesaw_product = (seesaw.get_version() >> 16) & 0xFFFF
print(f"Found product {seesaw_product}")
if seesaw_product != 5740:
print("Wrong firmware loaded? Expected 5740")
buttons = []
button_names = ["Select", "Up", "Left", "Down", "Right"]
button_states = []
for s in range(1, 6):
seesaw.pin_mode(s, seesaw.INPUT_PULLUP)
pin = digitalio.DigitalIO(seesaw, s)
pin_state = False
buttons.append(pin)
button_states.append(pin_state)
encoder = rotaryio.IncrementalEncoder(seesaw)
last_position = 0
if not buttons[0].value and button_states[0] is False:
button_states[0] = True
try:
import _bleio
time.sleep(2)
_bleio.adapter.erase_bonding()
time.sleep(2)
print("Last BLE bonding deleted, restarting..")
time.sleep(2)
microcontroller.reset()
except Exception: # pylint: disable=broad-except
pass
# BLE HID setup
hid = HIDService()
# keyboard & mouse HID setup
kbd = Keyboard(hid.devices)
advertisement = ProvideServicesAdvertisement(hid)
advertisement.appearance = 961
scan_response = Advertisement()
scan_response.complete_name = "CircuitPython HID"
ble = adafruit_ble.BLERadio()
if not ble.connected:
print("advertising")
ble.start_advertising(advertisement, scan_response)
else:
print("already connected")
print(ble.connections)
time.sleep(2)
while True:
# check for BLE connection
while not ble.connected:
pass
# while BLE connected
while ble.connected:
position = encoder.position
if position != last_position:
# if the encoder is turned to the right
if position > last_position:
kbd.send(Keycode.RIGHT_ARROW)
# if the encoder is turned to the left
if position < last_position:
kbd.send(Keycode.LEFT_ARROW)
# reset encoder position
last_position = position
print(f"Position: {position}")
for b in range(5):
if not buttons[b].value and button_states[b] is False:
button_states[b] = True
if b != 0:
kbd.press(*KBD_CODES[b])
print(*KBD_CODES[b])
else:
kbd.press(mod)
kbd.press(*KBD_CODES[b])
print(f"{button_names[b]} button pressed")
if buttons[b].value and button_states[b] is True:
button_states[b] = False
kbd.release_all()
print(f"{button_names[b]} button released")
# if BLE disconnects, begin advertising again
ble.start_advertising(advertisement)

View file

@ -2,9 +2,13 @@
#
# SPDX-License-Identifier: MIT
from binascii import unhexlify
from time import sleep
from adafruit_ble.uart_client import UARTClient
from adafruit_ble.scanner import Scanner
from micropython import const
from adafruit_ble import BLERadio
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
from adafruit_ble.services.nordic import UARTService
from adafruit_bluefruit_connect.packet import Packet
from adafruit_bluefruit_connect.button_packet import ButtonPacket
from adafruit_bluefruit_connect.color_packet import ColorPacket
@ -21,65 +25,75 @@ switch = Debouncer(pin)
pixels = NeoPixel(NEOPIXEL, 1) # Set up built-in NeoPixel
AQUA = 0x00FFFF # (0, 255, 255)
GREEN = 0x00FF00 # (0, 255, 0)
ORANGE = 0xFF8000 # (255, 128, 0)
RED = 0xFF0000 # (255, 0, 0)
BLUE = 0x0000FF # (0, 0, 255)
AQUA = const(0x00FFFF) # (0, 255, 255)
GREEN = const(0x00FF00) # (0, 255, 0)
ORANGE = const(0xFF8000) # (255, 128, 0)
RED = const(0xFF0000) # (255, 0, 0)
BLUE = const(0x0000FF) # (0, 0, 255)
gradients = {'Off': [(0.0, RED), (0.75, ORANGE)],
'On': [(0.0, GREEN), (1.0, AQUA)]}
palette = fancy.expand_gradient(gradients['Off'], 30)
gradients = {"Off": [(0.0, RED), (0.75, ORANGE)], "On": [(0.0, GREEN), (1.0, AQUA)]}
palette = fancy.expand_gradient(gradients["Off"], 30)
gamma_levels = (0.25, 0.3, 0.15)
color_index = 1
fade_direction = 1
TARGET = 'a0:b4:c2:d0:e7:f2' # CHANGE TO YOUR BLE ADDRESS
TARGET = "f0:74:72:60:87:d2" # CHANGE TO YOUR BLE ADDRESS
target_address = TARGET.split(":") # Convert address string to list of bytes
target_address.reverse() # Reverse bytes to match Address class little-endian
target_address = unhexlify("".join(target_address)) # Convert list to bytes
button_packet = ButtonPacket("1", True) # Transmits pressed button 1
scanner = Scanner() # BLE Scanner
uart_client = UARTClient() # BLE Client
ble = BLERadio()
uart_client = None
while True:
uart_addresses = []
pixels[0] = BLUE # Blue LED indicates disconnected status
pixels.show()
# Keep trying to find target UART peripheral
while not uart_addresses:
uart_addresses = uart_client.scan(scanner)
for address in uart_addresses:
if TARGET in str(address):
uart_client.connect(address, 5) # Connect to target
if not uart_client:
print("Trying to connect to BLE server...")
# Keep trying to find target UART peripheral
for adv in ble.start_scan(ProvideServicesAdvertisement):
print(adv.address.address_bytes) # Print detected addresses
if adv.address.address_bytes == target_address:
uart_client = ble.connect(adv)
print("Connected")
break
ble.stop_scan()
while uart_client.connected: # Connected
switch.update()
if switch.fell: # Check for button press
try:
uart_client.write(button_packet.to_bytes()) # Transmit press
except OSError:
pass
# Check for LED status receipt
if uart_client.in_waiting:
packet = Packet.from_stream(uart_client)
if isinstance(packet, ColorPacket):
if fancy.CRGB(*packet.color).pack() == GREEN: # Color match
# Green indicates on state
palette = fancy.expand_gradient(gradients['On'], 30)
else:
# Otherwise red indicates off
palette = fancy.expand_gradient(gradients['Off'], 30)
if uart_client and uart_client.connected:
uart_service = uart_client[UARTService]
while uart_client and uart_client.connected: # Connected
switch.update()
if switch.fell: # Check for button press
try:
# Transmit press
uart_service.write(button_packet.to_bytes())
except OSError:
pass
# Check for LED status receipt
if uart_service.in_waiting:
packet = Packet.from_stream(uart_service)
if isinstance(packet, ColorPacket):
# Color match
if fancy.CRGB(*packet.color).pack() == GREEN:
# Green indicates on state
palette = fancy.expand_gradient(gradients["On"], 30)
else:
# Otherwise red indicates off
palette = fancy.expand_gradient(gradients["Off"], 30)
# NeoPixel color fading routing
color = fancy.palette_lookup(palette, color_index / 29)
color = fancy.gamma_adjust(color, brightness=gamma_levels)
c = color.pack()
pixels[0] = c
pixels.show()
if color_index in (0, 28):
fade_direction *= -1 # Change direction
color_index += fade_direction
# NeoPixel color fading routing
color = fancy.palette_lookup(palette, color_index / 29)
color = fancy.gamma_adjust(color, brightness=gamma_levels)
c = color.pack()
pixels[0] = c
pixels.show()
if color_index in (0, 28):
fade_direction *= -1 # Change direction
color_index += fade_direction
sleep(0.02)
sleep(0.02)

View file

@ -0,0 +1,66 @@
# SPDX-FileCopyrightText: 2024 Liz Clark for Adafruit Industries
# SPDX-License-Identifier: MIT
import time
import board
import busio
from adafruit_bluefruit_connect.packet import Packet
from adafruit_bluefruit_connect.button_packet import ButtonPacket
from adafruit_ble import BLERadio
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
from adafruit_ble.services.nordic import UARTService
import neopixel
# baud rate for your device
baud = 38400
# commands for your device
commands = ["AVI=1", "AVI=2", "AVI=3", "AVI=4"]
# Initialize UART for the RS232
uart = busio.UART(board.TX, board.RX, baudrate=baud)
# onboard neopixel
pixels = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.5, auto_write=True)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
# BLE setup
ble = BLERadio()
ble_uart = UARTService()
advertisement = ProvideServicesAdvertisement(ble_uart)
advertising = False
print("advertising..")
while True:
if not ble.connected and not advertising:
# not connected in the app yet
pixels.fill(RED)
ble.start_advertising(advertisement)
advertising = True
while ble.connected:
pixels.fill(BLUE)
# after connected via app
advertising = False
if ble_uart.in_waiting:
# waiting for input from app
packet = Packet.from_stream(ble_uart)
if isinstance(packet, ButtonPacket):
# if buttons in the app are pressed
if packet.pressed:
if packet.button == ButtonPacket.BUTTON_1:
uart.write((commands[0] + "\r\n").encode('ascii'))
if packet.button == ButtonPacket.BUTTON_2:
uart.write((commands[1] + "\r\n").encode('ascii'))
if packet.button == ButtonPacket.BUTTON_3:
uart.write((commands[2] + "\r\n").encode('ascii'))
if packet.button == ButtonPacket.BUTTON_4:
uart.write((commands[3] + "\r\n").encode('ascii'))
# empty buffer to collect the incoming data
response_buffer = bytearray()
# check for data
time.sleep(1)
while uart.in_waiting:
data = uart.read(uart.in_waiting)
if data:
response_buffer.extend(data)
# decode and print
if response_buffer:
print(response_buffer.decode('ascii'), end='')
print()

View file

@ -0,0 +1,133 @@
// SPDX-FileCopyrightText: 2024 Liz Clark for Adafruit Industries
//
// SPDX-License-Identifier: MIT
// BNO055 + BMP280 BFF Demo
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BMP280.h>
#include <Adafruit_BNO055.h>
#include <utility/imumaths.h>
Adafruit_BNO055 bno = Adafruit_BNO055(55, 0x28, &Wire);
Adafruit_BMP280 bmp;
void setup(void)
{
Serial.begin(115200);
while (!Serial) delay(10); // wait for serial port to open!
Serial.println("Adafruit BNO055 + BMP280 BFF Demo");
/* Initialise the sensor */
if (!bno.begin())
{
/* There was a problem detecting the BNO055 ... check your connections */
Serial.print("Ooops, no BNO055 detected ... Check your wiring or I2C ADDR!");
while (1);
}
if (!bmp.begin()) {
Serial.print("Ooops, no BMP280 detected ... Check your wiring or I2C ADDR!");
while (1);
}
bmp.setSampling(Adafruit_BMP280::MODE_NORMAL, /* Operating Mode. */
Adafruit_BMP280::SAMPLING_X2, /* Temp. oversampling */
Adafruit_BMP280::SAMPLING_X16, /* Pressure oversampling */
Adafruit_BMP280::FILTER_X16, /* Filtering. */
Adafruit_BMP280::STANDBY_MS_500); /* Standby time. */
Serial.println("Found BNO055 and BMP280 sensors!");
Serial.println();
delay(1000);
}
void loop(void)
{
//could add VECTOR_ACCELEROMETER, VECTOR_MAGNETOMETER,VECTOR_GRAVITY...
sensors_event_t orientationData , angVelocityData , linearAccelData, magnetometerData, accelerometerData, gravityData;
bno.getEvent(&orientationData, Adafruit_BNO055::VECTOR_EULER);
bno.getEvent(&angVelocityData, Adafruit_BNO055::VECTOR_GYROSCOPE);
bno.getEvent(&linearAccelData, Adafruit_BNO055::VECTOR_LINEARACCEL);
bno.getEvent(&magnetometerData, Adafruit_BNO055::VECTOR_MAGNETOMETER);
bno.getEvent(&accelerometerData, Adafruit_BNO055::VECTOR_ACCELEROMETER);
bno.getEvent(&gravityData, Adafruit_BNO055::VECTOR_GRAVITY);
Serial.println("BNO055 data:");
printEvent(&orientationData);
printEvent(&angVelocityData);
printEvent(&linearAccelData);
printEvent(&magnetometerData);
printEvent(&accelerometerData);
printEvent(&gravityData);
Serial.println("--");
Serial.println("BMP280 data:");
Serial.print(F("Temperature = "));
Serial.print(bmp.readTemperature());
Serial.println(" *C");
Serial.print(F("Pressure = "));
Serial.print(bmp.readPressure());
Serial.println(" Pa");
Serial.print(F("Approx altitude = "));
Serial.print(bmp.readAltitude(1013.25)); /* Adjusted to local forecast! */
Serial.println(" m");
Serial.println();
delay(2000);
}
void printEvent(sensors_event_t* event) {
double x = -1000000, y = -1000000 , z = -1000000; //dumb values, easy to spot problem
if (event->type == SENSOR_TYPE_ACCELEROMETER) {
Serial.print("Accl:");
x = event->acceleration.x;
y = event->acceleration.y;
z = event->acceleration.z;
}
else if (event->type == SENSOR_TYPE_ORIENTATION) {
Serial.print("Orient:");
x = event->orientation.x;
y = event->orientation.y;
z = event->orientation.z;
}
else if (event->type == SENSOR_TYPE_MAGNETIC_FIELD) {
Serial.print("Mag:");
x = event->magnetic.x;
y = event->magnetic.y;
z = event->magnetic.z;
}
else if (event->type == SENSOR_TYPE_GYROSCOPE) {
Serial.print("Gyro:");
x = event->gyro.x;
y = event->gyro.y;
z = event->gyro.z;
}
else if (event->type == SENSOR_TYPE_ROTATION_VECTOR) {
Serial.print("Rot:");
x = event->gyro.x;
y = event->gyro.y;
z = event->gyro.z;
}
else if (event->type == SENSOR_TYPE_LINEAR_ACCELERATION) {
Serial.print("Linear:");
x = event->acceleration.x;
y = event->acceleration.y;
z = event->acceleration.z;
}
else if (event->type == SENSOR_TYPE_GRAVITY) {
Serial.print("Gravity:");
x = event->acceleration.x;
y = event->acceleration.y;
z = event->acceleration.z;
}
else {
Serial.print("Unk:");
}
Serial.print("\tx= ");
Serial.print(x);
Serial.print(" |\ty= ");
Serial.print(y);
Serial.print(" |\tz= ");
Serial.println(z);
}

View file

@ -0,0 +1,30 @@
# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
# SPDX-License-Identifier: MIT
#
# BNO055 + BMP280 BFF Demo
import time
import board
import adafruit_bno055
import adafruit_bmp280
i2c = board.I2C() # uses board.SCL and board.SDA
bno055 = adafruit_bno055.BNO055_I2C(i2c)
bmp280 = adafruit_bmp280.Adafruit_BMP280_I2C(i2c)
bmp280.sea_level_pressure = 1013.25
while True:
print(f"Temperature: {bmp280.temperature:0.1f} C")
print(f"Pressure: {bmp280.pressure:0.1f} hPa")
print(f"Altitude = {bmp280.altitude:0.2f} meters")
print(f"Accelerometer (m/s^2): {bno055.acceleration}")
print(f"Magnetometer (microteslas): {bno055.magnetic}")
print(f"Gyroscope (rad/sec): {bno055.gyro}")
print(f"Euler angle: {bno055.euler}")
print(f"Quaternion: {bno055.quaternion}")
print(f"Linear acceleration (m/s^2): {bno055.linear_acceleration}")
print(f"Gravity (m/s^2): {bno055.gravity}")
print()
time.sleep(1)

View file

@ -0,0 +1,100 @@
// SPDX-FileCopyrightText: 2024 Liz Clark for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#include <Arduino.h>
#include <SPI.h>
#include "Adafruit_TinyUSB.h"
#include "Adafruit_ThinkInk.h"
#include "RTClib.h"
#include <Wire.h>
#include <Fonts/FreeSans24pt7b.h>
#include <Fonts/FreeSans18pt7b.h>
#include <Fonts/FreeSans12pt7b.h>
#define EPD_DC PA3
#define EPD_CS PA2
#define EPD_BUSY -1 // can set to -1 to not use a pin (will wait a fixed delay)
#define SRAM_CS PA1
#define EPD_RESET -1 // can set to -1 and share with microcontroller Reset!
#define EPD_SPI &SPI // primary SPI
// 1.54" Monochrome displays with 200x200 pixels and SSD1681 chipset
ThinkInk_154_Mono_D67 display(EPD_DC, EPD_RESET, EPD_CS, SRAM_CS, EPD_BUSY, EPD_SPI);
RTC_DS3231 rtc;
char daysOfTheWeek[7][4] = {"SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"};
char monthsOfYear[13][4] = {"NULL", "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"};
bool start = true;
String lastTimeStr = "00:00";
void setup() {
Serial.begin(115200);
/*while (!Serial) {
delay(10);
}*/
Serial.println("eInk Calendar & Clock with CH32V203");
SPI.begin();
Serial.println("SPI started");
pinMode(EPD_CS, OUTPUT);
digitalWrite(EPD_CS, HIGH);
Wire.begin();
Serial.println("Wire started");
Serial.println("starting display, might take a bit..");
display.begin(THINKINK_MONO);
Serial.println("display started");
while (! rtc.begin()) {
Serial.println("Couldn't find RTC");
delay(10);
}
}
void loop() {
Serial.println("we did it!");
DateTime now = rtc.now();
Serial.println("read rtc");
char timeChar[6];
sprintf(timeChar, "%02d:%02d", now.hour(), now.minute());
String timeStr = String(timeChar);
String monthStr = monthsOfYear[now.month()];
String dateStr = String(now.day());
String yearStr = String(now.year());
String dayOfWeekStr = daysOfTheWeek[now.dayOfTheWeek()];
Serial.println(timeStr);
int16_t x1, y1;
uint16_t w, h;
if (lastTimeStr != timeStr) {
display.clearBuffer();
display.fillRect(0, 0, 200, 60, EPD_BLACK);
display.drawRect(0, 0, 200, 170, EPD_BLACK);
display.setTextColor(EPD_WHITE);
display.setFont(&FreeSans24pt7b);
display.getTextBounds(dayOfWeekStr.c_str(), 0, 0, &x1, &y1, &w, &h);
display.setCursor((200 - w) / 2, (60 - h) / 2 + h);
display.println(dayOfWeekStr);
display.setTextColor(EPD_BLACK);
display.getTextBounds(dateStr.c_str(), 0, 0, &x1, &y1, &w, &h);
display.setCursor((200 - w) / 2, (170 - h) / 2 + h + 28);
display.println(dateStr);
display.setFont(&FreeSans18pt7b);
display.getTextBounds(monthStr.c_str(), 0, 0, &x1, &y1, &w, &h);
display.setCursor((200 - w) / 2, (60 + h) + 3);
display.println(monthStr);
display.getTextBounds(yearStr.c_str(), 0, 0, &x1, &y1, &w, &h);
display.setCursor((200 - w) / 2, (170 - h) - 5 + h);
display.println(yearStr);
display.setFont(&FreeSans12pt7b);
display.getTextBounds(timeStr.c_str(), 0, 0, &x1, &y1, &w, &h);
display.setCursor((200 - w) / 2, (200 - h) - 5 + h);
display.println(timeStr);
display.display();
lastTimeStr = timeStr;
}
delay(30000);
}

View file

@ -0,0 +1,75 @@
# SPDX-FileCopyrightText: 2024 Liz Clark for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import sys
import serial
import keyboard
port = '/dev/ttyUSB0' # Replace with your actual serial port
# Define a mapping for special characters when shift is pressed
SHIFTED_KEYS = {
'1': '!', '2': '@', '3': '#', '4': '$', '5': '%',
'6': '^', '7': '&', '8': '*', '9': '(', '0': ')',
'`': '~', '-': '_', '=': '+', '[': '{', ']': '}',
'\\': '|', ';': ':', "'": '"', ',': '<', '.': '>',
'/': '?'
}
def send_key(serial_port, key):
"""
Send a key press to the CH9328 via UART.
Parameters:
serial_port (serial.Serial): The serial port connection.
key (str): The key to send.
"""
serial_port.write(key.encode('ascii'))
serial_port.flush()
def send_empty_report(serial_port):
"""
Send an empty HID report to reset the state of the device.
Parameters:
serial_port (serial.Serial): The serial port connection.
"""
try:
empty_report = bytearray([0] * 8)
serial_port.write(empty_report)
serial_port.flush()
except serial.SerialException as e:
print(f"Failed to send empty report: {e}")
def main():
# Configure the serial connection
baudrate = 9600 # Default baud rate for CH9328 in Mode 1
timeout = 1
with serial.Serial(port, baudrate, timeout=timeout) as ser:
print("Listening for keyboard inputs. Press 'ESC' to exit.")
def on_key_event(event):
if event.event_type == 'down':
key = event.name
if len(key) == 1: # Only process single character keys
if keyboard.is_pressed('shift'): # Check if shift is pressed
key = SHIFTED_KEYS.get(key, key.upper())
send_key(ser, key)
elif key == 'space':
send_key(ser, ' ')
elif key == 'enter':
send_key(ser, '\n')
send_empty_report(ser)
# Hook the keyboard event
keyboard.hook(on_key_event)
# Wait for ESC to exit
keyboard.wait('esc')
if __name__ == "__main__":
main()
sys.exit()

View file

@ -339,6 +339,8 @@ class BMP2LED:
# Accumulated error vals will all now be 0.0 to <2.0.
# Quantizing err into a new uint8 ndarray, all values
# will be 0 or 1.
# Convert float values in err to integers
err = [int(min(max(0, e), 255)) for e in err]
err_bits = ulab.numpy.array(err, dtype=ulab.numpy.uint8)
# Add the 1's back into 'got', increasing the
# brightness of certain pixels by 1. Because the max

View file

@ -230,7 +230,7 @@ class ClueLightPainter:
board.DISPLAY.root_group = group
sleep(4)
board.DISPLAY.show(displayio.Group()) # Clear display
board.DISPLAY.root_group = displayio.Group() # Clear display
self.clear_strip() # LEDs off

View file

@ -444,7 +444,7 @@ class Plotter:
if self._displayio_graph is None:
self._displayio_graph = self._make_empty_graph(tg_and_plot=tg_and_plot)
self._output.show(self._displayio_graph)
self._output.root_group = self._displayio_graph
def display_off(self):
pass

141
CNC_MacroPad/code.py Normal file
View file

@ -0,0 +1,141 @@
# SPDX-FileCopyrightText: 2024 Liz Clark for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import board
import keypad
import rotaryio
import neopixel
import usb_hid
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keycode import Keycode
from adafruit_hid.mouse import Mouse
mouse = Mouse(usb_hid.devices)
# neopixel colors
RED = (255, 0, 0)
ORANGE = (255, 127, 0)
YELLOW = (255, 255, 0)
GREEN = (0, 255, 0)
AQUA = (0, 255, 255)
BLUE = (0, 0, 255)
PURPLE = (127, 0, 255)
PINK = (255, 0, 255)
OFF = (50, 50, 50)
# axis states selected with keys 9-11
axis_states = [0, "x", "y", "z"]
state = axis_states[0]
# keymap for key matrix
keymap = {
(0): (axis_states[0], (Keycode.COMMAND, Keycode.COMMA), BLUE), # SETTINGS
(1): (axis_states[0], (Keycode.COMMAND, Keycode.P), ORANGE), # SLICE MODEL
(2): (axis_states[0], (Keycode.COMMAND, Keycode.D), RED), # CLEAR BED
(3): (axis_states[0], [Keycode.T], GREEN), # MOVE
(4): (axis_states[0], [Keycode.S], AQUA), # SCALE
(5): (axis_states[0], [Keycode.R], BLUE), # ROTATE
(6): (axis_states[0], [Keycode.M], AQUA), # MIRROR
(7): (axis_states[0], [Keycode.E], PURPLE), # SUPPORT BLOCKERS
(8): (axis_states[0], [Keycode.I], PINK), # TABS
(9): (axis_states[1], None, RED), # SET X-AXIS STATE
(10): (axis_states[2], None, GREEN), # SET Y-AXIS STATE
(11): (axis_states[3], None, BLUE), # SET Z-AXIS STATE
}
# keymap for encoder based on state; pos = [0], neg = [1]
encoder_map = {
("x"): ([Keycode.RIGHT_ARROW], [Keycode.LEFT_ARROW]),
("y"): ([Keycode.UP_ARROW], [Keycode.DOWN_ARROW]),
# ("z"): ([Keycode.W], [Keycode.S]),
}
# make a keyboard
kbd = Keyboard(usb_hid.devices)
# key matrix
COLUMNS = 3
ROWS = 4
keys = keypad.KeyMatrix(
row_pins=(board.D12, board.D11, board.D10, board.D9),
column_pins=(board.A0, board.A1, board.A2),
columns_to_anodes=False,
)
# neopixels and key num to pixel function
pixels = neopixel.NeoPixel(board.D5, 12, brightness=0.3)
def key_to_pixel_map(key_number):
row = key_number // COLUMNS
column = key_number % COLUMNS
if row % 2 == 1:
column = COLUMNS - column - 1
return row * COLUMNS + column
pixels.fill(OFF) # Begin with pixels off.
# make an encoder
encoder = rotaryio.IncrementalEncoder(board.D24, board.D25)
last_position = 0
while True:
# poll for key event
key_event = keys.events.get()
# get position of encoder
position = encoder.position
# if position changes..
if position != last_position:
# ..and it increases..
if position > last_position:
# ..and state is x:
if state is axis_states[1]:
kbd.press(*encoder_map[state][0])
# ..and state is y:
if state is axis_states[2]:
kbd.press(*encoder_map[state][0])
# ..and state is z:
if state is axis_states[3]:
mouse.move(wheel=-1)
# ..and it decreases..
if position < last_position:
# ..and state is x:
if state is axis_states[1]:
kbd.press(*encoder_map[state][1])
# ..and state is y:
if state is axis_states[2]:
kbd.press(*encoder_map[state][1])
# ..and state is z:
if state is axis_states[3]:
mouse.move(wheel=1)
# print(position)
# release all keys
kbd.release_all()
# update last_position
last_position = position
# if a key event..
if key_event:
# print(key_event)
# ..and it's pressed..
if key_event.pressed:
# ..and it's keys 0-8, send key presses from keymap:
if keymap[key_event.key_number][0] is axis_states[0]:
state = axis_states[0]
kbd.press(*keymap[key_event.key_number][1])
# ..and it's key 9, set state to x
if keymap[key_event.key_number][0] is axis_states[1]:
state = axis_states[1]
pixels[key_to_pixel_map(10)] = OFF
pixels[key_to_pixel_map(11)] = OFF
# ..and it's key 10, set state to y
if keymap[key_event.key_number][0] is axis_states[2]:
state = axis_states[2]
pixels[key_to_pixel_map(9)] = OFF
pixels[key_to_pixel_map(11)] = OFF
# ..and it's key 11, set state to z
if keymap[key_event.key_number][0] is axis_states[3]:
state = axis_states[3]
pixels[key_to_pixel_map(9)] = OFF
pixels[key_to_pixel_map(10)] = OFF
# turn on neopixel for key with color from keymap
pixels[key_to_pixel_map(key_event.key_number)] = keymap[key_event.key_number][2]
# ..and it's released..
if key_event.released:
# if it's key 0-8, release the key press and turn off neopixel
if keymap[key_event.key_number][0] is axis_states[0]:
kbd.release(*keymap[key_event.key_number][1])
pixels.fill(OFF)

View file

@ -47,6 +47,8 @@ hid = HIDService()
device_info = DeviceInfoService(software_revision=adafruit_ble.__version__,
manufacturer="Adafruit Industries")
advertisement = ProvideServicesAdvertisement(hid)
# Advertise as "Keyboard" (0x03C1) icon when pairing
# https://www.bluetooth.com/specifications/assigned-numbers/
advertisement.appearance = 961
scan_response = Advertisement()
scan_response.complete_name = "CircuitPython HID"

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View file

@ -0,0 +1,133 @@
# SPDX-FileCopyrightText: 2025 Tim Cocks
#
# SPDX-License-Identifier: MIT
"""
Cartoon Character Clock
This project features an analog clock face rendered on a round display.
The art and songs are inspired by an early 20th-century public domain
cartoon.
"""
import os
import time
import board
from adafruit_display_analogclock import AnalogClock
from adafruit_gc9a01a import GC9A01A
import rtc
import socketpool
import displayio
from fourwire import FourWire
import adafruit_ntp
import wifi
import audiobusio
from audiomp3 import MP3Decoder
# Set the desired offset from UTC time here.
# US Eastern Standard Time is -5
# US Eastern Daylight Time is -4
UTC_OFFSET = -5
# enable or disable the hourly chime jingle
HOURLY_CHIME = True
# Set to a tuple containing hour and minute values to
# enable the alarm e.g. 13, 25 will play the alarm at
# 1:25 pm adjusted for the given UTC_OFFSET.
ALARM_TIME = None
#ALARM_TIME = (13, 25)
# WiFi Setup
# Get wifi AP credentials from a settings.toml file
wifi_ssid = os.getenv("CIRCUITPY_WIFI_SSID")
wifi_password = os.getenv("CIRCUITPY_WIFI_PASSWORD")
if wifi_ssid is None:
print("WiFi credentials are kept in settings.toml, please add them there!")
raise ValueError("SSID not found in environment variables")
try:
wifi.radio.connect(wifi_ssid, wifi_password)
except ConnectionError:
print("Failed to connect to WiFi with provided credentials")
raise
# pylint: disable=unsubscriptable-object
if HOURLY_CHIME or ALARM_TIME is not None:
# Audio Setup
audio = audiobusio.I2SOut(board.A2, board.A1, board.A0)
songs = ["song_1.mp3", "song_2.mp3"]
mp3 = open(songs[0], "rb")
decoder = MP3Decoder(mp3)
# Display Setup
spi = board.SPI()
tft_cs = board.TX
tft_dc = board.RX
displayio.release_displays()
display_bus = FourWire(spi, command=tft_dc, chip_select=tft_cs, reset=None)
display = GC9A01A(display_bus, width=240, height=240)
display.rotation = 90
# Sync time from NTP server
pool = socketpool.SocketPool(wifi.radio)
ntp = adafruit_ntp.NTP(pool, server="time.nist.gov", tz_offset=0, cache_seconds=3600)
rtc.RTC().datetime = ntp.datetime
# Initialize the clock face
clockface = AnalogClock(
"clock_short_hand.bmp",
"clock_long_hand.bmp",
(120, 120), 106, number_label_scale=2,
background_color=0x989a97,
background_img_file="clock_center.bmp",
background_img_anchor_point=(0.5,0.5),
background_img_anchored_position=(display.width//2, display.height//2 - 2)
)
# set the clockface to show on the display
display.root_group = clockface
print(f"current time {time.localtime().tm_hour + UTC_OFFSET}:{time.localtime().tm_min}")
cur_hour = time.localtime().tm_hour + UTC_OFFSET
cur_minute = time.localtime().tm_min
clockface.set_time(cur_hour, cur_minute)
while True:
# If we need to update the clock hands
if cur_hour != time.localtime().tm_hour + UTC_OFFSET or cur_minute != time.localtime().tm_min:
# store current values to comapre with next iteration
cur_hour = time.localtime().tm_hour + UTC_OFFSET
cur_minute = time.localtime().tm_min
# update the clock face
clockface.set_time(cur_hour, cur_minute)
# if the hourly chime is enabled, and it's the top of the hour
if HOURLY_CHIME and cur_minute == 0:
# play the hour chime jingle
audio.play(decoder)
while audio.playing:
pass
# if the alarm is enabled and the current time is what
# it was set to.
if ALARM_TIME is not None and \
cur_hour == ALARM_TIME[0] and cur_minute == ALARM_TIME[1]:
# open the alarm song file
decoder.file = open("song_2.mp3", "rb")
# play the alarm song twice
for i in range(2):
audio.play(decoder)
while audio.playing:
pass
time.sleep(0.5)
# re-open the hourly chime file
decoder.file = open("song_1.mp3", "rb")
time.sleep(3)

Binary file not shown.

Binary file not shown.

View file

@ -5,21 +5,22 @@
import threading
import os
import sys
from datetime import datetime, timedelta
from queue import Queue
import time
import random
import configparser
from tempfile import NamedTemporaryFile
import azure.cognitiveservices.speech as speechsdk
import speech_recognition as sr
import openai
from openai import OpenAI
import board
import digitalio
from adafruit_motorkit import MotorKit
from listener import Listener
API_KEYS_FILE = "~/keys.txt"
# ChatGPT Parameters
SYSTEM_ROLE = (
"You are a helpful voice assistant in the form of a talking teddy bear"
@ -34,7 +35,6 @@ DEVICE_ID = None
# Speech Recognition Parameters
ENERGY_THRESHOLD = 1000 # Energy level for mic to detect
PHRASE_TIMEOUT = 3.0 # Space between recordings for sepating phrases
RECORD_TIMEOUT = 30
# Motor Parameters
@ -44,32 +44,60 @@ SPEECH_VARIANCE = 0.1 # Higher allows more mouth movement variance.
# It pauses for BASE_MOUTH_DURATION ± SPEECH_VARIANCE
MOTOR_DUTY_CYCLE = 1.0 # Lower provides less power to the motors
# Import keys from environment variables
openai.api_key = os.environ.get("OPENAI_API_KEY")
speech_key = os.environ.get("SPEECH_KEY")
service_region = os.environ.get("SPEECH_REGION")
# Do some checks and Import API keys from API_KEYS_FILE
config = configparser.ConfigParser()
if openai.api_key is None or speech_key is None or service_region is None:
print(
"Please set the OPENAI_API_KEY, SPEECH_KEY, and SPEECH_REGION environment variables first."
)
sys.exit(1)
username = os.environ["USER"]
user_homedir = os.path.expanduser(f"~{username}")
API_KEYS_FILE = API_KEYS_FILE.replace("~", user_homedir)
def get_config_value(section, key, min_length=None):
if not config.has_section(section):
print("Please make sure API_KEYS_FILE points to "
f"a valid file and has an [{section}] section.")
sys.exit(1)
if key not in config[section]:
print(
f"Please make sure your API keys file contains an {key} under the {section} section."
)
sys.exit(1)
value = config[section][key]
if min_length and len(value) < min_length:
print(f"Please set {key} in your API keys file with a valid key.")
sys.exit(1)
return config[section][key]
print(os.path.expanduser(API_KEYS_FILE))
config.read(os.path.expanduser(API_KEYS_FILE))
openai = OpenAI(
# This is the default and can be omitted
api_key=get_config_value("openai", "OPENAI_API_KEY", 10)
)
speech_key = get_config_value("azure", "SPEECH_KEY", 15)
service_region = get_config_value("azure", "SPEECH_REGION")
speech_config = speechsdk.SpeechConfig(subscription=speech_key, region=service_region)
speech_config.speech_synthesis_voice_name = AZURE_SPEECH_VOICE
def sendchat(prompt):
completion = openai.ChatCompletion.create(
response = ""
stream = openai.chat.completions.create(
model=CHATGPT_MODEL,
messages=[
{"role": "system", "content": SYSTEM_ROLE},
{"role": "user", "content": prompt},
],
stream=True,
)
# Send the heard text to ChatGPT and return the result
return completion.choices[0].message.content
for chunk in stream:
if chunk.choices[0].delta.content is not None:
response += chunk.choices[0].delta.content
# Send the heard text to ChatGPT and return the result
return response
def transcribe(wav_data):
# Read the transcription.
@ -88,69 +116,6 @@ def transcribe(wav_data):
return "I wasn't able to understand you. Please repeat that."
class Listener:
def __init__(self):
self.listener_handle = None
self.recognizer = sr.Recognizer()
self.recognizer.energy_threshold = ENERGY_THRESHOLD
self.recognizer.dynamic_energy_threshold = False
self.recognizer.pause_threshold = 1
self.last_sample = bytes()
self.phrase_time = datetime.utcnow()
self.phrase_timeout = PHRASE_TIMEOUT
self.phrase_complete = False
# Thread safe Queue for passing data from the threaded recording callback.
self.data_queue = Queue()
self.mic_dev_index = None
def listen(self):
if not self.listener_handle:
with sr.Microphone() as source:
print(source.stream)
self.recognizer.adjust_for_ambient_noise(source)
audio = self.recognizer.listen(source, timeout=RECORD_TIMEOUT)
data = audio.get_raw_data()
self.data_queue.put(data)
def record_callback(self, _, audio: sr.AudioData) -> None:
# Grab the raw bytes and push it into the thread safe queue.
data = audio.get_raw_data()
self.data_queue.put(data)
def speech_waiting(self):
return not self.data_queue.empty()
def get_speech(self):
if self.speech_waiting():
return self.data_queue.get()
return None
def get_audio_data(self):
now = datetime.utcnow()
if self.speech_waiting():
self.phrase_complete = False
if self.phrase_time and now - self.phrase_time > timedelta(
seconds=self.phrase_timeout
):
self.last_sample = bytes()
self.phrase_complete = True
self.phrase_time = now
# Concatenate our current audio data with the latest audio data.
while self.speech_waiting():
data = self.get_speech()
self.last_sample += data
# Use AudioData to convert the raw data to wav data.
with sr.Microphone() as source:
audio_data = sr.AudioData(
self.last_sample, source.SAMPLE_RATE, source.SAMPLE_WIDTH
)
return audio_data
return None
class Bear:
def __init__(self, azure_speech_config):
kit = MotorKit(i2c=board.I2C())
@ -234,15 +199,15 @@ class Bear:
if cancellation_details.reason == speechsdk.CancellationReason.Error:
print("Error details: {}".format(cancellation_details.error_details))
def main():
listener = Listener()
listener = Listener(openai.api_key, ENERGY_THRESHOLD, RECORD_TIMEOUT)
bear = Bear(speech_config)
transcription = [""]
bear.speak(
"Hello there! Just give my left foot a squeeze if you would like to get my attention."
)
while True:
try:
# If button is pressed, start listening
@ -250,25 +215,19 @@ def main():
bear.speak("How may I help you?")
listener.listen()
# Pull raw recorded audio from the queue.
if listener.speech_waiting():
audio_data = listener.get_audio_data()
bear.speak("Let me think about that")
bear.move_arms(hide=True)
text = transcribe(audio_data.get_wav_data())
text = listener.recognize()
if text:
if listener.phrase_complete:
transcription.append(text)
print(f"Phrase Complete. Sent '{text}' to ChatGPT.")
chat_response = sendchat(text)
transcription.append(f"> {chat_response}")
print("Got response from ChatGPT. Beginning speech synthesis.")
bear.move_arms(hide=False)
bear.speak(chat_response)
else:
print("Partial Phrase...")
transcription[-1] = text
transcription.append(text)
print(f"Phrase Complete. Sent '{text}' to ChatGPT.")
chat_response = sendchat(text)
transcription.append(f"> {chat_response}")
print("Got response from ChatGPT. Beginning speech synthesis.")
bear.move_arms(hide=False)
bear.speak(chat_response)
os.system("clear")
for line in transcription:

6
ChatGPT_Bear/keys.txt Normal file
View file

@ -0,0 +1,6 @@
[openai]
OPENAI_API_KEY = sk-...
[azure]
SPEECH_KEY = 4f1d...02a9
SPEECH_REGION = eastus

84
ChatGPT_Bear/listener.py Normal file
View file

@ -0,0 +1,84 @@
# SPDX-FileCopyrightText: 2023 Melissa LeBlanc-Williams for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import time
import speech_recognition as sr
class Listener:
def __init__(
self, api_key, energy_threshold=300, record_timeout=30
):
self.listener_handle = None
self.microphone = sr.Microphone()
self.recognizer = sr.Recognizer()
self.recognizer.energy_threshold = energy_threshold
self.recognizer.dynamic_energy_threshold = False
self.recognizer.pause_threshold = 1
self.phrase_time = time.monotonic()
with self.microphone as source:
self.recognizer.adjust_for_ambient_noise(
source
) # we only need to calibrate once, before we start listening
self.record_timeout = record_timeout
self._audio = None
self.listener_handle = None
self.api_key = api_key
def listen(self, ready_callback=None):
print("Start listening...")
self._start_listening()
if ready_callback:
ready_callback()
while (
self.listener_handle and not self.speech_waiting()
):
time.sleep(0.1)
self.stop_listening()
def _save_audio_callback(self, _, audio):
print("Saving audio")
self._audio = audio
def _start_listening(self):
if not self.listener_handle:
self.listener_handle = self.recognizer.listen_in_background(
self.microphone,
self._save_audio_callback,
phrase_time_limit=self.record_timeout,
)
def stop_listening(self, wait_for_stop=False):
if self.listener_handle:
self.listener_handle(wait_for_stop=wait_for_stop)
self.listener_handle = None
print("Stop listening...")
def is_listening(self):
return self.listener_handle is not None
def speech_waiting(self):
return self._audio is not None
def recognize(self):
if self._audio:
# Transcribe the audio data to text using Whisper
print("Recognizing...")
attempts = 0
while attempts < 3:
try:
result = self.recognizer.recognize_whisper_api(
self._audio, api_key=self.api_key
)
self._audio = None
return result.strip()
except sr.RequestError as e:
print(f"Error: {e}")
time.sleep(3)
attempts += 1
print("Retry attempt: ", attempts)
print("Failed to recognize")
return None
return None

View file

@ -66,7 +66,7 @@ class Robot:
def _init_display(self):
self.display = board.DISPLAY
self.display_group = displayio.Group()
self.display.show(self.display_group)
self.display.root_group = self.display_group
self.shape_color = 0
self.bg_color = 0xFFFF00
rect = vectorio.Rectangle(

View file

@ -0,0 +1,78 @@
# SPDX-FileCopyrightText: 2024 Liz Clark for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import os
import time
import wifi
import microcontroller
import board
import neopixel
import adafruit_connection_manager
import adafruit_requests
from adafruit_io.adafruit_io import IO_HTTP
from adafruit_ticks import ticks_ms, ticks_add, ticks_diff
timezone = "America/New_York"
color = 0xFF00FF
# The time of the thing!
EVENT_YEAR = 2024
EVENT_MONTH = 8
EVENT_DAY = 16
EVENT_HOUR = 0
EVENT_MINUTE = 0
# we'll make a python-friendly structure
event_time = time.struct_time((EVENT_YEAR, EVENT_MONTH, EVENT_DAY,
EVENT_HOUR, EVENT_MINUTE, 0, # we don't track seconds
-1, -1, False)) # we dont know day of week/year or DST
print("Connecting to WiFi...")
wifi.radio.connect(
os.getenv("CIRCUITPY_WIFI_SSID"), os.getenv("CIRCUITPY_WIFI_PASSWORD")
)
pool = adafruit_connection_manager.get_radio_socketpool(wifi.radio)
ssl_context = adafruit_connection_manager.get_radio_ssl_context(wifi.radio)
requests = adafruit_requests.Session(pool, ssl_context)
io = IO_HTTP(
os.getenv("AIO_USERNAME"), os.getenv("AIO_KEY"), requests
)
pixel_pin = board.SCL1
pixel_num = 16
pixels = neopixel.NeoPixel(pixel_pin, n = pixel_num, brightness=1, auto_write=True)
pixel_length = 0
last_length = -1
refresh_clock = ticks_ms()
refresh_timer = 3600 * 1000 # 1 hour
first_run = True
finished = False
while True:
if not finished:
if ticks_diff(ticks_ms(), refresh_clock) >= refresh_timer or first_run:
try:
print("Getting time from internet!")
now = time.struct_time(io.receive_time(timezone))
print(now)
total_seconds = time.mktime(now)
remaining = time.mktime(event_time) - total_seconds
if remaining < 0:
pixel_length = pixel_num + 1
finished = True
else:
if now.tm_mon == EVENT_MONTH:
pixel_length = now.tm_mday % (pixel_num + 1)
refresh_clock = ticks_add(refresh_clock, refresh_timer)
except Exception as e: # pylint: disable=broad-except
print("Some error occured, retrying via reset in 15 seconds! - ", e)
time.sleep(15)
microcontroller.reset()
if last_length != pixel_length:
if not pixel_length:
pixels.fill(0x000000)
else:
for i in range(pixel_length):
pixels[i] = color
last_length = pixel_length
first_run = False

View file

@ -0,0 +1,98 @@
# SPDX-FileCopyrightText: 2024 Liz Clark for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import time
import displayio
import picodvi
import board
import framebufferio
from adafruit_bitmap_font import bitmap_font
from adafruit_display_text import label
from adafruit_pcf8523.pcf8523 import PCF8523
from adafruit_ticks import ticks_ms, ticks_add, ticks_diff
EVENT_YEAR = 2024
EVENT_MONTH = 8
EVENT_DAY = 16
EVENT_HOUR = 0
EVENT_MINUTE = 0
# we'll make a python-friendly structure
event_time = time.struct_time((EVENT_YEAR, EVENT_MONTH, EVENT_DAY,
EVENT_HOUR, EVENT_MINUTE, 0, # we don't track seconds
-1, -1, False)) # we dont know day of week/year or DST
# check for DVI Feather with built-in display
if 'DISPLAY' in dir(board):
display = board.DISPLAY
# check for DVI feather without built-in display
elif 'CKP' in dir(board):
displayio.release_displays()
fb = picodvi.Framebuffer(320, 240,
clk_dp=board.CKP, clk_dn=board.CKN,
red_dp=board.D0P, red_dn=board.D0N,
green_dp=board.D1P, green_dn=board.D1N,
blue_dp=board.D2P, blue_dn=board.D2N,
color_depth=8)
display = framebufferio.FramebufferDisplay(fb)
# otherwise assume Pico
else:
displayio.release_displays()
fb = picodvi.Framebuffer(320, 240,
clk_dp=board.GP12, clk_dn=board.GP13,
red_dp=board.GP10, red_dn=board.GP11,
green_dp=board.GP8, green_dn=board.GP9,
blue_dp=board.GP6, blue_dn=board.GP7,
color_depth=8)
display = framebufferio.FramebufferDisplay(fb)
i2c = board.I2C()
rtc = PCF8523(i2c)
set_clock = False
if set_clock:
# year, mon, date, hour, min, sec, wday, yday, isdst
t = time.struct_time((2024, 8, 1, 16, 26, 00, 0, -1, -1))
print("Setting time to:", t)
rtc.datetime = t
print()
# variable to hold RTC datetime
t = rtc.datetime
pink = 0xf1078e
purple = 0x673192
aqua = 0x19beed
group = displayio.Group()
my_font = bitmap_font.load_font("/Helvetica-Bold-16.pcf")
clock_area = label.Label(my_font, text="00:00:00:00", color=pink)
clock_area.anchor_point = (0.0, 1.0)
clock_area.anchored_position = (display.width / 2 - clock_area.width / 2,
display.height - (clock_area.height + 20))
text1 = label.Label(my_font, text="Starting In:", color=aqua)
text1.anchor_point = (0.0, 0.0)
text1.anchored_position = (display.width / 2 - text1.width / 2,
display.height - (clock_area.height + text1.height + 35))
blinka_bitmap = displayio.OnDiskBitmap("/cpday_dvi.bmp")
blinka_grid = displayio.TileGrid(blinka_bitmap, pixel_shader=blinka_bitmap.pixel_shader)
group.append(blinka_grid)
group.append(text1)
group.append(clock_area)
display.root_group = group
clock_clock = ticks_ms()
clock_timer = 1000
while True:
if ticks_diff(ticks_ms(), clock_clock) >= clock_timer:
t = rtc.datetime
remaining = time.mktime(event_time) - time.mktime(t)
secs_remaining = remaining % 60
remaining //= 60
mins_remaining = remaining % 60
remaining //= 60
hours_remaining = remaining % 24
remaining //= 24
days_remaining = remaining
clock_area.text = (f"{days_remaining:0>2}:{hours_remaining:0>2}" +
f":{mins_remaining:0>2}:{secs_remaining:0>2}")
clock_clock = ticks_add(clock_clock, clock_timer)

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

View file

@ -0,0 +1,86 @@
# SPDX-FileCopyrightText: 2024 Liz Clark for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import os
import time
import wifi
import board
import displayio
import socketpool
import microcontroller
from adafruit_bitmap_font import bitmap_font
from adafruit_display_text import bitmap_label
import adafruit_ntp
from adafruit_ticks import ticks_ms, ticks_add, ticks_diff
timezone = -4
# The time of the thing!
EVENT_YEAR = 2024
EVENT_MONTH = 8
EVENT_DAY = 16
EVENT_HOUR = 0
EVENT_MINUTE = 0
# we'll make a python-friendly structure
event_time = time.struct_time((EVENT_YEAR, EVENT_MONTH, EVENT_DAY,
EVENT_HOUR, EVENT_MINUTE, 0, # we don't track seconds
-1, -1, False)) # we dont know day of week/year or DST
wifi.radio.connect(os.getenv("CIRCUITPY_WIFI_SSID"), os.getenv("CIRCUITPY_WIFI_PASSWORD"))
pool = socketpool.SocketPool(wifi.radio)
ntp = adafruit_ntp.NTP(pool, tz_offset=timezone, cache_seconds=3600)
display = board.DISPLAY
group = displayio.Group()
font = bitmap_font.load_font("/Helvetica-Bold-16.pcf")
blinka_bitmap = displayio.OnDiskBitmap("/cpday_tft.bmp")
blinka_grid = displayio.TileGrid(blinka_bitmap, pixel_shader=blinka_bitmap.pixel_shader)
scrolling_label = bitmap_label.Label(font, text=" ", y=display.height - 13)
group.append(blinka_grid)
group.append(scrolling_label)
display.root_group = group
display.auto_refresh = False
refresh_clock = ticks_ms()
refresh_timer = 3600 * 1000
clock_clock = ticks_ms()
clock_timer = 1000
scroll_clock = ticks_ms()
scroll_timer = 50
first_run = True
while True:
# only query the online time once per hour (and on first run)
if ticks_diff(ticks_ms(), refresh_clock) >= refresh_timer or first_run:
try:
print("Getting time from internet!")
now = ntp.datetime
print(now)
total_seconds = time.mktime(now)
first_run = False
refresh_clock = ticks_add(refresh_clock, refresh_timer)
except Exception as e: # pylint: disable=broad-except
print("Some error occured, retrying! -", e)
time.sleep(2)
microcontroller.reset()
if ticks_diff(ticks_ms(), clock_clock) >= clock_timer:
remaining = time.mktime(event_time) - total_seconds
secs_remaining = remaining % 60
remaining //= 60
mins_remaining = remaining % 60
remaining //= 60
hours_remaining = remaining % 24
remaining //= 24
days_remaining = remaining
scrolling_label.text = (f"{days_remaining} DAYS, {hours_remaining} HOURS," +
f"{mins_remaining} MINUTES & {secs_remaining} SECONDS")
total_seconds += 1
clock_clock = ticks_add(clock_clock, clock_timer)
if ticks_diff(ticks_ms(), scroll_clock) >= scroll_timer:
scrolling_label.x -= 1
if scrolling_label.x < -(scrolling_label.width + 5):
scrolling_label.x = display.width + 2
display.refresh()
scroll_clock = ticks_add(scroll_clock, scroll_timer)

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 416 KiB

View file

@ -0,0 +1,182 @@
# SPDX-FileCopyrightText: 2024 Liz Clark for Adafruit Industries
# SPDX-FileCopyrightText: 2024 Tyeth Gundry for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import os
import time
import wifi
import board
import displayio
import supervisor
import adafruit_connection_manager
import adafruit_requests
from adafruit_io.adafruit_io import IO_HTTP
from adafruit_bitmap_font import bitmap_font
from adafruit_display_text import bitmap_label
from adafruit_ticks import ticks_ms, ticks_add, ticks_diff
## See TZ Identifier column at https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
## If you want to set the timezone, you can do so with the following code, which
## attempts to get timezone from settings.toml or defaults to New York
timezone = os.getenv("ADAFRUIT_AIO_TIMEZONE", "America/New_York")
## Or instead rely on automatic timezone detection based on IP Address
# timezone = None
## The time of the thing!
EVENT_YEAR = 2024
EVENT_MONTH = 8
EVENT_DAY = 16
EVENT_HOUR = 0
EVENT_MINUTE = 0
## we'll make a python-friendly structure
event_time = time.struct_time(
(
EVENT_YEAR,
EVENT_MONTH,
EVENT_DAY,
EVENT_HOUR,
EVENT_MINUTE,
0, # we don't track seconds
-1, # we dont know day of week/year or DST
-1,
False,
)
)
print("Connecting to WiFi...")
wifi.radio.connect(
os.getenv("CIRCUITPY_WIFI_SSID"), os.getenv("CIRCUITPY_WIFI_PASSWORD")
)
## Initialize a requests session using the newer connection manager
## See https://adafruit-playground.com/u/justmobilize/pages/adafruit-connection-manager
pool = adafruit_connection_manager.get_radio_socketpool(wifi.radio)
ssl_context = adafruit_connection_manager.get_radio_ssl_context(wifi.radio)
requests = adafruit_requests.Session(pool, ssl_context)
## Create an instance of the Adafruit IO HTTP client
io = IO_HTTP(
os.getenv("ADAFRUIT_AIO_USERNAME"), os.getenv("ADAFRUIT_AIO_KEY"), requests
)
## Setup display and size appropriate assets
if board.board_id == "adafruit_qualia_s3_rgb666":
# Display Initialisation for 3.2" Bar display (320x820)
from qualia_bar_display_320x820 import setup_display
display = setup_display()
display.rotation = 90 # Rotate the display
BITMAP_FILE = "/circuitpython_day_2024_820x260_16bit.bmp"
FONT_FILE = "/font_free_mono_bold_48.pcf"
FONT_Y_OFFSET = 30
blinka_bitmap = displayio.OnDiskBitmap(BITMAP_FILE)
PIXEL_SHADER = displayio.ColorConverter(
input_colorspace=displayio.Colorspace.RGB565
)
else:
# Setup built-in display
display = board.DISPLAY
BITMAP_FILE = "/cpday_tft.bmp"
FONT_FILE = "/Helvetica-Bold-16.pcf"
FONT_Y_OFFSET = 13
PIXEL_SHADER = displayio.ColorConverter()
blinka_bitmap = displayio.OnDiskBitmap(BITMAP_FILE)
PIXEL_SHADER = blinka_bitmap.pixel_shader
group = displayio.Group()
font = bitmap_font.load_font(FONT_FILE)
blinka_grid = displayio.TileGrid(blinka_bitmap, pixel_shader=blinka_bitmap.pixel_shader)
scrolling_label = bitmap_label.Label(font, text=" ", y=display.height - FONT_Y_OFFSET)
group.append(blinka_grid)
group.append(scrolling_label)
display.root_group = group
display.auto_refresh = False
refresh_clock = ticks_ms()
refresh_timer = 3600 * 1000 # 1 hour
clock_clock = ticks_ms()
clock_timer = 1000
scroll_clock = ticks_ms()
scroll_timer = 50
first_run = True
finished = False
triggered = False
while True:
# only query the online time once per hour (and on first run)
if ticks_diff(ticks_ms(), refresh_clock) >= refresh_timer or first_run:
try:
print("Getting time from internet!")
now = time.struct_time(io.receive_time(timezone))
print(now)
total_seconds = time.mktime(now)
refresh_clock = ticks_add(refresh_clock, refresh_timer)
except Exception as e: # pylint: disable=broad-except
print("Some error occured, retrying via supervisor.reload in 5seconds! -", e)
time.sleep(5)
# Normally calling microcontroller.reset() would be the way to go, but due to
# a bug causing a reset into tinyUF2 bootloader mode we're instead going to
# disconnect wifi to ensure fresh connection + use supervisor.reload()
wifi.radio.enabled = False
supervisor.reload()
if ticks_diff(ticks_ms(), clock_clock) >= clock_timer:
remaining = time.mktime(event_time) - total_seconds
if remaining < 0:
# calculate time since event
remaining = abs(remaining)
secs_remaining = -(remaining % 60)
remaining //= 60
mins_remaining = -(remaining % 60)
remaining //= 60
hours_remaining = -(remaining % 24)
remaining //= 24
days_remaining = -remaining
finished = True
if not first_run and days_remaining == 0:
scrolling_label.text = (
"It's CircuitPython Day 2024! The snakiest day of the year!"
)
# Check for the moment of the event to trigger something (a NASA snake launch)
if not triggered and (
hours_remaining == 0
and mins_remaining == 0
and secs_remaining <= 1
# Change at/after xx:yy:01 seconds so we've already updated the display
):
# send a signal to an adafruit IO feed, where an Action is listening
print("Launch the snakes! (sending message to Adafruit IO)")
triggered = True
io.send_data("cpday-countdown", "Launch the snakes!")
else:
# calculate time until event
secs_remaining = remaining % 60
remaining //= 60
mins_remaining = remaining % 60
remaining //= 60
hours_remaining = remaining % 24
remaining //= 24
days_remaining = remaining
if not finished or (finished and days_remaining < 0):
# Add 1 to negative days_remaining to count from end of day instead of start
if days_remaining < 0:
days_remaining += 1
# Update the display with current countdown value
scrolling_label.text = (
f"{days_remaining} DAYS, {hours_remaining} HOURS,"
+ f"{mins_remaining} MINUTES & {secs_remaining} SECONDS"
)
total_seconds += 1
clock_clock = ticks_add(clock_clock, clock_timer)
if ticks_diff(ticks_ms(), scroll_clock) >= scroll_timer:
scrolling_label.x -= 1
if scrolling_label.x < -(scrolling_label.width + 5):
scrolling_label.x = display.width + 2
display.refresh()
scroll_clock = ticks_add(scroll_clock, scroll_timer)
first_run = False

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View file

@ -0,0 +1,101 @@
# SPDX-FileCopyrightText: 2024 Tyeth Gundry for Adafruit Industries
#
# SPDX-License-Identifier: MIT
# pylint: disable=import-outside-toplevel, line-too-long
def setup_display():
# Code taken from Adafruit Qualia ESP32-S3 for RGB-666 Displays Learn Guide - Rectangle Bar 3.2" display
# https://learn.adafruit.com/adafruit-qualia-esp32-s3-for-rgb666-displays/qualia-rgb666-with-tl032fwv01-3-2-320x820-bar-display
from displayio import release_displays
release_displays()
import busio
import board
import dotclockframebuffer
from framebufferio import FramebufferDisplay
init_sequence_tl032 = bytes((
b'\x11\x80d'
b'\xff\x05w\x01\x00\x00\x13'
b'\xef\x01\x08'
b'\xff\x05w\x01\x00\x00\x10'
b'\xc0\x02\xe5\x02'
b'\xc1\x02\x0c\n'
b'\xc2\x02\x07\x0f'
b'\xc3\x01\x02'
b'\xcc\x01\x10'
b'\xcd\x01\x08'
b'\xb0\x10\x00\x08Q\r\xce\x06\x00\x08\x08\x1d\x02\xd0\x0fo6?'
b'\xb1\x10\x00\x10O\x0c\x11\x05\x00\x07\x07\x1f\x05\xd3\x11n4?'
b'\xff\x05w\x01\x00\x00\x11'
b'\xb0\x01M'
b'\xb1\x01\x1c'
b'\xb2\x01\x87'
b'\xb3\x01\x80'
b'\xb5\x01G'
b'\xb7\x01\x85'
b'\xb8\x01!'
b'\xb9\x01\x10'
b'\xc1\x01x'
b'\xc2\x01x'
b'\xd0\x81\x88d'
b'\xe0\x03\x80\x00\x02'
b'\xe1\x0b\x04\xa0\x00\x00\x05\xa0\x00\x00\x00``'
b'\xe2\r00``<\xa0\x00\x00=\xa0\x00\x00\x00'
b'\xe3\x04\x00\x0033'
b'\xe4\x02DD'
b'\xe5\x10\x06>\xa0\xa0\x08@\xa0\xa0\nB\xa0\xa0\x0cD\xa0\xa0'
b'\xe6\x04\x00\x0033'
b'\xe7\x02DD'
b'\xe8\x10\x07?\xa0\xa0\tA\xa0\xa0\x0bC\xa0\xa0\rE\xa0\xa0'
b'\xeb\x07\x00\x01NN\xeeD\x00'
b"\xed\x10\xff\xff\x04Vr\xff\xff\xff\xff\xff\xff'e@\xff\xff"
b'\xef\x06\x10\r\x04\x08?\x1f'
b'\xff\x05w\x01\x00\x00\x13'
b'\xe8\x02\x00\x0e'
b'\xff\x05w\x01\x00\x00\x00'
b'\x11\x80x'
b'\xff\x05w\x01\x00\x00\x13'
b'\xe8\x82\x00\x0c\n'
b'\xe8\x02\x00\x00'
b'\xff\x05w\x01\x00\x00\x00'
b'6\x01\x00'
b':\x01f'
b'\x11\x80x'
b')\x80x'
))
board.I2C().deinit()
i2c = busio.I2C(board.SCL, board.SDA, frequency=400_000)
tft_io_expander = dict(board.TFT_IO_EXPANDER)
#tft_io_expander['i2c_address'] = 0x38 # uncomment for rev B
dotclockframebuffer.ioexpander_send_init_sequence(i2c, init_sequence_tl032, **tft_io_expander)
i2c.deinit()
tft_pins = dict(board.TFT_PINS)
tft_timings = {
"frequency": 16000000,
"width": 320,
"height": 820,
"hsync_pulse_width": 3,
"hsync_back_porch": 251,
"hsync_front_porch": 150,
"hsync_idle_low": False,
"vsync_pulse_width": 6,
"vsync_back_porch": 90,
"vsync_front_porch": 100,
"vsync_idle_low": False,
"pclk_active_high": False,
"pclk_idle_high": False,
"de_idle_high": False,
}
#bitmap = displayio.OnDiskBitmap("/display-ruler-720p.bmp")
fb = dotclockframebuffer.DotClockFramebuffer(**tft_pins, **tft_timings)
display = FramebufferDisplay(fb, auto_refresh=False)
return display

View file

@ -21,7 +21,7 @@ bg_palette[0] = 0xDDDD00
# Make the display context
main_group = displayio.Group()
display.show(main_group)
display.root_group = main_group
font = bitmap_font.load_font("fonts/LeagueSpartan-Bold-16.bdf")
reg_label = label.Label(

View file

@ -15,7 +15,7 @@ display = board.DISPLAY
# Make the display context
main_group = displayio.Group()
display.show(main_group)
display.root_group = main_group
font = terminalio.FONT

View file

@ -0,0 +1,139 @@
# SPDX-FileCopyrightText: 2024 Jeff Epler for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import array
import ulab
import rp2pio
import board
import adafruit_pioasm
import picodvi
import displayio
# The connections from the Xerox 820
vdata = board.D9 # Followed by hsync on D10 & vsync on D11
# The nominal frequency of the Xerox 820 video circuitry. Can modify by steps
# of approximately ±42000 to improve display stability
pixel_frequency = 10_694_250
# The PIO peripheral is run at a multiple of the pixel frequency. This must be less
# than the CPU speed, normally 120MHz.
clocks_per_pixel = 10
# The "fine pixel offset", shifts the sample time by this many sub-pixels
fine_pixel = 0
# A pin that shows when the Pico samples the pixel value. With an oscilloscope, this can
# be used to help fine tune the pixel_frequency & fine_pixel numbers. Ideally, the rising
# edge of pixel_sync_out is exactly in the middle of time a pixel is high/low.
pixel_sync_out = board.D5
# Details of the Xerox display timing. You may need to modify `blanking_lines` and
# `blanking_pixels` to adjust the vertical and horizontal position of the screen content.
# Normally you wouldn't change `active_lines` or `active_pixels`.
active_lines = 240
blanking_lines = 18
active_pixels = 640
blanking_pixels = 58
total_lines = active_lines + blanking_lines
# Pins for the DVI connector
dvi_pins = dict(
clk_dp=board.CKP,
clk_dn=board.CKN,
red_dp=board.D0P,
red_dn=board.D0N,
green_dp=board.D1P,
green_dn=board.D1N,
blue_dp=board.D2P,
blue_dn=board.D2N,
)
# Set up the display. Try 640x240 first (this mode is likely to be added in CircuitPython
# 9.1.x) then 640x480, which works in CircuitPython 9.0.x.
try:
displayio.release_displays()
dvi = picodvi.Framebuffer(640, 240, **dvi_pins, color_depth=1)
except ValueError:
print(
"Note: This version of CircuitPython does not support 640x240\n."
"Display will be compressed vertically."
)
displayio.release_displays()
dvi = picodvi.Framebuffer(640, 480, **dvi_pins, color_depth=1)
# Clear the display
ulab.numpy.frombuffer(dvi, dtype=ulab.numpy.uint8)[:] = 0
# Create the "control stream". The details are discussed in the Learn article
def control_gen():
yield total_lines - 2
for _ in range(blanking_lines):
yield from (1, 0) # 0 active pixels is special-cased
for _ in range(active_lines):
yield from (blanking_pixels - 1, active_pixels - 1)
control = array.array("L", control_gen())
# These little programs are run on the RP2040's PIO co-processor, and handle the pixel
# data and sync pulses.
jmp_0 = adafruit_pioasm.Program("jmp 0")
program = adafruit_pioasm.Program(
f"""
.side_set 1
.wrap_target
out y, 32 ; get total line count
wait 0, pin 2
wait 1, pin 2 ; wait for vsync
wait_line_inactive:
out x, 32 ; get total line count
wait 0, pin 1
wait 1, pin 1; wait for hsync
wait_line_active:
nop [{clocks_per_pixel-2}]
jmp x--, wait_line_active ; count off non-active pixels
out x, 32 [{fine_pixel}] ; get line active pixels & perform fine pixel adjust
jmp !x, wait_line_inactive ; no pixels this line, wait next hsync
capture_active_pixels:
in pins, 1 side 1
jmp x--, capture_active_pixels [{clocks_per_pixel-2}] ; more pixels
jmp y--, wait_line_inactive ; more lines?
.wrap
"""
)
# Set up PIO to transfer pixels from Xerox
pio = rp2pio.StateMachine(
program.assembled,
frequency=pixel_frequency * clocks_per_pixel,
first_in_pin=vdata,
in_pin_count=3,
in_pin_pull_up=True,
first_sideset_pin=pixel_sync_out,
auto_pull=True,
pull_threshold=32,
auto_push=True,
push_threshold=32,
offset=0,
**program.pio_kwargs,
)
# Set up the DVI framebuffer memory as a capture target
words_per_row = 640 // 32
first_row = (dvi.height - 240) // 2 # adjust text to center if in 640x480 mode
buf = memoryview(dvi).cast("L")[
first_row * words_per_row : (first_row + active_lines) * words_per_row
]
assert len(buf) == 4800 # Check that the right amount will be transferred
b = array.array("L", [0])
# Repeatedly transfer pixels from Xerox into DVI framebuffer.
while True:
pio.run(jmp_0.assembled)
pio.clear_rxfifo()
pio.write_readinto(control, buf)

View file

@ -0,0 +1,38 @@
{
"input": {
"karel": {
"x": 0,
"y": 0,
"direction": "east"
},
"world": [
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 7, 7, 7, 7, 7, 7, 7],
[6, 6, 5, 7, 7, 7, 7, 7, 7, 7],
[5, 4, 5, 7, 7, 7, 7, 7, 7, 7]
]},
"goal": {
"karel": {
"x": 0,
"y": 2,
"direction": "west"
},
"world": [
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[4, 5, 5, 7, 7, 7, 7, 7, 7, 7],
[6, 6, 5, 7, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 7, 7, 7, 7, 7, 7, 7]
]}
}

View file

@ -0,0 +1,38 @@
{
"input": {
"karel": {
"x": 0,
"y": 0,
"direction": "east"
},
"world": [
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 7, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 7, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 7, 7, 7, 7],
[5, 4, 5, 6, 6, 6, 7, 7, 7, 7]
]},
"goal": {
"karel": {
"x": 5,
"y": 1,
"direction": "east"
},
"world": [
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 7, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 7, 7, 7, 7],
[5, 5, 5, 5, 4, 5, 7, 7, 7, 7],
[5, 5, 5, 6, 6, 6, 7, 7, 7, 7]
]}
}

View file

@ -0,0 +1,38 @@
{
"input": {
"karel": {
"x": 0,
"y": 0,
"direction": "east"
},
"world": [
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 7, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 7, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 7, 7, 7, 7],
[5, 4, 5, 6, 6, 6, 7, 7, 7, 7]
]},
"goal": {
"karel": {
"x": 5,
"y": 1,
"direction": "east"
},
"world": [
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 7, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 7, 7, 7, 7],
[5, 5, 5, 5, 4, 5, 7, 7, 7, 7],
[5, 5, 5, 6, 6, 6, 7, 7, 7, 7]
]}
}

View file

@ -0,0 +1,38 @@
{
"input": {
"karel": {
"x": 0,
"y": 9,
"direction": "east"
},
"world": [
[5, 5, 5, 5, 6, 6, 5, 5, 5, 5],
[5, 5, 5, 5, 6, 6, 5, 5, 4, 5],
[5, 5, 5, 5, 6, 6, 5, 5, 5, 5],
[5, 5, 5, 5, 6, 6, 5, 5, 5, 5],
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5],
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5],
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5],
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5],
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5],
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
]},
"goal": {
"karel": {
"x": 1,
"y": 1,
"direction": "west"
},
"world": [
[5, 5, 5, 5, 6, 6, 5, 5, 5, 5],
[5, 5, 5, 5, 6, 6, 5, 5, 5, 5],
[5, 5, 5, 5, 6, 6, 5, 5, 5, 5],
[5, 5, 5, 5, 6, 6, 5, 5, 5, 5],
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5],
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5],
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5],
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5],
[5, 4, 5, 5, 5, 5, 5, 5, 5, 5],
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
]}
}

View file

@ -0,0 +1,38 @@
{
"input": {
"karel": {
"x": 0,
"y": 1,
"direction": "east"
},
"world": [
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 5, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 5, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 5, 7, 7, 7, 7, 7],
[6, 5, 6, 6, 6, 7, 7, 7, 7, 7]
]},
"goal": {
"karel": {
"x": 2,
"y": 1,
"direction": "east"
},
"world": [
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 5, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 5, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 5, 7, 7, 7, 7, 7],
[6, 4, 6, 6, 6, 7, 7, 7, 7, 7]
]}
}

View file

@ -0,0 +1,41 @@
{
"input": {
"karel": {
"x": 0,
"y": 0,
"direction": "east"
},
"world": [
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 7, 7, 7, 7, 7, 7]
]},
"goal": {
"karel": {
"x": 2,
"y": 0,
"direction": "east"
},
"beeper_counts": {
"1, 0": 42
},
"world": [
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 7, 7, 7, 7, 7, 7],
[5, 4, 5, 5, 7, 7, 7, 7, 7, 7]
]}
}

View file

@ -0,0 +1,39 @@
{
"input": {
"karel": {
"x": 0,
"y": 0,
"direction": "east"
},
"world": [
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 7, 7, 7, 7, 7, 7]
]},
"goal": {
"karel": {
"x": 0,
"y": 0,
"direction": "east"
},
"world": [
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[4, 5, 5, 4, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 7, 7, 7, 7, 7, 7],
[4, 5, 5, 4, 7, 7, 7, 7, 7, 7]
]}
}

View file

@ -0,0 +1,44 @@
{
"input": {
"karel": {
"x": 0,
"y": 0,
"direction": "east"
},
"world": [
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 7, 7, 7, 7, 7, 7]
]},
"goal": {
"karel": {
"x": 0,
"y": 0,
"direction": "east"
},
"beeper_counts": {
"0, 0": 5,
"3, 0": 5,
"0, 3": 5,
"3, 3": 5
},
"world": [
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[4, 5, 5, 4, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 7, 7, 7, 7, 7, 7],
[4, 5, 5, 4, 7, 7, 7, 7, 7, 7]
]}
}

View file

@ -0,0 +1,38 @@
{
"input": {
"karel": {
"x": 0,
"y": 0,
"direction": "east"
},
"world": [
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 7, 7, 7]
]},
"goal": {
"karel": {
"x": 6,
"y": 0,
"direction": "east"
},
"world": [
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 7, 7, 7]
]}
}

View file

@ -0,0 +1,38 @@
{
"input": {
"karel": {
"x": 0,
"y": 0,
"direction": "east"
},
"world": [
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 7, 7, 7]
]},
"goal": {
"karel": {
"x": 6,
"y": 0,
"direction": "east"
},
"world": [
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 7, 7, 7],
[4, 4, 4, 4, 4, 4, 4, 7, 7, 7]
]}
}

View file

@ -0,0 +1,38 @@
{
"input": {
"karel": {
"x": 0,
"y": 0,
"direction": "east"
},
"world": [
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 5, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 5, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 5, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 5, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 5, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 5, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 5, 7, 7],
[5, 4, 5, 4, 4, 5, 4, 5, 7, 7]
]},
"goal": {
"karel": {
"x": 7,
"y": 0,
"direction": "east"
},
"world": [
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 5, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 5, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 5, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 5, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 5, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 5, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 5, 7, 7],
[4, 5, 4, 5, 5, 4, 5, 4, 7, 7]
]}
}

View file

@ -0,0 +1,41 @@
{
"input": {
"karel": {
"x": 0,
"y": 0,
"direction": "east"
},
"world": [
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 5, 5, 7],
[5, 5, 5, 5, 5, 4, 5, 5, 5, 7],
[5, 5, 4, 5, 5, 4, 5, 5, 5, 7],
[4, 5, 4, 5, 5, 4, 5, 5, 5, 7],
[4, 4, 4, 5, 4, 4, 5, 5, 5, 7],
[4, 4, 4, 4, 4, 4, 5, 4, 5, 7],
[4, 4, 4, 4, 4, 4, 5, 4, 5, 7]
]},
"goal": {
"karel": {
"x": 0,
"y": 0,
"direction": "east"
},
"beeper_counts": {
"8, 0": 25
},
"world": [
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 5, 5, 5, 5, 5, 7],
[5, 5, 5, 5, 5, 5, 5, 5, 5, 7],
[5, 5, 5, 5, 5, 5, 5, 5, 5, 7],
[5, 5, 5, 5, 5, 5, 5, 5, 5, 7],
[5, 5, 5, 5, 5, 5, 5, 5, 5, 7],
[5, 5, 5, 5, 5, 5, 5, 5, 5, 7],
[5, 5, 5, 5, 5, 5, 5, 5, 4, 7]
]}
}

View file

@ -0,0 +1,38 @@
{
"input": {
"karel": {
"x": 0,
"y": 0,
"direction": "east"
},
"world": [
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 5, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 5, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 5, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 5, 7, 7, 7, 7, 7]
]},
"goal": {
"karel": {
"x": 0,
"y": 2,
"direction": "east"
},
"world": [
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 5, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 5, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 5, 7, 7, 7, 7, 7],
[5, 5, 5, 5, 5, 7, 7, 7, 7, 7]
]}
}

View file

@ -0,0 +1,36 @@
# SPDX-FileCopyrightText: 2024 Tim Cocks for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
Karel the Robot code.py for devices with built-in display available
at board.DISPLAY.
"""
# pylint: disable=wildcard-import, unused-wildcard-import
from karel.circuitpythonkarel import *
# load a chapter. Edit the chapter filename to change chapters
# see available chapter files in chapters/ directory.
chapter_data = load_state_file("chapters/karel_ch01.json")
def main():
"""
Karel main() function declaration.
Put your code for Karel into this function.
"""
## START OF MAIN FUNCTION, YOUR CODE GOES BELOW HERE ##
## END OF MAIN FUNCTION, YOUR CODE GOES ABOVE HERE ##
print(f"Goal state reached? {world.check_goal_state(chapter_data)}")
# call the main() function
main()
# Run forever so that the ending state of Karel and the world
# remains visible on the display.
while True:
pass

View file

@ -0,0 +1,3 @@
# SPDX-FileCopyrightText: 2024 Tim Cocks for Adafruit Industries
#
# SPDX-License-Identifier: MIT

View file

@ -0,0 +1,453 @@
# SPDX-FileCopyrightText: 2024 Tim Cocks for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
Karel the Robot helper class
"""
import json
import time
import terminalio
from adafruit_display_text.bitmap_label import Label
import adafruit_imageload
import board
from displayio import Group, Bitmap, Palette, TileGrid
EAST = 0
NORTH = 1
WEST = 2
SOUTH = 3
DIRECTION_WORDS = ["east", "north", "west", "south"]
DELAY = 0.2
TILE_SIZE = 24
COLOR_NAMES = [
"white",
"black",
"red",
"orange",
"yellow",
"green",
"blue",
"purple",
"pink",
"light_gray",
"gray",
"brown",
"dark_green",
"turquoise",
"dark_blue",
"dark_red",
]
COLOR_VALUES = [
0xFFFFFF,
0x000000,
0xFF0000,
0xFFA500,
0xFFEE00,
0x00C000,
0x0000FF,
0x8040C0,
0xFF40C0,
0xAAAAAA,
0x444444,
0xCA801D,
0x008700,
0x00C0C0,
0x0000AA,
0x800000,
]
class FrontIsBlocked(Exception):
pass
class BeeperNotPresent(Exception):
pass
class NoBeepersInBag(Exception):
pass
class Karel:
def __init__(self, spritesheet_bmp, spritesheet_palette):
self.tilegrid = TileGrid(
spritesheet_bmp,
pixel_shader=spritesheet_palette,
default_tile=0,
tile_width=TILE_SIZE,
tile_height=TILE_SIZE,
)
self._direction = EAST
self.beeper_count = 0
@property
def direction(self):
return self._direction
@direction.setter
def direction(self, new_value):
self._direction = new_value
if new_value == NORTH:
self.tilegrid[0, 0] = 1
if new_value == EAST:
self.tilegrid[0, 0] = 0
if new_value == SOUTH:
self.tilegrid[0, 0] = 3
if new_value == WEST:
self.tilegrid[0, 0] = 2
@property
def x(self):
return self.tilegrid.x // TILE_SIZE
@x.setter
def x(self, new_value):
self.tilegrid.x = new_value * TILE_SIZE
@property
def y(self):
return self.tilegrid.y // TILE_SIZE
@y.setter
def y(self, new_value):
self.tilegrid.y = new_value * TILE_SIZE
class World:
def __init__(self, display, world_width=10, world_height=10, beeper_limit=False):
self.world_width = world_width
self.world_height = world_height
color_count = len(COLOR_NAMES)
self.background_bmp = Bitmap(world_width, world_height, color_count)
self.background_palette = Palette(color_count)
for i, color_val in enumerate(COLOR_VALUES):
self.background_palette[i] = color_val
self.background_tilegrid = TileGrid(
bitmap=self.background_bmp, pixel_shader=self.background_palette
)
self.background_group = Group(scale=TILE_SIZE)
self.background_group.append(self.background_tilegrid)
self.display = display
self.world_group = Group()
self.world_group.append(self.background_group)
lib_dir = "karel"
self.spritesheet_bmp, self.spritesheet_palette = adafruit_imageload.load(
f"{lib_dir}/spritesheet.png"
)
self.spritesheet_palette.make_transparent(0)
self.world_tilegrid = TileGrid(
self.spritesheet_bmp,
pixel_shader=self.spritesheet_palette,
tile_width=TILE_SIZE,
tile_height=TILE_SIZE,
width=20,
height=15,
default_tile=7,
)
self.beeper_limit = beeper_limit
self.beeper_count_labels = None
self._init_beeper_counts()
self.world_group.append(self.world_tilegrid)
self.karel = Karel(self.spritesheet_bmp, self.spritesheet_palette)
self.world_group.append(self.karel.tilegrid)
display.root_group = self.world_group
time.sleep(DELAY)
def _init_beeper_counts(self):
if self.beeper_count_labels is not None:
for lbl in self.beeper_count_labels:
self.world_group.remove(lbl)
self.beeper_count_labels = {}
self.beeper_counts = []
for _ in range(self.world_height):
self.beeper_counts.append([0 for x in range(self.world_width)])
def load_state(self, state_obj):
self._init_beeper_counts()
if "beeper_counts" in state_obj["input"]:
for beeper_count_loc_str in state_obj["input"]["beeper_counts"].keys():
beeper_count_loc = [int(_) for _ in beeper_count_loc_str.split(",")]
self.beeper_counts[world.world_height - 1 - beeper_count_loc[1]][
beeper_count_loc[0]
] = state_obj["input"]["beeper_counts"][beeper_count_loc_str]
update_beeper_count_labels()
self.karel.x = state_obj["input"]["karel"]["x"]
self.karel.y = self.world_height - 1 - state_obj["input"]["karel"]["y"]
self.karel.direction = DIRECTION_WORDS.index(
state_obj["input"]["karel"]["direction"]
)
for y, row in enumerate(state_obj["input"]["world"]):
for x, cell in enumerate(row):
self.world_tilegrid[x, y] = cell
def check_goal_state(self, state_obj):
if (self.world_height - 1 - self.karel.y) != state_obj["goal"]["karel"]["y"]:
print("karel y incorrect")
return False
if self.karel.x != state_obj["goal"]["karel"]["x"]:
print("karel x incorrect")
return False
if self.karel.direction != DIRECTION_WORDS.index(
state_obj["goal"]["karel"]["direction"]
):
print("karel dir incorrect")
return False
if "beeper_counts" in state_obj["goal"]:
for beeper_count_loc_str in state_obj["goal"]["beeper_counts"].keys():
beeper_count_loc = [int(_) for _ in beeper_count_loc_str.split(",")]
if (
self.beeper_counts[world.world_height - 1 - beeper_count_loc[1]][
beeper_count_loc[0]
]
!= state_obj["goal"]["beeper_counts"][beeper_count_loc_str]
):
print(f"beeper count incorrect {beeper_count_loc}")
return False
for y in range(self.world_height):
for x in range(self.world_width):
goal_cell_index = state_obj["goal"]["world"][y][x]
if self.world_tilegrid[x, y] != goal_cell_index:
print(
f"world mismatch: {(x, world.world_height - 1 - y)}: "
+ f"{self.world_tilegrid[x, y]} != {goal_cell_index}"
)
return False
return True
world = World(board.DISPLAY)
def move():
if front_is_blocked():
raise FrontIsBlocked("Karel can't move there")
if world.karel.direction == EAST:
world.karel.x += 1
if world.karel.direction == WEST:
world.karel.x -= 1
if world.karel.direction == NORTH:
world.karel.y -= 1
if world.karel.direction == SOUTH:
world.karel.y += 1
time.sleep(DELAY)
def turn_left():
if world.karel.direction == EAST:
world.karel.direction = NORTH
elif world.karel.direction == NORTH:
world.karel.direction = WEST
elif world.karel.direction == WEST:
world.karel.direction = SOUTH
elif world.karel.direction == SOUTH:
world.karel.direction = EAST
time.sleep(DELAY)
def corner_is_blocked(corner_x, corner_y):
corner_loc = [corner_x, corner_y]
if corner_loc[0] < 0 or corner_loc[1] < 0:
return True
if corner_loc[0] >= world.world_width or corner_loc[1] >= world.world_height:
return True
tile_index = world.world_tilegrid[corner_loc[0], corner_loc[1]]
if tile_index in (6, 7):
return True
return False
def front_is_blocked():
front_loc = [world.karel.x, world.karel.y]
if world.karel.direction == EAST:
front_loc[0] += 1
if world.karel.direction == WEST:
front_loc[0] -= 1
if world.karel.direction == NORTH:
front_loc[1] -= 1
if world.karel.direction == SOUTH:
front_loc[1] += 1
return corner_is_blocked(front_loc[0], front_loc[1])
def right_is_blocked():
right_loc = [world.karel.x, world.karel.y]
if world.karel.direction == EAST:
right_loc[1] += 1
if world.karel.direction == WEST:
right_loc[1] -= 1
if world.karel.direction == NORTH:
right_loc[0] += 1
if world.karel.direction == SOUTH:
right_loc[0] -= 1
return corner_is_blocked(right_loc[0], right_loc[1])
def left_is_blocked():
left_loc = [world.karel.x, world.karel.y]
if world.karel.direction == EAST:
left_loc[1] -= 1
if world.karel.direction == WEST:
left_loc[1] += 1
if world.karel.direction == NORTH:
left_loc[0] -= 1
if world.karel.direction == SOUTH:
left_loc[0] += 1
return corner_is_blocked(left_loc[0], left_loc[1])
def paint_corner(color):
if color not in COLOR_NAMES:
raise ValueError(
f"Color {color} is not valid. Supported colors are {COLOR_NAMES}"
)
world.background_bmp[world.karel.x, world.karel.y] = COLOR_NAMES.index(color)
def update_beeper_count_labels():
for y, row in enumerate(world.beeper_counts):
for x, count in enumerate(row):
if count <= 1:
if (x, y) in world.beeper_count_labels:
world.world_group.remove(world.beeper_count_labels[(x, y)])
world.beeper_count_labels.pop((x, y))
else:
if (x, y) in world.beeper_count_labels:
world.beeper_count_labels[(x, y)].text = str(count)
else:
world.beeper_count_labels[(x, y)] = Label(
terminalio.FONT,
text=str(count),
color=0x000000,
anchor_point=(0.5, 0.5),
anchored_position=(
x * TILE_SIZE + TILE_SIZE // 2,
y * TILE_SIZE + TILE_SIZE // 2,
),
)
world.world_group.append(world.beeper_count_labels[(x, y)])
def pick_beeper():
if not beepers_present():
raise BeeperNotPresent("There is no beeper here")
world.karel.beeper_count += 1
world.beeper_counts[world.karel.y][world.karel.x] = max(
0, world.beeper_counts[world.karel.y][world.karel.x] - 1
)
update_beeper_count_labels()
if world.beeper_counts[world.karel.y][world.karel.x] == 0:
world.world_tilegrid[world.karel.x, world.karel.y] = 5
time.sleep(DELAY)
def beepers_in_bag():
return world.karel.beeper_count
def put_beeper():
if beepers_in_bag() == 0 and world.beeper_limit:
raise NoBeepersInBag("There are no beepers in Karel's bag")
world.karel.beeper_count -= 1
world.beeper_counts[world.karel.y][world.karel.x] += 1
update_beeper_count_labels()
world.world_tilegrid[world.karel.x, world.karel.y] = 4
time.sleep(DELAY)
def beepers_present():
tile_index = world.world_tilegrid[world.karel.x, world.karel.y]
if tile_index == 4:
return True
return False
def no_beepers_in_bag():
return world.karel.beeper_count == 0
def no_beepers_present():
return not beepers_present()
def facing_north():
return world.karel.direction == NORTH
def facing_east():
return world.karel.direction == EAST
def facing_west():
return world.karel.direction == WEST
def facing_south():
return world.karel.direction == SOUTH
def not_facing_north():
return not facing_north()
def not_facing_east():
return not facing_east()
def not_facing_west():
return not facing_west()
def not_facing_south():
return not facing_south()
def left_is_clear():
return not left_is_blocked()
def right_is_clear():
return not right_is_blocked()
def front_is_clear():
return not front_is_blocked()
def load_state_file(state_filepath):
with open(state_filepath, "r") as f:
ch_obj = json.load(f)
world.load_state(ch_obj)
time.sleep(DELAY)
return ch_obj

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

View file

@ -0,0 +1,41 @@
# SPDX-FileCopyrightText: 2024 Jeff Epler for Adafruit Industries
#
# SPDX-License-Identifier: MIT
# Stream MP3 audio to I2S decoder
#
# Tested with:
#
# * Adafruit Metro ESP32-S3
# * Adafruit Metro ESP32-S2
# * Adafruit Feather ESP32 V2
import time
import adafruit_connection_manager
import adafruit_requests
import audiobusio
import audiomp3
import board
import wifi
mp3_buffer = bytearray(16384)
mp3_decoder = audiomp3.MP3Decoder("/silence.mp3", mp3_buffer)
pool = adafruit_connection_manager.get_radio_socketpool(wifi.radio)
ssl_context = adafruit_connection_manager.get_radio_ssl_context(wifi.radio)
requests = adafruit_requests.Session(pool, ssl_context)
STREAMING_URL = "https://ice2.somafm.com/dronezone-128-mp3"
if "D27" in dir(board):
# Feather ESP32 V2 has D27 instead of D11
i2s = audiobusio.I2SOut(bit_clock=board.D12, word_select=board.D13, data=board.D27)
else:
i2s = audiobusio.I2SOut(bit_clock=board.D12, word_select=board.D13, data=board.D11)
with requests.get(STREAMING_URL, headers={"connection": "close"}, stream=True) as response:
mp3_decoder.file = response.socket
i2s.play(mp3_decoder)
while i2s.playing:
time.sleep(0.1)

Binary file not shown.

View file

@ -211,7 +211,7 @@ class Paint(object):
self._palette = self._make_palette()
self._splash.append(self._palette)
self._display.show(self._splash)
self._display.root_group = self._splash
try:
gc.collect()
self._display.refresh(target_frames_per_second=60)

View file

@ -171,28 +171,15 @@ class Pyloton:
blinka_bitmap = "blinka-pyloton.bmp"
# Compatible with CircuitPython 6 & 7
with open(blinka_bitmap, 'rb') as bitmap_file:
bitmap1 = displayio.OnDiskBitmap(bitmap_file)
tile_grid = displayio.TileGrid(bitmap1, pixel_shader=getattr(bitmap1, 'pixel_shader', displayio.ColorConverter()))
self.loading_group.append(tile_grid)
self.display.show(self.loading_group)
status_heading = label.Label(font=self.arial16, x=80, y=175,
text="Status", color=self.YELLOW)
rect = Rect(0, 165, 240, 75, fill=self.PURPLE)
self.loading_group.append(rect)
self.loading_group.append(status_heading)
# # Compatible with CircuitPython 7+
# bitmap1 = displayio.OnDiskBitmap(blinka_bitmap)
# tile_grid = displayio.TileGrid(bitmap1, pixel_shader=bitmap1.pixel_shader)
# self.loading_group.append(tile_grid)
# self.display.show(self.loading_group)
# status_heading = label.Label(font=self.arial16, x=80, y=175,
# text="Status", color=self.YELLOW)
# rect = Rect(0, 165, 240, 75, fill=self.PURPLE)
# self.loading_group.append(rect)
# self.loading_group.append(status_heading)
bitmap1 = displayio.OnDiskBitmap(blinka_bitmap)
tile_grid = displayio.TileGrid(bitmap1, pixel_shader=bitmap1.pixel_shader)
self.loading_group.append(tile_grid)
self.display.root_group = self.loading_group
status_heading = label.Label(font=self.arial16, x=80, y=175,
text="Status", color=self.YELLOW)
rect = Rect(0, 165, 240, 75, fill=self.PURPLE)
self.loading_group.append(rect)
self.loading_group.append(status_heading)
def _load_fonts(self):
"""
@ -495,7 +482,7 @@ class Pyloton:
self.splash.append(sprites)
self.display.show(self.splash)
self.display.root_group = self.splash
while self.loading_group:
self.loading_group.pop()

View file

@ -1,9 +1,9 @@
adafruit_bitmap_font
adafruit_ble
adafruit_ble_apple_media
adafruit_ble_cycling_speed_and_cadence
adafruit_ble_heart_rate
adafruit_button
adafruit_display_shapes
adafruit_display_text
adafruit_imageload
adafruit-circuitpython-bitmap-font
adafruit-circuitpython-ble
adafruit-circuitpython-ble-apple-media
adafruit-circuitpython-ble-cycling-speed-and-cadence
adafruit-circuitpython-ble-heart-rate
adafruit-circuitpython-display-button
adafruit-circuitpython-display-shapes
adafruit-circuitpython-display-text
adafruit-circuitpython-imageload

View file

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

View file

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

102
CircuitPython_Sound_Box_2/code.py Executable file
View file

@ -0,0 +1,102 @@
# SPDX-FileCopyrightText: 2024 Liz Clark for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import time
import os
import random
import board
from digitalio import DigitalInOut, Direction
import neopixel
import audiocore
import audiobusio
import keypad
import adafruit_lis3dh
from rainbowio import colorwheel
# enable external power pin
# provides power to the external components
external_power = DigitalInOut(board.EXTERNAL_POWER)
external_power.direction = Direction.OUTPUT
external_power.value = True
# external neopixels
num_pixels = 24
pixels = neopixel.NeoPixel(board.EXTERNAL_NEOPIXELS, num_pixels, brightness=0.4, auto_write=True)
delta_hue = 256//num_pixels
speed = 10 # higher numbers = faster rainbow spinning
i=0
# external button
switch = keypad.Keys((board.EXTERNAL_BUTTON,), value_when_pressed=False, pull=True)
colors = [
{'label': "BLUE", 'color': 0x0000FF},
{'label': "RED", 'color': 0xFF0000},
{'label': "GREEN", 'color': 0x00FF00},
{'label': "YELLOW", 'color': 0xFFFF00},
{'label': "AQUA", 'color': 0x00FFFF},
{'label': "PURPLE", 'color': 0xFF00FF},
{'label': "PINK", 'color': 0xFF0055},
{'label': "ORANGE", 'color': 0xFF5500},
{'label': "WHITE", 'color': 0x555555},
]
shake_wavs = []
color_wavs = []
for filename in os.listdir('/wavs'):
if filename.lower().endswith('.wav') and not filename.startswith('.'):
if "SHAKE" in filename:
shake_wavs.append("/wavs/" + filename)
else:
for color in colors:
if color['label'] in filename:
color_wavs.append("/wavs/" + filename)
break
audio = audiobusio.I2SOut(board.I2S_BIT_CLOCK, board.I2S_WORD_SELECT, board.I2S_DATA)
num_colors = len(color_wavs)
num_shakes = len(shake_wavs)
wav_index = 0
def open_audio(num, wavs):
n = wavs[num]
f = open(n, "rb")
w = audiocore.WaveFile(f)
# wn = wav_names[num]
return w, n
wave, wave_name = open_audio(wav_index, color_wavs)
i2c = board.I2C()
int1 = DigitalInOut(board.ACCELEROMETER_INTERRUPT)
lis3dh = adafruit_lis3dh.LIS3DH_I2C(i2c, int1=int1)
lis3dh.range = adafruit_lis3dh.RANGE_2_G
while True:
event = switch.events.get()
if event:
if event.pressed:
wave, wave_name = open_audio(random.randint(0, num_colors - 1), color_wavs)
audio.play(wave)
for color in colors:
if color['label'] in wave_name:
pixels.fill(color['color'])
else:
pass
time.sleep(1)
pixels.fill((0, 0, 0))
print('pressed')
if event.released:
pass
if lis3dh.shake(shake_threshold=12):
wave, wave_name = open_audio(random.randint(0, num_shakes - 1), shake_wavs)
audio.play(wave)
for l in range(len(pixels)):
pixels[l] = colorwheel( int(i*speed + l * delta_hue) % 255 )
pixels.show()
time.sleep(1)
pixels.fill((0, 0, 0))
print('shake')

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -48,8 +48,8 @@ pool = socketpool.SocketPool(wifi.radio)
# Initialize a new MQTT Client object
mqtt_client = MQTT.MQTT(
broker="io.adafruit.com",
username=os.getenv("ADAFRUIT_IO_USERNAME"),
password=os.getenv("ADAFRUIT_IO_KEY"),
username=os.getenv("ADAFRUIT_AIO_USERNAME"),
password=os.getenv("ADAFRUIT_AIO_KEY"),
socket_pool=pool,
ssl_context=ssl.create_default_context(),
)

View file

@ -0,0 +1,41 @@
# SPDX-FileCopyrightText: 2022 Kattni Rembor for Adafruit Industries
# SPDX-License-Identifier: MIT
"""
CircuitPython Multiple Button Digital Input Example - Handling multiple buttons with simple logic.
"""
import time
import board
import digitalio
# LED setup
led = digitalio.DigitalInOut(board.LED)
led.direction = digitalio.Direction.OUTPUT
# Button setup
button0 = digitalio.DigitalInOut(board.D0)
button0.switch_to_input(pull=digitalio.Pull.UP)
button1 = digitalio.DigitalInOut(board.D1)
button1.switch_to_input(pull=digitalio.Pull.DOWN)
button2 = digitalio.DigitalInOut(board.D2)
button2.switch_to_input(pull=digitalio.Pull.DOWN)
while True:
# Check Button D0
if not button0.value: # button0 is active (Pull.UP, active LOW)
print("Button D0 pressed")
led.value = True
# Check Button D1
elif button1.value: # button1 is active (Pull.DOWN, active HIGH)
print("Button D1 pressed")
led.value = True
# Check Button D2
elif button2.value: # button2 is active (Pull.DOWN, active HIGH)
print("Button D2 pressed")
led.value = True
else:
led.value = False # No buttons are pressed, turn off the LED
# Small delay to debounce buttons and reduce serial output spam
time.sleep(0.1)

View file

@ -5,6 +5,8 @@
import time
import os
# pylint: disable=unused-import
import mount_sd # You must create a module mount_sd.py that mounts your sd card!
# First, just write the file 'hello.txt' to the card
with open("/sd/hello.txt", "w") as f:

View file

@ -4,6 +4,9 @@
import os
# pylint: disable=unused-import
import mount_sd # You must create a module mount_sd.py that mounts your sd card!
def print_directory(path, tabs=0):
for file in os.listdir(path):
stats = os.stat(path + "/" + file)

Some files were not shown because too many files have changed in this diff Show more