Project tutorial
Morse "bug" keyer

Morse "bug" keyer © GPL3+

2 switches, a sounder and an interrupt

  • 1,069 views
  • 0 comments
  • 7 respects

Components and supplies

A000066 iso both
Arduino UNO & Genuino UNO
×1
Piezo sounder element
×1
7mm Pushbutton (Normally open type)
Note that the 7mm is the height of the button not the size of the square
×2

Apps and online services

About this project

Story:

There are various styles of automated Morse keys the bug being one of the simpler [1]. The real unit has a horizontal paddle type key which is pressed to the left or right [2]. Pressed one way the key produces a stream of dots, pressed the other way the key produces a single dash for each press. My circuit and code simulate this device.

Hardware description:

The circuit consists of 2 switches and a piezo sounder element.

The switches are attached to 2 inputs with their internal pull up’s enabled.Pressing a switch takes the respective input to ground.

One end of the piezo sounder is connected to ground, the other to a PWM pin which provides its drive signal.

Software overview:

The switches are debounced using a software routine I found in a pdf called Debouncing by Jack G Ganssle [3]. A timer interrupt polls the switches at regular intervals and the switch is considered debounced after it has registered as being in the down state for a set number of these intervals.

The main line code (loop{}) checks which button, if any, has been pressed. For the dot button dots are generated as long as the button is held down. For the dash button only one dash is generated for each button press.

The length and spacing of the dots and dash can be set between 3 speeds by taking pins A0, A1 or A2 to ground. The speeds are 5, 13 and 20 words per minute respectively. Detail of how the values for the code speed were calculated can be found in the references [4].

Quirks:

For a dash to be generated you must first release the dot key before pressing the dash key otherwise you get nothing until you have released and re-pressed the key.

On the other-hand, pressing and releasing the dash key with the dot key down will produce a gap in the stream of dots; this may prove useful if you are sending a letter pair e.g. ss.

References (Checked 4/4/2020):

[1]http://www.morsex.com/pubs/iambicmyth.pdf

[2]https://en.wikipedia.org/wiki/Vibroplex

[3]http://www.eng.utah.edu/~cs5780/debouncing.pdf

[4]http://www.kent-engineers.com/codespeed.htm

Code

"Bug" keyer codeC/C++
//Morse "bug" keyer

//buttons
#define button_1 2
#define button_2 3

//speed selector pins
//take one to ground to select speed 
//default is 5 words per minute
#define selector_1 A0
#define selector_2 A1
#define selector_3 A2

//the buzzer
const int sounder_pin=5;// the pin we attach our sounder to
const int duty_cycle=128;//50% duty cycle for analogWrite()


//required to debounce switches
#define MAX_CHECKS 10
volatile uint8_t Debounced_State=0;//accessed by isr and main loop code
uint8_t State[MAX_CHECKS]={0};
uint8_t Index=0;


void setup() {
  // put your setup code here, to run once:
  
          pinMode(button_1,INPUT_PULLUP);
          pinMode(button_2,INPUT_PULLUP);
          
          pinMode(selector_1,INPUT_PULLUP);
          pinMode(selector_2,INPUT_PULLUP);
          pinMode(selector_3,INPUT_PULLUP);
          
          pinMode(sounder_pin, OUTPUT);

              // initialize timer1 
  noInterrupts();           // disable all interrupts
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1  = 0;

  OCR1A = 625;              // compare match register  16MHX/256/100HZ 
  TCCR1B |= (1 << WGM12);   // CTC mode
  TCCR1B |= (1 << CS12);    // 256 prescaler 
  TIMSK1 |= (1 << OCIE1A);  // enable timer compare interrupt
  interrupts();             // enable all interrupts
  
}

void loop() {
  // put your main code here, to run repeatedly:

static int transferred_value=0;
static bool do_character=false;
static bool do_dot=false;
static bool wait_up=false;

    noInterrupts();           // disable all interrupts 
    transferred_value=Debounced_State;      
    interrupts();             // enable all interrupts

    
if (!transferred_value){wait_up=false;}//if all buttons released
if (transferred_value){
                      
                      if(transferred_value==2){do_dot=true;do_character=true;wait_up=true;}//dot                                           
                      if(transferred_value==1){if(!wait_up){do_dot=false;do_character=true;wait_up=true;}}//dash
                      
                    }   


//morse speed
//all timing is based around the length of the dot
//source for timing -> http://www.kent-engineers.com/codespeed.htm

static unsigned long unit_length=240;//default 5 words per minute
if(!digitalRead(A0)){unit_length=240;}//dot time calculated for 5 words per minute
if(!digitalRead(A1)){unit_length=92;}//dot time calculated for 13 words per minute
if(!digitalRead(A2)){unit_length=60;}//dot time calculated for 20 words per minute
  

const unsigned long dot=unit_length;
const unsigned long dash=unit_length*3;
const unsigned long intra_letter_space=unit_length;//space between dot's and dashes within letter


if (do_character) 
{
  do_character=false;


              if (!do_dot){ //dash
                        analogWrite(sounder_pin, duty_cycle); 
                        delay(dash);                       // wait for a while
                        analogWrite(sounder_pin,0);
                        delay(intra_letter_space);         // space between letter parts
                         }
                      else{ //dot
                            analogWrite(sounder_pin, duty_cycle);
                            delay(dot);                    // wait for a while
                            analogWrite(sounder_pin,0);
                            delay(intra_letter_space);     // space between letter parts
                            }

}


}
/*-----------------*/
//my_functions
ISR(TIMER1_COMPA_vect)          // timer compare interrupt service routine
{
//code source - A Guide to Debouncing by Jack G. Ganssle 
//read buttons
  uint8_t temp=0x00;
  
  temp|=!digitalRead(button_1)<<1;
  temp|=!digitalRead(button_2)<<0;
            
//debounce   
  uint8_t i,j;

  State[Index]= temp;
  ++Index;
  j=0xFF;
  for (i=0;i<MAX_CHECKS;i++){j=j&State[i];}
  Debounced_State=j;
  if(Index>=MAX_CHECKS){Index=0;} 

}
//-----------end------------

Schematics

Circuit diagram
Morse bug keyer bb 02hhrm6k1p

Comments

Similar projects you might like

Arduino Morse Code Machine

Project showcase by Vlada Krsmanovic

  • 14,779 views
  • 3 comments
  • 37 respects

Morse Code Machine

Project showcase by brzi

  • 3,989 views
  • 1 comment
  • 7 respects

Morse Code Communication Using Arduino

Project tutorial by Jalal Mansoori

  • 19,392 views
  • 19 comments
  • 36 respects

Morse Code Communication Using Laser Module (Both)

Project tutorial by SURYATEJA

  • 3,015 views
  • 1 comment
  • 14 respects

The Morse Reborn

Project tutorial by GiHOxp

  • 1,058 views
  • 0 comments
  • 4 respects

Morse Code Translator

Project showcase by Team ONYX

  • 19,688 views
  • 9 comments
  • 23 respects
Add projectSign up / Login