Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.
matherp Guru Joined: 11/12/2012 Location: United KingdomPosts: 11598
Posted: 03:22pm 02 Jul 2026
Copy link to clipboard
Print this post
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.
PhenixRising Guru Joined: 07/11/2023 Location: United KingdomPosts: 1979
Posted: 07:18pm 02 Jul 2026
Copy link to clipboard
Print this post
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)?
matherp Guru Joined: 11/12/2012 Location: United KingdomPosts: 11598
Posted: 11:17am 03 Jul 2026
Copy link to clipboard
Print this post
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
PhenixRising Guru Joined: 07/11/2023 Location: United KingdomPosts: 1979
Posted: 12:13pm 03 Jul 2026
Copy link to clipboard
Print this post
Makes me even more proud of the awesome MMBasic
matherp Guru Joined: 11/12/2012 Location: United KingdomPosts: 11598
Posted: 02:29pm 03 Jul 2026
Copy link to clipboard
Print this post
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
matherp Guru Joined: 11/12/2012 Location: United KingdomPosts: 11598
Posted: 07:05pm 03 Jul 2026
Copy link to clipboard
Print this post
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.
matherp Guru Joined: 11/12/2012 Location: United KingdomPosts: 11598
Posted: 02:41pm 04 Jul 2026
Copy link to clipboard
Print this post
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")