Gameduino 3X and MicroPython

[As this is the first newsletter of 2020 first some updates on the shop stuff. The I²CMini is now fully launched: the first batch went out to backers and you can buy it from Excamera, Crowd Supply and (soon) Amazon.

The I²CDriver User Guide has been updated to include the Mini, and has been improved with new sections on the GUI and the raw protocol.]


Last year I started looking at MicroPython (and CircuitPython, an easier to use fork of MicroPython) with the Gameduino boards.

This image was deleted by MailChimp when they shut down TinyLetter. 
What's the goal here? To be honest, ever since Gameduino 1, the most common feedback is that they are hard to program. Sure, a minority of programmers who specialize in graphics and are willing to devote time and energy to a graphical application can get good results. But what about people who only want a menu system, or a temperature display, or a photo frame, or show a kid how to make pong? All those people want to write a minimum of code, get the satisfaction of something that looks good, and move on.

And CircuitPython is part of that. It's much more beginner-friendly than the Arduino environment. It's just easier to use.

The downside of Python is its speed. It's much slower than C (or C++), because of the interpretive overhead. When CircuitPython is driving the Gameduino 3X, even with a speedy board like the Adafruit Metro M4, it's a little painful. The demo above - which draws ten spinning sprites - just about runs at about 30 fps on the M4, instead of 60 fps in C.

So the obvious thing to do is to rewrite the Gameduino interface functions in C. From the outside they work the same, but all the fiddly packing of arguments will be done in C, instead of Python. For example one heavily-used method is Vertex2f(), which sends a (x,y) point to the hardware. The Python implementation looks like:
 

    def Vertex2f(self, x, y):
        x = int(16 * x)
        y = int(16 * y)
        self.c4(0x40000000 | ((x & 32767) << 15) | (y & 32767))

and the C version for MicroPython/CircuitPython looks like:
STATIC mp_obj_t _vertex2f(mp_obj_t self , mp_obj_t a0, mp_obj_t a1) {
    int32_t x = (int32_t)(16 * mp_obj_get_float_to_f(a0));
    int32_t y = (int32_t)(16 * mp_obj_get_float_to_f(a1));
    C4(self, (0x40000000 | ((x & 32767) << 15) | (y & 32767)));
    return mp_const_none;
}

Each function can similarly be replaced with a fast C version. So after all this plumbing, what's the speedup? Measured on a pyboard running the latest 1.12 firmware it's about 3X. That is, what took 100ms with the pure-Python interface now runs in 33ms. It's the difference between a 10 Hz update rate and 30 Hz.

This image was deleted by MailChimp when they shut down TinyLetter.
The repo is here:

https://github.com/jamesbowman/py-bteve

This is all quite fresh, so changing quite rapidly as I hack on it. Hopefully it will produce some nice (30 Hz!) demos soon.

Thanks for reading.