1 Commits

Author SHA1 Message Date
8a402e90bd feat: allow reboot commands only if usb cable is inserted
Some checks failed
Build RPi Pico firmware image / Build-Firmware (push) Successful in 4m41s
Check code formatting / Check-C-Format (push) Successful in 7s
Check code formatting / Check-Python-Flake8 (push) Failing after 10s
Check code formatting / Check-Bash-Shellcheck (push) Successful in 5s
Run unit tests on host / Run-Unit-Tests (push) Successful in 8s
Run pytests / Check-Pytest (push) Successful in 11s
2025-12-21 17:43:00 +01:00
6 changed files with 13 additions and 69 deletions

View File

@@ -153,8 +153,6 @@
<li><button onclick="showScreen('playlist')">Open Playlist Editor</button></li>
<!-- More screens can be added later -->
</ul>
<hr>
<button onclick="requestReboot()">Reboot to bootloader</button>
</div>
<!-- CONFIG EDITOR SCREEN -->
@@ -966,14 +964,6 @@
return { init, onShow };
})();
// Misc
async function requestReboot() {
const resp = await fetch('/api/v1/reboot/bootloader', {'method': 'POST'});
if (!resp.ok) {
alert('Reboot to bootloader failed: ' + await resp.text());
}
}
// Initialization
Object.values(Screens).forEach(screen => {

View File

@@ -2,8 +2,7 @@
# Copyright (c) 2025 Matthias Blankertz <matthias@blankertz.org>
import _audiocore
import asyncio
from asyncio import Lock, ThreadSafeFlag
from asyncio import ThreadSafeFlag
from utils import get_pin_index
@@ -12,34 +11,19 @@ class Audiocore:
# PIO requires sideset pins to be adjacent
assert get_pin_index(lrclk) == get_pin_index(dclk)+1 or get_pin_index(lrclk) == get_pin_index(dclk)-1
self.notify = ThreadSafeFlag()
self.audiocore_lock = Lock()
self._audiocore = _audiocore.Audiocore(din, dclk, lrclk, self._interrupt)
def deinit(self):
assert not self.audiocore_lock.locked()
self._audiocore.deinit()
def _interrupt(self, _):
self.notify.set()
def flush(self):
assert not self.audiocore_lock.locked()
self._audiocore.flush()
async def async_flush(self):
async with self.audiocore_lock:
self._audiocore.flush(False)
while True:
if self._audiocore.get_async_result() is not None:
return
await self.notify.wait()
async def async_set_volume(self, volume):
async with self.audiocore_lock:
self._audiocore.set_volume(volume)
def set_volume(self, volume):
asyncio.create_task(self.async_set_volume(volume))
self._audiocore.set_volume(volume)
def put(self, buffer, blocking=False):
pos = 0

View File

@@ -5,8 +5,6 @@
// Include MicroPython API.
#include "py/mperrno.h"
#include "py/obj.h"
#include "py/persistentcode.h"
#include "py/runtime.h"
#include "shared/runtime/mpirq.h"
@@ -63,20 +61,6 @@ static uint32_t get_fifo_read_value_blocking(struct audiocore_obj *obj)
}
}
static bool get_fifo_read_value(struct audiocore_obj *obj, uint32_t *result)
{
const long flags = save_and_disable_interrupts();
const uint32_t value = obj->fifo_read_value;
obj->fifo_read_value = 0;
if (value & AUDIOCORE_FIFO_DATA_FLAG) {
restore_interrupts(flags);
*result = value & ~AUDIOCORE_FIFO_DATA_FLAG;
return true;
}
restore_interrupts(flags);
return false;
}
/*
* audiocore.Context.deinit(self)
*
@@ -141,31 +125,15 @@ static MP_DEFINE_CONST_FUN_OBJ_2(audiocore_put_obj, audiocore_put);
* Tells the audiocore to stop playback as soon as all MP3 frames still in the buffer have been decoded.
* This function blocks until playback has ended.
*/
static mp_obj_t audiocore_flush(size_t n_args, const mp_obj_t *args)
static mp_obj_t audiocore_flush(mp_obj_t self_in)
{
struct audiocore_obj *self = MP_OBJ_TO_PTR(args[0]);
mp_obj_t blocking = MP_ROM_TRUE;
if (n_args == 2) {
blocking = args[1];
}
struct audiocore_obj *self = MP_OBJ_TO_PTR(self_in);
multicore_fifo_push_blocking(AUDIOCORE_CMD_FLUSH);
wake_core1();
if (mp_obj_is_true(blocking))
get_fifo_read_value_blocking(self);
get_fifo_read_value_blocking(self);
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audiocore_flush_obj, 1, 2, audiocore_flush);
static mp_obj_t audiocore_get_async_result(mp_obj_t self_in)
{
uint32_t result;
struct audiocore_obj *self = MP_OBJ_TO_PTR(self_in);
if (get_fifo_read_value(self, &result)) {
return mp_obj_new_int(result);
}
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_1(audiocore_get_async_result_obj, audiocore_get_async_result);
static MP_DEFINE_CONST_FUN_OBJ_1(audiocore_flush_obj, audiocore_flush);
/*
* audiocore.set_volume(self, volume)
@@ -272,7 +240,6 @@ static const mp_rom_map_elem_t audiocore_locals_dict_table[] = {
{MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audiocore_deinit_obj)},
{MP_ROM_QSTR(MP_QSTR_put), MP_ROM_PTR(&audiocore_put_obj)},
{MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&audiocore_flush_obj)},
{MP_ROM_QSTR(MP_QSTR_get_async_result), MP_ROM_PTR(&audiocore_get_async_result_obj)},
{MP_ROM_QSTR(MP_QSTR_set_volume), MP_ROM_PTR(&audiocore_set_volume_obj)},
};
static MP_DEFINE_CONST_DICT(audiocore_locals_dict, audiocore_locals_dict_table);

View File

@@ -196,5 +196,8 @@ class PlayerApp:
def get_playlist_db(self):
return self.playlist_db
def get_timer_manager(self):
return self.timer_manager
def get_leds(self):
return self.leds

View File

@@ -74,7 +74,7 @@ class MP3Player:
# Call onPlaybackDone after flush
send_done = True
finally:
await self.audiocore.async_flush()
self.audiocore.flush()
if send_done:
# Only call onPlaybackDone if exit due to end of stream
# Use timer with time 0 to call callback "immediately" but from a different task

View File

@@ -34,7 +34,7 @@ def start_webserver(config_, app_):
nfc = app.get_nfc()
playlist_db = app.get_playlist_db()
leds = app.get_leds()
timer_manager = TimerManager()
timer_manager = app.get_timer_manager()
@webapp.before_request
@@ -259,12 +259,12 @@ async def audiofile_delete(request):
@webapp.route('/api/v1/reboot/<method>', methods=['POST'])
async def reboot(request, method):
if hwconfig.get_on_battery():
return 'not allowed: usb not connected', 403
return 'not allowed: no vbus', 403
if method == 'bootloader':
leds.set_state(LedManager.REBOOTING)
timer_manager.schedule(time.ticks_ms() + 1500, machine.bootloader)
elif method == 'application':
elif method =='application':
leds.set_state(LedManager.REBOOTING)
timer_manager.schedule(time.ticks_ms() + 1500, machine.reset)
else: