diff --git a/software/boards/tonberry_unix/manifest.py b/software/boards/tonberry_unix/manifest.py new file mode 100644 index 0000000..3af83c7 --- /dev/null +++ b/software/boards/tonberry_unix/manifest.py @@ -0,0 +1,5 @@ +include("$(PORT_DIR)/variants/manifest.py") + +include("$(MPY_DIR)/extmod/asyncio") + +module("microdot.py", "../../lib/microdot/src/microdot/") diff --git a/software/boards/tonberry_unix/mpconfigvariant.h b/software/boards/tonberry_unix/mpconfigvariant.h new file mode 100644 index 0000000..edc9991 --- /dev/null +++ b/software/boards/tonberry_unix/mpconfigvariant.h @@ -0,0 +1,31 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// Set base feature level. +#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES) + +// Enable extra Unix features. +#include "mpconfigvariant_common.h" diff --git a/software/boards/tonberry_unix/mpconfigvariant.mk b/software/boards/tonberry_unix/mpconfigvariant.mk new file mode 100644 index 0000000..c91db1a --- /dev/null +++ b/software/boards/tonberry_unix/mpconfigvariant.mk @@ -0,0 +1,3 @@ +# This is the default variant when you `make` the Unix port. + +FROZEN_MANIFEST ?= $(VARIANT_DIR)/manifest.py diff --git a/software/boards/tonberry_unix/mpconfigvariant_common.h b/software/boards/tonberry_unix/mpconfigvariant_common.h new file mode 100644 index 0000000..65c8743 --- /dev/null +++ b/software/boards/tonberry_unix/mpconfigvariant_common.h @@ -0,0 +1,126 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 Jim Mussared + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// This file enables and configures features common to all variants +// other than "minimal". + +// Send raise KeyboardInterrupt directly from the signal handler rather than +// scheduling it into the VM. +#define MICROPY_ASYNC_KBD_INTR (!MICROPY_PY_THREAD_GIL) + +// Enable helpers for printing debugging information. +#ifndef MICROPY_DEBUG_PRINTERS +#define MICROPY_DEBUG_PRINTERS (1) +#endif + +// Enable floating point by default. +#ifndef MICROPY_FLOAT_IMPL +#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_DOUBLE) +#endif + +// Don't use native _Float16 because it increases code size by a lot. +#ifndef MICROPY_FLOAT_USE_NATIVE_FLT16 +#define MICROPY_FLOAT_USE_NATIVE_FLT16 (0) +#endif + +// Enable arbitrary precision long-int by default. +#ifndef MICROPY_LONGINT_IMPL +#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) +#endif + +// Enable use of C libraries that need read/write/lseek/fsync, e.g. axtls. +#define MICROPY_STREAMS_POSIX_API (1) + +// REPL conveniences. +#define MICROPY_REPL_EMACS_WORDS_MOVE (1) +#define MICROPY_REPL_EMACS_EXTRA_WORDS_MOVE (1) +#define MICROPY_USE_READLINE_HISTORY (1) +#ifndef MICROPY_READLINE_HISTORY_SIZE +#define MICROPY_READLINE_HISTORY_SIZE (50) +#endif + +// Seed random on import. +#define MICROPY_PY_RANDOM_SEED_INIT_FUNC (mp_random_seed_init()) + +// Allow exception details in low-memory conditions. +#define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1) +#define MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE (256) + +// Allow loading of .mpy files. +#define MICROPY_PERSISTENT_CODE_LOAD (1) + +// Extra memory debugging. +#define MICROPY_MALLOC_USES_ALLOCATED_SIZE (1) +#define MICROPY_MEM_STATS (1) + +// Enable a small performance boost for the VM. +#define MICROPY_OPT_COMPUTED_GOTO (1) + +// Return number of collected objects from gc.collect(). +#define MICROPY_PY_GC_COLLECT_RETVAL (1) + +// Enable detailed error messages and warnings. +#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_DETAILED) +#define MICROPY_WARNINGS (1) +#define MICROPY_PY_STR_BYTES_CMP_WARN (1) + +// Configure the "sys" module with features not usually enabled on bare-metal. +#define MICROPY_PY_SYS_ATEXIT (1) +#define MICROPY_PY_SYS_EXC_INFO (1) + +// Configure the "os" module with extra unix features. +#define MICROPY_PY_OS_INCLUDEFILE "ports/unix/modos.c" +#define MICROPY_PY_OS_ERRNO (1) +#define MICROPY_PY_OS_GETENV_PUTENV_UNSETENV (1) +#define MICROPY_PY_OS_SYSTEM (1) +#define MICROPY_PY_OS_URANDOM (1) + +// Enable the unix-specific "time" module. +#define MICROPY_PY_TIME (1) +#define MICROPY_PY_TIME_TIME_TIME_NS (1) +#define MICROPY_PY_TIME_CUSTOM_SLEEP (1) +#define MICROPY_PY_TIME_INCLUDEFILE "ports/unix/modtime.c" + +#if MICROPY_PY_SSL +#define MICROPY_PY_HASHLIB_MD5 (1) +#define MICROPY_PY_HASHLIB_SHA1 (1) +#define MICROPY_PY_CRYPTOLIB (1) +#endif + +// The "select" module is enabled by default, but disable select.select(). +#define MICROPY_PY_SELECT_POSIX_OPTIMISATIONS (1) +#define MICROPY_PY_SELECT_SELECT (0) + +// Enable the "websocket" module. +#define MICROPY_PY_WEBSOCKET (1) + +// Enable the "machine" module, mostly for machine.mem*. +#define MICROPY_PY_MACHINE (1) +#define MICROPY_PY_MACHINE_PULSE (1) +#define MICROPY_PY_MACHINE_PIN_BASE (1) + +#define MICROPY_VFS_ROM (1) +#define MICROPY_VFS_ROM_IOCTL (0) diff --git a/software/build.sh b/software/build.sh index 883173e..b373a8e 100755 --- a/software/build.sh +++ b/software/build.sh @@ -9,6 +9,10 @@ set -eu make -C ports/rp2 BOARD=TONBERRY_RPI_PICO_W BOARD_DIR="$TOPDIR"/boards/RPI_PICO_W clean make -C ports/rp2 BOARD=TONBERRY_RPI_PICO_W BOARD_DIR="$TOPDIR"/boards/RPI_PICO_W \ USER_C_MODULES="$TOPDIR"/modules/micropython.cmake -j "$(nproc)" + + # build tonberry specific unix port of micropython + make -C ports/unix VARIANT_DIR="$TOPDIR"/boards/tonberry_unix clean + make -C ports/unix VARIANT_DIR="$TOPDIR"/boards/tonberry_unix -j "$(nproc)" ) ( cd tools/mklittlefs diff --git a/software/flash.sh b/software/flash.sh index 6acd447..459db12 100755 --- a/software/flash.sh +++ b/software/flash.sh @@ -41,7 +41,7 @@ flash_via_picotool() local device="${bus_device[1]//[!0-9]/}" echo "Found RP2 with serial $serial on Bus $bus Device $device" - picotool load --bus "$bus" --address "$device" "$IMAGEFILE" + picotool load --update --bus "$bus" --address "$device" "$IMAGEFILE" } FLASH_VIA_MOUNTPOINT=0 diff --git a/software/src/main.py b/software/src/main.py index 673b182..813b036 100644 --- a/software/src/main.py +++ b/software/src/main.py @@ -9,6 +9,7 @@ import micropython import network import os import time +import ubinascii # Own modules import app @@ -18,6 +19,7 @@ from mp3player import MP3Player from nfc import Nfc from rp2_neopixel import NeoPixel from utils import BTreeFileManager, Buttons, SDContext, TimerManager, LedManager +from webserver import start_webserver try: import hwconfig @@ -40,6 +42,16 @@ def setup_wifi(): wlan.config(ssid=f"TonberryPicoAP_{machine.unique_id().hex()}", security=wlan.SEC_OPEN) wlan.active(True) + # disable power management + wlan.config(pm=network.WLAN.PM_NONE) + + mac = ubinascii.hexlify(network.WLAN().config('mac'), ':').decode() + print(f" mac: {mac}") + print(f" channel: {wlan.config('channel')}") + print(f" essid: {wlan.config('essid')}") + print(f" txpower: {wlan.config('txpower')}") + print(f"ifconfig: {wlan.ifconfig()}") + DB_PATH = '/sd/tonberry.db' @@ -51,6 +63,7 @@ def run(): # Wifi with default config setup_wifi() + start_webserver() # Setup MP3 player with SDContext(mosi=hwconfig.SD_DI, miso=hwconfig.SD_DO, sck=hwconfig.SD_SCK, ss=hwconfig.SD_CS, diff --git a/software/src/microdot_test.py b/software/src/microdot_test.py deleted file mode 100644 index a98ff28..0000000 --- a/software/src/microdot_test.py +++ /dev/null @@ -1,38 +0,0 @@ -import rp2 -import network -import ubinascii -from microdot import Microdot - -rp2.country('DE') - -wlan = network.WLAN(network.AP_IF) -wlan.config(ssid='TonberryPico', security=network.WLAN.SEC_OPEN) -# Important: we cannot change the ip in station mode, otherwise dhcp won't work! -# wlan.ipconfig(addr4='10.0.0.1') -wlan.active(True) # loads the firmware -while wlan.active() is False: - pass -wlan.config(pm=network.WLAN.PM_NONE) - -mac = ubinascii.hexlify(network.WLAN().config('mac'), ':').decode() -print(f" mac: {mac}") -print(f" channel: {wlan.config('channel')}") -print(f" essid: {wlan.config('essid')}") -print(f" txpower: {wlan.config('txpower')}") -print(f"ifconfig: {wlan.ifconfig()}") - -app = Microdot() - - -@app.route('/') -async def index(request): - print("wohoo, a guest :)") - print(f" app: {request.app}") - print(f" client: {request.client_addr}") - print(f" method: {request.method}") - print(f" url: {request.url}") - print(f" headers: {request.headers}") - print(f" cookies: {request.cookies}") - return "TonberryPico says 'Hello World!'" - -app.run(port=80) diff --git a/software/src/tonberry.schema.json b/software/src/tonberry.schema.json new file mode 100644 index 0000000..ca5d77e --- /dev/null +++ b/software/src/tonberry.schema.json @@ -0,0 +1,53 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "definitions": { + "PlaybackPosition": { + "type": "object", + "properties": { + "position_seconds": { "type": "number" }, + "device_uptime": { "type": "number" } + }, + "required": ["position_seconds"] + }, + "AudioFile": { + "type": "object", + "properties": { + "id": { "type": "string", "format": "uuid" }, + "filename": { "type": "string" }, + "size_bytes": { "type": "integer" }, + "duration_seconds": { "type": "number" }, + "last_played_uptime": { "type": "number" }, + "playback_position": { "$ref": "#/definitions/PlaybackPosition" } + }, + "required": ["id", "filename"] + }, + "Playlist": { + "type": "object", + "properties": { + "id": { "type": "string", "format": "uuid" }, + "name": { "type": "string" }, + "audio_files": { + "type": "array", + "items": { "$ref": "#/definitions/AudioFile" } + }, + "current_track_index": { "type": "integer", "minimum": 0 }, + "last_played_uptime": { "type": "number" }, + "playback_position": { "$ref": "#/definitions/PlaybackPosition" } + }, + "required": ["id", "name", "audio_files"] + }, + "NfcTag": { + "type": "object", + "properties": { + "uid": { "type": "string" }, + "name": { "type": "string" }, + "linked_type": { + "type": "string", + "enum": ["audio_file", "playlist"] + }, + "linked_id": { "type": "string", "format": "uuid" } + }, + "required": ["uid", "linked_type", "linked_id"] + } + } +} diff --git a/software/src/webserver.py b/software/src/webserver.py new file mode 100644 index 0000000..eeb2a95 --- /dev/null +++ b/software/src/webserver.py @@ -0,0 +1,41 @@ +''' +SPDX-License-Identifier: MIT +Copyright (c) 2024-2025 Stefan Kratochwil +''' + +import asyncio + +from microdot import Microdot + +webapp = Microdot() +server = None + + +def start_webserver(): + global server + server = asyncio.create_task(webapp.start_server(port=80)) + + +@webapp.route('/') +async def index(request): + print("wohoo, a guest :)") + print(f" app: {request.app}") + print(f" client: {request.client_addr}") + print(f" method: {request.method}") + print(f" url: {request.url}") + print(f" headers: {request.headers}") + print(f" cookies: {request.cookies}") + return "TonberryPico says 'Hello World!'" + + +@webapp.route('/api/v1/filesystem', methods=['POST']) +async def filesystem_post(request): + # curl -X POST -d "burp" http://192.168.4.1/api/v1/filesystem + print(request) + return {'success': False} + + +@webapp.route('/api/v1/playlist', methods=['POST']) +async def playlist_post(request): + print(request) + return {'success': False}