New multi-tap keypad

After a Phi-panel user (Daniel) has requested for more symbols on the multi-tap keyboard, I added all symbols to the keypad, why not? 🙂

Now the keypad has 7 symbols for each numeric key except for 9, with 6 symbols only. The symbols such as greater and less, brackets, parentheses, and curly braces, are all symmetrically located so if you get used to the pad, say memorized “(” is 5 presses on key “4”, then the “)” will be also 5 presses on the next key, “5”. Again all + – * / are located together on key 1.

On HD44780 displays, the ‘~’ and the ‘\’ are displayed as right arrow and the Japanese Yen (HD probably means Hitachi display). That’s unfortunate. Hope that would be a big deal for most of us.

New keypad:

Old keypad:

To update your panel firmware, please go to the google code download page to download the firmware and follow the steps in the wiki page at the code site for updating. If you have questions, leave them here so I can answer them. Thanks.

Sourcing and sinking current

Occasionally you see terms such as sourcing and sinking current and you keep wondering what they mean. So to make this clear, let’s look at sourcing and sinking current to light up LEDs:

Say you hook up an LED with a serial resistor and between an arduino output pin and gnd. If you direct the output to HIGH, you put 5V over the LED and resistor so current flows from the arduino pin to ground through the LED and resistor. In this case, you are sourcing current from the output, meaning flowing current from the pin through the LED, like turning a faucet on and flowing water out of the faucet. If you direct the output to LOW, the potential on both sides of the LED and resistor is zero so no current flows.

Say now you connect the LED and resistor between an arduino output pin and 5V. When you set the output pin to gnd, you are sinking current to the output, meaning flowing current from 5V to the output pin through the LED and resistor. It is like the arduino pin is a sink in a kitchen. Your 5V source will flow water via the LED and resistor to the sink.

Arduino pins can both source and sink when they are outputting while some other output devices such as a TLC5940, can only sink current, while others can only source current. In case only one-way conduction is allowed, you need to set up the circuit correctly to source or sink otherwise the circuit will not work or get damaged.

Rotary encoder

A rotary encoder is a digital device in the shape of a knob. You can turn it in either clockwise direction or counter-clockwise direction infinite turns, without any limit. This is what a potentiometer can’t do. In the old days most knobs are potentiometers or rotary switches, such as those on stove top, washers and dryers. Potentiometer is much easier in the concept to sense than a rotary encoder so here we discuss how to sense a digital rotary encoder.

How does it work?

A simple rotary encoder has two channels or switches. If you turn the encoder’s shaft, the two switches will open and close repeatedly much like two tactile buttons. Depending on the direction of the rotation, the sequence of the open and close will be different. Say we have two channels A and B that open and close as we turn the encoder and if we turn the encoder clockwise, we will see:

A open->B open->A close->B close

If we turn counter clockwise, we see:

B open->A open->B close->A close

So to tell which way the encoder is turning, we just need to tell which sequence occurs. There are many ways to tell which sequence occurs but I will focus on the easiest way, which is also efficient enough.

First, you combine the status of the switches A and B into a 2-bit binary number, A being the low bit and B being the high bit. Let’s define open as 1, and close as 0. Here is how to combine the status bits:

stat=(digitalRead(EncoderChnB)*2) + digitalRead(EncoderChnA);

When both channels are closed, we get 0. When both are open, we get 3. When only a is open, we get 1. When only b is open, we get 2.

Now we can translate the sequences into numbers:

A open->B open->A close->B close ==>> 1,3,2,0

B open->A open->B close->A close ==>> 2,3,1,0

Note that in the first sequence when B opens after A opens, they are both open, so that corresponds to 3.

Now we can make an array to store both sequences with a pointer:

byte stat_seq[]={0,1,3,2,0,1,3,2,0};

byte stat_seq_ptr=4;

The pointer initially points to a status of both switches closes, which is a detent position for the encoder. You turn your encoder and it clicks into detent positions. If your detent position is when both switches are open, you may use {3,2,0,1,3,2,0,1,3} instead.

Now, every time you read the two channels, you compare the stat with the array element before and after the current pointer’s position. If the stat represent the array element after the current pointer, move the pointer there, same for the other way around. When the array pointer hits the left end of the array, with a 0, you have completed one CCW turn, report this turn and reset the pointer to the middle, 4. Same for CW turn when the pointer reaches the right end of the array.

This should be easier to understand than other ways of tracking the encoder and is much more robust. Here is the complete code:

The function returns the number of CW or CCW turns stored in its buffer. Every time you call it to ask number of CW turns, it returns the turns and decrements its internal count by one. All you need to do is to keep calling this function every loop by supplying which direction you want to read back. If the read back is greater than zero, a turn is made by the user. You can decide what to do, maybe advance in a menu. If the return is zero, there is no turn in that direction, you need to do nothing. The green section is sensing the encoder and the blue section is responding the the caller.

byte read_encoder(byte direction) // 1 for asking CW counts, -1 for asking CCW counts. Returns counts and decrements the counts till 0.
  static byte stat_seq[]={3,2,0,1,3,2,0,1,3}; // For always on switches use {0,1,3,2,0,1,3,2,0};
  static byte stat_seq_ptr=4;
  static byte cw_counter=0;
  static byte ccw_counter=0;

  byte stat_int=(digitalRead(EncoderChnB)<<1) | digitalRead(EncoderChnA);
  if (stat_int==stat_seq[stat_seq_ptr+1])
    if (stat_seq_ptr==8)
  else if (stat_int==stat_seq[stat_seq_ptr-1])
    if (stat_seq_ptr==0)
if (direction==1)
    if (cw_counter>0)
      return cw_counter+1;
    else return 0;

  else if (direction==255)
    if (ccw_counter>0)
      return ccw_counter+1;
    else return 0;

  else return 0;