Skip to content
An Arduino morse code stream library
C++
Branch: master
Clone or download

README.md

Lewis

A morse code interpreter: making your Morse easier to use and process.

Lewis is a Serial-type library for converting variable data to morse code output, or converting morse code input into strings and numbers. You can use Lewis to send and receive morse code from your Arduino, and even allow you to converse, send instructions, get status or transfer data using one pin (simplex) or two for full duplex communication.

Lewis can be used either as a set of blocking functions (pulsing out will stop any other commands until finished) or contains an interrupt routine for you to call or insert where ever suits your program best, using the timer, pin change or hardware interrupt that you have free.

Using Lewis

However you use Lewis, after installing the library (e.g. using the library manager to import it), you will have to included the library header file at the top of your sketch:

#include "Lewis.h"

And instantiate the Lewis class of functions with a name that you use later. I use Morse but you can choose whatever name you like, as long as you are consistent through the sketch:

Lewis Morse;

Using Lewis requires calling the begin function in setup(), to choose the input and output pins, the pulse rate, and if you will be using the interrupt function:

  Morse.begin(rx_pin, tx_pin, words_per_minute, use_interrupts);
  // e.g.
  Morse.begin(2, 9, 20, false);
  // you can specify the same input and output pin and Lewis will
  // switch modes as necessary
  Morse.begin(9, 9, 20, false);
  // or uses the short version that assumes 20WPM and no interrupts:
  Morse.begin(9);
  // if interrupts are required, use the long version:
  Morse.begin(9, 9, 20, true);

Sending morse code

Lewis uses the same function types as the Serial library so data can be sent using the print and write commands:

  // from the message_out example
  Morse.print("ok"); // will send: ---/-.-
  Morse.write('s'); // will send: ...

in the default initiation, the output code will be “blocking” — that is, it will stop at the print line while until it has finished sending the full code. This is fine for small implementations, but for more complex code, you'll want to keep running your main code, and have the morse code sent in the background.

If you call begin with use_interrupts set as true, then you will need to call Morse.timerISR() frequently enough that it can check if any dots or dashes need sending out, without the listener hearing any inaccuracy in the tone length. 100Hz seems to work nicely for me.

Lewis does not attach a timer interrupt when initialised (so as not to clash with anything you're already doing), so you will need to include one in your sketch. The examples use the TimerOne library for the interrupt.

// from the non_blocking_out example:

// additionally include the TimerOne library
#include <TimerOne.h>

void setup() {
  // start Lewis with interrupts
  Morse.begin(2, 9, 20, true);
  // start TimerOne running at 100Hz
  Timer1.initialize(10000);
  // call the MorseISR function from below, each time the timer fires
  Timer1.attachInterrupt(myISR);
}

// create a function that calls the Lewis ISR
void myISR()
{
  // inclued this function to actually output morse code, and check for input
  Morse.timerISR();
}

Receiving morse

Lewis needs to watch the receiving pin of your choice for it's LOW→HIGH changes often enough that it can see every change and accurately time the duration between each one to decode the dots, dashes, inter-letter spaces and inter-word spaces. To do so, your script will have do call the checkIncoming function rapidly enough (ideally 100 times or more per second) to pass decode the morse stream and pass them into the receiving buffer.

If you're using the interrupt initialisation of Lewis, then checkIncoming is already included in the timerISR function, so Lewis will already be watching your receive pin for button presses. If you're using the standard, non-interrupting initialisation then just putting Morse.checkIncoming() in a fast loop will be enough.

//from the read_respond example
void loop() {
  // call checkIncoming often enough to catch the incoming presses
  Morse.checkIncoming();

  if (Morse.available()) {
    char c = Morse.read();
    if (c == 'k') {
      delay(100);
      Morse.print("ok");
    }
  }
}

With such a small loop, the checking function is not likely to miss any button press changes, but you could also run checkIncoming as part of a hardware interrupt or pin change interrupt if necessary.

To process data that's in the receive buffer, you can check if there is data, and process that as you normally would with Serial input:

// first check if there's anything to process:
if (Morse.available();) { // available returns the number of characters available
  // read grabs the next character from the buffer:
  char c = Morse.read();
  // or peek checks the next character, but without moving the buffer index forward
  char p = Morse.peek();
  // any following call of peek (or the next read) will still return the same letter
}
You can’t perform that action at this time.