Morse Key to replace Keyboard - SOLVED!!!

SOLVED :slight_smile: - see my last comment!

Hi all,

i'm medical university student who is fairly new to the Arduino world and with basic knowledge of programming. In my first project i'm trying to "replace the keyboard" with a Morse key. So the solution sounds simple in theory: input Morse Code via a push button/switch and translate it into a keyboard.press according to a mapping.

So i started looking for a simple Morse to Text solution to use and extend further to map all the keys of the keyboard. But most of the solutions i found were using the dichotom structure to somehow map the input of dots and dashes to the corresponding letter. The character matched by somehow computing the position of the character in an array.

I would however like to use a matrix to match input to a keyboard press, by using the keyboard library. I would also like to differentiate between alpha-numeric keys and other keys like ctrl, alt, del, etc. So to do that I thought that one would have to switch modes: press a longer time or a specific sequence in order to switch from letters/numbers to other keys.

Does anyone have any helpful point-outs of similar projects or even hints to what could help me further.

FYI - I am using an Arduino Leonardo :D!

Thanks!

Ambitious project! Start with one of the Morse to ASCII character programs, and get that working.

The next step would be to add in the keyboard library and send those characters to the PC. Post if you have problems, following the guidelines in the "How to use this forum" post.

Ok so I made a "plan" to like code my own Morse to ASCII program, but I am taking it step by step by trying to build smaller projects and build upon them.

  1. Print some text when the button is pressed.
  2. Print for how long the button was pressed.
  3. Print a dot or a dash according to the time.
    ... and so on.

I somehow can't make it even with the first part. After some failed attempts (where it randomly printed button pressed and button not pressed, without me pressing the button), i switched to serial printing. Somehow i'm missing something. Then i used the same code with an arduino micro and it works fine. Am I doing something wrong on the Leonardo?

#include <Keyboard.h>

#define KEY_PRESSED LOW //defined to avoid mistakes
#define KEY_NO_PRESS 0 //key not pressed
#define KEY_SHORTPRESS 1//key pressed for a dot
#define KEY_LONGPRESS 2//key pressed for a dash
#define DOT_DASH_THRESHOLD 150 //threshold for a key pressed

int buttonPin = 10;
int nrPr = 0;
int st = 0;

void setup() {
  Keyboard.begin();
  pinMode(buttonPin, INPUT);
  Serial.begin(9600);
}

void loop() {
  st = digitalRead(buttonPin);
  if (st == LOW) {
    Serial.println("Button!"); // button is pressed 
  }else{
    Serial.println("No Button!"); // button is not pressed
  }
  delay(2000);
}

it randomly printed button pressed and button not pressed, without me pressing the button

That happens with a "floating input". You need a pullup or pulldown resistor as well as the button. Using pinMode(buttonpin, INPUT_PULLUP) can work.

Please describe how you wired the button, or post a hand drawn wiring diagram.

I tried something simple like in the attachment (simpleVersion2.jpg) and also the wiring from https://www.arduino.cc/en/Tutorial/Debounce (but with the same code!).

i changed the pinMode as yo suggested and it works! so now it's time to start counting how long i press the button :slight_smile:

simpleVersion2.jpg

paa36308:
i'm medical university student

And you have time for a hobby :wink:

elvon_blunden:
And you have time for a hobby :wink:

well not really.. but i try to squeeze it in somehow :smiley: so yeah...

Ok so i'm trying the second step where I want to find out how long a button was pressed. And i started from the debounce example. And this is what i wrote:

#include <Keyboard.h>

unsigned long timeBetweenChar1 = 0;// first time when button is pressed, for next charater
unsigned long timeBetweenChar2 = 0;// second time when button is pressed, for next charater
unsigned long millisBetweenChar = 0; // contains difference between first and second time, for next character

unsigned long timeBetweenWords1 = 0;//first time when button is pressed, for next word
unsigned long timeBetweenWords2 = 0;//second time when button is pressed, for next word
unsigned long timeBetweenWords = 0; //contains difference between first and second  time, for next word

int morseKeyPin = 5;    //input pin for the morse key/button, one goes to pin 5, the other one to groung
int nrOfPresses = 0;    //number of button presses, might come in handy to identify the charater
int soundOutputPin = 13;//the output pin for the sound.
int morseKeyCurrentState;//the current state of the button low/high
int morseKeyPreviousState = LOW; //previous morse key state;

unsigned long lastDebounceTime = 0; //the last time the output pin was toogled
unsigned long debounceDelay = 50; //the debouce time; increase if the ouput flicekrs

