Monday, February 21, 2011

Arduino notebook: 16x2 LCD display (Part 1)

As I experiment with my Arduino kit, I find I want an easy, self-contained way to display information. The system provides one easy method in the Serial object with its print functions to send things to the computer through the USB port. This works well in development phase when the computer is attached to the board. However, I want to be able to run the kit detached from the computer and still get feedback. When there's limited data, I can use LEDs. They can be on or off to provide one bit of information. Additionally, they can be blinked in various patterns to communicate more than one bit of data. However, this is more abstract than I want. It doesn't strike me as easy, either to program or to use. I just want to read something directly without resorting to Morse Code to get the information.

I also don't want to spend much money.

So, with these three requirements in mind, I searched around. It seemed an LCD display was the way to go, as I found a couple different options. The first is to connect the LCD directly to the Arduino kit. At about $10, it is an inexpensive choice. It requires a number of wires and a moderate amount of programming to drive the display. Yes, this meets the cost and self-contained requirements, but isn't as easy as I would like. The second option is a kit with one of these LCDs and support circuitry to allow me to simply hook power, ground and a single data line to my Arduino. It uses the same Serial object to display information as the computer uses to receive data via USB. So, it satisfies the easy requirement much better, is more self-contained than the bare LCD, and at $25, still doesn't break the bank.

Upon further investigation, the support circuitry turns out to be an Arduino. I'm pretty sure this controller is sitting idle, spinning around doing nothing, most of the time. In addition to getting an LCD display, I believe this option in essence gives me a second Arduino that I might be able to reprogram to have additional features.

With all this going for it, I purchased this kit from SparkFun.com.

How to build a Sparkfun Serial Enabled LCD kit (LCD-10097)

Some assembly required

SparkFun Serial LCD kit - Step 1
(Click any of the pictures for a larger view.)

This is definitely a kit. It comes as seen above: a printed circuit board (PCB), some capacitors, a resistor, a crystal, a transistor, a potentiometer, a 28-pin chip, the LCD screen assembled on its own board and miscellaneous hardware. All this comes in a plastic bag in a box.

There were no instructions with the kit, nor could I find any online. To make up for this, the circuit board was well marked with silk screened values for the components and orientation marks for items that have to go in one way. Since I have put together many electronics kits over the years, I wasn't intimidated by this at all. If you are new to building kits, not having instructions might put you off a bit. However, don't let it. It's really pretty simple if you know how to solder. I'll outline what I did below to help you along.

As an overview, I basically installed the smallest components first and worked my way up to the larger ones.

SparkFun Serial LCD kit - Step 2
First, I installed the small, ceramic capacitors and the one resistor. There are two values for the capacitors. The capacitors labeled "220" go in the locations marked "22pF" on the PCB and "104" capacitors go in the locations marked "0.1uF". The resistor goes in the place marked for it. How these components are oriented on the board does not matter. Either lead can go in either hole.

SparkFun Serial LCD kit - Step 3
Next I added the transistor, crystal and electrolytic capacitor. The crystal can go in either way. The transistor and capacitor however need to be oriented correctly. For the transistor, make sure the flat side of the component matches the outline on the PCB. The capacitor has a light band with a minus sign in it running down one side. Make sure the lead next to this marking goes in the hole with the minus sign silk screened next to it on the PCB.

SparkFun Serial LCD kit - Step 4
Following this, I added the power/data connector and the potentiometer. These pretty much can only go in one way. Just match the components to the silk screen outlines on the PCB.

SparkFun Serial LCD kit - Step 5
After installing all the discrete components, I went to my local electronics store and picked up some DIP sockets. Generally, I don't like soldering IC chips onto boards directly. If I damage them with too much heat while soldering, or do something else stupid to cause them not to work, I don't want to go through the hassle of unsoldering all those connections to replace it. I've had to do that before and it's no fun.

The store didn't have any 28-pin DIP sockets in the narrow variety, but two 14-pin ones will sit nicely next to each other without any problems. On the PCB, note the silk screened outline. One end has a flat edge and the other end has a divot taken out. By convention, the edge with the divot marks the end where pin one goes. The sockets should also have one end that's flat with the other end notched. It's best to put them in with the same orientation as the silk screen. This makes things much less confusing. After the sockets are soldered in place, the chip can be inserted into it. The chip also has a notch in one end. Just line up all the notches and everything should be good.

SparkFun Serial LCD kit - Step 6
Finally, I did a good visual inspection of all my solder joints with a magnifying glass. The next step involves soldering the two boards together. Once this is done, it will be very hard to take them apart, so I wanted to be sure everything looked good.

There are three primary things to look for:
  1. Solder bridges. This is when solder spans two pads when it shouldn't. This is particularly easy to do on the pins for the IC sockets, but they can happen anywhere.
  2. Cold or missing solder joints. When working down a row of IC pins, it's easy to accidentally miss one. A cold joint is when one part of the joint, either the PCB pad or the component leg isn't hot enough to melt the solder but the other one is. Solder will flow onto one but not the other. Both cold joints and missing joints can cause intermittent or unreliable operation.
  3. Solder splatters. Sometimes solder will splatter or drop onto the board where it shouldn't be. Make sure there isn't any messy blobs anywhere. If you do find one, typically they'll just scratch off pretty easily.

After the visual inspection, I soldered the header to the LCD's PCB. Then I placed a small piece of cardboard between the two PCBs to function as a spacer. I placed the controller's PCB on the other side of the header's pins and made sure the two boards were parallel. I wrapped a bit of scrap wire around the assembly to hold them together while I soldered the two end pins. Once they were soldered, I took the wire off and finished soldering the rest of the pins to the controller board. When all the pins were done, I removed the cardboard and declared myself finished with the construction phase.

