2 Commits

Author SHA1 Message Date
f99dec19c9 feat: store git version in fw and show in web ui
All checks were successful
Build RPi Pico firmware image / Build-Firmware (push) Successful in 4m41s
Check code formatting / Check-C-Format (push) Successful in 8s
Check code formatting / Check-Python-Flake8 (push) Successful in 9s
Check code formatting / Check-Bash-Shellcheck (push) Successful in 4s
Run unit tests on host / Run-Unit-Tests (push) Successful in 7s
Run pytests / Check-Pytest (push) Successful in 11s
Signed-off-by: Matthias Blankertz <matthias@blankertz.org>
2025-12-21 21:20:13 +01:00
704951074b feat: Long press VOL_DOWN button to shutdown/reset device
Signed-off-by: Matthias Blankertz <matthias@blankertz.org>
2025-12-21 18:31:13 +01:00
6 changed files with 96 additions and 3 deletions

View 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);

View File

@@ -20,3 +20,22 @@ set(GEN_PINS_CSV_ARG --board-csv "${GEN_PINS_BOARD_CSV}")
add_link_options("-Wl,--print-memory-usage") add_link_options("-Wl,--print-memory-usage")
set(PICO_USE_FASTEST_SUPPORTED_CLOCK 1) 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}")

View File

@@ -131,6 +131,12 @@
list-style: none; list-style: none;
} }
.footer {
font-size: x-small;
color: gray;
margin-top: 20px;
}
</style> </style>
</head> </head>
<body> <body>
@@ -257,7 +263,17 @@
</div> </div>
</div> </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> <script>
const Screens = {}; const Screens = {};
let activeScreen = null; let activeScreen = null;
@@ -981,6 +997,13 @@
}); });
showScreen("menu"); showScreen("menu");
fetch('/api/v1/info')
.then((resp) => resp.json())
.then((info) => {
const version = document.getElementById('footer-version');
version.innerText = info.version;
});
</script> </script>
</body> </body>

View File

@@ -2,6 +2,7 @@
# Copyright (c) 2025 Matthias Blankertz <matthias@blankertz.org> # Copyright (c) 2025 Matthias Blankertz <matthias@blankertz.org>
from utils.helpers import safe_callback from utils.helpers import safe_callback
from utils.timer import TimerManager
from utils.buttons import Buttons from utils.buttons import Buttons
from utils.config import Configuration from utils.config import Configuration
from utils.leds import LedManager from utils.leds import LedManager
@@ -9,7 +10,6 @@ from utils.mbrpartition import MBRPartition
from utils.pinindex import get_pin_index from utils.pinindex import get_pin_index
from utils.playlistdb import BTreeDB, BTreeFileManager from utils.playlistdb import BTreeDB, BTreeFileManager
from utils.sdcontext import SDContext from utils.sdcontext import SDContext
from utils.timer import TimerManager
__all__ = ["BTreeDB", "BTreeFileManager", "Buttons", "Configuration", "get_pin_index", "LedManager", "MBRPartition", __all__ = ["BTreeDB", "BTreeFileManager", "Buttons", "Configuration", "get_pin_index", "LedManager", "MBRPartition",
"safe_callback", "SDContext", "TimerManager"] "safe_callback", "SDContext", "TimerManager"]

View File

@@ -5,7 +5,7 @@ import asyncio
import machine import machine
import micropython import micropython
import time import time
from utils import safe_callback from utils import safe_callback, TimerManager
try: try:
from typing import TYPE_CHECKING # type: ignore from typing import TYPE_CHECKING # type: ignore
except ImportError: except ImportError:
@@ -32,6 +32,7 @@ class Buttons:
def __init__(self, cb: "ButtonCallback", config, hwconfig): def __init__(self, cb: "ButtonCallback", config, hwconfig):
self.button_map = config.get_button_map() self.button_map = config.get_button_map()
self.hw_buttons = hwconfig.BUTTONS self.hw_buttons = hwconfig.BUTTONS
self.hwconfig = hwconfig
self.cb = cb self.cb = cb
self.buttons = dict() self.buttons = dict()
for key_id, key_name in self.KEYMAP.items(): for key_id, key_name in self.KEYMAP.items():
@@ -42,6 +43,7 @@ class Buttons:
self.int_flag = asyncio.ThreadSafeFlag() self.int_flag = asyncio.ThreadSafeFlag()
self.pressed: list[int] = [] self.pressed: list[int] = []
self.last: dict[int, int] = {} self.last: dict[int, int] = {}
self.timer_manager = TimerManager()
for button in self.buttons.keys(): for button in self.buttons.keys():
button.irq(handler=self._interrupt, trigger=machine.Pin.IRQ_FALLING | machine.Pin.IRQ_RISING) button.irq(handler=self._interrupt, trigger=machine.Pin.IRQ_FALLING | machine.Pin.IRQ_RISING)
asyncio.create_task(self.task()) asyncio.create_task(self.task())
@@ -69,6 +71,10 @@ class Buttons:
# print(f'B{keycode} {now}') # print(f'B{keycode} {now}')
self.pressed.append(keycode) self.pressed.append(keycode)
self.int_flag.set() 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): async def task(self):
while True: while True:
@@ -76,3 +82,9 @@ class Buttons:
while len(self.pressed) > 0: while len(self.pressed) > 0:
what = self.pressed.pop() what = self.pressed.pop()
safe_callback(lambda: self.cb.onButtonPressed(what), "button callback") 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()

View File

@@ -4,11 +4,14 @@ Copyright (c) 2024-2025 Stefan Kratochwil <Kratochwil-LA@gmx.de>
''' '''
import asyncio import asyncio
import board
import hwconfig import hwconfig
import json import json
import machine import machine
import network
import os import os
import time import time
import ubinascii
from array import array from array import array
from microdot import Microdot, redirect, send_file, Request from microdot import Microdot, redirect, send_file, Request
@@ -270,3 +273,10 @@ async def reboot(request, method):
else: else:
return 'method not supported', 400 return 'method not supported', 400
return '', 204 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}