The constant war between Arduino lovers and haters


There are a number of boards, blogs, forums etc. where you can find controversies about the usage of such simple things as Arduinos. Quite often you can read the Arduino folks are starting with the blink example and just using copy-and-paste.

Actually, I must admit: yes there are people like this; they not only upload their copy-and-paste to boards like instructables, they even get awarding comments for their diligent copying.

One example is how people control a 7-segment display: separate functions for each number to be displayed.

Today, I want to examine a sketch working as a morse coder. I found it recently on http://www.instructables.com/id/Morse-code-with-arduinoLED/step2/Programming/ .

It is understood that for each character to transmit you have to send the appropriate code. So, in the said example you will find 26 lines like this:

    if (input == 'a' || input == 'A') {lA();}//if the input is a or A go to function lA

No doubt, you can do it this way. Well, you often can select between a decision-controlled algorithm and a table-controlled algorithm. Provided you have any idea about tables and indexes as they do not show up in the blink example. (And some knowledge about function parameters would be nice as well.)
But always keep in mind: if the input character is "z" your program has to run through all the other precedent cases. In this program speed does not matter, but in other ones it might. If you go for the table way everything is performed in no time.

Well, now let's have look at the characters. When defining his code in 1840, Samuel Morse did not distinguish between upper case and lower case letters. And when Bob Bemer (the father of ASCII) designed the ASCII code table he left a gap of 7 characters between "Z" and "a" to make it easy to swap between both cases by toggling just one bit. That is why in almost any programming language you will find a function like toupper in order to treat both cases in one go.

O.k. then you have:

 {lA();} 

Well, you can spend a lot of extra { } if you like but in this case they are completely superfluous. The compiler has some extra work to make sure all the brackets do match. The code generated will not differ at all.

So now we check how the morse codes are implemented:

void lA () {dot();dash();shortspace();}//letter A in morse code!

How would it look like in a table-based way?

I can think of at least three different ways to do it. First let's state that Morse used exactly two symbols: short sounds and longer sounds. He could not know anything about computers, but these symbols can be associated with the two values true and false. I find it natural to match short with false.

And the good news is: all Morse codes have no more than five such elements, so they fit in one byte. But if you put them in one byte there will be some extra bits that are not part of the Morse code. So you need to store the information of how many bits are needed for this particular character. This can be done by either

  1. placing this number in an extra table or maybe a two-dimensional table
  2. using the leftmost three bits of that byte (3 bits enable you to store numbers from 0 to 7)
  3. adding a start code similar to RS-232

I only will show the principle how to implement version 3.

const byte PROGMEM morse[] = {
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // ASCII 0-15
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // ASCII 16-31
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // ASCII 32-47
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0 - 9 ...
  0,B11111001,0,0,0,B11111100,0,0,B11100000,0,0,0,B11100100,0,0,B11110111, // A - O
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0  // P-Z 
};

I only created the values for these five letters (to enable you to say hello):
letter ASCII Morse in binary
A 65 . _ 01
E 69 . 0
H 72 . . . . 0000
L 76 . _ . . 0100
O 79 _ _ _ 111

So, if you just enter B00000001 for the letter "A" it also could mean "U" = ". . _" or "V" = ". . . _" or even "4" = ". . . . _"

What you have to do is defining a start bit which is inserted just before the location where Morse code is beginning. The bits before the start bit (if any) have to be different from the start bit. (I choose zero for the start bit but you can do it the other way round, using one for the preceding bits.)

Now, the rest is easy: just get this byte from the morse table. Using a mask, check all the bits from left to right. As long as the mask returns a "1" just continue. If the mask returns a zero skip this bit (this was the start bit). Now get one bit after the other and send it wherever you want.

void send(char c) {
  byte code = pgm_read_byte_near(morse + c);
  byte mask;
  // skip all the "1"s until you find the start bit:
  for (mask = B10000000; code & mask; mask = mask >> 1);
  // now output the rest:
  for (mask = mask >> 1; mask; Serial.print(code & mask ? "_ " : ". "), mask = mask >> 1);
  Serial.print("  ");
}

If you are concerned about the usage of for put your mind at rest; it's just standard C as defined by Brian W. Kernighan, Dennis M. Ritchie in 1972. Actually, even Java-8 still accepts this kind of for-loops! It proves such loops are immortal.

At the end of the day the compiler will tell you that you have saved some 1500 bytes of Flash memory.
To avoid the risk of running out of SRAM the PROGMEM was added to the array declaration; therefore you need to use the pgm_read_byte_near function to access the array values.


contact: nji(at)gmx.de