New Morse Code routine

I thought I would share the morse code routine I just wrote. I know there are others out there but I wanted a simple one that wasnt C++ and was table driven

//************************************************************************
//*      Morse code transmit
//*      
//*      This code is (C) by Mark Sproul
//*      
//*      This program is free software: you can redistribute it and/or modify
//*      it under the terms of the GNU General Public License as published by
//*      the Free Software Foundation, either version 3 of the License, or
//*      (at your option) any later version.
//*      
//*      This program is distributed in the hope that it will be useful,
//*      but WITHOUT ANY WARRANTY; without even the implied warranty of
//*      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//*      GNU General Public License for more details.
//*      
//************************************************************************




//************************************************************************
//*      in order to make this work in prgram memory, it is a char array
//*      2 bytes per entry, the first is the pattern, the 2nd is the length
uint8_t      gMorseData[] PROGMEM      =
{
//      B00000000,            0,      //      0x20      space      we dont need this in the table
      B00000000,            0,      //      0x21      !
      B01001000,            6,      //      0x22      "
      B00000000,            0,      //      0x23      #
      B00010010,            7,      //      0x24      $
      B00000000,            0,      //      0x25      %
      B00000000,            0,      //      0x26      &
      B01111000,            6,      //      0x27      '
      B10110000,            5,      //      0x28      (
      B10110100,            6,      //      0x29      )
      B10010000,            4,      //      0x2A      *
      B01010000,            5,      //      0x2B      +
      B11001100,            6,      //      0x2C      ,
      B10000100,            6,      //      0x2D      -
      B01010100,            6,      //      0x2E      .
      B10010000,            5,      //      0x2F      /
      
      B11111000,            5,      //      0x30      0
      B01111000,            5,      //      0x31      1
      B00111000,            5,      //      0x32      2
      B00011000,            5,      //      0x33      3
      B00001000,            5,      //      0x34      4
      B00000000,            5,      //      0x35      5
      B10000000,            5,      //      0x36      6
      B11000000,            5,      //      0x37      7
      B11100000,            5,      //      0x38      8
      B11110000,            5,      //      0x39      9
      B11100000,            6,      //      0x3A      :
      B10101000,            6,      //      0x3B      ;
      B10110000,            5,      //      0x3C      <      same as (
      B10001000,            5,      //      0x3D      =
      B10110100,            6,      //      0x3E      >      same as )
      B00110000,            6,      //      0x3F      ?

      B00000000,            6,      //      0x40      @
      B01000000,            2,      //      A
      B10000000,            4,      //      B
      B10100000,            4,      //      C
      B10000000,            3,      //      D
      B00000000,            1,      //      E
      B00100000,            4,      //      F
      B11000000,            3,      //      G
      B00000000,            4,      //      H
      B00000000,            2,      //      I
      B01110000,            4,      //      J
      B10100000,            3,      //      K
      B01000000,            4,      //      L
      B11000000,            2,      //      M
      B10000000,            2,      //      N
      B11100000,            3,      //      O
      B01100000,            4,      //      P
      B11010000,            4,      //      Q
      B01000000,            3,      //      R
      B00000000,            3,      //      S
      B10000000,            1,      //      T
      B00100000,            3,      //      U
      B00010000,            4,      //      V
      B01100000,            3,      //      W
      B10010000,            4,      //      X
      B10110000,            4,      //      Y
      B11000000,            4,      //      Z
      B10110000,            5,      //      0x5B      [      same as (
      B00000000,            0,      //      0x5C      \
      B10110100,            6,      //      0x5D      ]      same as )
      B00000000,            0,      //      0x5E      ^
      B00000000,            0,      //      0x5F      _

};




//************************************************************************
//*      Calculate the dot length in millseconds
//*            1 minute = 60,000 milliseconds
//*
//*            dotLength      =      (60,000 / 50) / wpm
//*            dotLength      =      1200 / wpm
//*
//*  http://www.kent-engineers.com/codespeed.htm
//************************************************************************

