Nibbles and Bits
The Care and Feeding of My Pet Arduino


by Budd Churchward - WB7FHC - NIBBLES AND BITS LIBRARY


Teaching Arduino to Copy Morse Code

« 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16  17  18 19 »

Section 17

The strategy we use to adjust to varying sender speeds is all about the length of the dah. A good sender will consistantly make his dahs three times the lenghth of his dits. In fact, today many Hams use electornic keyers that produce nice steady tones. What we will do here is keep a running average of the length of all the dahs and adjust all our counters around that value. We will declare one last new variable:


          int averageDah=150;            
I came up with the value of 150 by trial and error. I experimented with values from 0 to 2000. What I learned was that the further I got away from my final choice, the longer it took Arduino to home in on my sending speed and start copying solid code. You can experiment with different values yourself.

The plan is to keep a running average of the length of dahs. We learned in 5th grade that you get an average by adding up a group of numbers and then dividing by how many numbers you added. The technique that we will use here is what you might call 'sloppy arithmetic' or 'fuzzy math'. What we do is add the length of the current dah to our pervious average, and then divide by 2. We do this every time we get a dah. This gives us a psuedo average that will be pretty close to the real average. Here is what that looks like:


          averageDah=(downTime+averageDah)/2; 

If the sender speeds up, averageDah starts getting smaller. If he slows down, it gets larger. Now we need to align some key variables to the value of our average. The first one was figured out by trial and error. You might try tweaking it yourself, too. Logic says that multiplying the wait time by 22 makes no sense in Morse Code. Remember, this is not TIME (although we may think of it that way). This is the number of cycles Arduino runs through our loop while nothing else is happening. He's pretty darn quick when we don't send him on any errands.


          FullWait=averageDah*22; 

          dit=averageDah/3;       
          dit=dit*2;              
If you are paying attention you will see that I just pulled off a fast one by changing the value of dit twice. What's that all about? Remember, a sender's dit should be one third as long as his dah. So the divide by 3 makes sense. Then I go and double it. That is because we are really looking for down time that is less than this value. This will set a threshold that should catch a 'sloppy dit' that's a wee bit too long as well as a 'sloppy dah' that was cut a little short.

I had to solve a personal problem next. As I tested the sketch, especially with Morse Code at 20 wpm or more, I noticed Arduino consitantly printing a 6 when I know I sent a perfectly good B. I'm using a pretty old straight key and I know it was throwing out some key bounce. I tried adjusting the value of bounceDelay but any change I made there made things really bad. Finally I added one more line (highlighted in blue in the sketch) to weed out any dits that were really really short because of either my shakey hand or dirty contacts on my key. You might add this line, too.


          if (downTime<dit/3) return; 

Copy and paste the highlighted sections below and give the sketch a try. You should be able to speed up or slow down and see that Arduino is able handle the changes and print most of the letters correctly on your screen.

We are done for now. You will find the complete sketch, fully commented, in the next section and in the final one, there is a nice listing of International Morse Code that you can print and use for study.

Next Section »

International Morse Code
   A •-
   B -•••
   C --•
   D -••
   E •
   F ••-•
   G --•
   H ••••
   I ••
   J •---
   K --
   L •-••
   M --
   N -•
   O ---
   P •--•
   Q ---
   R •-•
   S •••
   T -
   U ••-
   V •••-
   W •--
   X -••-
   Y ---
   Z --••
   
   1 •----
   2 ••---
   3 •••--
   4 ••••-
   5 •••••
   6 -••••
   7 --•••
   8 ---••
   9 ----•
   0 -----
   
   . •--- [period]
   , --••-- [comma]
   ? ••--•• [question mark]
   ! ---- [exclamation mark]
   @ •---• [at sign]
   - -••••- [hyphen]
   : ---••• [colon]
 

int myKey=14;    // We are borrowing Analog Pin 0 and using it as digital
int speaker=11;  // Speaker will be hooked between pin 11 and ground

int val=0;       // A value for key up and down
int myTone=440;  // Freq. of our tone

boolean ditOrDah=true;
int dit=100;
int averageDah=150;

boolean characterDone=true;
int myBounce=2;
int downTime=0;

long FullWait=10000;
long WaitWait=FullWait;
long newWord=0;

int nearLineEnd=60;
int letterCount=0;

int myNum=0;

char mySet[] ="##TEMNAIOGKDWRUS##QZYCXBJP#L#FVH09#8###7#######61#######2###3#45";

void setup() {
  pinMode(myKey, INPUT);
  pinMode(speaker,OUTPUT);
  // initialize the serial communication:
  Serial.begin(9600);
}



 void loop() {
   val=digitalRead(myKey);
   if (val) keyIsDown();
   if (!val) keyIsUp();
 }
 
 void keyIsDown() {
   tone(speaker,myTone);
   WaitWait=FullWait;
   downTime++;   //Count how long the key is down
 
  if (myNum==0) {
      myNum=1;  // This is our start bit
    }
   characterDone=false;
   ditOrDah=false;
   delay(myBounce);
 }

 void keyIsUp() {
   noTone(speaker);

  if (newWord>0) newWord--;
  if (newWord==1) printSpace();

   if (!ditOrDah) {
       shiftBits();
   }
  if (!characterDone) {
      WaitWait--;  //We are counting down
      if (WaitWait==0) {
        printCharacter();
        
        characterDone=true;
        myNum=0;
      }

      downTime=0;
    }
}

void printSpace() {
  letterCount++;
  if (letterCount>nearLineEnd) {
    Serial.println();
    letterCount=0;
    return; // Go back to loop(), we're done here.
  }

  Serial.print(' ');
}

void printCharacter() {
  FullWait=averageDah*100;
  newWord=FullWait*5;
  letterCount++;
  if (myNum>63) {
    printPunctuation();
    return; // Go back to the main loop(), we're done here.
  }

  Serial.print(mySet[myNum]);
}

void printPunctuation() {
  byte pMark='#'; // Just in case nothing matches
  if (myNum==71) pMark=':';
  if (myNum==76) pMark=',';
  if (myNum==84) pMark='!';
  if (myNum==94) pMark='-';
  if (myNum==101) pMark='@';
  if (myNum==106) pMark='.';
  if (myNum==115) pMark='?';
  Serial.print(pMark);
}

void shiftBits() {
  WaitWait=FullWait;
  ditOrDah=true;
    if (downTime<dit/3) return; // ignore my key bounce
  if (downTime<dit) {
    // We got a dit
    myNum = myNum << 1;  //shift bits left
    myNum++;
  }
  else {
    // We got a dah
    myNum = myNum << 1; // shift bits left
    averageDah=(downTime+averageDah)/2;
    dit=averageDah/3;
    dit=dit*2;  }
}