void setup() {
  Keyboard.begin();
  Serial.begin(9600);
  pinMode(morseKeyPin, INPUT_PULLUP);
  pinMode(soundOutputPin, OUTPUT);
}

void loop() {
  int reading = digitalRead(morseKeyPin); // read the state of the pin in a local variable
  Serial.println("Reading button: " +reading);
  delay(1000);
  if (reading != morseKeyPreviousState) {
    lastDebounceTime = millis();
    timeBetweenChar1 = millis();
    Serial.println("The button was last pressed at: " + lastDebounceTime);
  }
  if ((millis() - lastDebounceTime) > debounceDelay) {
    if (reading != morseKeyCurrentState) {
      morseKeyCurrentState = reading;
    }

    if (morseKeyCurrentState = HIGH) {
      timeBetweenChar2 = millis();
      millisBetweenChar = timeBetweenChar2 - timeBetweenChar1;
      Serial.println("The button was pressed for: " + millisBetweenChar);
    }
  }
  morseKeyPreviousState = reading;
}

And it doesn't work... After pressing the button once, it gets stuck (see atachment with terminal snippet). What am i doing wrong?

Hint!
Get your amateur radio license and learn to send/receive Morse code. There is a lot more to actually communicating than you have written. There are "words" determined by spacing that is longer than spacing between letters. There are times when you want to cancel the current word by sending a string of "dots".

Paul

There is prior art for this. But developing from scratch is a great way to learn.

Paul_KD7HB:
Hint!
Get your amateur radio license and learn to send/receive Morse code. There is a lot more to actually communicating than you have written. There are "words" determined by spacing that is longer than spacing between letters. There are times when you want to cancel the current word by sending a string of "dots".

...yeah, I would like to but I kinda need to finish this. Thanks for the hint, much appreciated! The scope of my project however is just to offer movement impaired patients, who can still move a finger or a muscle, the ability to control/use/manipulate a pc via morse key. :slight_smile:

gbafamily:
There is prior art for this. But developing from scratch is a great way to learn.

Puff-Suck interface for fast text input | Hackaday.io

Morse Codes for Computer Access

Thanks for the links :slight_smile: . I have to admit that i've seen the first one.

Ok so i came up with something, which obviously doesn't work and I can't figure out why :frowning: ...

#include <Keyboard.h>

int morseKeyPin = 5;    //input pin for the morse key
int nrOfPresses = 0;    //number of button presses, might come in handy to identify the charater
int soundOutputPin = 13;//the output pin for the sound.
int morseKeyCurrentState;//the current state of the button low/high
int morseKeyPreviousState = LOW; //previous morse key state;
bool keyDown = false;
bool pause = false;
bool switchToCtrl = false;
unsigned long time = 0;
bool decoded;
int switchKeys = 0;

int DIT = 300;
int DAH = 3 * DIT;

String morseCode = "";
String message = "";

char LATIN_CHARACTERS[] = {
  // Numbers
  '0', '1', '2', '3', '4',
  '5', '6', '7', '8', '9',
  // Letters
  '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',
  // Special
  '.', '?', '@', ' '
};

String MORSE_CHARACTERS[] = {
  // Numbers
  "-----", ".----", "..---", "...--", "....-",
  ".....", "-....", "--...", "---..", "----.",
  // Letters
  ".-",    "-...",  "-.-.",  "-..", ".",
  "..-.",  "--.",   "....",  "..",  ".---",
  "-.-",   ".-..",  "--",    "-.",  "---",
  ".--.",  "--.-",  ".-.",   "...",  "-",
  "..-",   "...-",  ".--",   "-..-", "-.--",  "--..",
  // Special
  ".-.-.-", "..--..", ".--.-.", " "
};

String Keyboard_Key[] = {

};

unsigned long lastDebounceTime = 0; //the last time the output pin was toogled
unsigned long debounceDelay = 50; //the debouce time; increase if the ouput flicekrs

void setup() {
  Keyboard.begin();
  Serial.begin(9600);
  pinMode(morseKeyPin, INPUT_PULLUP);
  pinMode(soundOutputPin, OUTPUT);
  time = millis();
}

void decodeMorse() {
  if (morseCode.length() == 0) return;
  pause = true;

  decoded = false;
  for (int i = 0; i < 40; i++) {
    if (morseCode == MORSE_CHARACTERS[i]) {
      message += LATIN_CHARACTERS[i];
      decoded = true;
      break;
    }
  }
  if (!decoded) message += "";
  morseCode = "";
  Serial.print(message);

  pause = false;
}

