141 lines
3.6 KiB
Python
Executable file
141 lines
3.6 KiB
Python
Executable file
# SPDX-FileCopyrightText: 2023 Carter N. for Adafruit Industries
|
|
# SPDX-License-Identifier: MIT
|
|
|
|
import time
|
|
import board
|
|
import digitalio
|
|
import adafruit_si4713
|
|
from PIL import Image, ImageDraw, ImageFont
|
|
from adafruit_rgb_display import st7789
|
|
import mpd
|
|
|
|
#--| User Config |-----------------------------------
|
|
FREQ = 89.00
|
|
PLAYLIST = "test"
|
|
STATION_NAME = "PiPyPirate Radio"
|
|
UPDATE_RATE = 0.5
|
|
#----------------------------------------------------
|
|
|
|
#==| SETUP |=========================================================
|
|
|
|
# Display
|
|
disp = st7789.ST7789(
|
|
board.SPI(),
|
|
height=240,
|
|
y_offset=80,
|
|
rotation=180,
|
|
cs=digitalio.DigitalInOut(board.CE0),
|
|
dc=digitalio.DigitalInOut(board.D25),
|
|
rst=digitalio.DigitalInOut(board.D24),
|
|
baudrate=64000000,
|
|
)
|
|
|
|
backlight = digitalio.DigitalInOut(board.D22)
|
|
backlight.switch_to_output()
|
|
backlight.value = True
|
|
|
|
background = Image.open("radio_bg.png")
|
|
STAT_FNT = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSansCondensed-Bold.ttf", 55)
|
|
STAT_CLR = (30, 100, 200)
|
|
INFO_FNT = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 20)
|
|
ARTS_CLR = (0, 100, 0)
|
|
ALBM_CLR = (0, 100, 0)
|
|
TITL_CLR = (0, 100, 0)
|
|
PROG_CLR = (0, 100, 0)
|
|
|
|
# Buttons
|
|
button1 = digitalio.DigitalInOut(board.D23)
|
|
button1.switch_to_input(pull=digitalio.Pull.UP)
|
|
button2 = digitalio.DigitalInOut(board.D24)
|
|
button2.switch_to_input(pull=digitalio.Pull.UP)
|
|
|
|
# Radio
|
|
radio = adafruit_si4713.SI4713(
|
|
board.I2C(),
|
|
reset=digitalio.DigitalInOut(board.D26),
|
|
timeout_s = 0.5
|
|
)
|
|
radio.tx_frequency_khz = int(FREQ * 1000)
|
|
radio.tx_power = 115
|
|
radio.configure_rds(0xADAF, station=STATION_NAME.encode())
|
|
|
|
# MPD
|
|
mpc = mpd.MPDClient()
|
|
mpc.connect("localhost", 6600)
|
|
mpc.stop()
|
|
mpc.clear()
|
|
mpc.load(PLAYLIST)
|
|
mpc.play()
|
|
mpc.repeat(1)
|
|
#====================================================================
|
|
|
|
def button1_handler():
|
|
if status['state'] == 'play':
|
|
mpc.pause()
|
|
else:
|
|
mpc.play()
|
|
|
|
def button2_handler():
|
|
mpc.next()
|
|
|
|
def update_display():
|
|
image = background.copy()
|
|
draw = ImageDraw.Draw(image)
|
|
|
|
draw.text(
|
|
(150, 20),
|
|
"{:>5.1f}".format(FREQ),
|
|
anchor="mt",
|
|
font=STAT_FNT,
|
|
fill=STAT_CLR
|
|
)
|
|
|
|
if status['state'] == 'play':
|
|
r = 10 * (1 + int(time.monotonic() % 3))
|
|
draw.arc( (30-r, 20-r, 30+r, 20+r),
|
|
120, 60,
|
|
fill = (0, 0, 0),
|
|
width = 3
|
|
)
|
|
|
|
info = mpc.currentsong()
|
|
artist = info.get('artist', 'unknown')
|
|
album = info.get('album', 'unknown')
|
|
song = info.get('title', 'unknown')
|
|
draw.text( (5, 150), artist, font=INFO_FNT, fill=ARTS_CLR )
|
|
draw.text( (5, 170), album, font=INFO_FNT, fill=ALBM_CLR)
|
|
draw.text( (5, 190), song, font=INFO_FNT, fill=TITL_CLR)
|
|
rds_info = "{}:{}:{}".format(artist, album, song)
|
|
radio.rds_buffer = rds_info.encode()
|
|
|
|
perc = float(status['elapsed']) / float(status['duration'])
|
|
draw.rectangle( (5, 215, 235, 230), outline=PROG_CLR)
|
|
draw.rectangle (
|
|
(5, 215, 5 + int(230*perc), 230),
|
|
fill=PROG_CLR
|
|
)
|
|
|
|
disp.image(image)
|
|
|
|
last_update = time.monotonic()
|
|
|
|
print("Now broadcasting {} on {}FM".format(STATION_NAME, FREQ))
|
|
|
|
while True:
|
|
now = time.monotonic()
|
|
try:
|
|
status = mpc.status()
|
|
except ConnectionError:
|
|
mpc.connect("localhost", 6600)
|
|
status = mpc.status()
|
|
if not button1.value:
|
|
button1_handler()
|
|
while not button1.value:
|
|
time.sleep(0.001)
|
|
if not button2.value:
|
|
button2_handler()
|
|
while not button2.value:
|
|
time.sleep(0.001)
|
|
if now - last_update > UPDATE_RATE:
|
|
update_display()
|
|
last_update = now
|