Friday, October 31, 2014

Reflow Toaster Oven - Part 2

Having an oven is one thing, knowing what it's capable of is just as important. In order to accurately control the temperature it's not enough to simply turn the heating element on when it's too cold and off when it's warm enough. The heating element has quite a bit of thermal mass, it will stay warm for quite some time even when you turn it off, which means that it will keep on heating its environment, so the temperature in the oven may still rise even though the heating element has been turned off.

If I want my reflow oven to be accurate, I need more data on it. How fast does it heat up? How fast does it loose heat? What is the maximum temperature? How fast can it reach that temperature? The only way to get answers is by simply testing it.

So just stick a probe in the oven, connect it to an Arduino, turn the oven on and start logging!
The probe is a K-type thermocouple I got from eBay for a few bucks.
It's connected to a MAX6675 Cold-Junction-Compensated Thermocouple-to-Digital converter which converts the minuscule voltage generated by the thermocouple to a digital value. The MAX is connected to an Arduino which simply polls it about every second and prints the value on the console.
The first value is seconds since the last reset, the second one the temperature in degrees Celsius. I simply copied the output from the console to a file, named it .csv and opened it in Excel. The result is a nice little graph.

Just a few notes here. You can see a little bulge around 460 seconds. The thermostat of the oven is connected directly to the heating element. Around 440 seconds the heating element aparently reached 280 degrees C (which is what the thermostat was set to). It took a bit of time for the heating element to cool down, but it did so by warming up the rest of the oven. Around 530 seconds the element turns on again and the temperature starts to rise "rapidly" again.

When the temperature reached 245 degrees C, the wire of the probe got so hot that it was starting to melt the handle on the oven door it was touching. So that's when I turned the oven off and set the door ajar. I have no clue what the little hickup around 706 seconds is.

The time spent above liquidus (217 deg C) is 220 seconds, which is well above what's recommended (60-120 seconds). The time until peak temperature is 641 seconds, which is also well above the recommended maximum of 8 minutes.

Long story short: it's too slow, needs thermal insulation.

Wednesday, October 29, 2014

Reflow Toaster Oven - Part 1

Converting a toaster oven to a reflow oven is something that's been on my to-do list for quite some time now. Hand soldering really tiny SMT components is a PITA, having a reflow oven would make things a lot easier.

So this afternoon I finally got my hands on a suitable used oven :) A Tefal Activys. I think this one was also sold under the Moulinex brand. I guess it costs around 100 EUR new.


It's got the right size, has a large door, two large heating elements and no complicated electronics. It's pretty clean for a used oven, doesn't seem to be used that much. And best of all, I only paid 20 EUR for it :)

 

As some crazy ozzy bloke would say: "Don't turn it on, take it apart!"

The cover is held in place with a bunch of self-tappers, it's just sheet metal, in fact, the whole oven is made of sheet metal, sheesh.
Nothing much on the left side, just the spring for the door. There's a bit of room between the cover and inner panel so I can put some insulation in between it to improve the oven's performance.
More sheet metal on the top. Even less room for insulation, oh well. The back panel is just a single piece of sheet metal, that's going to get pretty darn hot when this thing turns on...
And here's what makes this oven tick, quite literally. No digital electronics at all. Plain and simple. Let's have a closer look.

This is the motor for rotating dead birds, that's gonna go, but the hole may be handy to route a thermocouple through.
A very crude thermostat. It's relies on a bi-metal. It's screwed almost directly to the bottom heating element.
And the temperature knob on the front is connected to it like this, expect the temperature to be off by at least 30 deg C...
And this is what makes it tick, it's a mechanical timer, with a nice ring at T=0 :) Partially shown below it is the mode selector (grill/oven/roast) which selects which heating element to use.
Another thermostat, probably a safety switch to prevent it from overheating. As you can see, the top heating element is held in place with a spring, you can pull the element down for easy cleaning of the interior.

The bottom element can be moved as well but is not spring loaded, it simply relies on gravity.
And a closeup of the knobs. 280 deg C? Probably a bit optimistic :)

2900W? Let's test that! With just the top heating element on my meter shows around 1300W. With both elements on it uses a whopping 2500W, still a bit short of 2900W but plenty nonetheless. This thing heats up pretty fast as it is already, but I'll need to do some more testing to get the actual figures. And as noted before, the back of the oven get's HOT! Not good. I'll probably add an extra panel at the back (either on the inside or outside) so I can add some extra insulation...

So much for now, stay tuned for more :)

