Playing Star Wars on the ESP8266 with micropython
I sometimes attend the London python dojo, a monthly event for anyone,of any level, who’s interested in exploring python for a few hours of a Thursday evening. The last one was hosted by M&S Digital, the great team behind Marks & Spencer technology.
Damien George came along as a guest speaker. Damien’s the person behind micropython.org, a brilliant project that has created a completely separate, implementation of Python 3 designed, and optimized, to run on low-end SOCs (think Ardiuno and similar). His latest kickstarter, funded earlier this month, raised money to create a port of micropython for the ESP8266 board. The success of the micropython project has been astounding, and Damien’s ongoing successes point to some rare talent and perseverance.
The ESP8266 is another marvel. A $5 chip that was marketed as just a plug-in Wifi module for other systems. People rapidly discovered that the chip was a fully-fledged programmable system with some really interesting features. Comprehensive IO, decent memory, a hardware RNG, the list goes on. Unfortunately, English documentation is poor, or lacking detail, so there’s been a coordinated effort among a group of developers to discover and document the chip’s features, resulting in a mass of information online about the thing.
I’m truly excited by all these developments, with the BBC micro:bit being given (yes, there’s a micropython port!) to 1,000,000 UK children, and all the work by the Raspberry PI foundation, the fact that it’s possible to throw together a self-contained internet client that can fit inside a matchbox, and show a streaming ascii video so trivially shows that we’re on the cusp of some fundamental changes in how technology works.
The fact that this tech is being given, uncontrolled, to a generation of children guarantees that exciting and unexpected things are going to appear, and soon. This is Minecraft, but 1,000 s of times more useful.
So there’s around 30 of us at the event, with 8 ESP8266 boards, some assorted hardware accessories, and a few hours to play around. Between us, we came up with: music playing (using digital outputs, no less!), an LED binary counter, an HTTP-controlled fan, and many other things during our 90 minutes:
One idea that came up at some point, was to connect to a public streaming telnet service: towel.blinkenlights.nl, and display the output on an LCD display someone had brought.
towel.blinkenlights.nl serves a complete retelling of the first Star Wars movie (not the Jar jar Binks one!) as a streaming ASCII movie. You just connect a telnet client, sit back, and let the story unfold.
The idea stuck in my mind, and later that night, I ordered some cheap (The one I used cost £2.38 including delivery!) displays online, with the intent of giving the problem a go
A few weeks later
Coming home last night, I found a new 0.96" OLED display through my letterbox, fresh off the ship from China. These displays are tiny but manage to pack 8,000 pixels (128x64) into the area of a 2 pence piece. They’re also really simple to drive, and with no back-light or polarizing filters, look sharp and clear under most lighting conditions.
The display uses an I²C interface which is a simple way of devices to communicate, and simple/slow enough to implement in software without much effort. As I was firing up my tools to set off, I discovered that some ESP8266 I²C code had landed in github literally hours earlier, making this project a lot simpler (Thanks!)
One git pull/make later, I had the updated firmware flashed
Luckily the pins on the board were labelled (seriously, some boards aren’t, and don’t have online official documentation either), so it was easy to match the pins to the ESP. Because there’s no (known) hardware I²C driver, any GPIO pins can be used. This was great, as there are two GPIO pins right next to some power pins on the NodeMCU board I’m using, allowing a dead simple connection. The display seems fine with 3.3 v or 5 v on its VCC pin:
As far as knowing what to send to the board went, there was a bit of a challenge. The only text on the board, or packaging was pin labels, and some component labels. No part number, display model, or anything. Luckily, these 0.96" displays are really common, and some googling uncovered the panel was an ssd1306.
The docs may seem a bit scary, but I love these types of technical doc. They’re worded, not to make it easiest to get started, but to help you understand exactly what’s happening, and sometimes why.
Here’s the section on the I²C interface:
I’ve highlighted the bits that the I²C library handles for us in yellow, while the rest is where the data we want to send goes.
Armed with this, I started trying to get the thing to turn on
Finally, after quite a bit of trial and error, I ended up with this:
The random display shows that it’s reading from a DRAM that has random values.
Next step was to write some output to the display. This was actually very simple. The display takes 1 byte at a time and uses that to toggle the display of a vertical strip of 8 pixels in one go. It then internally moves the pointer along one pixel, ready for the next block to be written. By doing this 128 times, an entire section of the screen can be updated:
>>> for i in range(128):
... iic.writeto(60, b’\x40\xff’)
Or, slightly more interesting:
>>> for i in range(128):
... iic.writeto(60, b’\x40’ + chr(i))
Coupling that with a command that lets you select which row of the screen to update:
>>> for i in range(8):
... iic.writeto(60, b’\x80' + (b’’ + (chr(0xb0 + i)))[-1:])
... iic.writeto(60, b’\x40' + b’\xaa\x55' * 64)
That’s basically all there is to it. There are commands for scrolling the display, and some other features, but these are minor by comparison.
The telnet protocol is really, really, simple. For our purposes (and 90% of purposes) the protocol just involves reading and writing to/from a network socket. There is some capability negotiation stuff in there too, but that can usually be ignored.
The other factor here, is escape sequences. The towel service uses escape sequences to clear the screen (luckily it doesn’t try to control the cursor any more than that!), These are very straight forward. To clear the screen, it’s: 0x1b 0x5b 0x4a, or ‘<esc>[J’. There’s a great reference for all commonly used escape sequences online here.
All that’s needed, therefore, is to:
- Connect to the wifi
- Find the IP address of the towel server
- Repeatedly read from the connection as data arrives, looking for newlines and control codes in the data
The code for this can be seen below, and is pretty straight-forward. The Wifi connect code will improve as Damian’s kickstarter progresses.
micropython has some fairly low (but understandable!) limits on the size of modules that can be embedded in the firmware, so I had to work a bit to keep the code size small. Comments were added afterwards:
That’s it. Please get in touch if you have any questions, or suggestions.