13. Networking

This week's individual assignment was to design, build, and connect wired or wireless node(s) with network or bus addresses and local input &/or output device(s). Basically this means we had to make 2 devices talk to each other with a wired connection or wirelessly. The other requirement was that at least one of our devices had to be one we designed. I decided I would use the board I created in electronics design week when I made a game console development board with a semi-functioning pong game.

View the group assignment here

Da Lobby

I knew I wanted to build on what I had and finally bring wireless multiplayer gaming to the world. I also knew I didn't want it to rely on a central server because I liked the idea of just playing people in your vicinity without any extra required resources. My first step was creating a simple game lobby where you could see other players around you and challenge them.

Based on what I read, I liked some of the features bluetooth low energy (BLE) had to offer.

BLE diagram (from arduino.cc)

BLE has 2 actors. Peripherals and centrals. Peripherals are generally things like sensors that offer useful data or updates about the temperature, moisture, etc... They can also get updates by centrals connected to them. This could be to turn on or off a piece of functionality or change what the peripheral measures for. The "centrals" are the devices that are requesting the data from these peripherals, like computers or things with more processing power to bring in the data and actually do somethign with it. In my initial thinking I decided each of the gaming devices would act as both a central and a peripheral. After a central connected to a peripheral, you see the name of the other player in your lobby. At this point you could offer them a challenge. Names were created at random with a combination of 2 animal names from a list of 10. The scanning id run periodically and if a peripheral hasn't been seen for a while, the player is removed from the lobby. For the bluetooth communication I used the ArduinoBLE library.

Not quite Noah's Arc

Then every 100 milliseconds the central on each controller does a check for peripherals advertising on a specific channel set by me, sort of like listening on a specific radio station.

Perpipheral setup code to advertise on LOBBY_UUID
Set up central to scan foradvertisers on LOBBY_UUID
Check for peripherals in main loop
Add peripheral to lobby with current time to track last time seen

TODO: Take video of Lobby working

I only did a prototype of this code and it seemed to work although sometimes names were removed from the lobby a bit prematurely. A longer timeout could fix this but I have considered using ESPNow, the protocol that comes builtin with ESP32 chips.

Wireless gameplay

The next task was actually allowing for wireless game play. I wanted to try something else so I decided to use ESP's ESPNow protocol which is used to connect ESP boards peer to peer with low latency but high throughput. The downside was that it does resend failed messages which is not generally how games are managed over a network. The UDP protocol is normally used and requires no response for each message. This allows for a lot more data and bandwidth to be used. I, however, decided ESPNows simplicity made it worth a try. With UDP there would be a bit more setup. ESPNow it was. My strategy was to have one controller be the parent and the other be the child. The child would allow the game simulation to happen as it normally does but could be updated by the parent when it received a new message. On my first version I made some mistakes

Jitter Bug

I investigated how many packets were being dropped by each controller...not enough to justify the jitters. I checked the cores the espnow code was running on vs the main code. They were indeed on different cores. The ESP32 has 2 available. This was definitely causing inconsistent results. I resolved this by using the atomic library in C++ to use thread safe variables. This means when one core makes a change to a variable, the other core can't modify it until the write is done. This did solve some things but the jitter bug continued. Eventually I realized that even though my parent/child model made sense, I actually was trying to do too much. I changed the code so that the child only controlled its own paddle. The rest of the game state would be dictated by the parent. This worked very very well. The parent/child is chosen by whoever as the greater mac address (which would be exchanged in the lobby). An easy comparison without any extra communication.

N'Sync

Now that I have a good baseline communication its time to make more games and clean up the code so different components can be reused, etc...

Model files