The original skateboard is an exercise in simplicity. Its code was written in C#, and totaled 7 classes. The new skateboard has a lot more electronics to monitor and utilize, so I knew I was in for a more complicated software project. Adding in an Arduino to control the LED lighting complicated things, as that meant I'd have two codebases. One in C# for the Netduino, and one in C++ for the Arduino. Additionally, the two controllers would have to communicate with one another.
Luckily I could pull much of the existing code over from Skate One with very few changes. This gave me an OSMC controller class, gyroscope and accelerometer classes, and a working complimentary filter class that were proven in the field. Since I was using effectively the same hardware in these areas, no material changes were required to these classes.
So the trick was to figure out how to manage multiple input sources (infared and Bluetooth, battery voltage, kill switch), handle multiple outputs (Bluetooth, LED strip, multiple other lights). I needed everything running at once, with no part of the system starving another.
Here's a simplified block diagram that shows the major parts of the design.
At the center of the architecture is a finite state machine. This class is responsible for keeping track of what state skateboard is in, and responding to events or conditions that might alter the state. FSMs are very handy concepts for machines like this, I can't recommend them enough. Here's a simplified version of the various states the board can be in, and the actions that move them to another state.
Most of the the other blocks are related to dealing with input. If we recieve data via infrared or Bluetooth, it has to be parsed and processed. We break this down into two steps (parsing and processing) due to much of the data showing up during interrupts. We parse it quickly, and stick the resulting command into a queue. That way we can exit the interrupt handler quickly, and process the command at a later time. A lot of this input is then fed into the state machine. For example, if the kill switch is activiated, a message is sent to the state machine so that it can cut power to the motor, flash the LED strip appropriately, and place the board back into the resting state.
The remaining blocks have to do with output, mainly related to sending commands to the OSMC controller and the Arduino.
There are a few useful patterns used throughout the code. I implemented a logger class similar to Log4J or Log4Net. Each class can use a logger to output messages to Bluetooth or the debug window in Visual Studio. Log messages are assigned a priority, and you can specify which messages you want to see from each class. For example, you may want to see detailed data from the state machine. Via a Bluetooth connection, you can issue a command that tells the software "Show me all log data for the state machine". Likewise, you can turn off all output from a class if so desired. I try to prioritize messages so that important ones show up by default, but you can crank up the message detail if you need to.
In order to simulate many things happening at once, I defined an IModule interface that allows a class to define a "Tick()" method. This method gets called once per main program loop, and is responsible for doing one small unit of work. For example, the command processor may check to see if any commands are queued up. If there are, it will take the next command in line, and process it. It then returns, regardless if there are more commands to process so that other IModule classes get a chance to do there thing. The key is to do a small slice of work each time Tick() is called. This means that many IModule classes maintain internal state. I could have used the built in threading classes, but they are very limited, and you give up some control. My needs were simple enought that implementing a simple round-robin module list was sufficient.
One interesting problem that surfaced was related to communicating with the Arduino. The Arduino handles the tricky timing necessary to drive the LED strip. The Netduino uses a serial link to the Arduino to send lighting commands. The issue is that during the lighting code execution, the Arduino has to disable interrupts. This can easily cause serial data to be dropped if it is sent while interrupts are disabled. To combat this, I use an additional line to communicate to the Arduino that I'm about to send it data. If the Arduino sees this line held high, it knows to hold off updating the LED strip until it receives a new command, or the line goes low. Luckily this was discovered during the prototyping phase, and I was able to include this solution in the custom shield board.
This same issue with interrupts meant that it was impossible to upload new software to the arduino while it was doing lighting animations. To resolve this, I added the "UpsideDown" state to the FSM. When the board enters this state, it turns off all lighting. So when I flip the board upside down to get to the USB ports, the state machine automatically turns off the lighting, which then allows me to upload new software.
Another problem to solve was allowing for critical variables to be modified by external input. I defined an interface that alerted a class to a variable change. If a variable was changed via Bluetooth or infared remote, each supporting class had a chance to read new values out of a generic variable store. Most of these configuration changes would occur while the skateboard was resting, so I didn't need to worry too much about being extremely efficient here.
I also changed the way the OSMC was driven for this version of the skateboard. I left the braking enabled all the time, since I had read that this was better for the OSMC board. I was afraid that this would make riding the board strange, but in reality it actually felt more "right" than the coasting mode. When I add an OSMC page to the site, I'll detail the driving logic.
I ended up with 30 classes in C#, and 20 classes in C++. A giant leap up from the original 7 classes that Skate One required. Most of these classes are tiny and easy to digest, many only being a few lines long.
I included all sorts of diagnostics in the software. One important thing I track is "Updates Per Second". This allows me to see how quickly the state machine and angle/motor calculations are being calculated. Things are currently running at about 100 updates per second, which is about perfect. The accelerometer and gyroscope both output data at that rate. If you were to update more often, there would be no benefit. If you update too slowly you can get wild over-correcting behavior out of the motor speed algorithm.
Once the frame, electronics, and software was complete, it was time to take the board for a ride.