//************************************************************************
static void      SendMorseCode(char theChar, int wpm)
{
int            tableIdx;
byte      theCode;
byte      bitLen;
int            ii;
int            myDotLength;

      //************************************************************************
      //*      Calculate the dot length in millseconds
      //*            1 minute = 60,000 milliseconds
      //*
      //*            dotLength      =      (60,000 / 50) / wpm
      //*            dotLength      =      1200 / wpm
      //************************************************************************
      myDotLength      =      1200 / wpm;

      if (theChar < 0x20)
      {
            //*      dont do anything
      }
      else if (theChar == 0x20)
      {
            delay(myDotLength * 7);
      }
      else
      {
                  
            if (theChar >= 0x60)
            {
                  theChar      =      theChar & 0x5f;      //*      force upper case
            }
            tableIdx      =      (theChar - 0x21) * 2;      //*      0x21 is the first entry
            theCode            =      pgm_read_byte_near(gMorseData + tableIdx);
            bitLen            =      pgm_read_byte_near(gMorseData + tableIdx + 1);\
            
            if (bitLen > 0)
            {
                  //*      step through the bits
                  for (ii = 0; ii < bitLen; ii++)
                  {
                        //*      turn the buzzer on
                        analogWrite(kPin_Buzzer, 128);
                        if (theCode & 0x80)
                        {
                              //*      dash time
                              delay(myDotLength * 3);
                        }
                        else
                        {
                              //*      dot time
                              delay(myDotLength);
                        }
                        //*      turn the buzzer OFF
                        analogWrite(kPin_Buzzer, 0);
                        delay(myDotLength);
                        
                        theCode      =      theCode << 1;
                  }
                  delay(myDotLength * 3);
            }
      }
}

EDIT: See full sketch in post below

Is this an Arduino sketch? I don't see a setup() or loop() section. Or is the code incomplete or am I missing something?

Lefty

This is a routine that you can use in a sketch. Define the PWM pin you are going to use for example

#define kPin_Buzzer 6

Then call the routine with the character and the speed (words per minute)
I can put together a sketch that takes input from the serial port and sends out as morse code if you like. Its part of the code I am writing to support the LCD serial shield I built http://coewww.rutgers.edu/~msproul/arduino/serialshield/

I will be making the entire program available but for I thought the routine would be useful by itself.

Mark

Lefty

Here is a complete sketch. It opens the serial port at 9600, sends out "Morse Code" and sends "CQ". Then what ever is received from the serial port is sent out via morse code. The char is also echoed back. This has been tested with version 0018.

The buzzer I am using is from sparkfun Mini Speaker - PC Mount 12mm 2.048kHz - COM-07950 - SparkFun Electronics

Hope this helps

Mark

//************************************************************************
//*      Morse code transmit
//*      
//*      This code is (C) by Mark Sproul
//*      
//*      This program is free software: you can redistribute it and/or modify
//*      it under the terms of the GNU General Public License as published by
//*      the Free Software Foundation, either version 3 of the License, or
//*      (at your option) any later version.
//*      
//*      This program is distributed in the hope that it will be useful,
//*      but WITHOUT ANY WARRANTY; without even the implied warranty of
//*      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//*      GNU General Public License for more details.
//*      
//************************************************************************

#include      <avr/pgmspace.h>

#define      kPin_Buzzer                        6


//************************************************************************
//*      in order to make this work in prgram memory, it is a char array
//*      2 bytes per entry, the first is the pattern, the 2nd is the length
uint8_t      gMorseData[] PROGMEM      =
{
//      B00000000,            0,      //      0x20      space      we dont need this in the table
      B00000000,            0,      //      0x21      !
      B01001000,            6,      //      0x22      "
      B00000000,            0,      //      0x23      #
      B00010010,            7,      //      0x24      $
      B00000000,            0,      //      0x25      %
      B00000000,            0,      //      0x26      &
      B01111000,            6,      //      0x27      '
      B10110000,            5,      //      0x28      (
      B10110100,            6,      //      0x29      )
      B10010000,            4,      //      0x2A      *
      B01010000,            5,      //      0x2B      +
      B11001100,            6,      //      0x2C      ,
      B10000100,            6,      //      0x2D      -
      B01010100,            6,      //      0x2E      .
      B10010000,            5,      //      0x2F      /
      
      B11111000,            5,      //      0x30      0
      B01111000,            5,      //      0x31      1
      B00111000,            5,      //      0x32      2
      B00011000,            5,      //      0x33      3
      B00001000,            5,      //      0x34      4
      B00000000,            5,      //      0x35      5
      B10000000,            5,      //      0x36      6
      B11000000,            5,      //      0x37      7
      B11100000,            5,      //      0x38      8
      B11110000,            5,      //      0x39      9
      B11100000,            6,      //      0x3A      :
      B10101000,            6,      //      0x3B      ;
      B10110000,            5,      //      0x3C      <      same as (
      B10001000,            5,      //      0x3D      =
      B10110100,            6,      //      0x3E      >      same as )
      B00110000,            6,      //      0x3F      ?

      B00000000,            6,      //      0x40      @
      B01000000,            2,      //      A
      B10000000,            4,      //      B
      B10100000,            4,      //      C
      B10000000,            3,      //      D
      B00000000,            1,      //      E
      B00100000,            4,      //      F
      B11000000,            3,      //      G
      B00000000,            4,      //      H
      B00000000,            2,      //      I
      B01110000,            4,      //      J
      B10100000,            3,      //      K
      B01000000,            4,      //      L
      B11000000,            2,      //      M
      B10000000,            2,      //      N
      B11100000,            3,      //      O
      B01100000,            4,      //      P
      B11010000,            4,      //      Q
      B01000000,            3,      //      R
      B00000000,            3,      //      S
      B10000000,            1,      //      T
      B00100000,            3,      //      U
      B00010000,            4,      //      V
      B01100000,            3,      //      W
      B10010000,            4,      //      X
      B10110000,            4,      //      Y
      B11000000,            4,      //      Z
      B10110000,            5,      //      0x5B      [      same as (
      B00000000,            0,      //      0x5C      \
      B10110100,            6,      //      0x5D      ]      same as )
      B00000000,            0,      //      0x5E      ^
      B00000000,            0,      //      0x5F      _

};


/*
http://www.kent-engineers.com/codespeed.htm

The word PARIS is the standard for determing CW code speed. Each dit is one element, each dah is three elements, 
intra-character spacing is one element, inter-character spacing is three elements and inter-word spacing is 
seven elements. The word PARIS is exactly 50 elements.
Note that after each dit/dah of the letter P -- one element spacing is used except the last one. (Intra-Character).
After the last dit of P is sent, 3 elements are added (Inter-Character). After the word PARIS - 7 elements are used.
Thus:
P = di da da di = 1 1 3 1 3 1 1 (3) = 14 elements
A = di da = 1 1 3 (3) = 8 elements
R = di da di = 1 1 3 1 1 (3) = 10 elements
I = di di = 1 1 1 (3) = 6 elements
S = di di di = 1 1 1 1 1 [7] = 12 elements
Total = 50 elements
() = intercharacter
[] = interword

If you send PARIS 5 times in a minute (5WPM) you have sent 250 elements (using correct spacing). 
250 elements into 60 seconds per minute = 240 milliseconds per element.

13 words-per-minute is one element every 92.31 milliseconds.

The Farnsworth method sends the dits and dahs and intra-character spacing at a higher speed, then 
increasing the inter-character and inter-word spacing to slow the sending speed down to the overall speed. 
For example, to send at 5 wpm with 13 wpm characters in Farnsworth method, the dits and intra-character 
spacing would be 92.3 milliseconds, the dah would be 276.9 milliseconds, the inter-character spacing 
would be 1.443 seconds and inter-word spacing would be 3.367 seconds.


*/


//************************************************************************
//*      Calculate the dot length in millseconds
//*            1 minute = 60,000 milliseconds
//*
//*            dotLength      =      (60,000 / 50) / wpm
//*            dotLength      =      1200 / wpm
//************************************************************************

//************************************************************************
static void      SendMorseCode(char theChar, int wpm)
{
int            tableIdx;
byte      theCode;
byte      bitLen;
int            ii;
int            myDotLength;

//cq cq cq cq cq the quick brown fox jumped over the lazy dogs back


      //************************************************************************
      //*      Calculate the dot length in millseconds
      //*            1 minute = 60,000 milliseconds
      //*
      //*            dotLength      =      (60,000 / 50) / wpm
      //*            dotLength      =      1200 / wpm
      //************************************************************************
      myDotLength      =      1200 / wpm;

      if (theChar < 0x20)
      {
            //*      dont do anything
      }
      else if (theChar == 0x20)
      {
            delay(myDotLength * 7);
      }
      else
      {
                  
            if (theChar >= 0x60)
            {
                  theChar      =      theChar & 0x5f;      //*      force upper case
            }
            tableIdx      =      (theChar - 0x21) * 2;      //*      0x21 is the first entry
            theCode            =      pgm_read_byte_near(gMorseData + tableIdx);
            bitLen            =      pgm_read_byte_near(gMorseData + tableIdx + 1);\
            
            if (bitLen > 0)
            {
                  //*      step through the bits
                  for (ii = 0; ii < bitLen; ii++)
                  {
                        //*      turn the buzzer on
                        analogWrite(kPin_Buzzer, 128);
                        if (theCode & 0x80)
                        {
                              //*      dash time
                              delay(myDotLength * 3);
                        }
                        else
                        {
                              //*      dot time
                              delay(myDotLength);
                        }
                        //*      turn the buzzer OFF
                        analogWrite(kPin_Buzzer, 0);
                        delay(myDotLength);
                        
                        theCode      =      theCode << 1;
                  }
                  delay(myDotLength * 3);
            }
      }
}


//************************************************************************
void setup()
{
      Serial.begin(9600);
      Serial.println();
      Serial.println("Morse Code");
      
      SendMorseCode('C', 13);
      SendMorseCode('Q', 13);

}

//************************************************************************
void loop()
{
char      theChar;
boolean      echoChar;


      // read all the available characters
      while (Serial.available() > 0)
      {
            theChar            =      Serial.read();
            
            SendMorseCode(theChar, 13);
            Serial.write(theChar);
      }

}
Hope this helps

Hey Mark, thanks a lot it works great.

Lefty WA6TKD 73