Wednesday, April 16, 2014

LED dimmer part 2: motion activated

My PIR motion sensors finally arrived today. I added one to the LED dimmer from the previous post. The motion sensor acts like an on/off switch that you can override using the touch sensor.

The motion sensor detects movement using infrared and it outputs a high signal when it does. This triggers the dimmer into its on state. A couple of seconds after the signal goes low (no motion) the dimmer goes into off state.


While the dimmer is on (caused by the motion sensor), you can force it off using the touch sensor. There are two potentiometers on the sensor; one for sensitivity (I left it at center position) and one for time. The time pot controls for how long the sensor will output a high signal after detecting motion. I use the lowest setting and handle the rest of the timing in the micro controller since tuning the pot is really inaccurate and annoying. There appears to be a forced 4 second delay between two high signals. No idea if that can be trimmed or not.
There's also some space allocated on both sides of the sensor board for a light sensor, so it will only detect motion when it's dark.

The Arduino program can control up to 6 output channels (could be more, but it doesn't look like I need more) and has 8 inputs, any of which can be motion or touch sensors. There are 256 levels of brightness, but the distribution of actual brightness does not seem to be very linear, it looks more logarithmic to me; it ramps up to full brightness really fast and doesn't do much after that. I'd like to have better control in the low brightness region...

Sunday, April 13, 2014

Adjustable single button LED dimmer

I'm planning on adding some LED strips to my bed. I currently have one small light next to it, and it's always annoying to find the switch, it often ends up with the light on the ground. And when it's on, it's usually too bright. So I thought, what if I add a LED strip to the back of my bed? And what if I could switch it on and off by just touching some part of the frame?

I have one of these IKEA bedframes. Putting a LED strip on the back of the headboard should provide a decent amount of light. Adding a capacitive touch sensor at a sensible, easy to reach part of the frame would make a nice on/off switch, possibly concealed by a piece of matching veneer for good looks.

But what about the brightness? I'd like to be able to control the brightness without having to deal with potentiometers or reprogramming. Well, my sister in law has one of those IKEA lamps that you can turn on and off by just touching it. What's even cooler is that you can adjust its brightness by just holding it.

The logic behind it is pretty simple. Touch (a button) shortly and it will switch the light on/off. Hold it longer and the brightness increases until it reaches full brightness, after that it will decrease. If you let go and hold it again the direction of brightness change also changes. Like this:


I'm using a touch resistor here since my capacitive touch sensors haven't arrived yet, but the effect is almost the same. Touching that strip at a given place will make it have a resistance between 0 and 10KOhm. It's like a potentiometer and the strip is the wiper.

I'm using a small BC337 transistor here to drive the LED, but I can use it for LED strips as well (up to about 1.5 meter) which will be perfect.

There's also a bit of fading code in there to smoothly turn the light on and off. The PWM signal is generated in software using a timer. The code is not really worth sharing at the moment and shouldn't be hard to figure out.

Sunday, April 6, 2014

Buzzer game

You've probably all seen this type of game before, where you have to guide a metal ring over a metal wire course without the ring touching the wire. It's nothing more than a buzzer, some batteries and perhaps a light/LED, but it's a great way to teach kids some basics about electricity and electronics.

A few weeks ago I had the pleasure of building 35 of these buzzer games. I assembled the battery holder, buzzer, an LED and resistor and some wires.
Each "kit" had a piece of steel wire about 4 feet / 120cm in length with some small loops on both ends for easy mounting. A second straight piece of about 10 inch / 25 cm in length soldered to the negative wire of the battery holder (and some extra wire in between for extra length). And the buzzer/led/resistor wired to the battery holder, with a bit of bare copper wire to connect it to the steel wire.

Needless to say, I spent roughly 8 hours preparing everything. All the kids needed to do was bend the long steel wire into whatever shape they would think would work, and mount it on a piece of wood using two screws. Then they'd have to mount the battery holder with another screw, and connect loose piece of copper wire to their now very oddly shaped steel wire. Lastly they had to bend the small piece of steel wire. Doesn't sound like much work, but don't underestimate how much time kids aged 8 to 10 need for even the most trivial tasks...

Unfortunately I forgot to take pictures of their creations, but here's the example I prepared myself:


In hindsight, I wouldn't solder everything. Instead you're better off just using a bunch of plastic terminal connectors. Don't connect everything with really thing copper wire, it breaks easily. And of course, don't use solid core copper wire but stranded.

I made another one, which is slightly larger:
It has a 12V LED strip on the base, is made of much thicker steel wire and is powered using a 3 cell lithium polymer battery from my RC parts box. There's a piezo buzzer soldered onto the power rails of the LED strip. I'd like to expand on this a bit further, add a bit of logic. The LED strip and buzzer should be latched, meaning they don't turn off as soon as you break contact. They also shouldn't remain on continuously when there's contact; that's just too annoying. I'd also like to add a few red and green strips and add "lives" to the game: you'd start with three lives, and lose one each time you make contact. After the last life is lost the red strip lights up indicating game over. An ATtiny, a few transistors, some resistors and a voltage regulator are probably all that's needed to build this.




Thursday, April 3, 2014

Concept: off-the-grid sensor module

I love the idea of home automation. I'd like to keep an eye on temperatures inside and around my house, monitor power, gas and water consumption, have smart lighting, etc. The possibilities are endless, and they are in fact feasible and affordable nowadays. If I were to get a new house, I'd make sure I'd have multiple power outlets in every wall, with LAN and 12 or 5 volt as well.

Unfortunately the house I bought was built in 1968, has very few outlets (2 per bedroom at most) and walls you really don't want to put a drill in, unless you like plastering and painting. So having temperature sensors in every room would involve lots of extension cords, wall warts and possibly LAN cables.

Going wireless

The first thing to get rid of is LAN cables. Let's go wireless instead. Since my house has only wooden floors and no thick reinforced concrete walls, there should be no problem getting coverage on all three floors. WiFi and ZigBee/XBee is a bit too expensive, so I'd rather settle with the Nordic nRF24L01+, you can get those 2.4 GHz boards with PCB antenna for as little as $1,50 a piece from eBay, whereas an XBee would cost about $20 to $30 a piece. Even though the range on the nRFs is limited, it should be enough. I don't need much bandwidth either; the lowest setting (256KBit/sec) should be plenty.
There should be a master unit somewhere in the house, the meter cupboard would be a good candidate; it already contains my switch, NAS, gas meter and power meter and fuse-box. This master unit should collect all the data from the sensor units and have an Ethernet interface to present the collected data to a web server that can host a front-end (my NAS for example).

The sensor units need power to operate obviously, but I don't like the idea of having to use a wall wart for every single sensor unit, nor do I want to have put low voltage power lines throughout my house. Having wall warts would not only occupy most of my power outlets, but also adds unnecessary power consumption. One way to get rid of that is by using batteries, but how long will those last? Well, bring out the datasheets and calculator!

Calculating power consumption

The ATmega328 used in  the Arduino Uno and Nano has a couple of low power options. First of all you can run it at a lower clock speed using the 128KHz internal oscillator, this reduces active consumption from around 10 mA to just 0.12 mA; quite a difference, and it frees up 2 IO pins :) If we reduce Vcc to 3.3V then we go down even further to about 0.06 mA. If we make it enter idle mode then power consumption drops to a measly 0.015 mA. Enter power save mode and we're down to 0.001 mA, sweet.

The nRF24L01+ doesn't exactly consume loads of power either. Full power transmission requires about 11.3 mA and receive mode at 250 Kbit/s needs 12.6 mA. Since I'm only planning on sampling data once every five minutes or so, we can just turn the nRF off for the rest of the time. Power down supply current is 0.0009 mA.

A simple temperature and humidity sensor like the DHT11 draws about 0.5mA when sampling and 0.2mA on average. We can probably power this straight from one of the ATmega's IO pins, so we can turn it off completely when we don't need it. A light sensor also draws current in the order or milliAmps, so we can use the same trick there to reduce consumption in low power mode.

I have a bunch of 120mAh Lithium Polymer batteries from a micro helicopter. LiPo's provide 4.2V when fully charged but shouldn't be drained below 3.5V. ATmega's and a lot of sensors will have no problems with this kind of supply voltage range, but the nRF requires 3.3V so we'll need a low dropout regulator (LDO) with a very low dropout voltage. A normal 7803 won't do, it needs in input voltage that is at least 1V above the desired output voltage. An MCP1700 will do the trick, it has a dropout voltage of 200 mV at full load (250 mA), perfect. The current drawn by it is extremely low as well; less than 2 micro amps at room temperature. We can use this to power everything, or just the nRF, I'm not quite sure yet.

Running the numbers

At full load the sensor setup will draw 0.06 (ATmega) + 12.6 (nRF) + 0.5 to 5 (sensor) mA of current. Add a bit for supporting hardware and our ballpark figure is 20 mA. In idle mode we end up in the micro amps range. 1 (ATmega) + 0.9 (nRF) + 2 (MCP)  = 4 uA or 0.004 mA. In order to calculate battery life we have to know the ratio between idle and active time, or the duty cycle. Say we sample and transmit data once every 5 minutes (300 seconds) and we can do this within 3 seconds, then we end up with a duty cycle of 1%. So 1% active vs 99% idle. Or average power consumption ends up being 1% x 20mA + 99% x 0.004 mA = 0.20mA (plus a few micro amps). Since my batteries have a capacity of 120mAh (meaning a 120mA device will drain it in 1 hour, or a 1 mA device can run on it for 120 hours), we end up with a battery life of 120 / 0.2 = 600 hours.

Now 600 hours sounds like a lot, but it's just 25 days. That means replacing and recharging batteries 15 times a year, probably more since you don't want to drain them completely. Having a bigger battery helps a bit, 1800mAh batteries are widely available for as little as $5, that would reduce recharging/replacement to once a year. Battery voltage can be monitored using the ADC and a 3.3V reference voltage and we can just add the voltage information to the sensor data.

Why stop there?

Why not create something that is truly off-the-grid and recharges itself? Small 6V solar panels are dirt cheap and so are small LiPo recharge controllers. A small 85 x 85 mm panel may deliver up to 150 mA at 6V and costs less that $5. A complete TP4056 charger controller board costs less than $3 and it'll accept anything between 5 and 8 volts on the input. So for less than $10 we can make the sensor unit self powered! Even if the panel gets only one hour of sunlight, that should be enough to recharge the battery with 100mAh, or 20 days of juice, that's plenty! Even something like a 50 mA panel will do the trick, as long as you can get at least 5V from it. Heck, even a couple of solar cells from those old calculators might do the trick, although you probably need a certain minimum current for the charger to work.

Efficient remote controlled switches

One last idea I'd like to pitch is a remote controlled AC switch. This kind of device would be perfect if you need to control a much larger current and already need a wall wart. The idea is to add a solid state relay between mains and a wall wart. The wall wart powers the charger and additional current hungry equipment like led strips. The ATmega can simply switch the relay on whenever current is required, or when the battery runs low. In case of automatic lights you no longer have the loss of the wall wart when the lights are off (which they are most of the time). The additional current required to recharge the battery is completely insignificant compared to a decent LED strip. The same trick can be used to create remote controlled AC switches for turning other devices on and off.

Wednesday, March 19, 2014

RC navigation lights

Someone on LinkedIn asked me for some help with some lights on his RC plane. Since I had an evening to spare anyway I was happy to help. There were just a few requirements:
  • Use an Arduino Nano
  • Control 5 LEDs
  • Each LED should be controllable using just one switch on a remote control
  • Flipping the switch X times will toggle LED X
That's just your basic programmable RC navigation/landing light system you'll find on quite a few scale model airplanes and helicopters. You can find these kind of systems in most stores that sell RC equipment, some for as little as $12, but most for $25 and up (and up up up). But where's the fun in that?

So after a bit of programming and testing this is what I came up with:
Total cost: less than $10. You can pick up a Nano on eBay for as little as $6 and resistors and LEDs should be in your parts bin anyway ;)

Here's the breadboard layout (doesn't match the one from the movie exactly, but you get the idea)
 The servo represents the receiver, I used it since it has the same connectors (signal, 5V, gnd)

And the schematics:

And of course, the code!

Let's cover the reading of the switch on the remote first. By connecting the signal wire (usually the white one) from the receiver to pin 9 on the Arduino we can read the state of the switch. There will be a pulse coming from the receiver about 50 times per second. This pulse will be between 1000 and 2000 microseconds in length depending on the position of the switch. We can assume that anything above 1500 is the up position, and below 1500 the down position. So it's simply a matter of detecting the pulse and measuring its length. Normally I'd use interrupts to handle this, but I wanted to keep this one simple so I used the pulseIn() function instead.

Let's define a few variables first:


1:  uint8_t cfg_inputPin;  
2:  bool     loop_lastSwitchPosition;  
3:  uint8_t    loop_switchCount;  
4:  unsigned long loop_lastSwitchTime;  
cfg_inputPin holds which pin the receiver is connected to. loop_lastSwitchPosition holds the last read state of the switch, loop_switchCount holds how often it has been flipped up and loop_lastSwitchTime holds when the switch was flipped up.

Now for the setup:
1:  void setup()  
2:  {  
3:      cfg_inputPin = 9;  
4:    
5:      loop_lastSwitchPosition = pulseIn(cfg_inputPin, HIGH) > 1500;  
6:  }  

First we set the input pin. Pins are already in INPUT mode initially so no need to set that here. Furthermore we initialize the loop variable here with some default value.

1:  void loop()  
2:  {  
3:      unsigned long duration = pulseIn(cfg_inputPin, HIGH);  
4:      while ( duration == 0 )  
5:      {  
6:          duration = pulseIn(cfg_inputPin, HIGH);  
7:      }  
8:        
9:      bool newSwitchPosition = duration > 1500;  
10:      if ( newSwitchPosition != loop_lastSwitchPosition )  
11:      {  
12:          loop_lastSwitchPosition = newSwitchPosition;  
13:          if ( newSwitchPosition )  
14:          {  
15:              ++loop_switchCount;  
16:              loop_lastSwitchTime = millis();  
17:          }  
18:      }  
19:      else  
20:      {  
21:          if ( loop_lastSwitchTime != 0 && millis() - loop_lastSwitchTime > 1000 )  
22:          {  
23:              uint8_t channel = loop_switchCount - 1;  
24:              if ( channel < CHANNELS )  
25:              {  
26:                  // do something  
27:              }  
28:              loop_switchCount = 0;  
29:              loop_lastSwitchTime = 0;  
30:          }  
31:      }  
32:  }  
That's more like it :)
In lines 3-7 we wait for a valid pulse.
Line 9 converts the pulse duration to a switch position.
Line 10 checks if there's a change in switch position since the last loop. If so, we update the stored switch position on line 12.
Lines 13-17 handle the transition of the switch from down to up. If that happens we increment the switch counter and update the "last switch" timestamp.
The rest of the lines handle the situation where the switch did not change.
Line 21 checks if there was a switch change in one of the previous loops (loop_lastSwitchTime != 0) and if more than a second has passed since. So this expression will evaluate to true when you've flipped the switch up a second ago. That basically means you've stopped flipping the switch.
Lines 23 to 29 convert the switch counter to a channel index, check if that's not out of bounds (flipped the switch more than there are channels to control) and line 26 is where you put your actual code. The remaining two lines reset the loop variables.

Let's add some LEDs!
1:  #define CHANNELS 5  
2:  uint8_t cfg_pins[CHANNELS];  
3:  bool   state_switch[CHANNELS];  
4:    
5:  void setChannelPin(uint8_t p_channel, uint8_t p_pin)  
6:  {  
7:      digitalWrite(p_pin, LOW);  
8:      pinMode(p_pin, OUTPUT);  
9:      cfg_pins[p_channel] = p_pin;  
10:  }  
11:    
12:  void setChannelSwitch(uint8_t p_channel, bool p_on)  
13:  {  
14:      state_switch[p_channel] = p_on;  
15:      if ( p_on == false )  
16:      {  
17:          digitalWrite(cfg_pins[p_channel], LOW);  
18:      }  
19:      else  
20:      {  
21:          digitalWrite(cfg_pins[p_channel], HIGH);  
22:      }  
23:  }  
24:    
25:    
26:  void toggleChannelSwitch(uint8_t p_channel)  
27:  {  
28:      setChannelSwitch(p_channel, !state_switch[p_channel]);  
29:  }  
30:    
The define is used to define how many channels we'll be able to control. cfg_pins holds the pin for each channel. state_switch holds the state of the "virtual" switch for each channel. setChannelPin can be used to configure the pin for a channel. It'll set the pin in output mode and make sure it's off. The setChannelSwitch function allows you to set the state of the "virtual" switch for the channel and turns the LED on or off. toggleChannelSwitch toggles the state of the switch.

Now we need to update our setup function and add a few lines:
1:      setChannelPin(0, 4);  
2:      setChannelPin(1, 5);  
3:      setChannelPin(2, 6);  
4:      setChannelPin(3, 7);  
5:      setChannelPin(4, 8);  
So our five channels are connected to pins 4 - 8.

In our loop function we need to replace the "// do something " comment with
1:  toggleChannelSwitch( channel );  

Tadaaah! We can now turn channels on and off with the switch on the remote.

Hmm, that's a bit boring though... Let's add some blinking and flashing! For this we need some sort of update function that updates all the "animations". We need to keep track of time as well. The easiest way to handle this is by giving the update function a parameter that holds the number of milliseconds passed since the last update.

1:  unsigned long loop_lastMillis;  
2:    
3:  void setup()  
4:  {  
5:      // previous code here  
6:      loop_lastMillis = millis();  
7:  }  
8:    
9:  void loop()  
10:  {  
11:      // previous code here  
12:    
13:      unsigned long now = millis();  
14:      uint16_t deltaMillis = now - loop_lastMillis);  
15:      loop_lastMillis = now;  
16:    
17:      if ( deltaMillis > 0 )  
18:      {  
19:          update( deltaMillis );  
20:      }  
21:  }  
22:    
23:  void update(uint16_t p_delta)  
24:  {  
25:      // animation code here  
26:  }  
Pretty self explanatory I guess? We calculate the difference between the current and last timestamp and pass that to the update function.

For the animations I want to be able to specify how often an LED should blink, how long it should blink, how much time should be between blinks and how much time should be between the last blink in a sequence and the first one of the next sequence. I want to be able to create a strobe that flashes two times rapidly and then waits for a second or so, but I also want to be able to have a LED that blinks at a constant rate. And of course, I want some LEDs to be on all of the time.

We'll need a few more variables...
1:  uint8_t cfg_blinks[CHANNELS];  
2:  uint16_t cfg_onDuration[CHANNELS];  
3:  uint16_t cfg_offDuration[CHANNELS];  
4:  uint16_t cfg_pauseDuration[CHANNELS];  
5:    
6:  bool   state_onOff[CHANNELS];  
7:  uint8_t state_currentBlink[CHANNELS];  
8:  uint16_t state_nextChange[CHANNELS];  
So we have our variable that specify how often a channel should blink (0 being constantly on), how long it should be on, how long it should be off between blinks and how long it should pause between blink sequences.
Then we have our variable to hold the state of the channels while the animation is running. We need to know whether it's currently on or off, which blink we're at and how many milliseconds remain until the next state change.

We'll want some convenience functions to configure the channels:
1:  void configureChannelBlink(uint8_t p_channel, uint8_t p_blinks, uint16_t p_onDuration, uint16_t p_offDuration, uint16_t p_pauseDuration)  
2:  {  
3:      cfg_blinks[p_channel]    = p_blinks;  
4:      cfg_onDuration[p_channel]  = p_onDuration;  
5:      cfg_offDuration[p_channel]  = p_offDuration;  
6:      cfg_pauseDuration[p_channel] = p_pauseDuration;  
7:      setChannelSwitch(p_channel, true);  
8:  }  
9:    
10:    
11:  void configureChannelConstant(uint8_t p_channel)  
12:  {  
13:      cfg_blinks[p_channel] = 0;  
14:      setChannelSwitch(p_channel, true);  
15:  }  
And we can call this from our setup function:
1:      configureChannelConstant(0);  
2:      configureChannelConstant(1);  
3:      configureChannelConstant(2);  
4:        
5:      configureChannelBlink(3, 1, 50, 0, 950);  
6:      configureChannelBlink(4, 2, 50, 50, 1850);  
So channels 0-2 are constantly on, channel 3 blinks constantly and channel 4 is a strobe (double flash).

We need to update our setChannelSwitch function so it will reset the animation when the channel is turned on:
1:  void setChannelSwitch(uint8_t p_channel, bool p_on)  
2:  {  
3:      state_switch[p_channel] = p_on;  
4:      if ( p_on == false )  
5:      {  
6:          digitalWrite(cfg_pins[p_channel], LOW);  
7:      }  
8:      else  
9:      {  
10:          state_onOff[p_channel]    = true;  
11:          state_currentBlink[p_channel] = 0;  
12:          state_nextChange[p_channel]  = cfg_onDuration[p_channel];  
13:          digitalWrite(cfg_pins[p_channel], HIGH);  
14:      }  
15:  }  
Lines 10-12 are new.

And lastly, our blinky code:
1:  void update(uint16_t p_delta)  
2:  {  
3:      for ( uint8_t channel = 0; channel < CHANNELS; ++channel )  
4:      {  
5:          if ( cfg_blinks[channel] == 0 )  
6:          {  
7:              continue;  
8:          }  
9:          if ( state_switch[channel] == false )  
10:          {  
11:              continue;  
12:          }  
13:            
14:          if ( p_delta >= state_nextChange[channel] )  
15:          {  
16:              state_onOff[channel] = ! state_onOff[channel];  
17:                
18:              if ( state_onOff[channel] )  
19:              {  
20:                  state_nextChange[channel] = cfg_onDuration[channel];  
21:                  digitalWrite(cfg_pins[channel], HIGH);  
22:              }  
23:              else  
24:              {  
25:                  state_currentBlink[channel]++;  
26:                  if ( state_currentBlink[channel] == cfg_blinks[channel] )  
27:                  {  
28:                      state_nextChange[channel] = cfg_pauseDuration[channel];  
29:                      state_currentBlink[channel] = 0;  
30:                  }  
31:                  else  
32:                  {  
33:                      state_nextChange[channel] = cfg_offDuration[channel];  
34:                  }  
35:                  digitalWrite(cfg_pins[channel], LOW);  
36:              }  
37:          }  
38:          else  
39:          {  
40:              state_nextChange[channel] = state_nextChange[channel] - p_delta;  
41:          }  
42:      }  
43:  }  
Lines 5-8 test if the channel should be constantly on, if so then we skip the rest of the loop and move to the next channel
Lines 9-12 test if the channel is switched off, if so we can again skip this channel.
Line 14 checks whether it's time to toggle the channel.
Line 15 toggles the state of the channel; from on to off and vice versa.
Lines 18-22 turn the LED on and update the "next change" counter.
Lines 23-37 turn the LED off, increment the blink counter and check if this was the last blink. If so then we use the pauseDuration, else we use the offDuration. Don't forget to reset the blink counter ( line 29).
Lines 38-41 handle the situation where a channel does not need to change state yet, all we need to do is decrement the amount of time remaining until the next state change.

