I’m going to assume that you’ve heard of Pong, probably one of the first computer games that ever existed. But you know, 2 dimensions is really hard to cope with, so a new – simpler – version has popped up recently: 1D pong. Just one line, where the ball continuously travels from one side to the other. The goal is that you press the button before the ball hits the end of your side of the line.
Many such projects have popped up on Hackaday.com, with probably all of them using nice LEDs in a row. Building this game is actually not that hard: you need something to control the LEDs and you need 2 buttons. But I wasn’t just going to make another version of what many people have done already. Instead, I’ve added a nice extra which probably seriously over complicates stuff, but it was a really educative project for me.
What’s different ?
I said the difference was most probably over complicating it. What do you think about adding a TCP/IP stack? That’s exactly what I did! Lately I’ve been messing around with a TCP/IP stack called PicoTCP which is open source (GPLV2 licensed).
Another stack? Why don’t you use LwIP? Well, I have to admit that I was involved in the development of this stack (as I was an employee of TASS Belgium who’s the copyright holder of the code). I have been porting this stack to the PIC24 platform, and have made a device driver for the ENC28J60 Ethernet controller.
To provide you with a bit more promo about this stack: you can get it as small as 10K, but fully featured it’s currently using up 30K of your precious RAM. That’s really not that much. It provides you with many of the standard protocols available nowadays, the only thing you’ll have to do is
- Write platform specific functions for memory allocation (zeroed alloc) and free and provide a stable 1ms time source
- Design a device driver that implements the create, destroy, poll and send functions
and you’re ready to go! PicoTCP is running in its own event loop, which you can use for your own application too. But it’s also possible to have PicoTCP work with an RTOS (like FreeRTOS, mBedRTOS…).
Tell me more!
First things first: the hardware. On the picture below you see everything that is required for one node. The brains are a PIC24F256GA106 on a Flyport Wifi which is placed on a small PCB that contains the buttons and a small level converter circuit for the LEDs. The LEDs are the every popular WS2812, of which I’ve placed 15 on one node. And lastly there is a 2000mAh USB battery powering the circuit (quite useful actually for these kinds of electronics projects – easy to recharge!)
The Flyport Wifi board contains a PIC24F and a Microchip wifi module. Microchip is a really good at crippling their software. Either you get the whole shebang on their hardware (including their own tcp/ip stack), or you have to start messing around. Hackers as we are, the second option was the preferred one! A good tip for making a device driver, is looking for the functions that provide you with the data coming from the ethernet layer. If you can intercept the data there, you can bypass the entire existing stack and give it to PicoTCP. Once PicoTCP compiles and is running on the board, you should already be able to ping it.
The WS2812 are really popular, but in my honest opinion the serial protocol they require for operation is just crap. The protocol runs at 800KHz, and unless you can set your SPI hardware to work at this exact frequency (there is some margin), you’re back to bitbanging. At 800KHz, that’s not an easy task. Especially if you know that the instruction clock for the PIC24F is running at 16MHz. The WS2812 is not only frequency sensitive but voltage sensitive too. Providing the data signal at 3.3V might be lacking a couple of mV! Adding a simply level shifter (2 diodes and a resistor), shifts both the 1 and the 0 0.7V up.
My first attempt was to get the SPI hardware to output the 800KHz waveform, but sadly at 32MHz input clock, you cannot set the clock division to get to 800KHz. So that option was off the table! Next I tried to bitbang from C code, which almost worked except for the shortest time that had to be achieved for communication. Second fail. My last option was to give inline assembly a shot. It was something new for me; I have written assembly code, but connecting C variables and pointers to assembly instructions was something new for me. The assembly code is kind of ugly and could probably be cleaned up into a decent loop. I still have a NOP() or two left to do so, so that should work. Something for the future… If you’re curious about the code, check it out below.
Now, the interesting part. So far, all components were more or less the same as you would have with a regular 1D Pong game. But the network functionality is something new. The application is running on top of UDP, mainly because it’s an easy protocol to work with. Using UDP does have its consequences: it’s unreliable, so you wouldn’t know if it did not arrive. However, UDP is packet oriented (either you get the packet in its entirety, or you don’t get anything at all). We’re working on top of Wifi, which means things might go wrong. So how do you cope with this problem?
One thing to keep in mind: Don’t try to recreate TCP. You’ll start with one thing, add another and one more and in the end you have TCP all over again, but probably a very crappy version. A good UDP protocol, is to re-transmit (keep re-transmitting) until the message is acknowledged by the other side. This is what I did. The protocol is using these messages:
- BALLSENT – one node sends the ball to another
- BALLRECV – the other node has received the ball
- BALLLOST – one node has lost the ball (did not press button in time)
- BALLINIT – the previous node has initiated a new ball
Every second message is an acknowledgement for the first one. The idea is that a first message is sent, and resent every 200ms, as soon as the other node acknowledges, the first one stops transmitting. Then the acknowledgement stops transmitting too. This protocol seems to work really well.
But there’s more! The idea was to create a setup of devices that can work entirely decentralized. There is one WiFi AP (but peer to peer would be a possibility too), that’s only used for connecting the nodes together. DHCP is not used, instead each node is acquiring an autoconfigure IP (so called SLAAC, one of the PicoTCP features), based on its MAC address. It does check if the address is unique on the network, so you won’t have any collisions!
Because there is no central organ, each of the nodes will have to know about the other ones. This is achieved by a small Hello UDP protocol that’s simply sending a broadcast message to all other nodes. This way each node can keep track of the other ones. And if you put all of these aspects together, you have this demo!
Code layout was a fail, so I’ve uploaded them to my bitbucket account. You’ll find there both the application code, and the assembly driver for the ws2812.
My demonstration was used by Tass Belgium as an eye catcher on multiple job fairs here in Belgium (for engineering schools). Try to find the boards on these pictures.