Monday, May 6, 2013

Return of the Arduino part 2: status indicators are your friends

The essence of user-friendliness is feedback. I won't be rivaling Apple with my little creation, but having more than zero indication that the IR receiver is actually picking up signal is probably wise. So the next step in the project was to mount some LEDs to the case to give me a bit of status indication.

A quick trip to Radio Shack netted me three red LEDs (2.6v, 28mA, 10mcd), one green LED (2.1v, 25mA, 6.3mcd), and a quad of 220-ohm resistors (and some 1K-ohm resistors). One thing I've learned during this process is to verify everything before busting out the permanent-connection hardware; before soldering or gluing anything, I bread-boarded the LEDs and used a slight modification on the Arduino "Fading" demo to confirm that all four LEDs would light. This turned out to be wise; my initial plan had been to use a 1K-ohm resistor between all four cathodes (in parallel) and ground, but I discovered that allowed too much back-current to leak across the LEDs---not desirable at all.

Breadboard. Arduino is connected by wires to four 220-ohm resistors, each leading to an LED, all leading to ground.
Final configuration for the LEDs and resistors on the testing breadboard.

Once I'd verified the configuration, it was time to get to work. Trimming the anode leads a bit on the LEDs, I soldered one resistor to each anode. I then grabbed my Dewalt and 3/16" bit again and bored four parallel holes in the upper half of the case, then glued the LEDs in place. Due to a slight mix-up on my part, the green LED ended up with its resistor on the cathode, but no harm seemed to be done. I soldered all the cathode leads (including the resistor lead from the green LED) into one, and left the anode leads to be connected to the Arduino.

I ran into a bit of a conundrum at this point: with the LEDs living on the top shell, I wanted some flexible wire with some nice, solid end plugs to connect the LEDs to the Arduino control pins, but nothing in my kit really fit the description. Fortunately, I discovered that Radio Shack sells a SideKick basic kit that includes what the kit calls "breadboard jumper wire." It turned out to be the perfect tool for the job if I cut them in half and soldered the loose wire to my LED leads.

With the jumper wires in place, I connected the LED anodes to Arduino pins 6, 9, 10, and 11, and the cathodes to one of the two GND pins. I then wrote a couple of short LED test functions:
// LED control library, by Mark T. Tomczak
// Public domain
// To use:
// * Call SetupLedControl with an array of LED inputs IDs
//   (-1 terminates the array as a sentinel value)
// * Call functions in the library to control the LEDs.

// Control LED firing patterns
// -1 used as sentinel value
int *g_led_control_leds;
int g_led_control_num_leds = 0;

// *leds is the arraay of LED pin IDs, terminated with a sentinel value of -1.
void SetupLedControl(int *leds) {
  g_led_control_leds = leds;
  g_led_control_num_leds = 0;
  while(*leds != -1) {
    pinMode(*leds, OUTPUT);

// Blink several times (off-on-off).
// - times: number of times to blink
// - flash_delay_millis: Number of milliseconds to wait
//   before toggling the light on or off. void FlashLights(int times, int flash_delay_millis) {
  for(int i=0; i < times; i++) {
    for(int i=0; i < g_led_control_num_leds; i++) {
      analogWrite(g_led_control_leds[i], 255);
    for(int i=0; i < g_led_control_num_leds; i++) {
      analogWrite(g_led_control_leds[i], 0);

// This is a fun little function that delays a specified
// number of milliseconds, while showing the time remaining as the
// intensity of several LEDs.
// When all the time is remaining, the LEDs all glow at maximum brightness.
// Elapsing time is shown as the LEDs slowly going out one-by-one. void CountDown(long time_to_countdown_millis) {
  unsigned long start_time = millis();
  // TODO(mtomczak): Arduino millis() function rolls over to 0 after 
  // about fifty days. You should account for this rollover... 
  // possibly build a timer library with some structs to hold that state.

    long time_elapsed = 0;
    time_elapsed < time_to_countdown_millis;
    time_elapsed = (long)(millis() - start_time)) {
    float percent_remaining = (float)(time_to_countdown_millis - time_elapsed) / (float)(time_to_countdown_millis);
    float led_fraction = 1.0 / (float)(g_led_control_num_leds);
    for (int i=0; i < g_led_control_num_leds; i++) {
      // The logic is exciting here, but it basically boils down to:
      // * consider the time remaining to be divided into a series of "ranges"
      //   (one per LED).
      // * If the time remaining is more than my range, glow maximum intensity.
      // * If time remaining is less than my range, shut off completely.
      // * Otherwise, glow proportional to how much time is left within my
      //   range.
      int output = 255;
      if (percent_remaining < led_fraction * (float)(i + 1)) {
        if (percent_remaining < led_fraction * (float)(i)) {
          output = 0;
        } else { // percent remaining is in range led_fraction * (i, i+1)
          output = (int)((percent_remaining - (led_fraction * (float)(i))) * 255.0 / led_fraction);
      analogWrite(g_led_control_leds[i], output);

I combined this library with the recognition code from my Arduino IR bridge project so that it would blink twice and then count-down over five seconds when it recognized an IR pattern. Overall, I'm not unhappy with the results!

What I learned

  • This is the part of the project where the insulating properties of hot glue were especially helpful. I basically glued over the forest of wired-together cathodes so that they couldn't short against anything, and I'm pretty pleased with the results.
  • Pay more attention to output ratings on LEDs. There's a noticeable visual difference between the green LED and the much brighter red LEDs (I'm guessing that my eye is reacting logarithmically to the difference between 6.3mcd and 10mcd). If I wanted to back this part out and try again, I'd go with four red LEDs.
Part three will wrap things up by adding the IR emitter and the code to drive the entire project.