Soinua jotzen du

This commit is contained in:
2026-06-24 18:20:48 +02:00
commit 36ab6b4290
5 changed files with 547 additions and 0 deletions

3
boot_out.txt Normal file
View File

@@ -0,0 +1,3 @@
Adafruit CircuitPython 10.2.1 on 2026-05-13; Raspberry Pi Pico with rp2040
Board ID:raspberry_pi_pico
UID:E6614C311B196825

21
code.py Normal file
View File

@@ -0,0 +1,21 @@
import board
import busio
from lib.audioplayer import AudioPlayer
print("Hello, world!")
uart = busio.UART(
tx=board.GP0,
rx=board.GP1,
baudrate=9600
)
player = AudioPlayer(uart)
print("Volumen:", player.get_volume())
player.set_volume(10)
player.play_index(1)

522
lib/audioplayer.py Normal file
View File

@@ -0,0 +1,522 @@
import time
class AudioPlayerError(Exception):
pass
class PlayStatus:
STOPPED = 0
PLAYING = 1
PAUSED = 2
ERROR = 0xFF
class PlayMode:
ALL_LOOP = 0
SINGLE_LOOP = 1
FOLDER_LOOP = 2
RANDOM = 3
SINGLE_STOP = 4
ALL_ONCE = 5
FOLDER_ONCE = 6
ERROR = 0xFF
class StorageDevice:
UDISK = 1
SD = 2
FLASH = 3
UDISK_OR_SD = 4
FLASH_OR_UDISK = 5
FLASH_OR_SD = 6
ERROR = 0xFF
class PlayDevice:
UDISK = 0
SD = 1
FLASH = 2
ERROR = 0xFF
class AudioPlayer:
DEFAULT_TIMEOUT_MS = 500
def __init__(self, uart, debug=False):
self.uart = uart
self.debug = debug
self._last_command = None
self._expected = None
# ---------------------------------------------------------
# Low level
# ---------------------------------------------------------
def _flush_rx(self):
while self.uart.in_waiting:
self.uart.read()
def _build_frame(self, command, data=b""):
frame = bytearray()
frame.append(command)
frame.append((~command) & 0xFF)
frame.append(len(data))
frame.extend(data)
checksum = sum(frame) & 0xFF
frame.append(checksum)
return frame
def _send_command(
self,
command,
data=b"",
expected=None,
):
self._flush_rx()
frame = self._build_frame(command, data)
self._last_command = command
self._expected = expected
if self.debug:
print(
"TX:",
" ".join(f"{b:02X}" for b in frame)
)
self.uart.write(frame)
def _read_response(self):
if self.uart.in_waiting < 6:
return None
raw = self.uart.read(min(self.uart.in_waiting, 32))
if raw is None:
return None
data = bytes(raw)
if self.debug:
print(
"RX:",
" ".join(f"{b:02X}" for b in data)
)
cmd_ok = (
(
data[0] == self._last_command
and
data[1] == ((~self._last_command) & 0xFF)
)
or
(
self._last_command == 0x05
and
data[0] == 0x0A
)
)
if not cmd_ok:
return None
if self._expected is not None:
if len(data) < 5:
return None
if (
data[2] != self._expected[0]
or
data[3] != self._expected[1]
):
return None
checksum = sum(data[:-1]) & 0xFF
if checksum != data[-1]:
return None
payload = data[4:-1]
return payload
def _transaction(
self,
command,
data=b"",
expected=None,
timeout_ms=DEFAULT_TIMEOUT_MS,
):
self._send_command(
command,
data,
expected,
)
start = time.monotonic()
while True:
payload = self._read_response()
if payload is not None:
return payload
elapsed_ms = (
time.monotonic() - start
) * 1000
if elapsed_ms > timeout_ms:
raise AudioPlayerError(
"Response timeout"
)
time.sleep(0.01)
@staticmethod
def _u16(payload):
return (payload[0] << 8) | payload[1]
# ---------------------------------------------------------
# Device checks
# ---------------------------------------------------------
def get_storage_device(self):
payload = self._transaction(
0x04,
bytes([0x08]),
expected=(0x02, 0x08),
)
return payload[0]
def is_ready(self):
return self.get_storage_device() == StorageDevice.SD
# ---------------------------------------------------------
# Playback control
# ---------------------------------------------------------
def status(self):
payload = self._transaction(
0x04,
bytes([0x00]),
expected=(0x02, 0x00),
)
return payload[0]
def play(self):
payload = self._transaction(
0x04,
bytes([0x01]),
expected=(0x02, 0x00),
)
return payload[0]
def pause(self):
payload = self._transaction(
0x04,
bytes([0x02]),
expected=(0x02, 0x00),
)
return payload[0]
def stop(self):
payload = self._transaction(
0x04,
bytes([0x03]),
expected=(0x02, 0x00),
)
return payload[0]
def next(self):
payload = self._transaction(
0x04,
bytes([0x05]),
expected=(0x03, 0x0E),
timeout_ms=600,
)
return self._u16(payload)
def previous(self):
payload = self._transaction(
0x04,
bytes([0x04]),
expected=(0x03, 0x0E),
timeout_ms=600,
)
return self._u16(payload)
def play_index(self, index):
payload = self._transaction(
0x04,
bytes([
0x06,
(index >> 8) & 0xFF,
index & 0xFF
]),
expected=(0x03, 0x0E),
)
return self._u16(payload)
def play_name(self, filename):
payload = self._transaction(
0x04,
bytes([0x07]) + filename.encode("ascii"),
expected=(0x03, 0x0E),
)
return payload[0]
# ---------------------------------------------------------
# Volume
# ---------------------------------------------------------
def get_volume(self):
payload = self._transaction(
0x06,
bytes([0x00]),
expected=(0x02, 0x00),
timeout_ms=600,
)
return payload[0]
def set_volume(self, volume):
self._send_command(
0x06,
bytes([
0x01,
volume
])
)
time.sleep(0.1)
def volume_up(self):
self._send_command(
0x06,
bytes([0x02])
)
time.sleep(0.1)
def volume_down(self):
self._send_command(
0x06,
bytes([0x03])
)
time.sleep(0.1)
# ---------------------------------------------------------
# Device info
# ---------------------------------------------------------
def get_play_device(self):
payload = self._transaction(
0x04,
bytes([0x09]),
expected=(0x02, 0x09),
)
return payload[0]
def get_total_tracks(self):
payload = self._transaction(
0x04,
bytes([0x0D]),
expected=(0x03, 0x0D),
)
return self._u16(payload)
def get_current_track(self):
payload = self._transaction(
0x04,
bytes([0x0E]),
expected=(0x03, 0x0E),
)
return self._u16(payload)
def get_current_path_count(self):
payload = self._transaction(
0x04,
bytes([0x18]),
expected=(0x03, 0x18),
)
return self._u16(payload)
# ---------------------------------------------------------
# Time functions
# ---------------------------------------------------------
def get_total_play_time(self):
payload = self._transaction(
0x05,
bytes([0x00]),
expected=(0x04, 0x00),
)
if len(payload) < 3:
raise AudioPlayerError(
"Invalid time response"
)
return (
payload[0],
payload[1],
payload[2]
)
def seek_current(self, minute, second):
self._send_command(
0x04,
bytes([
0x0F,
minute,
second
])
)
time.sleep(0.1)
def play_from_time(
self,
index,
minute,
second
):
self._send_command(
0x04,
bytes([
0x10,
(index >> 8) & 0xFF,
index & 0xFF,
minute,
second
])
)
time.sleep(0.1)
# ---------------------------------------------------------
# Directories
# ---------------------------------------------------------
def next_directory(self):
self._send_command(0x04, bytes([0x13]))
time.sleep(0.1)
def previous_directory(self):
self._send_command(0x04, bytes([0x12]))
time.sleep(0.1)
# ---------------------------------------------------------
# Play modes
# ---------------------------------------------------------
def get_play_mode(self):
payload = self._transaction(
0x0B,
bytes([0x00]),
expected=(0x02, 0x00),
)
return payload[0]
def set_play_mode(self, mode):
self._send_command(
0x0B,
bytes([
0x01,
mode
])
)
time.sleep(0.1)
# ---------------------------------------------------------
# Repeat
# ---------------------------------------------------------
def repeat_between(
self,
start_min,
start_sec,
end_min,
end_sec
):
self._send_command(
0x08,
bytes([
0x00,
start_min,
start_sec,
end_min,
end_sec
])
)
time.sleep(0.1)
def stop_repeat(self):
self._send_command(
0x08,
bytes([0x01])
)
time.sleep(0.1)
# ---------------------------------------------------------
# Sleep
# ---------------------------------------------------------
def sleep(self):
frame = bytes([
0x0D,
0xF3,
0x01,
0x01,
0x02
])
self.uart.write(frame)

1
sd/placeholder.txt Normal file
View File

@@ -0,0 +1 @@
SD cards mounted at /sd will hide this file from Python.

0
settings.toml Normal file
View File