Batteries not included

Now that I had the physical assembly done, the next step was to hook it up to the Arduino. I went to the oracle of all things technical, Google. Unfortunately, with a moderate amount of searching, I didn't find any definitive instructions regarding what to do next. I found some forum postings that alluded to using the Serial object and some others that said to just hook it up, but didn't give any details.

I figured I probably couldn't hurt anything too badly with a bit of guess work. The connector for the LCD was clearly marked with 5V, Gnd and Rx, so I connected the red wire to the 5V pin, black wire to the Gnd pin and the Rx wire to the Tx pin on the Arduino. Typically in serial communication, transmit on one device goes to receive on the other. With the normal trepidation that I always have turning on a kit for the first time, I plugged in the USB cable.

The backlight came on and no smoke escaped. Well, that's a good first step. The SparkFun site points to some documentation on github, but it only talks about command sequences to control the LCD, it doesn't talk about how to talk to the LCD. It presupposes you know how to do that. I found a library that seemed to imply it worked with this kit, but didn't outright say it did. When I tried using some of the code from it, I got nothing. At this point I didn't know if it was a software problem or a hardware problem. I turned everything off, unconnected the LCD and took a magnifying glass to the kit. I looked at all the solder joints I could. Unfortunately, most were in the middle where I couldn't see. I examined all the legs on the chip going into the socket to make sure none of them got folded up underneath the package instead of going into the socket. They all looked good. I hooked everything back up and tried again. Nothing. I tried more searching online to see if I could find more information. Nothing. Arg. Frustrated, I set it aside for a week.

When I came back to it, I rechecked all the hardware I could with the magnifying glass. I plugged it in and tried writing to it. Same result as before. The backlight came on but I couldn't get it to display anything. Looking at the library code, it seemed a bit suspect, but even if it was wrong, I thought I should still see something. I searched more online but found no new information. One last time I decided to take a look at the kit before contacting customer support. And then it hit me. Pin one was in the wrong position. I couldn't believe it, I'd stupidly put the chip in backwards. I pulled the chip out and put it in the correct way. This time when I applied power, it showed a splash screen. Oh, much, much better.

I uploaded a program that I expected to put some data on the screen and it worked. Well, sort of. That code that looked suspect in the library I used didn't work properly. I suspect it was for some other serial controller with a different command set. I threw together some code based on the manufacturer's documentation on github and suddenly it worked properly. Woo, hoo!

Lessons learned

  1. If you build this kit, expect to see a splash screen on power up. This will tell you if the board works or not, independent of serial communication.
  2. Make sure to get some sockets for the chip. I'm sure glad I did. It would have been a nightmare if I'd accidentally soldered the chip in backwards and then had to desolder first the header and then the chip itself to correct the mistake.
  3. If I ever build another one, I will get a plug for the header. This would allow the two boards to plug together rather than be soldered together. A ribbon cable could also be placed between them for more flexibility with mounting.

That's about it for this write up. Next for this project is to figure out the best way to communicate with it. I need to look at the library to see if I was using it wrong or if it really is designed for a different unit. After that, I plan to investigate reprogramming the LCD's controller to investigate using it for additional processing tasks.

Thursday, February 3, 2011

A Geek's Guide to Sports

I understand there is a big sports competition coming up this weekend. I don't know much about sports, but thought I'd share what I do know with my fellow geeks so they can be more informed around the office for water cooler conversations.

American Football

This is where two smart guys are mad at each other. They each have recruited two groups of really big supports. By "really big," I mean physically huge. Each group is trying to kill the other group's smart guy while at the same time trying to keep their smart guy from getting murdered by the opposing side's hit squad. They do this in a strange, alternating dance, where one team tries to kill the other's smart guy for a while and then they trade off and the second team tries to do in the first team's smart guy. To make their homicidal intents socially acceptable, they hide their true, life-ending goal behind a facade of moving an oblong ball from one end of a grassy expanse to the other.

Rugby

This is a variation of American Football, just without the smart guys.

Australian Football

This was developed in the southern hemisphere where everyone is upside down. I think the blood rushes to their heads since they've confused the rules. Like American Football and Rugby, there are two teams. However, half of each team plays American Football while the other half of each team plays Rugby.

Basketball

This is a track and field sport for tall people during the winter months when it's too cold to go outside. It involves a lot of running back and forth and back and forth (the track part) and jumping and heaving heavy objects (the field part) over their heads attempting to get it into a basket about the same size as the ball. All the while the other side is trying to stop the person with the ball. There are a couple guys running around dressed like zebras who randomly stop the action. Everyone gets angry at them. I think this has something to do with some hunting ritual that has slipped into the game. It's probably to remind us that sustenance is more important than games and we need to stop once in a while, come together and remember to focus on the hunt. Does zebra taste good? Seems like an odd animal to incorporate into the game.

Soccer

