A lot of people probably look at hardware that doesn’t come with drivers for the Netduino or Arduino and don’t even consider picking it up if they’re new to this scene. In this article I’ll show you how I wrote a driver for Grove’s chainable RGB LED by just carefully reading the specs and experimenting. I am no Netduino expert, I have only written a tiny bit of code for it since I got it, but this just reinforces how easy some drivers can be to write.
Keep in mind that my illustration of how easy it was to write this driver is not a reflection on how easy it is to write all drivers. Some drivers take a ton of work. Make sure you read the documentation before you buy something so you don’t get stuck with some hardware you can’t use.
My first step was to find the documentation for the protocol for this device. I then scanned around to find the “Communication Protocol” and started digging. What this showed me is that there are two connections to support the protocol for this device. The first connection is called “CIN” for clock input and the second connection is called “DIN” for data input. Simple enough, especially if we’re using the standard Grove base shield and connectors. Just hook it up and make sure you keep track of which port you’re using and you’re ready to start programming. I used header 6 on my base shield so the relevant pins for me were D6 and D7. D6 was CIN and D7 was DIN.
Now you’ll see that there are six well defined bullet points explaining the basics of the protocol:
Data needs to be ready before CIN, and DIN gets into the buffer on the rising edge of CIN.
First 32 bits ‘0’ are Start Frame
Flag bit is two ‘1’
Calibration bits B7’,B6’;G7’,G6’ and R7’,R6’ are inverse codes of B7,B6;G7,G6 and R7,R6
Gray data MSB first, and the order is BLUE, GREEN, and RED
After all nodes data sent, need to seed another 32 bits ‘0’ to update the data
Let’s step through these one by one to figure out how to send data to this device.
They first tell us that “data needs to be ready before CIN, and DIN gets into the buffer on the rising edge of CIN”. What this really translates to for you when programming is that when you want to send a bit to the device you should set that bit on the DIN pin (either 1 or 0), then set the CIN pin to high, and then set the CIN pin back to low. Now you’ve sent one bit of data to the device. Abstraction will make it so we can do this thinking once and then fall back on it later so let’s write a function that sends one bit:
1 2 3 4 5 6 7 8 9 10 11 |
|
This function makes the assumption that you’ve defined cin and din elsewhere. The setup for them in my case (using pins D6 and D7 as I described above) would look like this:
1 2 3 |
|
Now they tell us that the first 32 bits are all zeroes and that this is called a start frame. This makes me think it would be a good idea to expand our abstraction to let us send bytes and then write another function that would send this start frame. That would look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
In the sendByte function I’m taking a byte and using logical AND and equals to extract the bits one by one. One of the next bullet points says the data is MSB first so we want to get the most significant (ie. largest value holding) bits first so that’s how I went about sending the bits. Now that we can send bytes sending the start frame is as easy as calling that function four times with the value 0.
Next they talk about flag bits. In the protocol it shows that after the start frame there are some flag bits. This tells us the two flag bits are both ones. Here’s a simple function that can do that:
1 2 3 4 5 6 |
|
Now this part gets a bit trickier but not too bad. They tell us that we need to send the inverse of B7, B6, G7, G6, R7, R6, followed by the actual color data itself as bytes. B7 and B6 are the two highest bits in the blue color component, G7 and G6 are the two highest bits in the green color component, and R7 and R6 are the two highest bits in the red color component. Sending that data with the functions we built up now is really easy.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
We’re almost there, there’s only one step left! Now we need to send the end frame. It turns out that the end frame is the same as the start frame but to keep the code readable I did this:
1 2 3 4 5 |
|
Now you have enough information to send a color to your device. We should probably wrap it up so that we can make it even easier to use though. Let’s just think about how this is going to be used in practice. A typical user will have a few of these LEDs chained together but for testing you might want to use just one. We know that the protocol requires a start frame, then flag bits, then color data, and the end frame if we use a single LED but for two LEDs it looks like this:
Send start frame
Send flag bits
Send color data
Send flag bits
Send color data
Send end frame
So for our first LED we want to send the start frame, the flag bits and the color data. For our last LED we want to send flag bits, the color data, and the end frame. Here’s a function that does that:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
The extra else blocks have no impact on the executable generated so they’re just there for clarity. You can remove them if you want. Now if you want to send a bunch of colors to a string of three LEDs you can do this:
1 2 3 |
|
That would set a string of three LEDs to solid red, solid green, and solid blue. That’s it, your driver is written!
Check out my driver on Github to see a few more enhancements I added. My code has an abstraction of a color from three integers into an RGB object so it’s easier to pass around and also has a function that can set a string of LEDs from an array of RGB objects. There’s some sample code as well and if you want to see the system in action check out these simple videos:
Post in the comments and share your thoughts and project ideas. If you use this library please let me know!