10/07/2021 at 14:26 •
Pimoroni has released their PicoSystem, which is a handheld game console with an RP2040 microcontroller — and since it runs CircuitPython, of course I had to make it run the Stage library as well. I received a beta unit from Pimoroni, and with help from Gadgetoid, we got everything working:
The changes are getting merged into CircuitPython even as we speak, so they should be available in any version greater than 7.0.0 (not in 7.0.0 itself, though, too late for that).
I had to fix screen scaling, because I forgot to implement it in the CircuitPython version — I only had it working in MicroPython. I also improved the menu program, so that it adjusts to the screen size.
08/30/2021 at 19:34 •
Today I finally sat down and got Stage running on the Meowbit. I had this very neat little console in my drawer for years now, and CircuitPython has been ported to the STM32 chip that it uses, but there was a bug preventing me from re-initializing the display to the settings that Stage expects. A few days ago I got reminded about Meowbit, and realized that I don't have to re-initialize the display to make it work, I can change the default display settings, since I have to recompile CircuitPython firmware for it anyways to include the parts of Stage written in C.
So here it is:
As an addition, I also modified to Stage library to allow using different sound output than the audioio module it defaults to. The Meowbit, for example, uses audiopwmio, but you could also use audiobusio for a device that uses I2S.
If the pull request is accepted, the support should be available in CircuitPython 7.0 to be released soonish.
12/18/2020 at 17:33 •
After watching Unexpected Maker's stream on which he builds his Play Shield for Tinypico, I decided to try and see if the Stage library would run on it.
I asked him about which display he uses and how it is connected, and made this "driver" for it:
import ustruct import utime class Display(object): # ST7739 _BUF = bytearray(4) width = 240 height = 240 def __init__(self, spi, dc, cs=None, rst=None): self.spi = spi self.dc = dc self.cs = cs or (lambda x: x) self.rst = rst or (lambda x: x) self.reset() def reset(self): self.rst(0) utime.sleep_ms(50) self.rst(1) utime.sleep_ms(50) self.cs(0) for command, data in ( # (b'\x01', None), # reset (b'\x11', None), # wake (b'\x3a', b'\x55'), # format (b'\x36', b'\xc8'), # mad (b'\x21', None), # invert (b'\x13', None), # no partial (b'\x29', None), # on ): self.write(command, data) utime.sleep_ms(150) self.cs(1) utime.sleep_ms(50) def write(self, command=None, data=None): if command is not None: self.dc(0) self.spi.write(command) if data: self.dc(1) self.spi.write(data) def block(self, x0, y0, x1, y1): y0 += 80 y1 += 80 ustruct.pack_into('>HH', self._BUF, 0, x0, x1) self.write(b'\x2a', self._BUF) ustruct.pack_into('>HH', self._BUF, 0, y0, y1) self.write(b'\x2b', self._BUF) self.write(b'\x2c') self.dc(1) def clear(self, color=0x00): self.cs(0) self.block(0, 0, self.width, self.height) chunks, rest = divmod(self.width * self.height, 512) pixel = ustruct.pack('>H', color) if chunks: data = pixel * 512 for count in range(chunks): self.spi.write(data) if rest: self.spi.write(pixel * rest) self.cs(1) def __enter__(self): self.cs(0) return self def __exit__(self, exc_type, exc_val, exc_tb): self.cs(1)
One surprising thing that took me a while to figure out is that to get correct colors, you have to put the display in inverted color mode — a bit weird, but I guess it's a question of how the actual LCD is connected to the chip inside. A quick test confirms that it works:
Next I needed to handle the buttons. The Play Shield uses MPR121 chip to handle them, so I just added this button-handling class:
class Buttons: # mpr121 def __init__(self, i2c, address=0x5a): self._i2c = i2c self._address = address for register, value in ( (0x80, b'\x63'), # reset (0x53, b'\x00'), # stop mode, reset config (0x2b, b'\x01\x01\x0e\x00\x01\x05\x01\x00\x00\x00\x00'), (0x5b, b'\x00\x10\x20'), # debounce, config1, config2 (0x53, b'\x8f'), # exit stop mode ): self._i2c.writeto_mem(self._address, register, value) def _get_pressed(self): return int.from_bytes( self._i2c.readfrom_mem(self._address, 0x00, 2), 'big')
Not having an MPR121 chip at hand, I couldn't test it, but once I sent the compiled binaries and some example code to Unexpected Maker to test on the actual shield, it ran correctly, as he shows on Twitter: https://twitter.com/unexpectedmaker/status/1339756136224890880
Well, OK, that demo doesn't actually use the buttons, but it initializes the chip, and that seems to have worked.
I can do further refining once I get my hands on the actual shield.
The full code and compiling instructions are at https://github.com/python-ugame/micropython-stage/tree/master/tinypicost7789
03/08/2020 at 22:31 •
I got some help with that, and did a little bit of progress, though it still doesn't work the way it should.
People who are smarter than me started to figure things out, and there is a pull request with more documention with some additional discussion that has cleared some stuff for me.
Basically you can only call functions that have been explicitly exposed to the native module mechanism, and if you need some additional functions, they need to be added to a list. Sounds simple enough, and I managed to get that to work, but there is a small catch: it will only work with the firmware that has those functions added as well, so it kind of defeats the whole purpose.
Fortunately, there seems to be some ongoing work on solving this:
So hopefully in a couple of months it will be possible.
12/25/2019 at 23:04 •
MicroPython 1.12 is released, and the new release includes an interesting feature: the .mpy modules can now contain native code compiled with C or any other language that produces standard object files. Of course such .mpy files are then architecture-specific. This is great news for the stage library, because it means that you won't need to compile it into the firmware anymore — you should be able to just copy the .mpy file to the filesystem and import it as any other Python module.
But how to actually do this? There is very brief documentation at http://docs.micropython.org/en/latest/develop/natmod.html and there are some examples at https://github.com/micropython/micropython/tree/master/examples/natmod — not much to go by, but I will try anyways.
The first, naive, try failed badly. Just removing the module initialization code and replacing it with mpy_init with equivalent functions is maybe enough for the factorial example, but not for my library — in particular, the dicts that act as namespaces for the two classes that I defined fail to compile, because they would normally go to fixed code, and that is not supported with dynamic loading. So I started to dig through the examples, and figured out from the framebuf and regexp modules how to dynamically declare those classes. So far so good. Now the mpy_ld.py script crashes. Great. After naively fixing the script, the compilation still fails, because it can't locate the mp_obj_get_array function. Which is super-weird, because it has no problem locating other functions from the py/obj.c file.
My code is available https://github.com/python-ugame/micropython-stage/tree/master/mpy if anybody wants to give it their try.
At this point I've given up. I might try it again after the next release, maybe this feature will be more ready for actual use.
08/01/2019 at 20:02 •
I added support for the buttons on the game face of M5Stack, (it's very similar to my #D1 Mini X-Pad Shield, I basically just had to change the numbers for the button maping, the I²C address, and negate the output). Together with the automatic 2× scaling, it means you can play the µGame games on it now (and, what is more important, easily make your own).
What I don't like is the noise from the speakers — for some reason that happens only when the face is connected, and not on USB — perhaps grounding issues? There is no sound from the game, because so far MicroPython doesn't support it (though technically it's possible, someone just needs to write the code).
08/01/2019 at 14:00 •
One problem with supporting devices other than #µGame is that they use different displays. The 160×128 version of ST7735 is not that bad — there is just a strip of 32 unused pixels — but the ILI9341 is almost twice as large, with 320×240 pixels. So you either play the game on a tiny postage stamp, or you adapt it to the platform — but the 16×16 tiles and sprites are still too small.
That's why I just added a "scale" optional parameter to the Stage object in the library, that allows you to scale the display 2× or more, giving you back the chunky pixels we so love.
08/01/2019 at 13:53 •
While the CircuitPython port of this library is being used both on the #µGame and a number of Adafruit boards (see #PyBadge Hacking), and is included in the official repository, the MicroPython port is not as well supported. In fact, if you wanted to use it on MicroPython, you were up for a bit of a challenge, figuring out what files to modify in order to have the C portion of the library compiled in your firmware. But that is no more.
Recent releases of MicroPython have a primitive but workable way for adding third-party modules, and I finally took the time to follow that guide and make a proper repository for the MicroPython version of Stage, available now at https://github.com/python-ugame/micropython-stage. The documentation is still lacking, and the only fully working example is for a D1 Mini with the #D1 Mini X-Pad Shield and an ST7735 display, with some partial support for M5Stack, but I plan to extend that to a couple of third-party devices, including OdroidGo, Meowbit, and maybe even Pokitto.
01/30/2019 at 23:17 •
If you want to try playing with this, but don't feel like compiling your own MicroPython firmware, I prepared a build for the ESP8266 with the #D1 Mini X-Pad Shield and an ST7735 display module: https://github.com/deshipu/micropython/releases/download/v1.10-34-g711238a95-dirty/firmware-combined.bin
I also updated and rebased the "stage" branch, here: https://github.com/deshipu/micropython/tree/stage
01/08/2019 at 15:05 •
I just realized that while this project page links to the repository with the plain python portion of the library, the portion that is written in C is harder to find. So here it is: https://github.com/deshipu/micropython/tree/stage
The branch is a little bit dated, since I didn't upgrade it in a while, but if you look at the three last commits, you will see all the required code. It's not much, just enough to make the plain python version fast enough. Enjoy!