void loop() {

  if (pause) return;

  if (digitalRead(morseKeyPin) == HIGH) {
    if (keyDown) {
      unsigned long duration = millis() - time;
      keyDown = false;
      if (duration < (DIT + 50)) {
        morseCode += "-";
      } else if (duration < (2 * DAH)) {
        morseCode += ".";
      } else if (duration > 2000) {
        
        if (switchKeys == 0) {
          switchKeys = 1;
        }

        if (switchKeys == 1) {
          switchKeys = 0;
        }
    }
  } else {
    unsigned long duration = millis() - time;
      if (duration > 1500) {
        decodeMorse();
        if (message != "") message += " ";
      }
    }
  } else {
    if (!keyDown) {
      unsigned long duration = millis() - time;
      if (duration < 20) {
        return;
      } else if ((duration > DAH + 100) && (duration < 1500)) {
        decodeMorse();
      }
      keyDown = true;
      time = millis();
    } else {
      unsigned long duration = millis() - time;
      if (duration > 1000) {
        keyDown = false;
      }
    }
  }

}

any thoughts maybe? Thanks!

obviously doesn't work

That means nothing to us.

Please explain what should happen, and what happens instead.

jremington:
That means nothing to us.

Please explain what should happen, and what happens instead.

ok :smiley: so the circuit i use is the one from the debounce example (https://www.arduino.cc/en/Tutorial/Debounce) with a minor modification of the input pin the is set to 5 and not 2 as show in the example. the code i use the following:

#include <Keyboard.h>

int morseKeyPin = 5;    //input pin for the morse key
int nrOfPresses = 0;    //number of button presses, might come in handy to identify the charater
int soundOutputPin = 13;//the output pin for the sound.
int morseKeyCurrentState;//the current state of the button low/high
int morseKeyPreviousState = LOW; //previous morse key state;
bool keyDown = false;
bool pause = false;
bool switchToCtrl = false;
unsigned long time = 0;
bool decoded;
int switchKeys = 0;

int DIT = 300;
int DAH = 3 * DIT;

String morseCode = "";
String message = "";

char LATIN_CHARACTERS[] = {
  // Numbers
  '0', '1', '2', '3', '4',
  '5', '6', '7', '8', '9',
  // Letters
  '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',
  // Special
  '.', '?', '@', ' '
};

String MORSE_CHARACTERS[] = {
  // Numbers
  "-----", ".----", "..---", "...--", "....-",
  ".....", "-....", "--...", "---..", "----.",
  // Letters
  ".-",    "-...",  "-.-.",  "-..", ".",
  "..-.",  "--.",   "....",  "..",  ".---",
  "-.-",   ".-..",  "--",    "-.",  "---",
  ".--.",  "--.-",  ".-.",   "...",  "-",
  "..-",   "...-",  ".--",   "-..-", "-.--",  "--..",
  // Special
  ".-.-.-", "..--..", ".--.-.", " "
};

String Keyboard_Key[] = {

};

unsigned long lastDebounceTime = 0; //the last time the output pin was toogled
unsigned long debounceDelay = 50; //the debouce time; increase if the ouput flicekrs

void setup() {
  Keyboard.begin();
  Serial.begin(9600);
  pinMode(morseKeyPin, INPUT_PULLUP);
  pinMode(soundOutputPin, OUTPUT);
  time = 0;
}

void decodeMorse() {
  if (morseCode.length() == 0) return;
  pause = true;

  decoded = false;
  for (int i = 0; i < 40; i++) {
    if (morseCode == MORSE_CHARACTERS[i]) {
      message += LATIN_CHARACTERS[i];
      decoded = true;
      break;
    }
  }
  morseCode = "";
  Serial.print(message);
  message = "";
  pause = false;
}

void loop() {
  
  if (pause) return;
  
  if (digitalRead(morseKeyPin) == LOW) {
    
    if (!keyDown) {
      unsigned long duration = millis() - time;
      //Serial.println(duration);
      keyDown = false;
      if (duration < (DIT + 50)) {
        morseCode += "-";
      } else if (duration < (2 * DAH)) {
        morseCode += ".";
      } else if (duration > 2000) {
        
        if (switchKeys == 0) {
          switchKeys = 1;
        }

        if (switchKeys == 1) {
          switchKeys = 0;
        }
    }
    time = millis();
  } else {
    unsigned long duration = millis() - time;
      if (duration > 1500) {
        decodeMorse();
        message += " ";
        Serial.print(message);
        message="";
      }
    }
  } else {
    if (keyDown) {
      unsigned long duration = millis() - time;
      if (duration < 20) {
        return;
      } else if ((duration > DAH + 100) && (duration < 1500)) {
        decodeMorse();
      }
      keyDown = true;
      time = millis();
    }
  }

}

and the results is that even though i push the button (morsed several characters), nothing is shown in the serial terminal. I tried tuning the lenght of the dits and dahs.. but it did not resolve the problem.

SOLVED!!! :slight_smile:

So dear Arduino Community,

because I believe in giving back, here is my solution to HOW TO REPLACE THE KEYBOARD WITH A MORSE KEY! But not before thanking all those who replied and especially Calvin who wrote the instructable (https://www.instructables.com/id/USB-Arduino-Morse-Code-Key/).

I am using an Arduino Leonardo, the circuit described in http://roboticadiy.com/arduino-tutorial-debounce-pushbutton/ and the following code:

#include <Keyboard.h>

#define morseKeyInputPin 2
#define buzzerPin 3

unsigned long morseTimeUnitInMs = 50;
unsigned long lastDownMillis = 0;
unsigned long lastUpMillis = 0;
boolean switchKeys;
String keyMap[][2] =
{
  {"AT", ".--"},
  {"CC", "-.-.-.-.-."},
  {"CV", "-.-....-"},
  {"CA", "-.-..-"},
  {"SW", "......."}
   //HERE YOU CAN MAP MORE KEYS!
};

String morseMap[][2] =
{
  { "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", "--.." },
  // backspace
  { "*", "......--" },
  // backspace | turned it quickly to the switch!
  { "SW", "........" },
  //switch to other keys

  { "1", ".----" },
  { "2", "..---" },
  { "3", "...--" },
  { "4", "....-" },
  { "5", "....." },
  { "6", "-...." },
  { "7", "--..." },
  { "8", "---.." },
  { "9", "----." },
  { "0", "-----" },

  { ".", ".-.-.-" },
  { ",", "--..--" },
  { "?", "..--.." },
  { "!", "-.-.--" },
  { ":", "---..." },
  { ";", "-.-.-." },
  { "(", "-.--." },
  { ")", "-.--.-" },
  { "\"", ".-..-." },
  { "@", ".--.-." },
  { "&", ".-..." },
};
String code = "";

void setup() {
  Keyboard.begin();
  Serial.begin(9600);
  pinMode(morseKeyInputPin, INPUT_PULLUP);
  pinMode(buzzerPin, OUTPUT);
  switchKeys = false;
}


void loop() {
  int morseKeyInputState = digitalRead(morseKeyInputPin);
  //Serial.println(morseKeyInputState);
  // run every 5 milliseconds
  if (millis() % 3 == 0) {
    if (morseKeyInputState == HIGH) {
      if (lastDownMillis == 0)
        lastDownMillis = millis();
    } else {
      if (lastDownMillis != 0) {
        unsigned long downTime = millis() - lastDownMillis;
        if (downTime > 100) {
          lastDownMillis = 0;

          if (downTime > 1000) {
          } else if (downTime > morseTimeUnitInMs * 8) {
            if (code == "") {
              Keyboard.print(" ");
            }
          } else if (downTime > morseTimeUnitInMs * 3) {
            code += "-";
          } else {
            code += ".";
          }
          lastUpMillis = millis();
        }
      }

      if (lastUpMillis != 0) {
        unsigned long upTime = millis() - lastUpMillis;
        if (upTime > morseTimeUnitInMs * 6) {
          String text = decode(code);
          type(text);

          lastUpMillis = 0;
          code = "";
        }
      }
    }
  }
  if (morseKeyInputState == HIGH) {
    analogWrite(buzzerPin, 230);
  } else {
    analogWrite(buzzerPin, 0);
  }
}

String decode(String morse) {
  Serial.println(morse);
  if (!switchKeys) {
    for (int i = 0; i < 49; i++) {
      if (morseMap[i][1] == morse)
        return morseMap[i][0];
    }
    return "";
  } else {
    for (int i = 0; i < 3; i++) {
      if (keyMap[i][1] == morse)
        return keyMap[i][0];
    }
  }
}

void type(String text) {
  if (text == "*") {
    Keyboard.write(178);
  } else if (text == "SW") {
    if (switchKeys == false) {
      switchKeys = true;
      Serial.print("Switched to TRUE!(other keys");
    } else {
      switchKeys = false;
      Serial.print("Switched to FALSE!(alphanumeric)");
    }
  } else if (text == "AT") {
    //THIS IS WERE I PRESSED THE COMBINATION ALT+TAB
    Keyboard.press(134);
    Keyboard.press(179);
    Keyboard.releaseAll();
  } else {
    Keyboard.print(text);
  }
}

With this code you can only "press" alt+tab. For more combinations, one will have to code it themselves... Yes there one can do it better and therefore i'll appreciate every feedback that you'll give me!

Thanks again and I hope you'll have fun keying! :smiley: