Adafruit_Learning_System_Gu.../ESP32-S3_BLE_iOS_Media/code.py
2024-07-17 15:55:38 -04:00

210 lines
7.2 KiB
Python

# SPDX-FileCopyrightText: 2024 Liz Clark for Adafruit Industries
# SPDX-License-Identifier: MIT
import time
import gc
import board
import displayio
import terminalio
import adafruit_ble
from adafruit_ble.advertising.standard import SolicitServicesAdvertisement
from adafruit_ble_apple_media import AppleMediaService
from adafruit_bitmap_font import bitmap_font
from adafruit_button import Button
from adafruit_display_text import label, wrap_text_to_lines
import adafruit_hx8357
import adafruit_tsc2007
gc.collect()
displayio.release_displays()
small_font = bitmap_font.load_font("/Arial-16.bdf")
# display
spi = board.SPI()
tft_cs = board.D9
tft_dc = board.D10
display_bus = displayio.FourWire(spi, command=tft_dc, chip_select=tft_cs)
display = adafruit_hx8357.HX8357(display_bus, width=480, height=320)
# touch
i2c = board.I2C()
tsc = adafruit_tsc2007.TSC2007(i2c, invert_x=True, swap_xy=True)
splash = displayio.Group()
def ble_media_command(conn, command):
gc.collect()
# print("function started")
if not conn.paired:
print("trying to pair")
conn.pair()
print("paired")
# print("connected, getting ready")
ams = conn[AppleMediaService]
# print(ams)
tries = 10
for i in range(tries):
try:
# print("sending..")
if command != "refresh":
command(ams)
time.sleep(2)
# print("sent")
song_text = f"{ams.title}"
song_text = "\n".join(wrap_text_to_lines(song_text, 29))
song_label.text = song_text
album_label.text = f"Album: {ams.album}"
artist_label.text = f"Artist: {ams.artist}"
app_label.text = f"App: {ams.player_name}"
if ams.playing:
buttons[0].label = "Pause"
elif ams.paused:
buttons[0].label = "Play"
except Exception as error: # pylint: disable = broad-except
print(error)
# time.sleep(2)
if i < tries - 1:
continue
break
gc.collect()
connection = None
# commands
commands = [
lambda ams: ams.toggle_play_pause(),
lambda ams: ams.next_track(),
lambda ams: ams.previous_track(),
lambda ams: ams.advance_repeat_mode(),
lambda ams: ams.advance_shuffle_mode(),
lambda ams: ams.volume_up(),
lambda ams: ams.volume_down(),
]
# colors
RED = (255, 0, 0)
ORANGE = (255, 34, 0)
YELLOW = (255, 170, 0)
GREEN = (0, 255, 0)
CYAN = (0, 255, 255)
BLUE = (0, 0, 255)
VIOLET = (153, 0, 255)
MAGENTA = (255, 0, 51)
PINK = (255, 51, 119)
AQUA = (85, 125, 255)
WHITE = (255, 255, 255)
OFF = (0, 0, 0)
gc.collect()
spots = [
{'label': "Play/Pause", 'font': small_font,
'pos': ((display.width // 2) - 60, display.height - 70),
'size': (120, 60), 'color': RED,
'control': lambda: ble_media_command(connection, commands[0])},
{'label': ">>", 'font': small_font,
'pos': ((display.width // 2) + 70, display.height - 70),
'size': (60, 60), 'color': ORANGE,
'control': lambda: ble_media_command(connection, commands[1])},
{'label': "<<", 'font': small_font,
'pos': ((display.width // 2) - 130, display.height - 70),
'size': (60, 60), 'color': YELLOW,
'control': lambda: ble_media_command(connection, commands[2])},
{'label': "Repeat", 'font': terminalio.FONT,
'pos': (10, 250), 'size': (60, 60), 'color': GREEN,
'control': lambda: ble_media_command(connection, commands[3])},
{'label': "Shuffle", 'font': terminalio.FONT,
'pos': (410, 250), 'size': (60, 60), 'color': CYAN,
'control': lambda: ble_media_command(connection, commands[4])},
{'label': "Vol +", 'font': small_font, 'pos': (10, 10), 'size': (60, 120), 'color': BLUE,
'control': lambda: ble_media_command(connection, commands[5])},
{'label': "Vol -", 'font': small_font, 'pos': (410, 10), 'size': (60, 120), 'color': VIOLET,
'control': lambda: ble_media_command(connection, commands[6])},
{'label': "Refresh", 'font': small_font, 'pos': (70, 10), 'size': (340, display.height - 80),
'color': None, 'control': lambda: ble_media_command(connection, "refresh")},
]
buttons = []
for spot in spots:
button = Button(x=spot['pos'][0], y=spot['pos'][1],
width=spot['size'][0], height=spot['size'][1],
label=spot['label'], label_font=spot['font'],
style=Button.ROUNDRECT,
fill_color=spot['color'],
name=spot['label'])
splash.append(button)
buttons.append(button)
header_label = label.Label(small_font, text="Now Playing:", color=WHITE)
header_label.anchor_point = (0.5, 0.0)
header_label.anchored_position = (display.width / 2, 10)
splash.append(header_label)
song_label = label.Label(small_font, text=" ", color=WHITE)
song_label.anchor_point = (0.5, 0.0)
song_label.anchored_position = (display.width / 2, 40)
splash.append(song_label)
artist_label = label.Label(small_font, text="Artist: ", color=WHITE)
artist_label.anchor_point = (0.5, 0.0)
artist_label.anchored_position = (display.width / 2, 124)
splash.append(artist_label)
album_label = label.Label(small_font, text="Album: ", color=WHITE)
album_label.anchor_point = (0.5, 0.0)
album_label.anchored_position = (display.width / 2, 154)
splash.append(album_label)
app_label = label.Label(small_font, text="App: ", color=WHITE)
app_label.anchor_point = (0.5, 0.0)
app_label.anchored_position = (display.width / 2, 184)
splash.append(app_label)
touch_state = False
display.root_group = splash
def scale_touch_coordinates(raw_x, raw_y):
# raw coordinate ranges
raw_x_min = 275
raw_x_max = 3900
raw_y_min = 487
raw_y_max = 3800
# scale the raw coordinates to display coordinates
display_x = (raw_x - raw_x_min) * display.width / (raw_x_max - raw_x_min)
display_y = (raw_y - raw_y_min) * display.height / (raw_y_max - raw_y_min)
# clamp values to ensure they stay within display bounds
display_x = max(0, min(display_x, display.width))
display_y = max(0, min(display_y, display.height))
return int(display_x), int(display_y)
gc.collect()
# PyLint can't find BLERadio for some reason so special case it here.
radio = adafruit_ble.BLERadio() # pylint: disable=no-member
a = SolicitServicesAdvertisement()
a.solicited_services.append(AppleMediaService)
if not radio.connected:
print("advertising")
radio.start_advertising(a)
else:
print("already connected")
print(radio.connected)
print(gc.mem_free())
while True:
while not radio.connected:
pass
known_notifications = set()
while radio.connected:
if tsc.touched and not touch_state:
gc.collect()
touch_state = True
p = tsc.touch
point = scale_touch_coordinates(p["x"], p["y"])
connection = radio.connections[0]
for button in buttons:
b = buttons.index(button)
if button.contains(point):
print(gc.mem_free())
print("Touched", button.name)
spots[b]['control']()
if not tsc.touched and touch_state:
touch_state = False
print("disconnected")
print("advertising")
radio.start_advertising(a)
print("reconnected")