Compare commits

..

1 commit

Author SHA1 Message Date
Jeff Epler
59f972dc84
Merge pull request #2896 from jepler/mp3-stream-circuitpython
initial commit of circuitpython audio fx polyphonic
2024-10-04 08:24:45 -05:00
1579 changed files with 7997 additions and 333136 deletions

View file

@ -61,7 +61,7 @@ jobs:
strategy:
fail-fast: false
matrix:
arduino-platform: ["cpb", "cpc", "cpx_ada", "esp32", "esp8266", "feather32u4", "feather_esp32c6", "feather_m0_express", "feather_m4_express", "feather_rp2040", "feather_rp2040_adalogger", "feather_rp2350", "flora", "fruit_jam_tinyusb", "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", "metro_rp2350", "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"]
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", "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
@ -97,8 +97,6 @@ jobs:
git clone --quiet https://github.com/adafruit/HID /home/runner/Arduino/libraries/HID_Project
rm -rf /home/runner/Arduino/libraries/ArduinoHttpClient
git clone --quiet https://github.com/arduino-libraries/ArduinoHttpClient.git /home/runner/Arduino/libraries/ArduinoHttpClient
git clone --quiet https://github.com/pschatzmann/ESP32-A2DP /home/runner/Arduino/libraries/ESP32-A2DP
git clone --quiet https://github.com/pschatzmann/arduino-audio-tools /home/runner/Arduino/libraries/arduino-audio-tools
- name: test platforms
run: python3 ci/build_platform.py ${{ matrix.arduino-platform }}

View file

@ -8,7 +8,6 @@ import digitalio
from rainbowio import colorwheel
import keypad
import displayio
import i2cdisplaybus
import busio
import adafruit_seesaw.seesaw
import adafruit_seesaw.neopixel
@ -51,7 +50,7 @@ oled_reset = board.D13
i2c = board.STEMMA_I2C()
# STEMMA OLED setup
display_bus = i2cdisplaybus.I2CDisplayBus(i2c, device_address=0x3D, reset=oled_reset)
display_bus = displayio.I2CDisplay(i2c, device_address=0x3D, reset=oled_reset)
display = adafruit_displayio_ssd1306.SSD1306(display_bus, width=128, height=64)
splash = displayio.Group()

View file

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

View file

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

View file

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

View file

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

View file

@ -11,7 +11,6 @@ import adafruit_ahtx0
# OLED
import displayio
import i2cdisplaybus
import terminalio
from adafruit_display_text import label
import adafruit_displayio_ssd1306
@ -26,7 +25,7 @@ aht20 = adafruit_ahtx0.AHTx0(i2c)
#OLED
display_bus = i2cdisplaybus.I2CDisplayBus(i2c, device_address=0x3C)
display_bus = displayio.I2CDisplay(i2c, device_address=0x3C)
display = adafruit_displayio_ssd1306.SSD1306(display_bus, width=128, height=32)
# Make the display context

View file

@ -1,43 +0,0 @@
# SPDX-FileCopyrightText: Copyright (c) 2025 Liz Clark for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""AS5600 Encoder"""
import usb_hid
import board
from adafruit_hid.consumer_control import ConsumerControl
from adafruit_hid.consumer_control_code import ConsumerControlCode
import adafruit_as5600
i2c = board.STEMMA_I2C()
sensor = adafruit_as5600.AS5600(i2c)
enc_inc = ConsumerControlCode.VOLUME_INCREMENT
enc_dec = ConsumerControlCode.VOLUME_DECREMENT
cc = ConsumerControl(usb_hid.devices)
last_val = sensor.angle
THRESHOLD = sensor.max_angle // 2 # default max_angle is 4095
# you can change the max_angle. ex: sensor.max_angle = 1000
MIN_CHANGE = 25 # minimum change to register as movement
# increase to make less sensitive, decrease to make more sensitive
while True:
enc_val = sensor.angle
if abs(enc_val - last_val) >= MIN_CHANGE or abs(enc_val - last_val) > THRESHOLD:
# Calculate the difference
diff = enc_val - last_val
# Check for wraparound
if diff > THRESHOLD:
# Wrapped from ~4095 to ~0 (actually turning backwards)
cc.send(enc_dec)
elif diff < -THRESHOLD:
# Wrapped from ~0 to ~4095 (actually turning forwards)
cc.send(enc_inc)
elif diff > 0:
# Normal forward rotation
cc.send(enc_inc)
else:
# Normal backward rotation (diff < 0)
cc.send(enc_dec)
last_val = enc_val

View file

@ -1,40 +0,0 @@
// SPDX-FileCopyrightText: 2025 Carter Nelson for Adafruit Industries
//
// SPDX-License-Identifier: MIT
// Basic ON/OFF control of DC motor via DRV8833
#define AIN1 5
#define AIN2 6
#define SLP 7
void setup() {
Serial.begin(115200);
Serial.println("Adafruit DRV8833 DC Motor Example - ON/OFF");
// configure pins
pinMode(AIN1, OUTPUT);
pinMode(AIN2, OUTPUT);
pinMode(SLP, OUTPUT);
// enable DRV8833
digitalWrite(SLP, HIGH);
}
void loop() {
//
// FORWARD
//
Serial.println("Forward");
digitalWrite(AIN1, HIGH);
digitalWrite(AIN2, LOW);
delay(1000);
//
// REVERSE
//
Serial.println("Reverse");
digitalWrite(AIN1, LOW);
digitalWrite(AIN2, HIGH);
delay(1000);
}

View file

@ -1,60 +0,0 @@
// SPDX-FileCopyrightText: 2025 Carter Nelson for Adafruit Industries
//
// SPDX-License-Identifier: MIT
// PWM speed control of DC motor via DRV8833
#define AIN1 5
#define AIN2 6
#define SLP 7
void setup() {
Serial.begin(115200);
Serial.println("Adafruit DRV8833 DC Motor Example - PWM");
// configure pins
pinMode(AIN1, OUTPUT);
pinMode(AIN2, OUTPUT);
pinMode(SLP, OUTPUT);
// enable DRV8833
digitalWrite(SLP, HIGH);
}
void loop() {
//
// FORWARD
//
Serial.println("Forward");
digitalWrite(AIN2, LOW);
// ramp speed up
Serial.println(" ramping up");
for (int duty_cycle=0; duty_cycle<256; duty_cycle++) {
analogWrite(AIN1, duty_cycle);
delay(10);
}
// ramp speed down
Serial.println(" ramping down");
for (int duty_cycle=255; duty_cycle>=0; duty_cycle--) {
analogWrite(AIN1, duty_cycle);
delay(10);
}
//
// REVERSE
//
Serial.println("Reverse");
digitalWrite(AIN1, LOW);
// ramp speed up
Serial.println(" ramping up");
for (int duty_cycle=0; duty_cycle<256; duty_cycle++) {
analogWrite(AIN2, duty_cycle);
delay(10);
}
// ramp speed down
Serial.println(" ramping down");
for (int duty_cycle=255; duty_cycle>=0; duty_cycle--) {
analogWrite(AIN2, duty_cycle);
delay(10);
}
}

View file

@ -1,30 +0,0 @@
// SPDX-FileCopyrightText: 2025 lady ada for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#include <Stepper.h>
// change this to the number of steps on your motor
#define STEPS 200
// create an instance of the stepper class, specifying
// the number of steps of the motor and the pins it's
// attached to
Stepper stepper(STEPS, 4, 5, 6, 7);
void setup()
{
Serial.begin(9600);
Serial.println("Stepper test!");
// set the speed of the motor to 30 RPMs
stepper.setSpeed(60);
}
void loop()
{
Serial.println("Forward");
stepper.step(STEPS);
Serial.println("Backward");
stepper.step(-STEPS);
}

View file

@ -1,37 +0,0 @@
# SPDX-FileCopyrightText: 2025 Carter Nelson for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import time
import board
import digitalio
# Configure pins
AIN1 = digitalio.DigitalInOut(board.D5)
AIN2 = digitalio.DigitalInOut(board.D6)
SLP = digitalio.DigitalInOut(board.D7)
AIN1.switch_to_output()
AIN2.switch_to_output()
SLP.switch_to_output()
# Enable DRV8833
SLP.value = True
# Loop forever
while True:
#
# FORWARD
#
print("Forward")
AIN1.value = True
AIN2.value = False
time.sleep(1)
#
# REVERSE
#
print("Reverse")
AIN1.value = False
AIN2.value = True
time.sleep(1)

View file

@ -1,48 +0,0 @@
# SPDX-FileCopyrightText: 2025 Carter Nelson for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import time
import board
import digitalio
import pwmio
# Configure pins
AIN1 = pwmio.PWMOut(board.D5, frequency=2000)
AIN2 = pwmio.PWMOut(board.D6, frequency=2000)
SLP = digitalio.DigitalInOut(board.D7)
SLP.switch_to_output()
# Enable DRV8833
SLP.value = True
# Loop forever
while True:
#
# FORWARD
#
print("Forward")
AIN2.duty_cycle = 0
print(" ramping up")
for duty_cycle in range(0, 65536, 100):
AIN1.duty_cycle = duty_cycle
time.sleep(0.01)
print(" ramping down")
for duty_cycle in range(65535, -1, -100):
AIN1.duty_cycle = duty_cycle
time.sleep(0.01)
#
# REVERSE
#
print("Reverse")
AIN1.duty_cycle = 0
print(" ramping up")
for duty_cycle in range(0, 65536, 100):
AIN2.duty_cycle = duty_cycle
time.sleep(0.01)
print(" ramping down")
for duty_cycle in range(65535, -1, -100):
AIN2.duty_cycle = duty_cycle
time.sleep(0.01)

View file

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

View file

@ -109,32 +109,16 @@ void setup() {
digitalWrite(ESP32_RESETN, HIGH);
pixel.setPixelColor(0, 20, 20, 0); pixel.show();
delay(100);
#if defined(LED_BUILTIN)
pinMode(LED_BUILTIN, OUTPUT);
#endif
}
void loop() {
while (Serial.available()) {
#if defined(ARDUINO_ARCH_RP2040) // Neopixel is blocking and this annoys esptool
#if defined(LED_BUILTIN)
digitalWrite(LED_BUILTIN, HIGH);
#endif
#else
pixel.setPixelColor(0, 10, 0, 0); pixel.show();
#endif
SerialESP32.write(Serial.read());
}
while (SerialESP32.available()) {
#if defined(ARDUINO_ARCH_RP2040) // Neopixel is blocking and this annoys esptool
#if defined(LED_BUILTIN)
digitalWrite(LED_BUILTIN, LOW);
#endif
#else
pixel.setPixelColor(0, 0, 0, 10); pixel.show();
#endif
Serial.write(SerialESP32.read());
}
}

View file

@ -1,105 +0,0 @@
// SPDX-FileCopyrightText: 2025 Limor Fried for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#include "config.h"
#include <Adafruit_BME280.h>
#include <Adafruit_NeoPixel.h>
Adafruit_BME280 bme; // I2C
AdafruitIO_Feed *temperature = io.feed("temperature");
AdafruitIO_Feed *humidity = io.feed("humidity");
AdafruitIO_Feed *pressure = io.feed("pressure");
float temp, humid, pres;
Adafruit_NeoPixel pixel(1, PIN_NEOPIXEL, NEO_GRB + NEO_KHZ800);
void setup() {
Serial.begin(115200);
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
// wait for serial monitor to open
//while(! Serial);
// turn on neopixel
pinMode(NEOPIXEL_POWER, OUTPUT);
digitalWrite(NEOPIXEL_POWER, HIGH);
pixel.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
pixel.setBrightness(10); // not so bright
pixel.setPixelColor(0, 0xFF0000); // red
pixel.show();
if (! bme.begin()) {
Serial.println("Could not find a valid BME280 sensor, check wiring, address, sensor ID!");
deepSleep();
}
Serial.println("Found BME280");
float temp = bme.readTemperature();
float pres = bme.readPressure() / 100.0F;
float hum = bme.readHumidity();
// shhh time to close your eyes
bme.setSampling(Adafruit_BME280::MODE_SLEEP,
Adafruit_BME280::SAMPLING_X16, Adafruit_BME280::SAMPLING_X16, Adafruit_BME280::SAMPLING_X16,
Adafruit_BME280::FILTER_OFF,
Adafruit_BME280::STANDBY_MS_1000);
Serial.print("Connecting to Adafruit IO");
pixel.setPixelColor(0, 0xFFFF00); // yellow
pixel.show();
// connect to io.adafruit.com
io.connect();
// wait for a connection
while(io.status() < AIO_CONNECTED) {
Serial.print(".");
delay(100);
}
// we are connected
pixel.setPixelColor(0, 0x00FF00); // green
pixel.show();
Serial.println();
Serial.println(io.statusText());
io.run();
temp = temp * 9.0 / 5.0 + 32;
Serial.print("Temperature = ");
Serial.print(temp);
Serial.println(" *F");
temperature->save(temp);
Serial.print("Pressure = ");
Serial.print(pres);
Serial.println(" hPa");
pressure->save(pres);
Serial.print("Humidity = ");
Serial.print(hum);
Serial.println(" %");
humidity->save(hum);
Serial.println();
deepSleep();
}
void loop() {
// we never get here!
}
void deepSleep() {
pinMode(NEOPIXEL_POWER, OUTPUT);
digitalWrite(NEOPIXEL_POWER, LOW); // off
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
esp_sleep_enable_timer_wakeup(300000000); // 5 minutes
esp_deep_sleep_start();
}

View file

@ -1,10 +0,0 @@
// SPDX-FileCopyrightText: 2025 Limor Fried for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#define IO_USERNAME "your-aio-username"
#define IO_KEY "your-aio-token"
#define WIFI_SSID "your-wifi-ssid"
#define WIFI_PASS "your-wifi-pass"
#include "AdafruitIO_WiFi.h"
AdafruitIO_WiFi io(IO_USERNAME, IO_KEY, WIFI_SSID, WIFI_PASS);

View file

@ -3,8 +3,6 @@
"""
CircuitPython Adafruit IO Example for BME280 and LC709203 Sensors
"""
from os import getenv
import time
import ssl
import alarm
@ -16,21 +14,11 @@ import adafruit_requests
from adafruit_io.adafruit_io import IO_HTTP, AdafruitIO_RequestError
from adafruit_lc709203f import LC709203F, PackSize
from adafruit_bme280 import basic as adafruit_bme280
# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml
# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.)
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
aio_username = getenv("ADAFRUIT_AIO_USERNAME")
aio_key = getenv("ADAFRUIT_AIO_KEY")
if None in [ssid, password, aio_username, aio_key]:
raise RuntimeError(
"WiFi and Adafruit IO settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"'ADAFRUIT_AIO_USERNAME' and 'ADAFRUIT_AIO_KEY' at a minimum."
)
try:
from secrets import secrets
except ImportError:
print("WiFi and Adafruit IO credentials are kept in secrets.py, please add them there!")
raise
# Duration of sleep in seconds. Default is 600 seconds (10 minutes).
# Feather will sleep for this duration between sensor readings / sending data to AdafruitIO
@ -90,9 +78,9 @@ def send_io_data(feed, value):
# Wi-Fi connections can have issues! This ensures the code will continue to run.
try:
# Connect to Wi-Fi
wifi.radio.connect(ssid, password)
print(f"Connected to {ssid}!")
print(f"IP: {wifi.radio.ipv4_address}")
wifi.radio.connect(secrets["ssid"], secrets["password"])
print("Connected to {}!".format(secrets["ssid"]))
print("IP:", wifi.radio.ipv4_address)
pool = socketpool.SocketPool(wifi.radio)
requests = adafruit_requests.Session(pool, ssl.create_default_context())
@ -102,6 +90,12 @@ except Exception as e: # pylint: disable=broad-except
print(e)
go_to_sleep(60)
# Set your Adafruit IO Username and Key in secrets.py
# (visit io.adafruit.com if you need to create an account,
# or if you need your Adafruit IO key.)
aio_username = secrets["aio_username"]
aio_key = secrets["aio_key"]
# Initialize an Adafruit IO HTTP API object
io = IO_HTTP(aio_username, aio_key, requests)

View file

@ -5,8 +5,8 @@ CircuitPython Simple Example for BME280 and LC709203 Sensors
"""
import time
import board
from adafruit_lc709203f import LC709203F, PackSize
from adafruit_bme280 import basic as adafruit_bme280
from adafruit_lc709203f import LC709203F, PackSize
# Create sensor objects, using the board's default I2C bus.
i2c = board.I2C() # uses board.SCL and board.SDA

View file

@ -1,110 +0,0 @@
# SPDX-FileCopyrightText: Copyright (c) 2025 Tim Cocks for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
CircuitPython example for deep sleep and BME280 sensor sending data
to Adafruit IO.
"""
from os import getenv
import time
import alarm
import board
import digitalio
import neopixel
import wifi
from adafruit_bme280 import advanced as adafruit_bme280
import adafruit_connection_manager
import adafruit_requests
from adafruit_io.adafruit_io import IO_HTTP, AdafruitIO_RequestError
# enable power to NeoPixels.
np_power = digitalio.DigitalInOut(board.NEOPIXEL_POWER)
np_power.switch_to_output(value=True)
# standard LED
builtin_led = digitalio.DigitalInOut(board.LED)
builtin_led.switch_to_output(value=True)
# neopixel to use for status
status_pixel = neopixel.NeoPixel(
board.NEOPIXEL, 1, brightness=0.1, pixel_order=neopixel.GRB, auto_write=True
)
status_pixel[0] = 0xFF0000
# Create sensor object, using the board's default I2C bus.
i2c = board.I2C() # uses board.SCL and board.SDA
bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c)
print("Found BME280")
# change this to match the location's pressure (hPa) at sea level
bme280.sea_level_pressure = 1013.25
# temperature converted to F
temperature = bme280.temperature * 9 / 5 + 32
humidity = bme280.relative_humidity
pressure = bme280.pressure
print("\nTemperature: %0.1f F" % temperature)
print("Humidity: %0.1f %%" % humidity)
print("Pressure: %0.1f hPa" % pressure)
bme280.mode = adafruit_bme280.MODE_SLEEP
bme280.overscan_temperature = adafruit_bme280.OVERSCAN_X16
bme280.overscan_humidity = adafruit_bme280.OVERSCAN_X16
bme280.overscan_pressure = adafruit_bme280.OVERSCAN_X16
bme280.iir_filter = adafruit_bme280.IIR_FILTER_DISABLE
bme280.standby_period = adafruit_bme280.STANDBY_TC_1000
# set status pixel to yellow
status_pixel[0] = 0xFFFF00
print("Connecting to AdafruitIO")
# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml
# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.)
ssid = getenv("WIFI_SSID")
password = getenv("WIFI_PASSWORD")
aio_username = getenv("ADAFRUIT_AIO_USERNAME")
aio_key = getenv("ADAFRUIT_AIO_KEY")
print("Connecting to %s" % ssid)
wifi.radio.connect(ssid, password)
print("Connected to %s!" % ssid)
# setup socket pool and requests session
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)
# Initialize an Adafruit IO HTTP API object
io = IO_HTTP(aio_username, aio_key, requests)
# set status pixel to green
status_pixel[0] = 0x00FF00
try:
# Get the feeds from Adafruit IO
temperature_feed = io.get_feed("temperature")
humidity_feed = io.get_feed("humidity")
pressure_feed = io.get_feed("pressure")
# send data to the feeds
io.send_data(temperature_feed["key"], temperature)
io.send_data(humidity_feed["key"], humidity)
io.send_data(pressure_feed["key"], pressure)
except AdafruitIO_RequestError as e:
print(e)
print(
"You must create feeds on AdafruitIO for: temperature, humidity, and pressure"
)
# turn off the neopixel and builtin LED
np_power.value = False
builtin_led.value = False
# Create an alarm that will trigger 5 minutes from now.
time_alarm = alarm.time.TimeAlarm(monotonic_time=time.monotonic() + (5 * 60))
# Exit the program, and then deep sleep until the alarm wakes us.
alarm.exit_and_deep_sleep_until_alarms(time_alarm)
# Does not return, so we never get here.

View file

@ -3,8 +3,6 @@
"""
CircuitPython Adafruit IO Example for LC709203 Sensor
"""
from os import getenv
import time
import ssl
import alarm
@ -15,21 +13,11 @@ import socketpool
import adafruit_requests
from adafruit_io.adafruit_io import IO_HTTP
from adafruit_lc709203f import LC709203F, PackSize
# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml
# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.)
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
aio_username = getenv("ADAFRUIT_AIO_USERNAME")
aio_key = getenv("ADAFRUIT_AIO_KEY")
if None in [ssid, password, aio_username, aio_key]:
raise RuntimeError(
"WiFi and Adafruit IO settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"'ADAFRUIT_AIO_USERNAME' and 'ADAFRUIT_AIO_KEY' at a minimum."
)
try:
from secrets import secrets
except ImportError:
print("WiFi and Adafruit IO credentials are kept in secrets.py, please add them there!")
raise
# Duration of sleep in seconds. Default is 600 seconds (10 minutes).
# Feather will sleep for this duration between sensor readings / sending data to AdafruitIO
@ -70,9 +58,9 @@ def send_io_data(feed, value):
# Wi-Fi connections can have issues! This ensures the code will continue to run.
try:
# Connect to Wi-Fi
wifi.radio.connect(ssid, password)
print(f"Connected to {ssid}!")
print(f"IP: {wifi.radio.ipv4_address}")
wifi.radio.connect(secrets["ssid"], secrets["password"])
print("Connected to {}!".format(secrets["ssid"]))
print("IP:", wifi.radio.ipv4_address)
pool = socketpool.SocketPool(wifi.radio)
requests = adafruit_requests.Session(pool, ssl.create_default_context())
@ -82,6 +70,12 @@ except Exception as e: # pylint: disable=broad-except
print(e)
go_to_sleep(60)
# Set your Adafruit IO Username and Key in secrets.py
# (visit io.adafruit.com if you need to create an account,
# or if you need your Adafruit IO key.)
aio_username = secrets["aio_username"]
aio_key = secrets["aio_key"]
# Initialize an Adafruit IO HTTP API object
io = IO_HTTP(aio_username, aio_key, requests)

View file

@ -3,8 +3,6 @@
"""
CircuitPython GitHub Stars viewer
"""
from os import getenv
import time
import ssl
import wifi
@ -15,17 +13,12 @@ from adafruit_display_text import bitmap_label
from adafruit_bitmap_font import bitmap_font
import adafruit_requests
# Get WiFi details, ensure these are setup in settings.toml
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
if None in [ssid, password]:
raise RuntimeError(
"WiFi settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"at a minimum."
)
# Get WiFi details secrets.py file
try:
from secrets import secrets
except ImportError:
print("WiFi secrets are kept in secrets.py, please add them there!")
raise
display = board.DISPLAY
@ -45,10 +38,10 @@ group.append(text_area)
display.root_group = group
# Connect to WiFi
print(f"Connecting to {ssid}")
wifi.radio.connect(ssid, password)
print(f"Connected to {ssid}!")
print(f"My IP address is {wifi.radio.ipv4_address}")
print("Connecting to %s"%secrets["ssid"])
wifi.radio.connect(secrets["ssid"], secrets["password"])
print("Connected to %s!"%secrets["ssid"])
print("My IP address is", wifi.radio.ipv4_address)
pool = socketpool.SocketPool(wifi.radio)
requests = adafruit_requests.Session(pool, ssl.create_default_context())

View file

@ -11,7 +11,7 @@ LOOP = False # Update to True loop WAV playback. False plays once.
audio = audiobusio.I2SOut(board.A2, board.A1, board.A0)
with open("booploop.wav", "rb") as wave_file:
with open("chikken.wav", "rb") as wave_file:
wav = audiocore.WaveFile(wave_file)
print("Playing wav file!")

View file

@ -2,7 +2,6 @@
#
# SPDX-License-Identifier: MIT
from os import getenv
import time
import board
import busio
@ -23,23 +22,15 @@ USE_CELSIUS = False
# Interval the sensor publishes to Adafruit IO, in minutes
PUBLISH_INTERVAL = 10
# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml
# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.)
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
aio_username = getenv("ADAFRUIT_AIO_USERNAME")
aio_key = getenv("ADAFRUIT_AIO_KEY")
if None in [ssid, password, aio_username, aio_key]:
raise RuntimeError(
"WiFi and Adafruit IO settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"'ADAFRUIT_AIO_USERNAME' and 'ADAFRUIT_AIO_KEY' at a minimum."
)
### 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
# AirLift FeatherWing
esp32_cs = DigitalInOut(board.D13)
esp32_reset = DigitalInOut(board.D12)
@ -47,8 +38,8 @@ esp32_ready = DigitalInOut(board.D11)
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
status_pixel = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)
wifi = adafruit_esp32spi_wifimanager.WiFiManager(esp, ssid, password, status_pixel=status_pixel)
status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)
wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light)
# Connect to a PM2.5 sensor over UART
reset_pin = None
@ -151,7 +142,7 @@ def read_bme(is_celsius=False):
# Create an instance of the Adafruit IO HTTP client
io = IO_HTTP(aio_username, aio_key, wifi)
io = IO_HTTP(secrets["aio_user"], secrets["aio_key"], wifi)
# Describes feeds used to hold Adafruit IO data
feed_aqi = io.get_feed("air-quality-sensor.aqi")
@ -159,11 +150,11 @@ feed_aqi_category = io.get_feed("air-quality-sensor.category")
feed_humidity = io.get_feed("air-quality-sensor.humidity")
feed_temperature = io.get_feed("air-quality-sensor.temperature")
# Set up location metadata from settings.toml file
# Set up location metadata from secrets.py file
location_metadata = {
"lat": getenv("latitude"),
"lon": getenv("longitude"),
"ele": getenv("elevation"),
"lat": secrets["latitude"],
"lon": secrets["longitude"],
"ele": secrets["elevation"],
}
elapsed_minutes = 0

View file

@ -14,20 +14,13 @@ from adafruit_esp32spi import adafruit_esp32spi_wifimanager
import adafruit_minimqtt.adafruit_minimqtt as MQTT
# Get WiFi details, ensure these are setup in settings.toml
ssid = os.getenv("CIRCUITPY_WIFI_SSID")
password = os.getenv("CIRCUITPY_WIFI_PASSWORD")
if None in [ssid, password]:
raise RuntimeError(
"WiFi settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"at a minimum."
)
### WiFi ###
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)
esp32_ready = DigitalInOut(board.ESP_BUSY)
@ -41,19 +34,19 @@ esp32_reset = DigitalInOut(board.ESP_RESET)
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
"""Use below for Most Boards"""
status_pixel = neopixel.NeoPixel(
status_light = neopixel.NeoPixel(
board.NEOPIXEL, 1, brightness=0.2
) # Uncomment for Most Boards
"""Uncomment below for ItsyBitsy M4"""
# status_pixel = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2)
# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2)
# Uncomment below for an externally defined RGB LED
# import adafruit_rgbled
# from adafruit_esp32spi import PWMOut
# RED_LED = PWMOut.PWMOut(esp, 26)
# GREEN_LED = PWMOut.PWMOut(esp, 27)
# BLUE_LED = PWMOut.PWMOut(esp, 25)
# status_pixel = adafruit_rgbled.RGBLED(RED_LED, BLUE_LED, GREEN_LED)
wifi = adafruit_esp32spi_wifimanager.WiFiManager(esp, ssid, password, status_pixel=status_pixel)
# status_light = adafruit_rgbled.RGBLED(RED_LED, BLUE_LED, GREEN_LED)
wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light)
# Set up a pin for controlling the relay
power_pin = DigitalInOut(board.D3)

View file

@ -2,7 +2,6 @@
#
# SPDX-License-Identifier: MIT
from os import getenv
import time
import board
import busio
@ -15,21 +14,6 @@ from adafruit_esp32spi import adafruit_esp32spi_wifimanager
import adafruit_minimqtt.adafruit_minimqtt as MQTT
# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml
# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.)
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
aio_username = getenv("ADAFRUIT_AIO_USERNAME")
aio_key = getenv("ADAFRUIT_AIO_KEY")
if None in [ssid, password, aio_username, aio_key]:
raise RuntimeError(
"WiFi and Adafruit IO settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"'ADAFRUIT_AIO_USERNAME' and 'ADAFRUIT_AIO_KEY' at a minimum."
)
### Sensor Calibration ###
# Appliance power LED's light level, in Lux
APPLIANCE_ON_LUX = 30.0
@ -38,6 +22,13 @@ SENSOR_READ_TIME = 10.0
### 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
# If you are using a board with pre-defined ESP32 Pins:
esp32_cs = DigitalInOut(board.ESP_CS)
esp32_ready = DigitalInOut(board.ESP_BUSY)
@ -51,19 +42,19 @@ esp32_reset = DigitalInOut(board.ESP_RESET)
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
"""Use below for Most Boards"""
status_pixel = neopixel.NeoPixel(
status_light = neopixel.NeoPixel(
board.NEOPIXEL, 1, brightness=0.2
) # Uncomment for Most Boards
"""Uncomment below for ItsyBitsy M4"""
# status_pixel = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2)
# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2)
# Uncomment below for an externally defined RGB LED
# import adafruit_rgbled
# from adafruit_esp32spi import PWMOut
# RED_LED = PWMOut.PWMOut(esp, 26)
# GREEN_LED = PWMOut.PWMOut(esp, 27)
# BLUE_LED = PWMOut.PWMOut(esp, 25)
# status_pixel = adafruit_rgbled.RGBLED(RED_LED, BLUE_LED, GREEN_LED)
wifi = adafruit_esp32spi_wifimanager.WiFiManager(esp, ssid, password, status_pixel=status_pixel)
# status_light = adafruit_rgbled.RGBLED(RED_LED, BLUE_LED, GREEN_LED)
wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light)
# Set up a pin for controlling the relay
power_pin = DigitalInOut(board.D3)
@ -76,10 +67,10 @@ sensor = adafruit_bh1750.BH1750(i2c)
### Feeds ###
# Set up a feed named Relay for subscribing to the relay feed on Adafruit IO
feed_relay = f"{aio_username}/feeds/relay"
feed_relay = secrets["aio_username"] + "/feeds/relay"
# Set up a feed named status for subscribing to the status feed on Adafruit IO
feed_status = f"{aio_username}/feeds/status"
feed_status = secrets["aio_username"] + "/feeds/status"
### Code ###
@ -131,8 +122,8 @@ ssl_context = adafruit_connection_manager.get_radio_ssl_context(esp)
# Set up a MiniMQTT Client
client = MQTT.MQTT(
broker="io.adafruit.com",
username=aio_username,
password=aio_key,
username=secrets["aio_username"],
password=secrets["aio_key"],
socket_pool=pool,
ssl_context=ssl_context,
)

View file

@ -1,27 +0,0 @@
{
"exportVersion": "1.0.0",
"exportedBy": "tyeth_demo",
"exportedAt": "2025-05-02T17:08:03.857Z",
"exportedFromDevice": {
"board": "rpi-pico-w",
"firmwareVersion": "1.0.0-beta.100"
},
"components": [
{
"name": "Reed Switch",
"pinName": "D13",
"type": "reed_switch",
"mode": "DIGITAL",
"direction": "INPUT",
"period": 0,
"pull": "UP",
"isPin": true,
"visualization": {
"offLabel": "Open",
"offIcon": "fa6:solid:door-open",
"onLabel": "Closed",
"onIcon": "fa6:regular:door-closed"
}
}
]
}

View file

@ -2,7 +2,6 @@
#
# SPDX-License-Identifier: MIT
from os import getenv
import time
import board
import busio
@ -14,23 +13,15 @@ import neopixel
import adafruit_minimqtt.adafruit_minimqtt as MQTT
from adafruit_io.adafruit_io import IO_MQTT
# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml
# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.)
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
aio_username = getenv("ADAFRUIT_AIO_USERNAME")
aio_key = getenv("ADAFRUIT_AIO_KEY")
if None in [ssid, password, aio_username, aio_key]:
raise RuntimeError(
"WiFi and Adafruit IO settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"'ADAFRUIT_AIO_USERNAME' and 'ADAFRUIT_AIO_KEY' at a minimum."
)
### 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
# If you are using a board with pre-defined ESP32 Pins:
esp32_cs = DigitalInOut(board.ESP_CS)
esp32_ready = DigitalInOut(board.ESP_BUSY)
@ -44,19 +35,19 @@ esp32_reset = DigitalInOut(board.ESP_RESET)
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
"""Use below for Most Boards"""
status_pixel = neopixel.NeoPixel(
status_light = neopixel.NeoPixel(
board.NEOPIXEL, 1, brightness=0.2
) # Uncomment for Most Boards
"""Uncomment below for ItsyBitsy M4"""
# status_pixel = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2)
# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2)
# Uncomment below for an externally defined RGB LED
# import adafruit_rgbled
# from adafruit_esp32spi import PWMOut
# RED_LED = PWMOut.PWMOut(esp, 26)
# GREEN_LED = PWMOut.PWMOut(esp, 27)
# BLUE_LED = PWMOut.PWMOut(esp, 25)
# status_pixel = adafruit_rgbled.RGBLED(RED_LED, BLUE_LED, GREEN_LED)
wifi = adafruit_esp32spi_wifimanager.WiFiManager(esp, ssid, password, status_pixel=status_pixel)
# status_light = adafruit_rgbled.RGBLED(RED_LED, BLUE_LED, GREEN_LED)
wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light)
# Set up a pin for controlling the relay
power_pin = DigitalInOut(board.D3)
@ -98,11 +89,11 @@ def on_message(client, feed_id, payload):
def on_relay_msg(client, topic, message):
# Method called whenever user/feeds/relay has a new value
if message == "1":
print("Received 1 - turning outlet ON")
if message == "morning":
print("Morning - turning outlet ON")
power_pin.value = True
elif message == "0":
print("Received 0 - turning outlet OFF")
elif message == "night":
print("Night - turning outlet OFF")
power_pin.value = False
else:
print("Unexpected value received on relay feed.")
@ -119,8 +110,8 @@ ssl_context = adafruit_connection_manager.get_radio_ssl_context(esp)
# Initialize a new MQTT Client object
mqtt_client = MQTT.MQTT(
broker="io.adafruit.com",
username=aio_username,
password=aio_key,
username=secrets["aio_username"],
password=secrets["aio_key"],
socket_pool=pool,
ssl_context=ssl_context,
)

View file

@ -99,7 +99,7 @@ while True:
time.sleep(30)
# Stepper 1
if not st1.is_alive() and int(stepper_start.value):
if not st1.isAlive() and int(stepper_start.value):
stepper_1_steps = aio.receive(feed_step_1_steps.key)
stepper_1_steps = int(stepper_1_steps.value)
if stepper_1_steps > 0: # stepper slider is set
@ -125,7 +125,7 @@ while True:
st1.start()
# Stepper 2
if not st2.is_alive() and int(stepper_start.value):
if not st2.isAlive() and int(stepper_start.value):
stepper_2_steps = aio.receive(feed_step_2_steps.key)
stepper_2_steps = int(stepper_2_steps.value)
if stepper_2_steps > 0: # stepper slider is set

View file

@ -1,83 +0,0 @@
{
"exportVersion": "1.0.0",
"exportedBy": "tyeth",
"exportedAt": "2025-06-10T18:13:03.071Z",
"exportedFromDevice": {
"board": "rpi-pico-w",
"firmwareVersion": "1.0.0-beta.100"
},
"components": [
{
"name": "💦 Wee Button",
"pinName": "D18",
"type": "push_button",
"mode": "DIGITAL",
"direction": "INPUT",
"period": 0,
"pull": "UP",
"isPin": true
},
{
"name": "💦 Wee LED",
"pinName": "D2",
"type": "led",
"mode": "DIGITAL",
"direction": "OUTPUT",
"isPin": true
},
{
"name": "💩 Poo Button",
"pinName": "D19",
"type": "push_button",
"mode": "DIGITAL",
"direction": "INPUT",
"period": 0,
"pull": "UP",
"isPin": true
},
{
"name": "💩 Poo LED",
"pinName": "D3",
"type": "led",
"mode": "DIGITAL",
"direction": "OUTPUT",
"isPin": true
},
{
"name": "❌ Didn't Go Button",
"pinName": "D20",
"type": "push_button",
"mode": "DIGITAL",
"direction": "INPUT",
"period": 0,
"pull": "UP",
"isPin": true
},
{
"name": "❌ Didn't Go LED",
"pinName": "D4",
"type": "led",
"mode": "DIGITAL",
"direction": "OUTPUT",
"isPin": true
},
{
"name": "🔔 Tell Adult Button",
"pinName": "D21",
"type": "push_button",
"mode": "DIGITAL",
"direction": "INPUT",
"period": 0,
"pull": "UP",
"isPin": true
},
{
"name": "🔔 Tell Adult LED",
"pinName": "D5",
"type": "led",
"mode": "DIGITAL",
"direction": "OUTPUT",
"isPin": true
}
]
}

View file

@ -1,72 +0,0 @@
<b>🌟 Daily Potty Training Report 🌟</b>
Hi there! Here's how our little superstar did:
---
<b>🚽 Successful Wees: {{ vars.wee_progress }}</b>
{% for i in (1..vars.wee_progress) %}💧{% endfor %}
{% if vars.wee_progress <= 5 -%}
Great start! Every success counts, and they're building good habits one wee at a time! 🌱
{% elsif vars.wee_progress <= 15 -%}
Fantastic progress! They're really getting the hang of this - keep up the amazing work! 🎯
{% else -%}
SUPERSTAR ALERT! 🌟 Absolutely crushing it with those wee successes! They're a potty champion! 🏆
{% endif %}---
<b>💩 Successful Poos: {{ vars.poo_progress }}</b>
{% for i in (1..vars.poo_progress) %}🟤{% endfor %}
{% if vars.poo_progress == 0 -%}
Poos can be tricky, but they're being so brave! Every try is a step forward! 💪
{% elsif vars.poo_progress < 2 -%}
Look at them go! They're becoming a real poo pro - that's awesome progress! 🎉
{% else -%}
POO CHAMPION! 🏅 They've mastered one of the trickiest parts - so proud! 🎊
{% endif %}---
<b>🤝 Told an Adult: {{ vars.informed_progress }}</b>
{% for i in (1..vars.informed_progress) %}🗣️{% endfor %}
{% if vars.informed_progress <= 5 -%}
Communication is key! Keep practicing saying when they need to go - They're doing great! 📢
{% elsif vars.informed_progress <= 15 -%}
Wonderful communication skills! They're really good at letting us know - that's so helpful! 👏
{% else -%}
COMMUNICATION SUPERSTAR! 🌟 They're amazing at telling adults - that's such a big kid skill! 🎯
{% endif %}---
<b>👻 Nothing Happened: {{ vars.nothing_progress }}</b>
{% for i in (1..vars.nothing_progress) %}⭕{% endfor %}
{% if vars.nothing_progress <= 3 -%}
That's okay! Trying is what matters, and their body will let them know when it's ready! 🌈
{% else -%}
So patient and persistent! Even when nothing happens, they keep trying - that's real determination! 💫
{% endif %}---
<b>📊 Daily Summary:</b>
{% capture total_tries -%}{{ vars.wee_progress | plus: vars.poo_progress | plus: vars.nothing_progress }}{% endcapture -%}
{% capture successes -%}{{ vars.wee_progress | plus: vars.poo_progress }}{% endcapture -%}
{% capture success_rate -%}{% if total_tries != "0" -%}{{ successes | times: 100 | divided_by: total_tries }}{% else -%}100{% endif -%}{% endcapture -%}
{% capture bar_filled -%}{{ success_rate | divided_by: 10 }}{% endcapture -%}
{% capture bar_empty -%}{{ 10 | minus: bar_filled }}{% endcapture -%}
Total potty visits: {{ total_tries }}
Success rate: {{ success_rate }}% [{%- for i in (1..bar_filled) -%}█{%- endfor -%}{%- for i in (1..bar_empty) -%}░{%- endfor -%}]
{%- assign total_events = vars.wee_progress | plus: vars.poo_progress | plus: vars.nothing_progress | plus: vars.informed_progress -%}
({{ total_events }} events today )
{% if total_events <= 3 %}
💝 <b>Today:</b> They're doing such a great job learning! Every day gets a little easier...
{% elsif total_events <= 5 %}
🌟 <b>Today:</b> Ayee! They're really getting the hang of this potty training thing! Keep it up!
{% elsif total_events <= 8 %}
🌟 <b>Today:</b> WOW! Look at all that practice! They're becoming such a potty expert.
{% else %}
🏆 <b>Today:</b> INCREDIBLE DAY! They're absolutely rocking this potty training journey! 🎊🎉
{%- endif %}
Keep being awesome!
With love and high-fives! 🙌
---
{%- assign event_mod = total_events | modulo: 3 %}
{% if event_mod == 0 -%}
<i>P.S. Remember: Every expert was once a beginner - they're doing brilliantly! 🌟</i>
{% elsif event_mod == 1 -%}
<i>P.S. Fun fact: Even superheroes had to learn to use the potty! 🦸</i>
{% else -%}
<i>P.S. Remember: accidents are just practice in disguise! They're doing amazingly! 💕</i>
{% endif %}

View file

@ -1,71 +0,0 @@
// SPDX-FileCopyrightText: 2025 Tim Cocks for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#include <Adafruit_NeoPixel.h>
int sensorPin = A0; // select the input pin for the potentiometer
int neoPixelPin = 4; // select the pin for the LED
int sensorValue = 0; // variable to store the value coming from the sensor
int readings[10];
size_t count = 0;
Adafruit_NeoPixel strip = Adafruit_NeoPixel(1, neoPixelPin, NEO_GRB + NEO_KHZ800);
// Insert an int value at index 0 of an int array, shifting all other elements up.
// If the array already contains 'maxCount' elements, the last one is dropped.
// 'count' is the number of valid elements currently stored in the array.
void insert(int arr[], size_t maxCount, int value, size_t &count){
// Determine how many elements we need to shift (cannot exceed the array bounds)
size_t shiftCount = (count < maxCount) ? count : maxCount - 1;
// Shift elements up by one position
for (size_t i = shiftCount; i > 0; --i) {
arr[i] = arr[i - 1];
}
// Insert the new value at the beginning
arr[0] = value;
// Update the element count
if (count < maxCount) {
++count; // we added a new element
} // else count stays the same the last element was overwritten
}
// Input an array of 10 or fewer int's, and a count of how many have values
// Returns average of the values in the array.
int average(int arr[], size_t count){
int sum = 0;
for(int i = 0; i < 10; i++){
sum = sum + arr[i];
}
return sum / count;
}
// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
WheelPos = 255 - WheelPos;
if(WheelPos < 85) {
return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
}
if(WheelPos < 170) {
WheelPos -= 85;
return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
WheelPos -= 170;
return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}
void setup() {
Serial.begin(115200);
strip.begin();
}
void loop() {
sensorValue = analogRead(sensorPin);
insert(readings, 10, sensorValue, count);
strip.setPixelColor(0, Wheel(average(readings, count) / 4));
strip.show();
}

View file

@ -1,36 +0,0 @@
# SPDX-FileCopyrightText: 2025 Tim Cocks for Adafruit Industries
# SPDX-License-Identifier: MIT
"""
NeoPot NeoPixel Rainbow Demo
"""
import board
import analogio
import neopixel
import rainbowio
analog_pin = analogio.AnalogIn(board.A0)
knob_pixel = neopixel.NeoPixel(board.D4, 1, brightness=0.75, auto_write=True)
analog_smoothing_buffer = []
def map_range(x, in_min, in_max, out_min, out_max):
return (x - in_min) / (in_max - in_min) * out_max - out_min
def average(values):
if not values:
return 0
return sum(values) / len(values)
while True:
analog_smoothing_buffer.insert(0, analog_pin.value)
if len(analog_smoothing_buffer) >= 10:
analog_smoothing_buffer.pop(-1)
knob_pixel[0] = rainbowio.colorwheel(
map_range(average(analog_smoothing_buffer), 0, 65525, 0, 255)
)

View file

@ -7,7 +7,6 @@ library example esp32spi_simpletest.py:
https://github.com/adafruit/Adafruit_CircuitPython_ESP32SPI/
blob/master/examples/esp32spi_simpletest.py '''
from os import getenv
import board
import busio
from digitalio import DigitalInOut
@ -15,17 +14,12 @@ import adafruit_connection_manager
import adafruit_requests
from adafruit_esp32spi import adafruit_esp32spi
# Get WiFi details, ensure these are setup in settings.toml
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
if None in [ssid, password]:
raise RuntimeError(
"WiFi settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"at a minimum."
)
# Get wifi details and more from a secrets.py file
try:
from secrets import secrets
except ImportError:
print("WiFi secrets are kept in secrets.py, please add them there!")
raise
print("Arduino Nano RP2040 Connect webclient test")
@ -52,16 +46,16 @@ 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:
try:
esp.connect_AP(ssid, password)
esp.connect_AP(secrets["ssid"], secrets["password"])
except RuntimeError as e:
print("could not connect to AP, retrying: ", e)
continue
print("Connected to", str(esp.ap_info.ssid, "utf-8"), "\tRSSI:", esp.ap_info.rssi)
print("Connected to", str(esp.ssid, "utf-8"), "\tRSSI:", esp.rssi)
print("My IP address is", esp.pretty_ip(esp.ip_address))
print(

View file

@ -1,50 +0,0 @@
// 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);
}

File diff suppressed because it is too large Load diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -1,161 +0,0 @@
# SPDX-FileCopyrightText: 2025 Liz Clark for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import os
import time
import ssl
import board
import wifi
import socketpool
import microcontroller
import displayio
from adafruit_display_text.bitmap_label import Label
from adafruit_bitmap_font import bitmap_font
import adafruit_imageload
from fourwire import FourWire
import adafruit_requests
from adafruit_gc9a01a import GC9A01A
from adafruit_ticks import ticks_ms, ticks_add, ticks_diff
cad_url = ("https://ssd-api.jpl.nasa.gov/cad.api?"
"des=2024%20YR4&body=ALL&"
"date-min=2030-01-01&date-max=2060-01-01")
sentry_url = "https://ssd-api.jpl.nasa.gov/sentry.api?des=2024%20YR4"
# connect to wifi
try:
wifi.radio.connect(os.getenv('CIRCUITPY_WIFI_SSID'), os.getenv('CIRCUITPY_WIFI_PASSWORD'))
except TypeError:
print("Could not find WiFi info. Check your settings.toml file!")
raise
context = ssl.create_default_context()
with open("/ssd-api-jpl-nasa-gov-chain.pem", "rb") as certfile:
context.load_verify_locations(cadata=certfile.read())
pool = socketpool.SocketPool(wifi.radio)
requests = adafruit_requests.Session(pool, context)
spi = board.SPI()
tft_cs = board.TX
tft_dc = board.RX
tft_reset = None
displayio.release_displays()
display_bus = FourWire(spi, command=tft_dc, chip_select=tft_cs, reset=tft_reset)
display = GC9A01A(display_bus, width=240, height=240, auto_refresh=False)
main_group = displayio.Group()
display.root_group = main_group
bitmap_bg, palette_bg = adafruit_imageload.load("/earth_bg.bmp",
bitmap=displayio.Bitmap,
palette=displayio.Palette)
grid_bg = displayio.TileGrid(bitmap_bg, pixel_shader=palette_bg)
main_group.append(grid_bg)
font = bitmap_font.load_font('/Arial-14.bdf')
name_area = Label(font, text="2024 YR4", color=0xFFFFFF, background_color=0x000000)
name_area.anchored_position = (display.width / 2, 0)
name_area.anchor_point = (0.5, 0.0)
date_area = Label(font, text="2032-12-22", color=0xFFFFFF, background_color=0x000000)
date_area.anchored_position = (display.width / 2, name_area.height+10)
date_area.anchor_point = (0.5, 0.0)
moon_area = Label(font, text="Moon: ", color=0xFFFFFF, background_color=0x000000)
moon_area.anchored_position = (display.width / 2, name_area.height+10 + date_area.height+5)
moon_area.anchor_point = (0.5, 0.0)
earth_area = Label(font, text="Earth: ", color=0xFFFFFF, background_color=0x000000)
earth_area.anchored_position = (display.width / 2, name_area.height+10 +
moon_area.height+5 +
date_area.height + 5)
earth_area.anchor_point = (0.5, 0.0)
impact_area = Label(font, text="Earth Impact: 0.0000%", color=0xFFFFFF, background_color=0x000000)
impact_area.anchored_position = (display.width / 2, name_area.height+10 +
moon_area.height+5 +
earth_area.height + 5 +
date_area.height + 5)
impact_area.anchor_point = (0.5, 0.0)
main_group.append(impact_area)
main_group.append(earth_area)
main_group.append(moon_area)
main_group.append(date_area)
main_group.append(name_area)
bit_asteroid, pal_asteroid = adafruit_imageload.load("/asteroid.bmp",
bitmap=displayio.Bitmap,
palette=displayio.Palette)
asteroid = displayio.TileGrid(bit_asteroid, pixel_shader=pal_asteroid,
x = 25, y=100)
pal_asteroid.make_transparent(0)
main_group.append(asteroid)
def diagonal_travel(bitmap_object, start_x=-59, start_y=-59, end_x=240, end_y=240, delay=0.01):
# Set initial position
bitmap_object.x = start_x
bitmap_object.y = start_y
# Calculate total movement distance
distance_x = end_x - start_x
distance_y = end_y - start_y
# Calculate number of steps (use the larger distance)
steps = max(abs(distance_x), abs(distance_y)) // 1
# Calculate step size for each axis to maintain diagonal movement
step_x = distance_x / steps
step_y = distance_y / steps
# Animate the movement
for i in range(steps + 1):
# Update position
bitmap_object.x = int(start_x + (step_x * i))
bitmap_object.y = int(start_y + (step_y * i))
display.refresh()
# Pause to control animation speed
time.sleep(delay)
def au_to_miles(au):
# 1 AU = 92,955,807 miles
miles_per_au = 92955807
return au * miles_per_au
timer_clock = ticks_ms()
timer = 3600 * 1000
first_run = True
while True:
try:
if first_run or ticks_diff(ticks_ms(), timer_clock) >= timer:
sentry_response = requests.get(sentry_url)
sentry_json = sentry_response.json()
impact = sentry_json['summary']['ip']
sentry_response.close()
overall_ip = float(impact) * 100
cad_response = requests.get(cad_url)
cad_json = cad_response.json()
earth_distance = au_to_miles(float(cad_json['data'][0][4]))
earth_area.text = f"{cad_json['data'][0][10]}: {int(earth_distance)} mi"
moon_distance = au_to_miles(float(cad_json['data'][1][4]))
moon_area.text = f"{cad_json['data'][1][10]}: {int(moon_distance)} mi"
date = cad_json['data'][0][3]
date = date.split()
date_area.text = f"{date[0]}"
cad_response.close()
impact_area.text = f"Earth Impact: {overall_ip:.4f}%"
display.refresh()
timer_clock = ticks_add(timer_clock, timer)
diagonal_travel(asteroid, start_x=-45, start_y=300, end_x=300, end_y=-45)
time.sleep(0.1)
# pylint: disable=broad-except
except Exception as e:
print("Error:\n", str(e))
print("Resetting microcontroller in 10 seconds")
time.sleep(10)
microcontroller.reset()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

View file

@ -1,109 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIHRjCCBS6gAwIBAgIQV82HcwAIb4tFlHpCQCtGuzANBgkqhkiG9w0BAQsFADBR
MQswCQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSgwJgYDVQQD
DB9FbnRydXN0IE9WIFRMUyBJc3N1aW5nIFJTQSBDQSAxMB4XDTI1MDIwNTE0MDkw
MloXDTI2MDMwMzE0MTg1OFoweDELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlm
b3JuaWExETAPBgNVBAcMCFBhc2FkZW5hMSIwIAYDVQQKDBlKZXQgUHJvcHVsc2lv
biBMYWJvcmF0b3J5MR0wGwYDVQQDDBRzc2QtYXBpLmpwbC5uYXNhLmdvdjCCASIw
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKXzsG9/H8CQzmmu1vwdM1RYrziS
8dzCuw2fqOJXfvOEB+jbZZGIPDvUXCqug0Gb+Z5MsbAdwxT8RsKRuWl/xgRZWr/Y
mtafmEIf3QeKy3/Hu2uUS1GQjTkfmY/3cj6szXJDF4YAC3lngFVWftmGrXhA1Dmm
WqApxguIf2XPjetKjcX1TMdC5XyQ/lsy/vJTX+S8G9HRk+OhO45kAf8AvVeCWCOa
XQ7jpEVBd610RGgD972XNazhoYtL2QKBm6GrSkx1rW/7aiPU0QLbkCoTJIno1yHn
nlYKVYWn0V1uG2vcuChhebX6WOHp7U6KAcaKVbTvaTxv8whi+t4gjcJRbgECAwEA
AaOCAvEwggLtMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUaA1FyjXC55ob8LOE
3dXafwuJwREwaAYIKwYBBQUHAQEEXDBaMDYGCCsGAQUFBzAChipodHRwOi8vY2Vy
dC5zc2wuY29tL0VudHJ1c3QtT1ZUTFMtSS1SMS5jZXIwIAYIKwYBBQUHMAGGFGh0
dHA6Ly9vY3Nwcy5zc2wuY29tMB8GA1UdEQQYMBaCFHNzZC1hcGkuanBsLm5hc2Eu
Z292MCMGA1UdIAQcMBowCAYGZ4EMAQICMA4GDCsGAQQBgqkwAQMBAjAdBgNVHSUE
FjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwOwYDVR0fBDQwMjAwoC6gLIYqaHR0cDov
L2NybHMuc3NsLmNvbS9FbnRydXN0LU9WVExTLUktUjEuY3JsMB0GA1UdDgQWBBQ9
28d7XB5zNhW4Z/FiFGVefv8TpDAOBgNVHQ8BAf8EBAMCBaAwggF/BgorBgEEAdZ5
AgQCBIIBbwSCAWsBaQB3AJaXZL9VWJet90OHaDcIQnfp8DrV9qTzNm5GpD8PyqnG
AAABlNZ6jp0AAAQDAEgwRgIhAM/xkbmguT7NTj3lc6p/F3Um9y7fDhYjzHhWvyDc
HIaQAiEAmRCNTCoJAOFtiXFseiUANEn16Wr3rW5cAUiAJ3mQPewAdwDLOPcViXyE
oURfW8Hd+8lu8ppZzUcKaQWFsMsUwxRY5wAAAZTWeo6SAAAEAwBIMEYCIQCExfjt
QsfbwEpS8cI3YNcXOAW3fMiAh6vOW2wD88iruwIhALfInWVFlobCtQYXLtXq2iqe
HO083hp/cD0oDIChPD9SAHUAZBHEbKQS7KeJHKICLgC8q08oB9QeNSer6v7VA8l9
zfAAAAGU1nqOzgAABAMARjBEAiBTSQczvVZCJ41l+JiC9n8fOkuYNf8jQ1uG5rKy
M/g6IwIgK7YZziGBfBHHKYR8e+IyitJktRevFehnGPEwbMNOO1wwDQYJKoZIhvcN
AQELBQADggIBAGCVD/1ROYGTKPhFeDUFiPjW7JKagsmldnWdtYQ7ran5Ozd8Hkju
XaEi/sDed8wHv1Bf8lesHd3dz2KlaefLVBknntOMSC7fxr7cvkHHgJyJbWtrOESF
JTuy13CdbIHZTxSRtVErCqumUC4omAOfffoGEMlcKvLlIK1NtZTibPsmeUVceMjI
1iZ6OeAOQfZHwlHk49r1zN2D41/5NOT6hpg1bP+rwp/ZhRpT0cB0Q+PBF/GeRCJp
fyLWng/yfILPl2EZoo2RJ0FbdM7VhxGK9359CbKpIPDrZAqagTafEvzRoL9afBAo
n/fht0RCmHiz56vmYo4a742Hwr1Hgempgx+UBlTluf7UGEJ3ju9JEgM/wY+Zc9wm
HyBcQjzPFcDMGGJo/cQwPNYOI06LePpvGiWLe/8WvW+MSPk19JEQj4uOf02Yowzj
1LXFvSzTase6AjrjiOPd57rgmwUKju+/ouX5qyOBbB4dKJ1Q7pOvb2wR+yF/2Mro
YJS73WJVK64mk6aletwcL0uy+vZiZlesBWIiERPR915NiDEeEYSXYtmwu0XhiS8b
jxpMbXIEuhRtA3c8WeqKCbuSnQs+8SMaYpHZ3QgwCreBWl25J5J4nPtW3tPgINkG
gKDVZyGsOJE4t/sQs7zoQAf4rX7eY+U4w+6CIE7XAkACI7Mug6kXOJZJ
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIGVjCCBD6gAwIBAgIQb+Y+3l/BwDr7bXqFvToVbTANBgkqhkiG9w0BAQsFADBO
MQswCQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQD
DBxTU0wuY29tIFRMUyBSU0EgUm9vdCBDQSAyMDIyMB4XDTI0MDgyMjE3NTMzOFoX
DTI3MDgyMjE3NTMzN1owUTELMAkGA1UEBhMCVVMxGDAWBgNVBAoMD1NTTCBDb3Jw
b3JhdGlvbjEoMCYGA1UEAwwfRW50cnVzdCBPViBUTFMgSXNzdWluZyBSU0EgQ0Eg
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKCL+NvDMIL3efFuNaQc
aYwzFpZQd/B6gkTd0s+Cu5jFu3Iw5qxis7xsRQWf739+ohRm42PYEiH7IjErxQAw
+jbLstW7HP7UaTDoYSN/IZ5mG0wHF/QLlAyXsGz9hNPPE31CN8xfA4JFH8jaQftN
QJJnRgWKFys3LK/U2YWbOkx4M50XHFsEDkAuwkt11vEzveJkglfd2O9srAhMsz21
YciZed5VQRncdFbY0hh/hbs9n9eRkmg/ItDOmvKbWiljXP7Pigl9mMJWfnqEJaUd
tt4FzpiizghGgTwGytDAUH7GxtiLdf3F/Vs5UhRUdEQEnm5Y1OVdjLb3CZpTc3vw
XViwO/jG7b64Ancehrrpagbj8yVXGk4Vh8Rj4nf99whmGGP+z9+9T5DXaWTF2xt9
PCbviJaIotT3XO7J2VGZyxnV85us4WVY7/vpCQgUEajsglaFW53UCD3uzuBUR3lW
YDWZvd7wiPg5wWhA6DXL//MKVQ0dvUJ03AI+zxUvYCDyhBPOEx9ojRgF7HOvq+wG
EvX6kV34e5ZLaeR0Wr8iaUq8Wl9oPB0vxuZdJMT4ewNHjLB6IFJY4cszBQBPmxMx
jnRkaDb7B/dlS/I2sjTyxB/n+CwbHbuiwkKqVVQpbYws7cmrkUjLQklqFO1xJrVI
Me3bAHHqOZeMfUYCo5gxrPCLAgMBAAGjggErMIIBJzASBgNVHRMBAf8ECDAGAQH/
AgEAMB8GA1UdIwQYMBaAFPsuN+7jhHonLs0ZNbEzfP/UREK5MEwGCCsGAQUFBwEB
BEAwPjA8BggrBgEFBQcwAoYwaHR0cDovL2NlcnQuc3NsLmNvbS9TU0xjb20tVExT
LVJvb3QtMjAyMi1SU0EuY2VyMBEGA1UdIAQKMAgwBgYEVR0gADAdBgNVHSUEFjAU
BggrBgEFBQcDAgYIKwYBBQUHAwEwQQYDVR0fBDowODA2oDSgMoYwaHR0cDovL2Ny
bHMuc3NsLmNvbS9TU0xjb20tVExTLVJvb3QtMjAyMi1SU0EuY3JsMB0GA1UdDgQW
BBRoDUXKNcLnmhvws4Td1dp/C4nBETAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcN
AQELBQADggIBABNdeusuCgNkxuATfOU8MHWeX7BYQA77yRx/fdV5rEMOdIRMRH/J
Ar7qQ+RH9SY4tqAwkfsKBNhgqZnjzeYOMmOVRTlOQpKJwMwZbX5M1IvuF45EF5BO
dwRBIMAqSxLzFV4CAS1LUEptuA6SGMC6thY4TdQoHN1YR5A/tFmPj9ASDxlqE7Wc
7ZkeL1R8NAKNbcYGPEUXAy9NMiIwwnTqJqQSQXAquf8rhOiOfqWoghMU1xUQ4VgO
aPPCbHCanTLWLLo6MEcHuVNdYvtTUmxixuTcqU2E+XfzUH0qoOskiwxAXncRaM+H
7diEROecsP9PQFui/ll7QmiEE4goazA72Mvk1IsL7+2gI9BrUgWGxGLOoCcJqvUg
Z/8K6N5UJZKXnjOL+tjQVk8qCcF818vuOtOvSAQUeOjSdb1QjaM18Fc62qyclga8
FIxqs4UPJg7ozHrCkPBUXb1MlUu0yf0Y9i8R9woh6S0k4TZGZKKKdxmS7QnF4D6M
Rr60DDCwdUKP5dMmqPsWd2qaBxlaS3wacNqjhdt0DbXmEOz18BRiKbRxaZ4sDxn9
O8XngqHUi9j5bulLTfQSqxDXuMwG0WjkqgkJaCujQ1zIZ7sSIcfGzBevRSy1R32Y
Wp1i1vr3oWsj+Cw9gr8FPEw/pPcW7GWfoJvpiHVQ99u7+vUqjQQ13ieL
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFiTCCA3GgAwIBAgIQb77arXO9CEDii02+1PdbkTANBgkqhkiG9w0BAQsFADBO
MQswCQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQD
DBxTU0wuY29tIFRMUyBSU0EgUm9vdCBDQSAyMDIyMB4XDTIyMDgyNTE2MzQyMloX
DTQ2MDgxOTE2MzQyMVowTjELMAkGA1UEBhMCVVMxGDAWBgNVBAoMD1NTTCBDb3Jw
b3JhdGlvbjElMCMGA1UEAwwcU1NMLmNvbSBUTFMgUlNBIFJvb3QgQ0EgMjAyMjCC
AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANCkCXJPQIgSYT41I57u9nTP
L3tYPc48DRAokC+X94xI2KDYJbFMsBFMF3NQ0CJKY7uB0ylu1bUJPiYYf7ISf5OY
t6/wNr/y7hienDtSxUcZXXTzZGbVXcdotL8bHAajvI9AI7YexoS9UcQbOcGV0ins
S657Lb85/bRi3pZ7QcacoOAGcvvwB5cJOYF0r/c0WRFXCsJbwST0MXMwgsadugL3
PnxEX4MN8/HdIGkWCVDi1FW24IBydm5MR7d1VVm0U3TZlMZBrViKMWYPHqIbKUBO
L9975hYsLfy/7PO0+r4Y9ptJ1O4Fbtk085zx7AGL0SDGD6C1vBdOSHtRwvzpXGk3
R2azaPgVKPC506QVzFpPulJwoxJF3ca6TvvC0PeoUidtbnm1jPx7jMEWTO6Af77w
dr5BUxIzrlo4QqvXDz5BjXYHMtWrifZOZ9mxQnUjbvPNQrL8VfVThxc7wDNY8VLS
+YCk8OjwO4s4zKTGkH8PnP2L0aPP2oOnaclQNtVcBdIKQXTbYxE3waWglksejBYS
d66UNHsef8JmAOSqg+qKkK3ONkRN0VHpvB/zagX9wHQfJRlAUW7qglFA35u5CCoG
AtUjHBPW6dvbxrB6y3snm/vg1UYk7RBLY0ulBY+6uB0rpvqR4pJSvezrZ5dtmi2f
gTIFZzL7SAg/2SW4BCUvAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0j
BBgwFoAU+y437uOEeicuzRk1sTN8/9REQrkwHQYDVR0OBBYEFPsuN+7jhHonLs0Z
NbEzfP/UREK5MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAjYlt
hEUY8U+zoO9opMAdrDC8Z2awms22qyIZZtM7QbUQnRC6cm4pJCAcAZli05bg4vsM
QtfhWsSWTVTNj8pDU/0quOr4ZcoBwq1gaAafORpR2eCNJvkLTqVTJXojpBzOCBvf
R4iyrT7gJ4eLSYwfqUdYe5byiB0YrrPRpgqU+tvT5TgKa3kSM/tKWTcWQA673vWJ
DPFs0/dRa1419dvAJuoSc06pkZCmF8NsLzjUo3KUQyxi4U5cMj29TH0ZR6LDSeeW
P4+a0zvkEdiLA9z2tmBVGKaBUfPhqBVq6+AL8BQx1rmMRTqoENjwuSfr98t67wVy
lrXEj5ZzxOhWc5y8aVFjvO9nHEMaX3cZHxj4HCUp+UmZKbaSPaKDN7EgkaibMOlq
bLQjk2UEqxHzDh1TJElTHaE/nUiSEeJ9DU/1172iWD54nR4fK/4huxoTtrEoZP2w
AgDHbICivRZQIA9ygV/MlP+7mea6kMvq+cYMwq7FGc4zoWtcu358NFcXrfA/rs3q
r5nsLFR+jM4uElZI7xc7P0peYNLcdDa8pUNjyw9bowJWCZ4kLOGGgYz+qxcs+sji
Mho6/4UIyYOf8kpIEFR3N+2ivEC+5BB09+Rbu7nzifmPQdjH5FCQNYA+HLhNkNPU
98OwoX6EyneSMSy4kLGCenROmxMmtNVQZlR4rmA=
-----END CERTIFICATE-----

View file

@ -1,7 +1,6 @@
# SPDX-FileCopyrightText: 2022 Liz Clark for Adafruit Industries
# SPDX-License-Identifier: MIT
from os import getenv
import time
import json
import board
@ -39,20 +38,15 @@ nau7802 = NAU7802(board.STEMMA_I2C(), address=0x2A, active_channels=2)
nau7802.gain = 128
enabled = nau7802.enable(True)
# Get WiFi details, ensure these are setup in settings.toml
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
if None in [ssid, password]:
raise RuntimeError(
"WiFi settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"at a minimum."
)
# Get wifi details and more from a secrets.py file
try:
from secrets import secrets
except ImportError:
print("WiFi secrets are kept in secrets.py, please add them there!")
raise
print("Connecting to WiFi...")
wifi.radio.connect(ssid, password)
wifi.radio.connect(secrets["ssid"], secrets["password"])
print("Connected to WiFi!")
# check system time
@ -71,7 +65,7 @@ else:
esp = None
pool = socketpool.SocketPool(wifi.radio)
device = IoTCentralDevice(
pool, esp, getenv("id_scope"), getenv("device_id"), getenv("device_primary_key")
pool, esp, secrets['id_scope'], secrets['device_id'], secrets['device_primary_key']
)
display.fill(0)
display.print("DIALING*")
@ -197,8 +191,6 @@ avg_oz = []
values = []
val_offset = 0
avg_values = []
the_ounces = 0
the_grams = 0
# initial reading from the scale
for w in range(5):

View file

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

View file

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

View file

@ -1,44 +0,0 @@
// SPDX-FileCopyrightText: 2020 Phil Schatzmann
//
// SPDX-License-Identifier: GPL-3.0-or-later
/*
Streaming Music from Bluetooth
Copyright (C) 2020 Phil Schatzmann
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// ==> Example which shows how to use the built in ESP32 I2S >= 3.0.0
#include "ESP_I2S.h"
#include "BluetoothA2DPSink.h"
const uint8_t I2S_SCK = 8; /* Audio data bit clock */
const uint8_t I2S_WS = 7; /* Audio data left and right clock */
const uint8_t I2S_SDOUT = 14; /* ESP32 audio data output (to speakers) */
I2SClass i2s;
BluetoothA2DPSink a2dp_sink(i2s);
void setup() {
i2s.setPins(I2S_SCK, I2S_WS, I2S_SDOUT);
if (!i2s.begin(I2S_MODE_STD, 44100, I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO, I2S_STD_SLOT_BOTH)) {
Serial.println("Failed to initialize I2S!");
while (1); // do nothing
}
a2dp_sink.start("Lumon Industries Speaker");
}
void loop() {
}

View file

@ -1,730 +0,0 @@
// SPDX-FileCopyrightText: 2025 John Park for Adafruit Industries
//
// SPDX-License-Identifier: MIT
//
// made with assistance from Claude Sonnet 4
//
// Unified Bluetooth HID Bridge - BT Classic & BLE
// put keyboard in pairing mode, then press reset or BOOT button
// slow blinks mean scanning
// fast blinks mean keyboard detected, press a keyboard key to connect
// === CONFIGURATION ===
#define DEBUG_MODE false // Set to false to reduce serial output
#define BLINK_MODE true // Set to false for solid LED (no keypress blinks)
#define SCAN_MODE "Both" // Options: "BT_Classic", "BLE", "Both"
// Include both Bluetooth stacks
extern "C" {
#include "btstack.h"
}
#include <BluetoothHCI.h>
#include <BluetoothHIDMaster.h>
#include <Keyboard.h>
#include <Mouse.h>
// Connection state management
typedef enum {
INIT,
SCANNING_CLASSIC,
CLASSIC_CONNECTING,
CLASSIC_CONNECTED,
SCANNING_BLE,
BLE_CONNECTED,
BOTH_FAILED,
DISCONNECTED
} connection_state_t;
connection_state_t connection_state = INIT;
// BT Classic components
BluetoothHCI hci;
bd_addr_t target_keyboard_addr;
bool target_keyboard_found = false;
uint16_t hid_control_cid = 0;
uint16_t hid_interrupt_cid = 0;
static btstack_packet_callback_registration_t hci_event_callback_registration;
// BLE components
BluetoothHIDMaster ble_hid;
HIDKeyStream keystream;
// Shared state tracking
static uint8_t last_modifiers = 0;
static uint8_t last_keys[6] = {0};
bool keyPressed[256] = {0}; // Track which keys are currently pressed
// LED management
unsigned long ledTimer = 0;
bool ledState = false;
unsigned long ledOffTime = 0;
bool ledBlinking = false;
int pairingBlinks = 0;
unsigned long pairingBlinkTimer = 0;
// Timing management
unsigned long lastScan = 0;
unsigned long stateStartTime = 0;
const unsigned long CLASSIC_SCAN_TIMEOUT = 10000; // 10 seconds
const unsigned long BLE_SCAN_TIMEOUT = 15000; // 15 seconds
void setup() {
Serial.begin(115200);
delay(3000);
Serial.println("=== UNIFIED BLUETOOTH HID BRIDGE ===");
Serial.printf("Scan mode: %s\n", SCAN_MODE);
Serial.println("Put your Bluetooth device in pairing mode now");
// Initialize LED
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
Serial.println("LED initialized");
// Initialize USB HID
Keyboard.begin();
Mouse.begin();
Serial.println("USB HID initialized");
// Start with LED blinking to show we're alive
ledTimer = millis();
ledState = true;
digitalWrite(LED_BUILTIN, HIGH);
Serial.println("Starting Bluetooth stack initialization...");
// Choose scan mode based on user setting
if (strcmp(SCAN_MODE, "BT_Classic") == 0) {
Serial.println("BT Classic only mode");
connection_state = SCANNING_CLASSIC;
stateStartTime = millis();
initAndScanClassic();
} else if (strcmp(SCAN_MODE, "BLE") == 0) {
Serial.println("BLE only mode");
connection_state = SCANNING_BLE;
stateStartTime = millis();
initAndScanBLE();
} else {
Serial.println("Both protocols mode - trying Classic first");
connection_state = SCANNING_CLASSIC;
stateStartTime = millis();
initAndScanClassic();
}
}
void initAndScanClassic() {
Serial.println("\n=== INITIALIZING BT CLASSIC STACK ===");
// Initialize HCI for Classic
l2cap_init();
sm_init();
gap_set_default_link_policy_settings(LM_LINK_POLICY_ENABLE_SNIFF_MODE | LM_LINK_POLICY_ENABLE_ROLE_SWITCH);
hci_set_master_slave_policy(HCI_ROLE_MASTER);
hci_set_inquiry_mode(INQUIRY_MODE_RSSI_AND_EIR);
Serial.println("BTStack components initialized");
hci.install();
hci.begin();
Serial.println("HCI installed and started");
// Register BTStack event handler
hci_event_callback_registration.callback = &classic_packet_handler;
hci_add_event_handler(&hci_event_callback_registration);
Serial.println("Event handler registered");
// Turn on Bluetooth
hci_power_control(HCI_POWER_ON);
Serial.println("Bluetooth power ON");
delay(2000); // Give it time to initialize
Serial.println("Starting BT Classic device scan...");
scanForClassicDevices();
}
void scanForClassicDevices() {
Serial.println("Scanning for BT Classic devices...");
auto devices = hci.scan(BluetoothHCI::any_cod);
Serial.printf("Classic scan completed. Found %d devices:\n", devices.size());
if (devices.size() == 0) {
Serial.println("No Classic devices found. Will try BLE after timeout.");
return;
}
Serial.println("Address | RSSI | Class | Name");
Serial.println("------------------|------|----------|------------------");
for (auto device : devices) {
uint32_t cod = device.deviceClass();
uint8_t majorClass = (cod >> 8) & 0x1F;
uint8_t minorClass = (cod >> 2) & 0x3F;
Serial.printf("%s | %4d | %08lx | %s",
device.addressString(), device.rssi(), cod, device.name());
// Look for HID keyboards in Classic scan
if (majorClass == 5 && (minorClass & 0x10)) { // HID Keyboard
Serial.print(" [HID KEYBOARD] *** CONNECTING ***");
// We found a Classic keyboard!
const char* addrStr = device.addressString();
sscanf(addrStr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
&target_keyboard_addr[0], &target_keyboard_addr[1], &target_keyboard_addr[2],
&target_keyboard_addr[3], &target_keyboard_addr[4], &target_keyboard_addr[5]);
target_keyboard_found = true;
Serial.printf("\nFound Classic HID keyboard: %s\n", device.name());
Serial.printf("Address: %s\n", device.addressString());
// Start Classic connection
connection_state = CLASSIC_CONNECTING;
stateStartTime = millis();
startClassicConnection();
Serial.println();
return; // Exit the loop - we found our keyboard
} else {
// Show device type for debugging
switch (majorClass) {
case 1: Serial.print(" [Computer]"); break;
case 2: Serial.print(" [Phone]"); break;
case 3: Serial.print(" [Network]"); break;
case 4: Serial.print(" [Audio/Video]"); break;
case 5: Serial.print(" [HID Device]"); break;
default: Serial.printf(" [Class:%d]", majorClass); break;
}
}
Serial.println();
}
Serial.println("No HID keyboards found in Classic scan.");
}
void fallbackToBLE() {
Serial.println("\n=== FALLING BACK TO BLE ===");
connection_state = SCANNING_BLE;
stateStartTime = millis();
initAndScanBLE();
}
void initAndScanBLE() {
Serial.println("Initializing BLE stack...");
// Setup the HID key to ASCII conversion
keystream.begin();
Serial.println("KeyStream initialized");
// Setup BLE callbacks
setupBLECallbacks();
Serial.println("BLE callbacks configured");
// Start BLE HID master
ble_hid.begin(true);
Serial.println("BLE HID master started");
// Start BLE connection attempt
ble_hid.connectBLE();
Serial.println("BLE connection initiated - waiting for device...");
Serial.println("(BLE devices will be detected on first keypress)");
}
void setupBLECallbacks() {
// BLE Mouse callbacks
ble_hid.onMouseMove([](void *cbdata, int dx, int dy, int dw) {
(void) cbdata;
if (DEBUG_MODE) {
Serial.printf("BLE Mouse: X:%d Y:%d Wheel:%d\n", dx, dy, dw);
}
Mouse.move(dx, dy);
if (dw != 0) Mouse.move(0, 0, dw);
blinkOnActivity();
});
ble_hid.onMouseButton([](void *cbdata, int butt, bool down) {
(void) cbdata;
if (DEBUG_MODE) {
Serial.printf("BLE Mouse: Button %d %s\n", butt, down ? "DOWN" : "UP");
}
if (down) {
if (butt == 1) Mouse.press(MOUSE_LEFT);
else if (butt == 2) Mouse.press(MOUSE_RIGHT);
else if (butt == 3) Mouse.press(MOUSE_MIDDLE);
} else {
if (butt == 1) Mouse.release(MOUSE_LEFT);
else if (butt == 2) Mouse.release(MOUSE_RIGHT);
else if (butt == 3) Mouse.release(MOUSE_MIDDLE);
}
blinkOnActivity();
});
// BLE Keyboard callbacks
ble_hid.onKeyDown([](void *cbdata, int key) {
handleBLEKey(key, true);
}, (void *)true);
ble_hid.onKeyUp([](void *cbdata, int key) {
handleBLEKey(key, false);
}, (void *)false);
}
void startClassicConnection() {
if (!target_keyboard_found) {
Serial.println("ERROR: No Classic target keyboard found");
fallbackToBLE();
return;
}
Serial.println("=== STARTING CLASSIC HID CONNECTION ===");
Serial.println("Creating L2CAP Control channel...");
// Create control channel first
l2cap_create_channel(&classic_packet_handler, target_keyboard_addr, BLUETOOTH_PSM_HID_CONTROL,
48, &hid_control_cid);
}
void handleBLEKey(int key, bool state) {
if (DEBUG_MODE) {
Serial.printf("BLE Keyboard: %02x %s\n", key, state ? "DOWN" : "UP");
}
if (key >= 256) return; // Bounds check
// Check if this is the first BLE key press (connection detection)
if (connection_state == SCANNING_BLE) {
Serial.printf("\n*** BLE KEYBOARD DETECTED ON FIRST KEYPRESS ***\n");
Serial.printf("=== BLE DEVICE CONNECTED ===\n");
Serial.printf("Ready to forward BLE input to USB.\n");
Serial.printf("========================\n");
connection_state = BLE_CONNECTED;
celebrationBlinks(8); // 4 blinks for BLE
// After celebration, LED will go solid (handled in LED patterns)
}
// Forward the key to USB
forwardBLEKeyToUSB(key, state);
blinkOnActivity();
}
void classic_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
UNUSED(size);
if (packet_type == HCI_EVENT_PACKET) {
uint8_t event = hci_event_packet_get_type(packet);
switch (event) {
case BTSTACK_EVENT_STATE:
if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING) {
Serial.println("Classic BTstack ready");
}
break;
case L2CAP_EVENT_CHANNEL_OPENED:
{
uint16_t cid = l2cap_event_channel_opened_get_local_cid(packet);
uint16_t psm = l2cap_event_channel_opened_get_psm(packet);
uint8_t status = l2cap_event_channel_opened_get_status(packet);
if (status) {
Serial.printf("Classic L2CAP connection failed, status 0x%02x\n", status);
handleClassicConnectionError(status);
return;
}
Serial.printf("Classic L2CAP channel opened: CID=0x%04x, PSM=0x%04x\n", cid, psm);
if (psm == BLUETOOTH_PSM_HID_CONTROL) {
hid_control_cid = cid;
Serial.println("Classic HID Control channel established");
Serial.println("Creating L2CAP Interrupt channel...");
// Create interrupt channel
l2cap_create_channel(&classic_packet_handler, target_keyboard_addr, BLUETOOTH_PSM_HID_INTERRUPT,
48, &hid_interrupt_cid);
} else if (psm == BLUETOOTH_PSM_HID_INTERRUPT) {
hid_interrupt_cid = cid;
Serial.println("Classic HID Interrupt channel established");
Serial.println("*** CLASSIC HID CONNECTION COMPLETE ***");
connection_state = CLASSIC_CONNECTED;
celebrationBlinks(6); // 3 blinks for Classic
// After celebration, LED will go solid (handled in LED patterns)
}
}
break;
case L2CAP_EVENT_CHANNEL_CLOSED:
{
uint16_t cid = l2cap_event_channel_closed_get_local_cid(packet);
Serial.printf("Classic L2CAP channel closed: CID=0x%04x\n", cid);
if (cid == hid_control_cid) {
hid_control_cid = 0;
} else if (cid == hid_interrupt_cid) {
hid_interrupt_cid = 0;
}
if (hid_control_cid == 0 && hid_interrupt_cid == 0) {
Serial.println("Classic HID connection lost.");
connection_state = DISCONNECTED;
target_keyboard_found = false;
}
}
break;
default:
break;
}
} else if (packet_type == L2CAP_DATA_PACKET) {
// Classic HID input data
if (channel == hid_interrupt_cid) {
if (DEBUG_MODE) {
Serial.printf("Classic HID Input Data (%d bytes): ", size);
for (int i = 0; i < size; i++) {
Serial.printf("%02X ", packet[i]);
}
Serial.println();
}
processClassicHIDReport(packet, size);
}
}
}
void handleClassicConnectionError(uint8_t status) {
Serial.printf("Classic connection failed with status 0x%02x - ", status);
switch (status) {
case 0x04: Serial.println("Page timeout"); break;
case 0x05: Serial.println("Authentication failure"); break;
case 0x08: Serial.println("Connection timeout"); break;
default: Serial.printf("Error code 0x%02x\n", status); break;
}
Serial.println("Trying BLE fallback...");
fallbackToBLE();
}
void processClassicHIDReport(uint8_t *report, uint16_t length) {
if (length < 10) {
Serial.printf("Invalid Classic HID report length: %d\n", length);
return;
}
uint8_t modifiers = report[2];
uint8_t *keys = &report[4];
// Forward to USB HID
forwardClassicToUSB(modifiers, keys);
blinkOnActivity();
}
// Shared key mapping function
uint8_t hidToUsbKey(uint8_t hidKey) {
switch (hidKey) {
// Letters
case 0x04: return 'a'; case 0x05: return 'b'; case 0x06: return 'c'; case 0x07: return 'd';
case 0x08: return 'e'; case 0x09: return 'f'; case 0x0A: return 'g'; case 0x0B: return 'h';
case 0x0C: return 'i'; case 0x0D: return 'j'; case 0x0E: return 'k'; case 0x0F: return 'l';
case 0x10: return 'm'; case 0x11: return 'n'; case 0x12: return 'o'; case 0x13: return 'p';
case 0x14: return 'q'; case 0x15: return 'r'; case 0x16: return 's'; case 0x17: return 't';
case 0x18: return 'u'; case 0x19: return 'v'; case 0x1A: return 'w'; case 0x1B: return 'x';
case 0x1C: return 'y'; case 0x1D: return 'z';
// Numbers
case 0x1E: return '1'; case 0x1F: return '2'; case 0x20: return '3'; case 0x21: return '4';
case 0x22: return '5'; case 0x23: return '6'; case 0x24: return '7'; case 0x25: return '8';
case 0x26: return '9'; case 0x27: return '0';
// Special keys
case 0x28: return KEY_RETURN; case 0x29: return KEY_ESC;
case 0x2A: return KEY_BACKSPACE; case 0x2B: return KEY_TAB;
case 0x2C: return ' '; case 0x39: return KEY_CAPS_LOCK;
// Symbols
case 0x2D: return '-'; case 0x2E: return '='; case 0x2F: return '['; case 0x30: return ']';
case 0x31: return '\\'; case 0x33: return ';'; case 0x34: return '\''; case 0x35: return '`';
case 0x36: return ','; case 0x37: return '.'; case 0x38: return '/';
// Function keys
case 0x3A: return KEY_F1; case 0x3B: return KEY_F2; case 0x3C: return KEY_F3;
case 0x3D: return KEY_F4; case 0x3E: return KEY_F5; case 0x3F: return KEY_F6;
case 0x40: return KEY_F7; case 0x41: return KEY_F8; case 0x42: return KEY_F9;
case 0x43: return KEY_F10; case 0x44: return KEY_F11; case 0x45: return KEY_F12;
// Arrow keys
case 0x4F: return KEY_RIGHT_ARROW; case 0x50: return KEY_LEFT_ARROW;
case 0x51: return KEY_DOWN_ARROW; case 0x52: return KEY_UP_ARROW;
// Navigation
case 0x49: return KEY_INSERT; case 0x4A: return KEY_HOME;
case 0x4B: return KEY_PAGE_UP; case 0x4C: return KEY_DELETE;
case 0x4D: return KEY_END; case 0x4E: return KEY_PAGE_DOWN;
// Modifiers
case 0xE0: return KEY_LEFT_CTRL; case 0xE1: return KEY_LEFT_SHIFT;
case 0xE2: return KEY_LEFT_ALT; case 0xE3: return KEY_LEFT_GUI;
case 0xE4: return KEY_RIGHT_CTRL; case 0xE5: return KEY_RIGHT_SHIFT;
case 0xE6: return KEY_RIGHT_ALT; case 0xE7: return KEY_RIGHT_GUI;
default: return 0;
}
}
void forwardClassicToUSB(uint8_t modifiers, uint8_t *keys) {
// Handle modifier changes
uint8_t modifier_changes = modifiers ^ last_modifiers;
// Process each modifier bit
if (modifier_changes & 0x01) (modifiers & 0x01) ? Keyboard.press(KEY_LEFT_CTRL) : Keyboard.release(KEY_LEFT_CTRL);
if (modifier_changes & 0x02) (modifiers & 0x02) ? Keyboard.press(KEY_LEFT_SHIFT) : Keyboard.release(KEY_LEFT_SHIFT);
if (modifier_changes & 0x04) (modifiers & 0x04) ? Keyboard.press(KEY_LEFT_ALT) : Keyboard.release(KEY_LEFT_ALT);
if (modifier_changes & 0x08) (modifiers & 0x08) ? Keyboard.press(KEY_LEFT_GUI) : Keyboard.release(KEY_LEFT_GUI);
if (modifier_changes & 0x10) (modifiers & 0x10) ? Keyboard.press(KEY_RIGHT_CTRL) : Keyboard.release(KEY_RIGHT_CTRL);
if (modifier_changes & 0x20) (modifiers & 0x20) ? Keyboard.press(KEY_RIGHT_SHIFT) : Keyboard.release(KEY_RIGHT_SHIFT);
if (modifier_changes & 0x40) (modifiers & 0x40) ? Keyboard.press(KEY_RIGHT_ALT) : Keyboard.release(KEY_RIGHT_ALT);
if (modifier_changes & 0x80) (modifiers & 0x80) ? Keyboard.press(KEY_RIGHT_GUI) : Keyboard.release(KEY_RIGHT_GUI);
// Handle key releases
for (int i = 0; i < 6; i++) {
if (last_keys[i] != 0) {
bool still_pressed = false;
for (int j = 0; j < 6; j++) {
if (keys[j] == last_keys[i]) {
still_pressed = true;
break;
}
}
if (!still_pressed) {
uint8_t usb_key = hidToUsbKey(last_keys[i]);
if (usb_key != 0) Keyboard.release(usb_key);
}
}
}
// Handle key presses
for (int i = 0; i < 6; i++) {
if (keys[i] != 0) {
bool already_pressed = false;
for (int j = 0; j < 6; j++) {
if (last_keys[j] == keys[i]) {
already_pressed = true;
break;
}
}
if (!already_pressed) {
uint8_t usb_key = hidToUsbKey(keys[i]);
if (usb_key != 0) Keyboard.press(usb_key);
}
}
}
// Save current state
last_modifiers = modifiers;
memcpy(last_keys, keys, 6);
}
void forwardBLEKeyToUSB(int key, bool state) {
if (key >= 256) return;
bool isModifier = (key >= 0xE0 && key <= 0xE7);
if (isModifier) {
uint8_t usbKey = hidToUsbKey(key);
if (state) Keyboard.press(usbKey);
else Keyboard.release(usbKey);
return;
}
// Handle regular keys
uint8_t usbKey = hidToUsbKey(key);
if (usbKey != 0) {
if (state && !keyPressed[key]) {
keyPressed[key] = true;
Keyboard.press(usbKey);
} else if (!state && keyPressed[key]) {
keyPressed[key] = false;
Keyboard.release(usbKey);
}
}
}
void celebrationBlinks(int count) {
pairingBlinks = count;
pairingBlinkTimer = millis();
digitalWrite(LED_BUILTIN, HIGH);
}
void blinkOnActivity() {
if (BLINK_MODE && pairingBlinks == 0 && (connection_state == CLASSIC_CONNECTED || connection_state == BLE_CONNECTED)) {
digitalWrite(LED_BUILTIN, LOW); // Turn OFF briefly to show activity
ledBlinking = true;
ledOffTime = millis() + 50; // Stay off for 50ms
}
}
void loop() {
unsigned long currentTime = millis();
// Handle state timeouts
handleStateTimeouts(currentTime);
// Handle LED patterns
handleLEDPatterns(currentTime);
// Handle BOOTSEL button
handleBootselButton();
delay(10);
}
void handleStateTimeouts(unsigned long currentTime) {
switch (connection_state) {
case SCANNING_CLASSIC:
if (currentTime - stateStartTime > CLASSIC_SCAN_TIMEOUT) {
if (strcmp(SCAN_MODE, "BT_Classic") == 0) {
Serial.println("Classic scan timeout - BT Classic only mode, retrying...");
stateStartTime = currentTime;
scanForClassicDevices(); // Retry Classic scan
} else {
Serial.println("Classic scan timeout - falling back to BLE");
fallbackToBLE();
}
}
break;
case SCANNING_BLE:
if (currentTime - stateStartTime > BLE_SCAN_TIMEOUT) {
if (strcmp(SCAN_MODE, "BLE") == 0) {
Serial.println("BLE scan timeout - BLE only mode, retrying...");
stateStartTime = currentTime;
initAndScanBLE(); // Retry BLE scan
} else {
Serial.println("BLE scan timeout - restarting from Classic");
connection_state = BOTH_FAILED;
stateStartTime = currentTime;
}
}
break;
case BOTH_FAILED:
if (currentTime - stateStartTime > 5000) { // Wait 5 seconds before retry
Serial.println("Retrying scan sequence...");
connection_state = SCANNING_CLASSIC;
stateStartTime = currentTime;
initAndScanClassic();
}
break;
}
}
void handleLEDPatterns(unsigned long currentTime) {
// Handle pairing celebration blinks first
if (pairingBlinks > 0) {
if (currentTime - pairingBlinkTimer >= 150) {
pairingBlinks--;
bool state = (pairingBlinks % 2 == 0);
digitalWrite(LED_BUILTIN, state);
pairingBlinkTimer = currentTime;
}
return;
}
// Handle activity blinks
if (ledBlinking && currentTime >= ledOffTime) {
digitalWrite(LED_BUILTIN, HIGH); // Turn back ON after brief off period
ledBlinking = false;
return;
}
// Handle state-based LED patterns
switch (connection_state) {
case SCANNING_CLASSIC:
case SCANNING_BLE:
// Slow blink while scanning (1 second cycle)
if (currentTime - ledTimer >= 1000) {
ledState = !ledState;
digitalWrite(LED_BUILTIN, ledState);
ledTimer = currentTime;
}
break;
case CLASSIC_CONNECTING:
// Fast blink while connecting - tells user to press a key
if (currentTime - ledTimer >= 250) {
ledState = !ledState;
digitalWrite(LED_BUILTIN, ledState);
ledTimer = currentTime;
}
break;
case CLASSIC_CONNECTED:
case BLE_CONNECTED:
// Solid ON while connected (unless doing activity blinks)
if (!ledBlinking) {
digitalWrite(LED_BUILTIN, HIGH);
}
break;
case BOTH_FAILED:
case DISCONNECTED:
// Very slow pulse when failed/disconnected (2 second cycle)
if (currentTime - ledTimer >= 2000) {
ledState = !ledState;
digitalWrite(LED_BUILTIN, ledState);
ledTimer = currentTime;
}
break;
}
}
void handleBootselButton() {
if (BOOTSEL) {
while (BOOTSEL) delay(1);
Serial.println("\nBOOTSEL pressed - restarting scan sequence");
// Clean up current connections
if (connection_state == CLASSIC_CONNECTED) {
if (hid_control_cid) l2cap_disconnect(hid_control_cid);
if (hid_interrupt_cid) l2cap_disconnect(hid_interrupt_cid);
} else if (connection_state == BLE_CONNECTED) {
ble_hid.disconnect();
ble_hid.clearPairing();
}
// Reset all state
Keyboard.releaseAll();
Mouse.release(MOUSE_LEFT | MOUSE_RIGHT | MOUSE_MIDDLE);
for (int i = 0; i < 256; i++) keyPressed[i] = false;
memset(last_keys, 0, 6);
last_modifiers = 0;
target_keyboard_found = false;
hid_control_cid = 0;
hid_interrupt_cid = 0;
pairingBlinks = 0;
ledBlinking = false;
// Restart from appropriate scan mode
if (strcmp(SCAN_MODE, "BT_Classic") == 0) {
connection_state = SCANNING_CLASSIC;
Serial.println("Restarting BT Classic scan...");
initAndScanClassic();
} else if (strcmp(SCAN_MODE, "BLE") == 0) {
connection_state = SCANNING_BLE;
Serial.println("Restarting BLE scan...");
initAndScanBLE();
} else {
connection_state = SCANNING_CLASSIC;
Serial.println("Restarting unified scan...");
initAndScanClassic();
}
}
}

View file

@ -22,17 +22,31 @@ clue.pixel.fill(0) # turn off NeoPixel
clue_display = displayio.Group()
# draw the dry plant
dry_plant_bmp = displayio.OnDiskBitmap("dry.bmp")
dry_plant_file = open("dry.bmp", "rb")
dry_plant_bmp = displayio.OnDiskBitmap(dry_plant_file)
# CircuitPython 6 & 7 compatible
dry_plant_sprite = displayio.TileGrid(
dry_plant_bmp, pixel_shader=dry_plant_bmp.pixel_shader
dry_plant_bmp,
pixel_shader=getattr(dry_plant_bmp, "pixel_shader", displayio.ColorConverter()),
)
# CircuitPython 7 compatible
# dry_plant_sprite = displayio.TileGrid(
# dry_plant_bmp, pixel_shader=dry_plant_bmp.pixel_shader
# )
clue_display.append(dry_plant_sprite)
# draw the happy plant on top (so it can be moved out of the way when needed)
happy_plant_bmp = displayio.OnDiskBitmap("happy.bmp")
happy_plant_file = open("happy.bmp", "rb")
happy_plant_bmp = displayio.OnDiskBitmap(happy_plant_file)
# CircuitPython 6 & 7 compatible
happy_plant_sprite = displayio.TileGrid(
happy_plant_bmp, pixel_shader=happy_plant_bmp.pixel_shader
happy_plant_bmp,
pixel_shader=getattr(happy_plant_bmp, "pixel_shader", displayio.ColorConverter()),
)
# CircuitPython 7 compatible
# happy_plant_sprite = displayio.TileGrid(
# happy_plant_bmp, pixel_shader=happy_plant_bmp.pixel_shader
# )
clue_display.append(happy_plant_sprite)
# Create text

View file

@ -5,7 +5,6 @@ import time
import board
import terminalio
import displayio
import i2cdisplaybus
from digitalio import DigitalInOut
from adafruit_mcp2515.canio import Message, RemoteTransmissionRequest
from adafruit_mcp2515 import MCP2515 as CAN
@ -16,7 +15,7 @@ displayio.release_displays()
i2c = board.STEMMA_I2C()
# STEMMA OLED setup
display_bus = i2cdisplaybus.I2CDisplayBus(i2c, device_address=0x3D, reset=None)
display_bus = displayio.I2CDisplay(i2c, device_address=0x3D, reset=None)
display = adafruit_displayio_ssd1306.SSD1306(display_bus, width=128, height=64)
cs = DigitalInOut(board.A3)

View file

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

View file

@ -1,2 +0,0 @@
Files for the Adafruit Learning System guide Clue Coffee Scale
https://learn.adafruit.com/clue-coffee-scale/overview

View file

@ -1,6 +0,0 @@
## CLUE Project Subdirectory
Adafruit Learning System Guides relating to the Adafruit CLUE learning boards should be
placed in this folder (rather than the top level Learning System repo).
Please support Open Source software from Adafruit by buying Adafruit hardware

0
CLUE/CLUE_Altimeter/code.py → CLUE_Altimeter/code.py Normal file → Executable file
View file

View file

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View file

View file

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

0
CLUE/CLUE_Beacon/bg.bmp → CLUE_Beacon/bg.bmp Normal file → Executable file
View file

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 113 KiB

View file

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 113 KiB

View file

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 113 KiB

View file

@ -22,14 +22,32 @@ splash = displayio.Group()
# bad egg
BAD_EGG_FILENAME = "broken_egg.bmp"
begg_bmp = displayio.OnDiskBitmap(BAD_EGG_FILENAME)
begg_sprite = displayio.TileGrid(begg_bmp, pixel_shader=begg_bmp.pixel_shader)
# CircuitPython 6 & 7 compatible
begg_file = open(BAD_EGG_FILENAME, "rb")
begg_bmp = displayio.OnDiskBitmap(begg_file)
begg_sprite = displayio.TileGrid(
begg_bmp,
pixel_shader=getattr(begg_bmp, 'pixel_shader', displayio.ColorConverter())
)
# # CircuitPython 7+ compatible
# begg_bmp = displayio.OnDiskBitmap(BAD_EGG_FILENAME)
# begg_sprite = displayio.TileGrid(begg_bmp, pixel_shader=begg_bmp.pixel_shader)
# good egg
GOOD_EGG_FILENAME = "good_egg.bmp"
gegg_bmp = displayio.OnDiskBitmap(GOOD_EGG_FILENAME)
gegg_sprite = displayio.TileGrid(gegg_bmp, pixel_shader=gegg_bmp.pixel_shader)
# CircuitPython 6 & 7 compatible
gegg_file = open(GOOD_EGG_FILENAME, "rb")
gegg_bmp = displayio.OnDiskBitmap(gegg_file)
gegg_sprite = displayio.TileGrid(
gegg_bmp,
pixel_shader=getattr(gegg_bmp, 'pixel_shader', displayio.ColorConverter())
)
# # CircuitPython 7+ compatible
# gegg_bmp = displayio.OnDiskBitmap(GOOD_EGG_FILENAME)
# gegg_sprite = displayio.TileGrid(gegg_bmp, pixel_shader=gegg_bmp.pixel_shader)
# draw the bad egg!
splash.append(begg_sprite)

View file

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 113 KiB

View file

@ -21,19 +21,39 @@ clue_display = displayio.Group()
# draw the background image
WASH_ON_FILENAME = "wash_on.bmp"
wash_on_bmp = displayio.OnDiskBitmap(WASH_ON_FILENAME)
wash_on_sprite = displayio.TileGrid(wash_on_bmp, pixel_shader=wash_on_bmp.pixel_shader)
# CircuitPython 6 & 7 compatible
wash_on_file = open(WASH_ON_FILENAME, "rb")
wash_on_bmp = displayio.OnDiskBitmap(wash_on_file)
wash_on_sprite = displayio.TileGrid(
wash_on_bmp,
pixel_shader=getattr(wash_on_bmp, 'pixel_shader', displayio.ColorConverter())
)
# # CircuitPython 7+ compatible
# wash_on_bmp = displayio.OnDiskBitmap(WASH_ON_FILENAME)
# wash_on_sprite = displayio.TileGrid(wash_on_bmp, pixel_shader=wash_on_bmp.pixel_shader)
clue_display.append(wash_on_sprite)
# draw the foreground image
WASH_OFF_FILENAME = "wash_off.bmp"
wash_off_bmp = displayio.OnDiskBitmap(WASH_OFF_FILENAME)
wash_off_sprite = displayio.TileGrid(wash_off_bmp, pixel_shader=wash_off_bmp.pixel_shader)
# CircuitPython 6 & 7 compatible
wash_off_file = open(WASH_OFF_FILENAME, "rb")
wash_off_bmp = displayio.OnDiskBitmap(wash_off_file)
wash_off_sprite = displayio.TileGrid(
wash_off_bmp,
pixel_shader=getattr(wash_off_bmp, 'pixel_shader', displayio.ColorConverter())
)
# # CircuitPython 7+ compatible
# wash_off_bmp = displayio.OnDiskBitmap(WASH_OFF_FILENAME)
# wash_off_sprite = displayio.TileGrid(wash_off_bmp, pixel_shader=wash_off_bmp.pixel_shader)
clue_display.append(wash_off_sprite)
# Create text
# first create the group
text_group = displayio.Group()

View file

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 113 KiB

View file

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 113 KiB

View file

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

View file

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

View file

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View file

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View file

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View file

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View file

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View file

View file

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