Posted: 03:22pm 02 Jul 2026 Copy link to clipboard
matherp Guru
It is clear that, as good as MMbasic is, there is a category of users who will go "yuck - basic"
However in MMBasic on the PicoMite there is a huge amount of useful IP so I thought why not integrate some of that into Micropython
My target platform is the Pico Computer 3 (One board to rule them all) as that has everything that any self-respecting stand-alone Micropython computer could need for hardware.
There is a bit of a learning curve getting a micropython build environment up and running, particularly on windows where it has to run under Ubuntu on wsl. However, with that out of the way I could start integrating.
I used as a starting point the Pico2-W version of micropython. This give things like pin access (digital in/out, analogue in, i2c, spi) as well as web support and a LFS file system in the flash memory (same as the MMbasic A: drive).
First thing was then to enable PSRAM and the extra RP2350B pins.
Next job was to swap the cdc console for a serial one so the USB could be reserved for USB host activiites.
Then the sdcard drivers from MMbasic were ported across so the sd could be read.
Next an editor was added (pye at the moment but this may get upgraded) and the ability to run a program from SDcard added.
Last easy bit was adding support for all the standard command line actions
That was the easy stuff out of the way now the fun bit
First big decision was what HDMI resolutions should be supported. This may come back to bite but currently 307,200bytes are allocated to a framebuffer allowing 640x480 RGB332 and 320x240 RGB565. This has been implemented and so far so good. HDMI is up and running and you can swap between the two modes without rebooting. Micropython includes a framebuf module that has the usual graphics commands - circles etc. so this has been integrated. Now underway is reflecting the serial console commands onto the screen. that works but is still WIP. The next crucial bit of the jigsaw will be moving the MMbasic HID host functionality across. AFAIK this is not typically available in any micropython port so that will be an adventure. I'll keep you informed. If you want to play get yourself a PicoComputer 3 or patch wire something using the same pinout.
Posted: 07:18pm 02 Jul 2026 Copy link to clipboard
PhenixRising Guru
I wanted to ask about the plan but didn't want to distract you.
Does this mean compatibility with Mpython libraries (they seem to support every darned thing)?
Posted: 11:17am 03 Jul 2026 Copy link to clipboard
matherp Guru
HID host functionality now running with USB keyboard able to interact in the full set of language variants so we have achieved "stand-alone status". I'm now working on audio. Whatever, anyone has claimed before Micropython is significantly slower than MMBasic. Taking approximately twice the time to run functionally equivalent benchmarks. All Micropython libraries that are available should work with no issues. For example, the first test of audio required no code changes as I2S can be called direct from python
from machine import I2S, Pin import array, math N = 100 # 44100/100 = exactly 441 Hz cyc = array.array('h') # array append is amortised O(1) for i in range(N): v = int(18000 * math.sin(2*math.pi*i/N)) cyc.append(v); cyc.append(v) # L, R mv = memoryview(cyc) i2s = I2S(0, sck=Pin(10), ws=Pin(11), sd=Pin(22), mode=I2S.TX, bits=16, format=I2S.STEREO, rate=44100, ibuf=8192) for _ in range(441): # ~1 s, starts instantly i2s.write(mv)
Edited 2026-07-03 21:19 by matherp
Posted: 12:13pm 03 Jul 2026 Copy link to clipboard
PhenixRising Guru
Makes me even more proud of the awesome MMBasic
Posted: 02:29pm 03 Jul 2026 Copy link to clipboard
matherp Guru
Audio WAV, Flac and MP3 playback all now working. DS3231 integrated and working. Next is some enhancements to HDMI and then to enhance the ls command and a first release is there. Edited 2026-07-04 02:06 by matherp
Posted: 07:05pm 03 Jul 2026 Copy link to clipboard
matherp Guru
Continuing to go well. We now have a 512x300x16-bit mode (MMBasic can't do this) as well as allowing 640x480x8 and 320x240x16-bits at 252 or 378MHz. The 512x300x16-bit mode has been tested while playing an mp3 and connecting to wifi at the same time. Not bad when the video bandwidth is 19.3 M words/s. ls now allows filtering ls("/sd/*.jpg") and yet more code is being imported from MMbasic jpg, png, bmp display and bmp save. After that it is some small niceties and I will release a first beta.
Posted: 02:41pm 04 Jul 2026 Copy link to clipboard
matherp Guru
Here is the first cut uf2 and the user manual. Note, it will probably run up on any RP2350B but only if there is PSRAM on GP47, its uses a serial console on GP8,GP9. The usb port is dedicated to host functionality so after flashing it is best to disconnect the USB and use a power supply.
Video of Micropython solving Sudoku here . Program below.
Now supports xmodem and a different way of displaying text
| `hdmi.putc(x, y, ch, fg, bg)` | blit one 8×12 console glyph at pixel `x,y` (native-format `fg`/`bg`) | | `hdmi.text(s, x, y, fg, bg=-1, scale=1)` | draw a string in the 8×12 console font at pixel `x,y`; `bg=-1` is transparent, `scale` enlarges each glyph pixel into a `scale`×`scale` block. Returns the x just past the string | | `hdmi.test()` | draw an 8-bar colour test pattern | xrecv("/sd/prog.py") # receive a file INTO the board, then start an XMODEM *Send* in the terminal xsend("/sd/prog.py") # send a file FROM the board, then start an XMODEM *Receive* in the terminal ```
- `xrecv(path)` opens `path` for writing, sends `NAK`, and waits (up to ~60 s) for the terminal to start sending. Start the XMODEM **Send** in your terminal. - `xsend(path)` waits for the terminal to start receiving, then transmits the file. Start the XMODEM **Receive** in your terminal. - Works to both `/sd` and the internal flash filesystem (any path). - While a transfer runs, the serial console is dedicated to the protocol (the REPL is paused and nothing is echoed to the HDMI screen); it returns to normal when the transfer finishes. - 128-byte packets; the receiver uses the additive checksum, the sender accepts either checksum (`NAK`) or CRC-16 (`C`). Trailing packet padding is trimmed from received files, so a transferred `.py` runs as-is. - On failure it raises `OSError` with a message (`Remote did not respond`, `Too many errors`, `Cancelled by remote`, …).
The full names are `xmodem.recv(path)` / `xmodem.send(path)` (`xrecv`/`xsend` are just convenience aliases injected into the REPL).
# Graphical Sudoku solver for the Pico Computer 3 (MicroPython, HDMI output). # Iterative back-tracking (no deep recursion) so it never overflows the stack. # Uses hdmi.text (8x12 font) if the firmware has it, else a scaled framebuf font. # Run: run("sudoku.py") — Ctrl-C stops and restores the console. import hdmi, framebuf, time
def put(s, x, y, col, scale=1): if HAVE_HTEXT: hdmi.text(s, x, y, col, -1, scale) return for ch in s: _gfb.fill(0) _gfb.text(ch, 0, 0, 1) for gy in range(8): row = _glyph[gy] if row: yy = y + gy * scale bit = 0x80 for gx in range(8): if row & bit: disp.fill_rect(x + gx * scale, yy, scale, scale, col) bit >>= 1 x += 8 * scale
def cell_xy(r, c): return OX + c * CELL, OY + r * CELL
def draw_cell(r, c, val, col, bg=None): x, y = cell_xy(r, c) disp.fill_rect(x + 1, y + 1, CELL - 1, CELL - 1, bg if bg is not None else C_BG) if val: put(str(val), x + GOX, y + GOY, col, SCALE)
def draw_grid(): disp.fill(C_BG) for i in range(10): disp.vline(OX + i * CELL, OY, GRID + 1, C_LINE) disp.hline(OX, OY + i * CELL, GRID + 1, C_LINE) for i in range(0, 10, 3): gx, gy = OX + i * CELL, OY + i * CELL disp.vline(gx - 1, OY, GRID + 1, C_LINE) disp.vline(gx + 1, OY, GRID + 1, C_LINE) disp.hline(OX, gy - 1, GRID + 1, C_LINE) disp.hline(OX, gy + 1, GRID + 1, C_LINE)
board = [list(row) for row in PUZZLE]
def valid(r, c, n): for i in range(9): if board[r][i] == n or board[i][c] == n: return False br, bc = (r // 3) * 3, (c // 3) * 3 for i in range(br, br + 3): for j in range(bc, bc + 3): if board[i][j] == n: return False return True
def status(msg, col): if PANEL: disp.fill_rect(PX, OY + 70, PANEL - 20, 16 * SCALE, C_BG) put(msg, PX, OY + 70, col, 2)
def solve(): empties = [(r, c) for r in range(9) for c in range(9) if board[r][c] == 0] n_empty = len(empties) i = 0 steps = 0 while 0 <= i < n_empty: r, c = empties[i] n = board[r][c] + 1 # resume just above the last value tried here board[r][c] = 0 # clear so valid() ignores our own cell while n <= 9 and not valid(r, c, n): n += 1 if n <= 9: steps += 1 board[r][c] = n draw_cell(r, c, n, C_TRY, C_HILITE) if STEP_MS: time.sleep_ms(STEP_MS) if PANEL and steps % 32 == 0: disp.fill_rect(PX, OY + 104, PANEL - 20, 12, C_BG) put("steps: %d" % steps, PX, OY + 104, C_TXT) i += 1 else: draw_cell(r, c, 0, C_TXT) # un-fill on backtrack i -= 1 return i == n_empty, steps
def main(): try: console(False) except NameError: pass draw_grid() for r in range(9): for c in range(9): if board[r][c]: draw_cell(r, c, board[r][c], C_CLUE) if PANEL: put("SUDOKU", PX, OY, C_OK, 3 if HAVE_HTEXT else 2) put("SOLVER", PX, OY + (36 if HAVE_HTEXT else 20), C_OK, 3 if HAVE_HTEXT else 2) t0 = time.ticks_ms() ok, steps = solve() dt = time.ticks_diff(time.ticks_ms(), t0) if ok: # repaint the solved-in digits blue for r in range(9): for c in range(9): if not PUZZLE[r][c]: draw_cell(r, c, board[r][c], C_OK) if PANEL: status("SOLVED!" if ok else "no soln", C_DONE if ok else C_TRY) put("steps: %d" % steps, PX, OY + 104, C_TXT) put("%d ms" % dt, PX, OY + 124, C_TXT) print("solved" if ok else "no solution", "in", steps, "steps,", dt, "ms")
Posted: 06:47pm 10 Jul 2026 Copy link to clipboard
okwatts Regular Member
I am no micropython expert (actually no expert at most anything at all) but I did give this a go while waiting for my Pico Computer 3. I am using the HDMIUSBI2S reference board and most everything works (no wifi at the moment) except the sdcard because of the different pins used for the interface between the 2 boards. With the xmodem commands added in v0.2, I can transfer files to the flash drive to try things out. Is it possible to adjust the sdcard pin outs with a simple edit/recompile? This would make it accessible to more folks at least in the testing phase even if the "full blown" version is intended for the Pico Computer 3.
Posted: 01:47am 11 Jul 2026 Copy link to clipboard
scruss Senior Member
MicroPython already has a very solid file transfer/utility/terminal in mpremote.
I've been using MicroPython solidly for almost a decade. It's good.
Posted: 07:59am 11 Jul 2026 Copy link to clipboard
The build should support mpremote but you would need to specify the port as the console is on a UART and not USB-CDC
Not really sensibly doable as the previous board didn't use SPI pins for the SDcard (An error on my part in the design of that board but MMBasic doesn't care)
What's new since v0.2 Display: native 1024×600 16-colour mode
hdmi.RGB1024 — a true native 1024×600 mode at 16 colours (4bpp packed, RGB121), an exact fit in the existing framebuffer with no RAM increase. Sits alongside RGB512 (which fakes 1024×600 by pixel-doubling 512×300). Settable, persistent 16-colour palette — hdmi.palette() gets/sets the palette live; palette(...) (via pcconfig) persists it and restores it at boot. Format-aware console & graphics — fill/scroll/putc/text/blit and pcgfx.Display all work in RGB1024, so the on-screen console runs in the new mode. Image loaders gained a 4bpp path — draw_jpg/draw_bmp/draw_png and save_image map to the nearest of 16 palette colours, with optional error-diffusion dithering (dither=True = Atkinson, 1 = Floyd–Steinberg, 2 = Atkinson) for JPEG/BMP, covering both RGB1024 and RGB640.
Shell
Paged cat — cat("/big.py") now shows one screenful at a time and pauses at a PRESS ANY KEY ... prompt (any key = next page, q or Ctrl-C to stop), so long files no longer scroll off the HDMI display. cat(path, False) still dumps the whole file continuously. Wildcards in cp / mv / rm — a * / ? glob in the last path component now expands to every matching file (reusing the ls glob engine): cp("*.py", "/sd"), mv("*.bas", "/sd/bas"), rm("*.tmp"). Linux-style: sub-directories are skipped, an unmatched pattern is an error, and the destination must be a directory when more than one file matches.
Misc
REPL banner keeps the -preview marker so the build doesn't masquerade as an unreleased MicroPython 1.29.0.
Posted: 02:46pm 11 Jul 2026 Copy link to clipboard
PhenixRising Guru
Hey Pete, do we have a benchmark yet? Just curious
Posted: 06:43pm 12 Jul 2026 Copy link to clipboard
scruss Senior Member
If those are the Rugg-Feldman/PCW benchmarks, they're getting too short to measure on modern microcontrollers. They also measure completely arbitrary metrics that have little relevance to modern problems. Old BASIC interpreters ran slower depending on how you spaced your code, and in pathological cases (Commodore PET v1) how you numbered the lines.
On looking down the thread a bit, I see the Maximite benchmark code. Never gonna work in MicroPython: GOTO isn't supported.
For some things, MicroPython is going to appear slow. Integer handling, for example. But MicroPython uses bigints, so they don't roll over at arbitrary bit boundaries like 2^63.
MicroPython also includes a couple of optimised code emitters that can speed up code many times. There's also a built-in assembler, but that makes the code difficult to port. There are details here: Maximising MicroPython speed. Edited 2026-07-13 04:48 by scruss
Posted: 11:47pm 12 Jul 2026 Copy link to clipboard
okwatts Regular Member
Just a note that in my testing so far (albeit HDMIUSBI2S version) the beep command starts to distort for freq>700 Hz. Eg beep(700,150) is fine, beep(701,150) has a blip included and it gets worse as you increase frequency. MMBasic 6.03 Play Tone(750,750,150)on the same board is fine.
Posted: 07:46am 13 Jul 2026 Copy link to clipboard
matherp Guru
Thanks - will fix. Big update coming later today (I hope)
Posted: 09:35am 13 Jul 2026 Copy link to clipboard
What's new since v0.3 Audio: tracker music, tones and a synthesiser
MOD tracker playback — play("/sd/song.mod", loop=True) plays Amiga MOD music with the hxcmod engine (returns the song title). mod_sample(n) fires one of the module's instrument samples as a sound effect mixed over the running music — background music plus game SFX from a single file. tone(left, right, ms) — two independent sine-wave channels. Calling tone() again while one plays retunes it seamlessly (click-free), so melodies and arpeggios work. sound(voice, side, wave, freq, vol) — a 4-voice synthesiser with sine / square / triangle / sawtooth / periodic-noise / white-noise waveforms per voice and side; volume changes ramp to avoid clicks. pause() / resume() for the current playback.
Graphics: layers, off-screen buffers, a blitter and a sprite engine
Overlay layer + off-screen framebuffer — hdmi.layer() adds a transparent overlay merged live over the display (RGB320); hdmi.create() adds an off-screen buffer in PSRAM. hdmi.write("N"/"L"/"F") chooses where all drawing goes (including the image loaders and console), and hdmi.copy(src, dst) block-copies whole buffers. hdmi.blit(x, y, w, h, x1, y1, src, dst, skip) — a rectangle blitter that copies within or between buffers (or to/from RAM (buffer, w, h) surfaces), with an optional skip colour for cut-out sprites; overlapping copies are safe in any direction. hdmi.vsync() — wait for vertical blanking for tear-free updates and frame pacing (input and audio keep running while it waits). pcsprite — a full sprite engine (import pcsprite): layers, AABB collisions (sprites, screen edges and static walls, edge-triggered), z-order and background scrolling — MMBasic sprite semantics on a dirty-rectangle / overlay-layer compositor. update() commits moves and returns collisions.
Input
keydown(n) — read which keys are held right now (not just typed characters) — essential for games: count, per-key codes, modifier and lock bitmaps. keyboard.on_key(cb) — a callback on every keypress / auto-repeat. Num Lock now defaults on and, when off, the numeric keypad acts as the navigation cluster (arrows / Home / End / PgUp / PgDn / Ins / Del).
Console
console("both" / "serial" / "screen") routes console output (MMBasic OPTION CONSOLE) — keep prints off the HDMI screen while testing graphics, or off the serial port; keyboard input always works. Power-up default is both. On-screen editor scrolling now works: the pcconsole terminal emulator gained a scroll region (DECSTBM) and reverse index, so the pye editor scrolls correctly on the HDMI screen (not just over serial).
Reliability
DS3231 RTC survives a live CPU-clock change — the I2C bus is rebuilt when screen(mode, 315/378) changes the clock, so gettime/settime stay correct. On-device test suite — boards/PICO_COMPUTER_3/tests/ (copy to the SD card, run("test_all.py")): pixel-verified automatic tests for the blitter, buffers, sprites and images, plus interactive tests for the keyboard, audio and console routing.
See USER_MANUAL.md (in the board directory) for full documentation.