Compare commits
7 Commits
feat/api_e
...
f99dec19c9
| Author | SHA1 | Date | |
|---|---|---|---|
| f99dec19c9 | |||
| 704951074b | |||
| cac61f924f | |||
| 9320a3cff2 | |||
| 65efebc5c2 | |||
| 040ae4a731 | |||
| 9cf044bc80 |
29
software/boards/RPI_PICO_W/board.c
Normal file
29
software/boards/RPI_PICO_W/board.c
Normal file
@@ -0,0 +1,29 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2025 Matthias Blankertz <matthias@blankertz.org>
|
||||
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/objstr.h"
|
||||
|
||||
#ifndef TONBERRY_GIT_REVISION
|
||||
#define TONBERRY_GIT_REVISION "unknown"
|
||||
#endif
|
||||
#ifndef TONBERRY_VERSION
|
||||
#define TONBERRY_VERSION "unknown"
|
||||
#endif
|
||||
|
||||
static const MP_DEFINE_STR_OBJ(tonberry_git_revision_obj, TONBERRY_GIT_REVISION);
|
||||
static const MP_DEFINE_STR_OBJ(tonberry_version_obj, TONBERRY_VERSION);
|
||||
|
||||
static const mp_rom_map_elem_t board_module_globals_table[] = {
|
||||
{MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_board)},
|
||||
{MP_ROM_QSTR(MP_QSTR_revision), MP_ROM_PTR(&tonberry_git_revision_obj)},
|
||||
{MP_ROM_QSTR(MP_QSTR_version), MP_ROM_PTR(&tonberry_version_obj)},
|
||||
};
|
||||
static MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table);
|
||||
|
||||
const mp_obj_module_t board_cmodule = {
|
||||
.base = {&mp_type_module},
|
||||
.globals = (mp_obj_dict_t *)&board_module_globals,
|
||||
};
|
||||
MP_REGISTER_MODULE(MP_QSTR_board, board_cmodule);
|
||||
@@ -20,3 +20,22 @@ set(GEN_PINS_CSV_ARG --board-csv "${GEN_PINS_BOARD_CSV}")
|
||||
|
||||
add_link_options("-Wl,--print-memory-usage")
|
||||
set(PICO_USE_FASTEST_SUPPORTED_CLOCK 1)
|
||||
|
||||
find_program(GIT git)
|
||||
|
||||
execute_process(COMMAND ${GIT} rev-parse HEAD
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}"
|
||||
OUTPUT_VARIABLE TONBERRY_GIT_REVISION
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
execute_process(COMMAND ${GIT} describe --match 'v*' --always
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}"
|
||||
OUTPUT_VARIABLE TONBERRY_VERSION
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
set(MICROPY_SOURCE_BOARD "${CMAKE_CURRENT_LIST_DIR}/board.c")
|
||||
set(MICROPY_DEF_BOARD
|
||||
TONBERRY_GIT_REVISION="${TONBERRY_GIT_REVISION}"
|
||||
TONBERRY_VERSION="${TONBERRY_VERSION}")
|
||||
|
||||
@@ -131,6 +131,12 @@
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.footer {
|
||||
font-size: x-small;
|
||||
color: gray;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -153,6 +159,8 @@
|
||||
<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 -->
|
||||
@@ -255,7 +263,17 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<hr>
|
||||
<div class="flex-horizontal">
|
||||
<div>
|
||||
<a href="https://git.ka.blankertz.org/TonBERRY/tonberry-pico">TonBERRY pico</a>
|
||||
</div>
|
||||
<div>
|
||||
Version: <span id="footer-version">unknown</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
const Screens = {};
|
||||
let activeScreen = null;
|
||||
@@ -964,6 +982,14 @@
|
||||
|
||||
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 => {
|
||||
@@ -971,6 +997,13 @@
|
||||
});
|
||||
|
||||
showScreen("menu");
|
||||
|
||||
fetch('/api/v1/info')
|
||||
.then((resp) => resp.json())
|
||||
.then((info) => {
|
||||
const version = document.getElementById('footer-version');
|
||||
version.innerText = info.version;
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
@@ -196,8 +196,5 @@ 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
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# Copyright (c) 2025 Matthias Blankertz <matthias@blankertz.org>
|
||||
|
||||
from utils.helpers import safe_callback
|
||||
from utils.timer import TimerManager
|
||||
from utils.buttons import Buttons
|
||||
from utils.config import Configuration
|
||||
from utils.leds import LedManager
|
||||
@@ -9,7 +10,6 @@ from utils.mbrpartition import MBRPartition
|
||||
from utils.pinindex import get_pin_index
|
||||
from utils.playlistdb import BTreeDB, BTreeFileManager
|
||||
from utils.sdcontext import SDContext
|
||||
from utils.timer import TimerManager
|
||||
|
||||
__all__ = ["BTreeDB", "BTreeFileManager", "Buttons", "Configuration", "get_pin_index", "LedManager", "MBRPartition",
|
||||
"safe_callback", "SDContext", "TimerManager"]
|
||||
|
||||
@@ -5,7 +5,7 @@ import asyncio
|
||||
import machine
|
||||
import micropython
|
||||
import time
|
||||
from utils import safe_callback
|
||||
from utils import safe_callback, TimerManager
|
||||
try:
|
||||
from typing import TYPE_CHECKING # type: ignore
|
||||
except ImportError:
|
||||
@@ -32,6 +32,7 @@ class Buttons:
|
||||
def __init__(self, cb: "ButtonCallback", config, hwconfig):
|
||||
self.button_map = config.get_button_map()
|
||||
self.hw_buttons = hwconfig.BUTTONS
|
||||
self.hwconfig = hwconfig
|
||||
self.cb = cb
|
||||
self.buttons = dict()
|
||||
for key_id, key_name in self.KEYMAP.items():
|
||||
@@ -42,6 +43,7 @@ class Buttons:
|
||||
self.int_flag = asyncio.ThreadSafeFlag()
|
||||
self.pressed: list[int] = []
|
||||
self.last: dict[int, int] = {}
|
||||
self.timer_manager = TimerManager()
|
||||
for button in self.buttons.keys():
|
||||
button.irq(handler=self._interrupt, trigger=machine.Pin.IRQ_FALLING | machine.Pin.IRQ_RISING)
|
||||
asyncio.create_task(self.task())
|
||||
@@ -69,6 +71,10 @@ class Buttons:
|
||||
# print(f'B{keycode} {now}')
|
||||
self.pressed.append(keycode)
|
||||
self.int_flag.set()
|
||||
if keycode == self.VOLDOWN:
|
||||
self.timer_manager.schedule(time.ticks_ms() + 5000, self.long_press_shutdown)
|
||||
if button.value() == 1 and keycode == self.VOLDOWN:
|
||||
self.timer_manager.cancel(self.long_press_shutdown)
|
||||
|
||||
async def task(self):
|
||||
while True:
|
||||
@@ -76,3 +82,9 @@ class Buttons:
|
||||
while len(self.pressed) > 0:
|
||||
what = self.pressed.pop()
|
||||
safe_callback(lambda: self.cb.onButtonPressed(what), "button callback")
|
||||
|
||||
def long_press_shutdown(self):
|
||||
if self.hwconfig.get_on_battery():
|
||||
self.hwconfig.power_off()
|
||||
else:
|
||||
machine.reset()
|
||||
|
||||
@@ -4,10 +4,14 @@ Copyright (c) 2024-2025 Stefan Kratochwil <Kratochwil-LA@gmx.de>
|
||||
'''
|
||||
|
||||
import asyncio
|
||||
import board
|
||||
import hwconfig
|
||||
import json
|
||||
import machine
|
||||
import network
|
||||
import os
|
||||
import time
|
||||
import ubinascii
|
||||
|
||||
from array import array
|
||||
from microdot import Microdot, redirect, send_file, Request
|
||||
@@ -33,7 +37,7 @@ def start_webserver(config_, app_):
|
||||
nfc = app.get_nfc()
|
||||
playlist_db = app.get_playlist_db()
|
||||
leds = app.get_leds()
|
||||
timer_manager = app.get_timer_manager()
|
||||
timer_manager = TimerManager()
|
||||
|
||||
|
||||
@webapp.before_request
|
||||
@@ -257,12 +261,22 @@ 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
|
||||
|
||||
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:
|
||||
return 'method not supported', 400
|
||||
return '', 204
|
||||
|
||||
|
||||
@webapp.route('/api/v1/info', methods=['GET'])
|
||||
async def get_info(request):
|
||||
mac = ubinascii.hexlify(network.WLAN().config('mac'), ':').decode()
|
||||
return {'version': board.version,
|
||||
'mac': mac}
|
||||
|
||||
Reference in New Issue
Block a user