That's all there's to it.
Here's the final complete code for your convenience:
1:  // NavLights.ino  
2:  // Arduino based remote controlled navigation lights  
3:  //  
4:  // By Daniel van den Ouden, 2014  
5:  //  
6:    
7:  // configuration for input  
8:  uint8_t cfg_inputPin;  
9:    
10:  // 5 output channels  
11:  #define CHANNELS 5  
12:    
13:  uint8_t cfg_blinks[CHANNELS];            // how often a channel will blink in a sequence, 8 bit, so value is 0 - 255, 0 means no blinking  
14:  uint16_t cfg_onDuration[CHANNELS];        // how long the channel will be on in milliseconds, 16 bit, so value is 0 - 65535  
15:  uint16_t cfg_offDuration[CHANNELS];        // how long the channel will be off between blinks in milliseconds, 16 bit  
16:  uint16_t cfg_pauseDuration[CHANNELS];    // how long the channel will be off between the last and first blink of a sequence, 16 bit  
17:  uint8_t cfg_pins[CHANNELS];            // which pin to use for which channel  
18:    
19:  bool   state_switch[CHANNELS];        // state of the channel's "switch" (true = on, false = off)  
20:  bool   state_onOff[CHANNELS];            // the output state of the channel (true = high, false = low)  
21:  uint8_t state_currentBlink[CHANNELS];    // how often this channel has blinked in the current sequence  
22:  uint16_t state_nextChange[CHANNELS];    // number of milliseconds until next change  
23:    
24:  unsigned long loop_lastMillis;            // time of last update  
25:  bool     loop_lastSwitchPosition;    // last measured switch position  
26:  uint8_t    loop_switchCount;            // number of switches in current sequence  
27:  unsigned long loop_lastSwitchTime;        // time at which the switch was flipped last  
28:    
29:  void setChannelPin(uint8_t p_channel, uint8_t p_pin)  
30:  {  
31:      digitalWrite(p_pin, LOW);  
32:      pinMode(p_pin, OUTPUT);  
33:      cfg_pins[p_channel] = p_pin;  
34:  }  
35:    
36:    
37:  void configureChannelBlink(uint8_t p_channel, uint8_t p_blinks, uint16_t p_onDuration, uint16_t p_offDuration, uint16_t p_pauseDuration)  
38:  {  
39:      cfg_blinks[p_channel]    = p_blinks;  
40:      cfg_onDuration[p_channel]  = p_onDuration;  
41:      cfg_offDuration[p_channel]  = p_offDuration;  
42:      cfg_pauseDuration[p_channel] = p_pauseDuration;  
43:      setChannelSwitch(p_channel, true);  
44:  }  
45:    
46:    
47:  void configureChannelConstant(uint8_t p_channel)  
48:  {  
49:      cfg_blinks[p_channel] = 0;  
50:      setChannelSwitch(p_channel, true);  
51:  }  
52:    
53:    
54:  void setup()  
55:  {  
56:      cfg_inputPin = 9;  
57:        
58:      setChannelPin(0, 4);  
59:      setChannelPin(1, 5);  
60:      setChannelPin(2, 6);  
61:      setChannelPin(3, 7);  
62:      setChannelPin(4, 8);  
63:        
64:      configureChannelConstant(0);  
65:      configureChannelConstant(1);  
66:      configureChannelConstant(2);  
67:        
68:      configureChannelBlink(3, 1, 50, 0, 950);  
69:      configureChannelBlink(4, 2, 50, 50, 1850);  
70:        
71:      loop_lastMillis = millis();  
72:      loop_lastSwitchPosition = pulseIn(cfg_inputPin, HIGH) > 1500;  
73:  }  
74:    
75:    
76:  void loop()  
77:  {  
78:      unsigned long duration = pulseIn(cfg_inputPin, HIGH);  
79:      while ( duration == 0 )  
80:      {  
81:          duration = pulseIn(cfg_inputPin, HIGH);  
82:      }  
83:        
84:      bool newSwitchPosition = duration > 1500;  
85:      if ( newSwitchPosition != loop_lastSwitchPosition )  
86:      {  
87:          loop_lastSwitchPosition = newSwitchPosition;  
88:          if ( newSwitchPosition )  
89:          {  
90:              ++loop_switchCount;  
91:              loop_lastSwitchTime = millis();  
92:          }  
93:      }  
94:      else  
95:      {  
96:          if ( loop_lastSwitchTime != 0 && millis() - loop_lastSwitchTime > 1000 )  
97:          {  
98:              uint8_t channel = loop_switchCount - 1;  
99:              if ( channel < CHANNELS )  
100:              {  
101:                  toggleChannelSwitch( channel );  
102:              }  
103:              loop_switchCount = 0;  
104:              loop_lastSwitchTime = 0;  
105:          }  
106:      }  
107:        
108:      unsigned long now = millis();  
109:      uint16_t deltaMillis = now - loop_lastMillis;  
110:      loop_lastMillis = now;  
111:        
112:      if ( deltaMillis > 0 )  
113:      {  
114:          update( deltaMillis );  
115:      }  
116:  }  
117:    
118:    
119:  void update(uint16_t p_delta)  
120:  {  
121:      for ( uint8_t channel = 0; channel < CHANNELS; ++channel )  
122:      {  
123:          if ( cfg_blinks[channel] == 0 )  
124:          {  
125:              continue;  
126:          }  
127:          if ( state_switch[channel] == false )  
128:          {  
129:              continue;  
130:          }  
131:            
132:          if ( p_delta >= state_nextChange[channel] )  
133:          {  
134:              state_onOff[channel] = ! state_onOff[channel];  
135:                
136:              if ( state_onOff[channel] )  
137:              {  
138:                  state_nextChange[channel] = cfg_onDuration[channel];  
139:                  digitalWrite(cfg_pins[channel], HIGH);  
140:              }  
141:              else  
142:              {  
143:                  state_currentBlink[channel]++;  
144:                  if ( state_currentBlink[channel] == cfg_blinks[channel] )  
145:                  {  
146:                      state_nextChange[channel] = cfg_pauseDuration[channel];  
147:                      state_currentBlink[channel] = 0;  
148:                  }  
149:                  else  
150:                  {  
151:                      state_nextChange[channel] = cfg_offDuration[channel];  
152:                  }  
153:                  digitalWrite(cfg_pins[channel], LOW);  
154:              }  
155:          }  
156:          else  
157:          {  
158:              state_nextChange[channel] = state_nextChange[channel] - p_delta;  
159:          }  
160:      }  
161:  }  
162:    
163:    
164:  void setChannelSwitch(uint8_t p_channel, bool p_on)  
165:  {  
166:      state_switch[p_channel] = p_on;  
167:      if ( p_on == false )  
168:      {  
169:          digitalWrite(cfg_pins[p_channel], LOW);  
170:      }  
171:      else  
172:      {  
173:          state_onOff[p_channel]    = true;  
174:          state_currentBlink[p_channel] = 0;  
175:          state_nextChange[p_channel]  = cfg_onDuration[p_channel];  
176:          digitalWrite(cfg_pins[p_channel], HIGH);  
177:      }  
178:  }  
179:    
180:    
181:  void toggleChannelSwitch(uint8_t p_channel)  
182:  {  
183:      setChannelSwitch(p_channel, !state_switch[p_channel]);  
184:  }  
185:    
186: