Merge branch 'refs/heads/main' into displayio_api_9x
10
.github/workflows/arduino_cron.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
31
A4988_Examples/Arduino_A4988/Arduino_A4988.ino
Normal 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
|
||||
}
|
||||
32
A4988_Examples/CircuitPython_A4988/code.py
Normal 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)
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
26
ADG72x_Examples/CircuitPython_ADG728/code.py
Normal 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)
|
||||
31
ADG72x_Examples/CircuitPython_ADG729/code.py
Normal 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)
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
50
Arduino_VCNL4200_simpletest/Arduino_VCNL4200_simpletest.ino
Normal 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);
|
||||
}
|
||||
113
BLE_Apple_Watch_Controller/code.py
Normal 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)
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
66
BLE_RS232_Controller/code.py
Normal 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()
|
||||
|
|
@ -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);
|
||||
}
|
||||
30
BNO055_BMP280_BFF_Examples/CircuitPython/code.py
Normal 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)
|
||||
BIN
CH32V203_eInk_Calendar_Clock/CH32V203-eInk-Calendar-Clock.bin
Normal file
100
CH32V203_eInk_Calendar_Clock/CH32V203_eInk_Calendar_Clock.ino
Normal 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);
|
||||
}
|
||||
75
CH9328_CPython_Demo/code.py
Normal 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()
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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)
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
BIN
Cartoon_Character_Clock/clock_center.bmp
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
Cartoon_Character_Clock/clock_long_hand.bmp
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
Cartoon_Character_Clock/clock_short_hand.bmp
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
133
Cartoon_Character_Clock/code.py
Normal 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)
|
||||
BIN
Cartoon_Character_Clock/song_1.mp3
Normal file
BIN
Cartoon_Character_Clock/song_2.mp3
Normal 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
|
|
@ -0,0 +1,6 @@
|
|||
[openai]
|
||||
OPENAI_API_KEY = sk-...
|
||||
|
||||
[azure]
|
||||
SPEECH_KEY = 4f1d...02a9
|
||||
SPEECH_REGION = eastus
|
||||
84
ChatGPT_Bear/listener.py
Normal 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
|
||||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
After Width: | Height: | Size: 75 KiB |
|
|
@ -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)
|
||||
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 416 KiB |
|
|
@ -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
|
||||
|
After Width: | Height: | Size: 32 KiB |
|
|
@ -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
|
||||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
139
CircuitPython_Feather_DVI_Xerox_820/code.py
Normal 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)
|
||||
38
CircuitPython_Karel_The_Robot/chapters/karel_ch01.json
Normal 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]
|
||||
]}
|
||||
}
|
||||
38
CircuitPython_Karel_The_Robot/chapters/karel_ch02.json
Normal 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]
|
||||
]}
|
||||
}
|
||||
38
CircuitPython_Karel_The_Robot/chapters/karel_ch03a.json
Normal 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]
|
||||
]}
|
||||
}
|
||||
38
CircuitPython_Karel_The_Robot/chapters/karel_ch03b.json
Normal 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]
|
||||
]}
|
||||
}
|
||||
38
CircuitPython_Karel_The_Robot/chapters/karel_ch04.json
Normal 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]
|
||||
]}
|
||||
}
|
||||
41
CircuitPython_Karel_The_Robot/chapters/karel_ch05a.json
Normal 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]
|
||||
]}
|
||||
}
|
||||
39
CircuitPython_Karel_The_Robot/chapters/karel_ch05b.json
Normal 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]
|
||||
]}
|
||||
}
|
||||
44
CircuitPython_Karel_The_Robot/chapters/karel_ch05c.json
Normal 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]
|
||||
]}
|
||||
}
|
||||
38
CircuitPython_Karel_The_Robot/chapters/karel_ch06a.json
Normal 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]
|
||||
]}
|
||||
}
|
||||
38
CircuitPython_Karel_The_Robot/chapters/karel_ch06b.json
Normal 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]
|
||||
]}
|
||||
}
|
||||
38
CircuitPython_Karel_The_Robot/chapters/karel_ch07.json
Normal 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]
|
||||
]}
|
||||
}
|
||||
41
CircuitPython_Karel_The_Robot/chapters/karel_ch08.json
Normal 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]
|
||||
]}
|
||||
}
|
||||
|
|
@ -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]
|
||||
]}
|
||||
}
|
||||
36
CircuitPython_Karel_The_Robot/code.py
Normal 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
|
||||
3
CircuitPython_Karel_The_Robot/karel/__init__.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# SPDX-FileCopyrightText: 2024 Tim Cocks for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
453
CircuitPython_Karel_The_Robot/karel/circuitpythonkarel.py
Normal 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
|
||||
BIN
CircuitPython_Karel_The_Robot/karel/spritesheet.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
41
CircuitPython_MP3StreamPlayer/code.py
Normal 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)
|
||||
BIN
CircuitPython_MP3StreamPlayer/silence.mp3
Normal 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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.4 KiB |
102
CircuitPython_Sound_Box_2/code.py
Executable 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')
|
||||
BIN
CircuitPython_Sound_Box_2/wavs/AQUA color.wav
Executable file
BIN
CircuitPython_Sound_Box_2/wavs/BLUE color.wav
Executable file
BIN
CircuitPython_Sound_Box_2/wavs/GREEN color.wav
Executable file
BIN
CircuitPython_Sound_Box_2/wavs/ORANGE color.wav
Executable file
BIN
CircuitPython_Sound_Box_2/wavs/PINK color.wav
Executable file
BIN
CircuitPython_Sound_Box_2/wavs/PURPLE color.wav
Executable file
BIN
CircuitPython_Sound_Box_2/wavs/RED color.wav
Executable file
BIN
CircuitPython_Sound_Box_2/wavs/SHAKE adabot.wav
Executable file
BIN
CircuitPython_Sound_Box_2/wavs/SHAKE excellent.wav
Executable file
BIN
CircuitPython_Sound_Box_2/wavs/SHAKE what.wav
Executable file
BIN
CircuitPython_Sound_Box_2/wavs/WHITE color.wav
Executable file
BIN
CircuitPython_Sound_Box_2/wavs/YELLOW color.wav
Executable 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(),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||