(This is known as Football in the rest of the world which actually makes more sense than what Americans call Football since it actually uses the feet whereas American Football uses the hands. Perhaps we should rename American Football to Handball. It'd certainly be more logical.) Anyway, soccer is just the same as Basketball except it's for short people who can't use their hands.

Hockey

This is soccer for people living in Canada where it's too cold for grass to grow. Since it's icy all the time, they play on frozen ponds while wearing skates. Other than that it's just like soccer. I think they must be really mad they have to play on ice since score is kept based on how much blood is shed.

Baseball

The strategy of this game is to play it so slow that you bore the other team into forfeiting. It's played with a small ball that, one by one, each team member tries to hit with a thin, long stick. Since these two items are both so small, they only come into contact randomly, thus prolonging the game. This is a great strategy to make the other team think they really are trying to move the game along when in fact it's simply a delaying tactic.

Cricket

The English have taken Baseball and really perfected the underlying goal of boring everyone. They obviously have much more patience that Americans though and have adapted the game to prolong it even further than the short-attention spans we have here in the colonies.

Well, now that you're better informed, I hope that helps you the next time the topic of sports comes up in the lunch room or during hallway conversations.

Monday, January 31, 2011

Arduino notebook: an RGB LED and the color wheel (Part 3)

In my last article, I demonstrated use of a potentiometer to control the hue of an RGB LED. In the HSV color space, the hue is one of three parameters controling the color. The other two are saturation and value. With a single control, there isn't a way to vary the other two parameters to give us a greater color range. In this article, I show one technique to control any of the three color variables.

Designing the input

But first, a short design discussion. In order to change what the potentiometer does, we need some method to change its mode of operation. I toyed with several ideas. My preferred method was three buttons: one button puts it in hue change mode, the second button puts it in saturation change mode and finally, the third button puts it in value change mode. The user would press a button and twist the potentiometer, setting that button's associated value. I like this because there is a one-to-one mapping between the buttons and the mode. It makes it easy for the user to understand and use. The problem: I only have two buttons.

My next idea was no button pressed changes hue, one button pressed changes saturation and the other button pressed changes the value. However, as I thought this through, I realized this would not give the user a nice flow between modes since every time one of the buttons was released, the hue mode would be entered, setting it to whatever value the potentiometer was in for the end of the last mode. This would cause the colors to jump around as modes changed and the un-pressed mode, hue, would have to be changed last. This clearly would not work very well.

After further thought, I hit upon the idea of using one button to cycle through the three modes with each button press and using the other button to enter that mode (or in other words, activate the potentiometer). Sort of like one button controls a menu and the other selects and deselects that menu option. To implement this, two additional LEDs are needed to indicate the mode and a third LED to indicate whether the potentiometer is enabled or not. This allows switching between modes without changing the output of the LED until the mode is selected. When the mode is exited, the LED would not change color as it would have in the previous scenario.

I implemented this design. However, after building it, I didn't like it since a binary readout is used to indicate the mode. I considered this sub-optimal from a usability standpoint. How many people can read binary numbers? In addition to the usability issue, it required three LEDs in addition to the switches, increasing the parts count and clutter on the breadboard. It worked but just seemed pretty clunky.

As I thought about it some more, I realized I could use the buttons directly in a binary pattern to select the mode and simply use a short delay before entering the mode. In other words, press and hold one button to change hue. Press and hold the other button to change saturation. Press and hold both buttons to change the value. I like this. It will reduce the parts count and eliminate a level of abstraction in the user interface; the buttons will directly control the mode. This is what I'll present below.

Hardware changes

Starting with the last circuit with in the previous article, I added two switches, two resistors and some jumpers. If you look at the schematic, pins 12 and 13 are connected to ground through a resistor when the switch is open. This means these two pins are normally in the LOW state when digitalRead is called with their pin numbers. When the button is closed, they are connected to the 5V side and become HIGH. The resistor puts a load on the circuit when the button is closed. If the resistor is removed, a direct short between ground and power exists when the button closes; not a good thing to do.

That's it. Pretty simple circuit changes.RGB LED color wheel with HSV controls
Click the image for a larger view or here for a schematic.

Software changes

The software changes are a bit more involved. First, the simple part, I added two declarations for the pins. The next change is a semi-advanced technique, but first a bit of analysis.

There are two buttons. For each button, the software needs to time how long the user has pressed it and, when they have held it down long enough, switch into active mode. To do this, each button requires a timer and a memory of the last button state. The code is the same for both, but the memory to track their status needs to be separate.

To facilitate this type of design, where there are two sections of memory with the same functions working on it, I used something called a class. Put simply, a class associates data with code. To create a class, start with the class keyword followed by the name of the class. This is the name you as a programmer decide to call it. Since this class contains the state information for a mode switch, I called the class ModeSwitchState.

Next, I defined the state information tracked for each switch. This includes the pin the switch is connected to, a timer to track when the last change was made to the switch's state and the switch's last value (i.e. was it pressed or not). These three things are what are called private variables, as indicated by the private keyword. This means nothing outside of functions defined in the class can access their values. The big, technical term for this is data encapsulation. All that means is it's harder to accidentally change the wrong thing because the only things that can get to the memory is this one class.

After I defined the private memory, I created two functions that any code can use. The first is what's called the constructor. This simply means it's the function that is called when a new variable of this type is created. In this case, the constructor takes a parameter to indicate the Arduino pin the rest of the functions will use. Since the pin variable is private, this is the only way to set its value. The idea is you create a variable of this class type for each button to be controlled. The constructor also sets initial values for the other variables and calls pinMode for the pin it reads.

Finally, I wrote a function to read the current value of the pin and, if it's different from the last value, reset the timer and the last value read variables. It then returns true if the button is pressed and the difference between the timer and the current time is greater than 500 ms. Otherwise it will return false.

That's it for the definition of this class. Next, I created two variables of this class type, called modeSwitch1 and modeSwitch2. The constructor is called with the values of the pin assignment previously defined.

And that's it for the advanced stuff. All the other changes are in the loop function.

First the loop gets the current mode state of each switch. Remember, since this is calling the function on the class, all the timer code is neatly tucked away out of sight from the main processing loop. All we have to do now is worry about getting the current value as defined by the readModeSwitch function. That's done by the first two lines.

Next I check to see if either of the switches are currently active. If so, then the potentiometer is read. Following this, either hue, saturation or value is changed to the pot's value based on which combination of switches are pressed. The case where both switches are pressed needs to be checked first, followed by the test for a single switch. If neither of these conditions are met, then the other switch must be pressed and its associated value changed.

Finally, after all this, the RGB color is computed and the LED is set to that color.

If neither switch is pressed, then nothing happens, the loop exits and immediately begins again.

Here's the final code.
int ledPins[] = { 11, 9, 10 };
int analogPin = 0;
int modeBit0pin = 12;
int modeBit1pin = 13;

int hueValue = 0;
double satValue = 1.0;
double lumValue = 1.0;

class ModeSwitchState
{
  private:
    int pin;
    long timer;
    boolean lastValue;

  public:
    ModeSwitchState(int switchPin)
      : pin(switchPin), timer(0), lastValue(false)
    {
      pinMode(pin, OUTPUT);
    }
    
    boolean readModeSwitch()
    {
      boolean curValue = digitalRead(pin) == HIGH;
      if (curValue != lastValue)
      {
        lastValue = curValue;
        timer = millis();
      }
      return curValue && ((millis() - timer) > 500);
    }
};

ModeSwitchState modeSwitch1(modeBit0pin), modeSwitch2(modeBit1pin);

void setup()
{
  pinMode(analogPin, INPUT);
  
  for(int i = 0; i < 3; i++)
    pinMode(ledPins[i], OUTPUT);
}

void loop()
{
  boolean mode1enabled = modeSwitch1.readModeSwitch();
  boolean mode2enabled = modeSwitch2.readModeSwitch();
  
  if (mode1enabled || mode2enabled)
  {
    int analogValue = analogRead(analogPin);

    if (mode1enabled && mode2enabled)
        hueValue = map(analogValue, 0, 1024, 0, 360);
    else if (mode1enabled)
        satValue = analogValue / 1024.0;
    else
        lumValue = analogValue / 1024.0;
  
    byte* colors = hsvToRgb(hueValue, satValue, lumValue);
    setColor(ledPins, colors);
  }
}

void setColor(int* led, const byte* color)
{
  for(int i = 0; i < 3; i++)
    analogWrite(led[i], 255-color[i]);
}

byte rgb[3];
byte* hsvToRgb(int h, double s, double v)
{
        // Make sure our arguments stay in-range
        h = max(0, min(360, h));
        s = max(0, min(1.0, s));
        v = max(0, min(1.0, v));
        if(s == 0)
        {
                // Achromatic (grey)
                rgb[0] = rgb[1] = rgb[2] = round(v * 255);
                return rgb;
        }
        double hs = h / 60.0; // sector 0 to 5
        int i = floor(hs);
        double f = hs - i; // factorial part of h
        double p = v * (1 - s);
        double q = v * (1 - s * f);
        double t = v * (1 - s * (1 - f));
        double r, g, b;
        switch(i)
        {
                case 0:
                        r = v;
                        g = t;
                        b = p;
                        break;
                case 1:
                        r = q;
                        g = v;
                        b = p;
                        break;
                case 2:
                        r = p;
                        g = v;
                        b = t;
                        break;
                case 3:
                        r = p;
                        g = q;
                        b = v;
                        break;
                case 4:
                        r = t;
                        g = p;
                        b = v;
                        break;
                default: // case 5:
                        r = v;
                        g = p;
                        b = q;
        }
        rgb[0] = round(r * 255.0);
        rgb[1] = round(g * 255.0);
        rgb[2] = round(b * 255.0);
        
        return rgb;
}

Conclusion and downloads

That's about all I'm going to do with the RGB LED for now. The source code for this and the past two articles can be downloaded here:
In the next article I will talk about the joys and sorrows of building a simple LCD kit to use with the Arduino.

Friday, January 21, 2011

Arduino notebook: an RGB LED and the color wheel (Part 2)

In the last Arduino Notebook article, I showed how to take one of the standard tutorials and modify the sketch to do something a bit different than given. I've updated that article with breadboard and schematic diagrams.

In this article I'll add a potentiometer that came with my kit to allow additional control over the LED. With this new input, I'll show how to use it to control two different aspects of the LED. First, I will change the sketch so the potentiometer controls the speed the LED cycles through the color wheel. At one end it will be so fast it will look more like a rapid switch between a reddish color and a blueish color. At the other extreme end, it will take about 30 seconds to cycle all the way through.

Second, I will make the position of the potentiometer control the color. So, as you turn the control, it will change to the hue represented by that position of the control. In other words at the starting position it will be red. Turn it one third of the way and it'll be green. Another one third and it will be blue. Turn it all the way to the end and it'll go back to red.

So, let's get started.

First, I place a potentiometer on the breadboard so each of the three pins is in a separate row of the breadboard. Then I connect one side to ground and the other side to 5V. The middle pin I connect to the Arduino's analog 0 pin. That's it for the hardware. It should look something like this.
RGB LED color wheel with pot_bb
Click the image for a larger view or here for a schematic.

Ok, now for the software.

The first set of changes will let the potentiometer change the speed the LED cycles through the color wheel. Starting with the sketch I ended with in the previous article, in the setup function, I add a call to initialize pin 0 for input. Next I change the loop to get the current value of the analog pin. This value will be from 0 to 1024 depending on the position of the potentiometer. Since I want the total time to cycle through the colors to be 30 seconds and there are 360 steps, then each step should take 83 milliseconds. To convert the number read from pin 0 from one range to another the Arduino has a function called map. This is much easier to use than doing the math to handle the conversion. So, I call this function with the value read from the potentiometer and the beginning and ending values of the two ranges. The result is fed into the delay in the loop. Compile and upload to the Arduino and now the potentiometer controls the speed.
int ledPins[] = { 11, 9, 10 };
int inputPin = 0;

void setup()
{
  pinMode(inputPin, INPUT);
  for(int i = 0; i < 3; i++)
    pinMode(ledPins[i], OUTPUT);
}

void loop()
{
  int inputValue = analogRead(inputPin);
  int delayValue = map(inputValue, 0, 1024, 0, 83);

  setHueValue(computeNextValue());
  
  delay(delayValue);
}

int colorValue = 0;

int computeNextValue()
{
  colorValue = (colorValue+1) % 360;
  return colorValue;
}

void setHueValue(int hueValue)
{
  setColor(ledPins, hsvToRgb(hueValue, 1, 1));
}

void setColor(int* led, const byte* color)
{
  for(int i = 0; i < 3; i++)
    analogWrite(led[i], 255-color[i]);
}

byte rgb[3];

byte* hsvToRgb(int h, double s, double v)
{
        // Make sure our arguments stay in-range
        h = max(0, min(360, h));
        s = max(0, min(1.0, s));
        v = max(0, min(1.0, v));
        if(s == 0)
        {
                // Achromatic (grey)
                rgb[0] = rgb[1] = rgb[2] = round(v * 255);
                return rgb;
        }
        double hs = h / 60.0; // sector 0 to 5
        int i = floor(hs);
        double f = hs - i; // factorial part of h
        double p = v * (1 - s);
        double q = v * (1 - s * f);
        double t = v * (1 - s * (1 - f));
        double r, g, b;
        switch(i)
        {
                case 0:
                        r = v;
                        g = t;
                        b = p;
                        break;
                case 1:
                        r = q;
                        g = v;
                        b = p;
                        break;
                case 2:
                        r = p;
                        g = v;
                        b = t;
                        break;
                case 3:
                        r = p;
                        g = q;
                        b = v;
                        break;
                case 4:
                        r = t;
                        g = p;
                        b = v;
                        break;
                default: // case 5:
                        r = v;
                        g = p;
                        b = q;
        }
        rgb[0] = round(r * 255.0);
        rgb[1] = round(g * 255.0);
        rgb[2] = round(b * 255.0);
        return rgb;
}

The second set of changes let the potentiometer set the color. This is a really simple change. Everything is done in the loop function. Remember, setColorWheel takes a value in the range 0 to 360 representing the angle of the hue on the color wheel. All that needs to be done is to change the second range in the map function to end at 360 instead of 83. The result is now fed into the setColorWheel function instead of computeNextValue. And finally, the time delay is removed and the computeNextValue can be deleted.

And that's it. Pretty simple changes and now the potentiometer can be used to set the color.
int ledPins[] = { 11, 9, 10 };
int inputPin = 0;

void setup()
{
  pinMode(inputPin, INPUT);
  for(int i = 0; i < 3; i++)
    pinMode(ledPins[i], OUTPUT);
}

void loop()
{
  int inputValue = analogRead(inputPin);
  int hueValue = map(inputValue, 0, 1024, 0, 360);

  setHueValue(hueValue);
}

void setHueValue(int hueValue)
{
  setColor(ledPins, hsvToRgb(hueValue, 1, 1));
}

void setColor(int* led, const byte* color)
{
  for(int i = 0; i < 3; i++)
    analogWrite(led[i], 255-color[i]);
}

byte rgb[3];

byte* hsvToRgb(int h, double s, double v)
{
        // Make sure our arguments stay in-range
        h = max(0, min(360, h));
        s = max(0, min(1.0, s));
        v = max(0, min(1.0, v));
        if(s == 0)
        {
                // Achromatic (grey)
                rgb[0] = rgb[1] = rgb[2] = round(v * 255);
                return rgb;
        }
        double hs = h / 60.0; // sector 0 to 5
        int i = floor(hs);
        double f = hs - i; // factorial part of h
        double p = v * (1 - s);
        double q = v * (1 - s * f);
        double t = v * (1 - s * (1 - f));
        double r, g, b;
        switch(i)
        {
                case 0:
                        r = v;
                        g = t;
                        b = p;
                        break;
                case 1:
                        r = q;
                        g = v;
                        b = p;
                        break;
                case 2:
                        r = p;
                        g = v;
                        b = t;
                        break;
                case 3:
                        r = p;
                        g = q;
                        b = v;
                        break;
                case 4:
                        r = t;
                        g = p;
                        b = v;
                        break;
                default: // case 5:
                        r = v;
                        g = p;
                        b = q;
        }
        rgb[0] = round(r * 255.0);
        rgb[1] = round(g * 255.0);
        rgb[2] = round(b * 255.0);
        return rgb;
}


All in all, I think this demonstrates pretty well how a small amount of hardware and some trivial software changes can provide very different end user results. The combination of hardware and software that Arduino makes available to the experimenter I find really cool.

Next time, I have some other ideas to add another input method to this circuit and some minor tweaks in software to increase the range of colors that can be generated by the RGB LED.

Tuesday, January 18, 2011

Arduino notebook: an RGB LED and the color wheel (Part 1)

As I recently wrote about, I received a new Arduino experimental kit a couple weeks ago for Christmas. In a simple learning phase, last weekend I built one of the projects in the getting started book and then expanded on it to make it do a bit more.

The project was to control a RGB LED that came with the kit. A RGB LED basically has three LEDs in a single physical package. The LEDs have a common anode so the package has four leads coming out of it. One you connect to +5V and each of the others you connect to its own pin on the Arduino through a resistor. This configuration allows you to turn on each color individually by setting that color's pin off (to ground). If the pin is set on, the voltage is +5V and the LED will turn off since there is no current going through the LED. This is a bit backwards from normal intuition, but it makes sense if you understand what's happening with the voltage/current.

RGB LED color wheel_bbClick image to see larger. Here's a schematic view.

I started with a simple circuit as shown in CIRC-12 here. [Update: also shown above.] It simply consists of the LED connected to +5V on its anode and three resistors on the other legs. The other side of the resistors are connected to three pins on the controller. Pretty simple stuff. I then entered the following program from the manual and ran it.
int ledPins[] = { 9, 10, 11 };
int inputPin = 0;

const boolean ON = LOW;
const boolean OFF = HIGH;

const boolean RED[] = {ON, OFF, OFF};
const boolean GREEN[] = {OFF, ON, OFF};
const boolean BLUE[] = {OFF, OFF, ON};
const boolean YELLOW[] = {ON, ON, OFF};
const boolean CYAN[] = {OFF, ON, ON};
const boolean MAGENTA[] = {ON, OFF, ON};
const boolean WHITE[] = {ON, ON, ON};
const boolean BLACK[] = {OFF, OFF, OFF};
const boolean* COLORS[] = {RED, YELLOW, GREEN, CYAN, BLUE, MAGENTA, WHITE, BLACK};

void setup()
{
for(int i = 0; i < 3; i++)
pinMode(ledPins[i], OUTPUT);
}

void loop()
{
//setColor(ledPins, YELLOW);
randomColor();
}

void randomColor()
{
int rand = random(0, sizeof(COLORS) / sizeof(boolean*));
setColor(ledPins, COLORS[rand]);
delay(1000);
}

void setColor(int* led, const boolean* color)
{
for(int i = 0; i < 3; i++)
digitalWrite(led[i], color[i]);
}
This simple program (sketch in Arduino terminology), will either set the color to one of the predefined values if the first line in loop() is uncommented or will randomly select one of the colors each second if the second line is uncommented. One thing to note, because the LED has a common anode, to turn a color off, we have to give that pin a voltage. This is why the ON constant to indicate the color should be lit is defined as a LOW voltage and the OFF is defined as a HIGH voltage. This definition makes it easier to read the color definitions.

So far, so good, but a bit on the boring side. I decided in order to juice things up a bit I wanted it to cycle through the colors of the color wheel rather than randomly pick things out of an array. In addition to this, I wanted to fade from one color to the next rather than have abrupt jumps. At this point, one of the beauties of microcontrollers comes to the forefront: all this can be done through simply software changes.

The example above turns each LED either full on or full off. To make a smooth transition, I needed to display the LEDs in partially lit states. This is done with what's called pulse-width modulation, or PWM. The beauty of the Arduino is that it has this facility built-in. Instead of calling digitalWrite as in the code above, you call analogWrite with a value between 0 and 255 where 0 is full off, 255 is full on and values in-between represent a proportional state between full off and full on. So, the first step was to change the boolean types to bytes. This was a simple change of type and the definitions for ON and OFF. Finally, I changed setColor to use analogWrite instead of digitalWrite. This gave a functionally equivalent version that was now setup to use intermediate values.
int ledPins[] = { 9, 10, 11 };
int inputPin = 0;

const byte ON = 0;
const byte OFF = 255;

const byte RED[] = {ON, OFF, OFF};
const byte GREEN[] = {OFF, ON, OFF};
const byte BLUE[] = {OFF, OFF, ON};
const byte YELLOW[] = {ON, ON, OFF};
const byte CYAN[] = {OFF, ON, ON};
const byte MAGENTA[] = {ON, OFF, ON};
const byte WHITE[] = {ON, ON, ON};
const byte BLACK[] = {OFF, OFF, OFF};
const byte* COLORS[] = {RED, YELLOW, GREEN, CYAN, BLUE, MAGENTA, WHITE, BLACK};

void setup()
{
for(int i = 0; i < 3; i++)
pinMode(ledPins[i], OUTPUT);
}

void loop()
{
//setColor(ledPins, YELLOW);
randomColor();
}

void randomColor()
{
int rand = random(0, sizeof(COLORS) / sizeof(byte*));
setColor(ledPins, COLORS[rand]);
delay(1000);
}

void setColor(int* led, const byte* color)
{
for(int i = 0; i < 3; i++)
analogWrite(led[i], color[i]);
}

The next step is to change the code to display colors in the order of the color wheel rather than randomly. To start, I used the existing COLORS array. I changed the loop method to call a new method setHueValue with a parameter that computed the new hue value and added a delay. I also removed the randomColor function since I wasn't using it anymore.
int ledPins[] = { 9, 10, 11 };
int inputPin = 0;

const byte ON = 0;
const byte OFF = 255;

const byte RED[] = {ON, OFF, OFF};
const byte GREEN[] = {OFF, ON, OFF};
const byte BLUE[] = {OFF, OFF, ON};
const byte YELLOW[] = {ON, ON, OFF};
const byte CYAN[] = {OFF, ON, ON};
const byte MAGENTA[] = {ON, OFF, ON};
const byte WHITE[] = {ON, ON, ON};
const byte BLACK[] = {OFF, OFF, OFF};
const byte* COLORS[] = {RED, YELLOW, GREEN, CYAN, BLUE, MAGENTA, WHITE, BLACK};

void setup()
{
for(int i = 0; i < 3; i++)
pinMode(ledPins[i], OUTPUT);
}

void loop()
{
setHueValue(computeNextValue());
delay(1000);
}

int colorValue = -1;

int computeNextValue()
{
colorValue = (colorValue+1) % 6;
return colorValue;
}

void setHueValue(int hueValue)
{
setColor(ledPins, COLORS[hueValue]);
}

void setColor(int* led, const byte* color)
{
for(int i = 0; i < 3; i++)
analogWrite(led[i], color[i]);
}
This was great for five values, but I wanted it to cycle smoothly from one color to the next. To do this with a table lookup method was going to require a huge table. A different approach to the problem was needed.

RGB is a common way to model color with anything that projects light, such as computer monitors and LEDs. However, there are other ways of thinking about color. For example, things that reflect light such as painting and printing, use CMYK or Cyan-Magenta-Yellow-Black. If you ever mixed paint in art class, this is the color wheel you learned. There are another models that can be handy when doing various types of computations, one of which is Hue-Saturation-Value, or HSV. For what I wanted to do, this works well.

The first step in this stage was to find an easy to use function for converting HSV values to RGB values. A simple search of Google for "hsv to rgb converter" gave me an open source PHP (or perhaps it was Python) function. A quick conversion to C used by the Arduino and I had the hsvToRgb function below. This function takes a hue value in the range 0 to 360. This is because the HSV model maps the hue to a circle and so it uses a degree measurement to specify the hue value. The Saturation and Value portions of the model are a percentage from 0 to 100%, represented by fractional values from 0 to 1. For the purposes of this experiment, I just set them both to 1.

Once I had the color converter, I next changed computeNextValue to return a max value of 359 instead of 5. This will give me the proper input for the hue value. Then I removed all the color definitions at the top of the sketch since the color values are now computed and the constants won't be needed. I reduced the time delay to 10 milliseconds. With 360 steps, this will give about a 3.5 second cycle time. Next I changed the COLORS array reference in setColor to call the new function. Since the HSV code returns values in 0 to 255 range and the pins need things inverted for voltage reasons, in setColor, I subtract the color in the byte array from 255 rather than passing it in directly. The only real difference this makes from a user perspective is the starting color is at red instead of 180 degrees out of phase at cyan.

With all this, I accomplished my goals for this experiment. Here is the final sketch.
int ledPins[] = { 9, 10, 11 };
int inputPin = 0;
void setup()
{
for(int i = 0; i < 3; i++)
pinMode(ledPins[i], OUTPUT);
}

void loop()
{
setHueValue(computeNextValue());
delay(10);
}

int colorValue = 0;

int computeNextValue()
{
colorValue = (colorValue+1) % 360;
return colorValue;
}

void setHueValue(int hueValue)
{
setColor(ledPins, hsvToRgb(hueValue, 1, 1));
}

void setColor(int* led, const byte* color)
{
for(int i = 0; i < 3; i++)
analogWrite(led[i], 255-color[i]);
}

byte rgb[3];

byte* hsvToRgb(int h, double s, double v)
{
// Make sure our arguments stay in-range
h = max(0, min(360, h));
s = max(0, min(1.0, s));
v = max(0, min(1.0, v));
if(s == 0)
{
// Achromatic (grey)
rgb[0] = rgb[1] = rgb[2] = round(v * 255);
return rgb;
}
double hs = h / 60.0; // sector 0 to 5
int i = floor(hs);
double f = hs - i; // factorial part of h
double p = v * (1 - s);
double q = v * (1 - s * f);
double t = v * (1 - s * (1 - f));
double r, g, b;
switch(i)
{
case 0:
r = v;
g = t;
b = p;
break;
case 1:
r = q;
g = v;
b = p;
break;
case 2:
r = p;
g = v;
b = t;
break;
case 3:
r = p;
g = q;
b = v;
break;
case 4:
r = t;
g = p;
b = v;
break;
default: // case 5:
r = v;
g = p;
b = q;
}
rgb[0] = round(r * 255.0);
rgb[1] = round(g * 255.0);
rgb[2] = round(b * 255.0);
return rgb;
}
In the next installment, I'll walk through adding some simple control mechanisms to this circuit along with various software changes to make it respond differently with the same hardware.

Wednesday, January 12, 2011

Arduino Notebook: a multi-input device

As I previously mentioned, I recently got an Arduino development kit for Christmas and have been experimenting with it. One of the things on my to do list involves using a joystick. Since the kit only came with a momentary contact switch and a potentiometer, I went online to try to find something that would fit my needs. The cheap one I initially wanted to get was out of stock, so I searched around a bit.

I ran across a site talking about a Wii Nunchuck as an input device. Hmm. That sounded interesting. Not being a gamer, I didn't quite know what a nunchuck was. Although many times I would have liked to take a traditional one to the computer, I didn't quite think that was what was associated with the Wii gaming system. A bit of research told me it's an extension controller that plugs into the standard Wii controller giving you an extra two buttons, a joystick and 3-axis accelerometer. The idea is you can use the regular controller in one hand and use the extension controller in your other hand. The examples say this lets you run and jump with one hand while the other swings a sword. Yeah. Right. Like that's going to happen. I have enough problem trying to use one controller on the rare occasion that I try a game out. There's no way I'm coordinated enough to use two.

Be that as it may, I'm not interested in gaming applications; I'm interested in Arduino control. I found some controllers for sale on Amazonfor less than $20. A whole lot more than the $5 for the joystick I first looked at, but this one has extra sensors and was packaged in a neat little hand-held device. And even though it's an order of magnitude more expensive, at less than the cost of a fast food dinner with my wife, it's not going to break the bank. (But being as frugal as I am, I still opted for the black one at $1.70 less than the white one.)

The biggest drawback to this hack was the original site instructed you to cut off the connector to use it with the Arduino. That didn't sound too appealing. A bit more surfing and I found someone who made an adapter. Yeah. That'll work. The adapter is sold through several places online, I got it from SparkFun since they had several other things I was interested in (and I'll probably blog about in the future). So, I placed the two orders.

Well, they arrived yesterday and I opened them tonight. The adapter was just a PC board with no instructions. I went to the adaptor creator's web site and found some pictures and software. First, it was obvious that it needed some header pins to plug it into the Uno board. Fortunately, the development kit came with a strip of break-off headers. I broke four pins off and soldered them onto the board based on the pictures on the web. Next I downloaded the software in a zip file.

As instructed on the site, I plugged the adapter into the Arduino's analog pins 2, 3, 4 and 5. The zip file has a demo sketch that I uploaded onto the board. It ran, but when I opened the Serial Monitor all I got was garbage. Hmm. I looked at the code for the sketch and it initialized the Serial port to 19200 but the Serial Monitor window defaults to 9600. After a simple change of the baud rate, everything worked.

The demo sketch displayed the data from the switches and the x- and y-axis accelerometers. Since the joystick info was what really interested me, I started reading the library code to figure out how to get it. I quickly found a routine in the library to display all the sensor information. I removed the display code in the demo and replaced it with the single line call to the library and uploaded the new code. Viola: all the information.

In a quick five minute review of the library code, there are some things that on the surface don't look like the most effective way of handling the data. When I dive into this to really start looking at it, I'll do a code review and write up some notes if my initial impressions prove correct. I'll probably also write up some notes if I'm wrong about it since that would probably be interesting too.

While looking up the web site addresses for this article, I ran across a competitor's adapter. This looks like a better design and if I were to do it again, I'd probably get this one rather than the one I got, even though it's a buck more expensive. Huh. There goes my savings from getting the black nunchuck instead of the white one.

Monday, January 10, 2011

Let the games begin: My own microcontroller kit

Woo, hoo! I'm excited.

I've had two opportunities in my career to work with embedded software directly. The first was in mid-80's when the company I was working for developed a Z80-based product. And then a couple years ago, I was able to play with an 8051 based development kit that I wrote about here and here.

I've had a passing interest in controlling things from my earliest days of leaning computers but have always been side tracked somewhat by the extra investment in hardware. Development kits have typically cost in the couple hundred dollar range and it's pretty easy to smoke them, so I've hesitated investing in anything. Recently I've become increasingly aware of the Arduino platform. I don't really know enough of all the details to write about them here, but basically it's an open source hardware/software platform for microcontroller development. Depending on features, they can be acquired for less than $10 on the low end to several tens of dollars on the high end. At this cost point, the high end is an order of magnitude cheaper than what I've seen most other development kits start at. This makes the thought of smoking something much more palatable. Destroying a $20 part because you hooked it up wrong is much easier to take than a $200 part.

So, when my wife asked me what I wanted for Christmas this year, I had a ready answer: an Arduino starter kit. And she got me this one.

So far I am quite pleased with it. It has everything listed on the web page and things seem to work well. The only assembly needed is to attach the controller board (with screws) and a breadboard (with double sided adhesive) to a clear acrylic base. For anyone putting one together, I recommend putting the controller board on first and then the breadboard second. Since the controller is held on with machine screws, it can only go in one location, with no wiggle room. The adhesive on the breadboard is permanent. There is no ability to move it once placed, but its location isn't as critical as the controller. I put the breadboard on first and didn't leave myself as much room as would have been ideal for the controller board. It would have been easier to do it the other way around. That minor issue aside, everything else is ready to go out of the box.

There is a freely available IDE available that has to be downloaded from the internet. Once downloaded, there's no other internet requirement. The IDE is pretty bare bones. It provides a text editor and upload capability to the controller via USB. It has a window to allow serial communication with the controller if the software you've loaded on the controller is programmed to communicate with it. Don't expect modern conveniences like code completion, debugging and the like. It's not there. Although, since it's open source, I'm sure if you wanted to make a patch to do that, others would appreciate it. :-)

At this point, I have only experimented with some minor motor and LED control. One cool thing about the system is it has both digital I/O and analog input and PWM output baked into both the hardware and the software. Reading an analog signal is simply a matter of connecting it to one of the analog pins and in software doing something like "inValue = analogRead(0);" where inValue is a byte and the 0 is the pin number to read. The voltages on the pin can be from ground to +5V and the corresponding byte value will be 0 to 1024. Analog type output can be achieved with pulse width modulation. (I don't want to delve into the details of what this entails here. For more information on PWM, start here and here.) This is doesn't work for all devices, but for many that one would want to control with a microcontroller, like motors and LEDs, it works well. This is done with a simple "analogWrite(value);" where value is in the range 0 to 255, 0 being off, 255 being full on and anything in between be a linear intermediate value (e.g. 64 is 25% on, 127 is 50% on, 192 is 75% on).

I've got some things on order to play with and more ideas to experiment with than I know what to do with. Lots of things to learn and explore. It's so much fun to have a new toy.