How to Make a Morse Code Application

July 30, 2024
Written by
Aina Rakotoson
Contributor
Opinions expressed by Twilio contributors are their own
Reviewed by
Diane Phan
Twilion

Telegraphs change the way humans communicate by allowing long-distance communication.

Many implementations were done before the electrical version using systems such as optical and fire. It was with the evolution of electricity that the telegraph system became famous, commercialized, and widely used. Morse code is the standard encoding for telegraph transmission. It is an effective communication system, but can we still have telegram messages like in an electric telegraph with our modern device - a mobile phone?

In this tutorial, you will discover and learn how you can send a telegram message encoded with Morse code to a mobile phone using Twilio Programmable Voice to replicate a telegram message that our grandfather in 1900 would have received.

Prerequisites

To complete this tutorial, you will need:

Understand the history of the telegraph system and Morse code

A telegraph is a system or a device that allows one to transmit a message over a distance. It was in 1791 that Claude Chappe designed the first version of this telegraph system. It is based on optical principles and is generally called semaphore. Below is an illustration of the Chappe telegraph device.

With the evolution of electricity, the electrical telegraph arrived In 1832 with the invention of Pavel Schilling. In 1844 a single-wired version of the electrical telegraph appeared, designed by Samuel Morse and Alfred Vail. This uses Morse code, which became the standard for telegraph communication in Europe.

Morse code is a system that encodes text characters with a succession of dots and dashes. When transmitting a message with a telegraph, the operator translates the text message into a dot and dash (Morse code) and then types it on the telegraph device. In the receiver, dots and dashes are received and then translated to text to have the original text.

Morse code was used in many fields of application and was first designed to be written on paper tape which rotated as long as messages were received. The movement of the telegraph device when moving on and off the paper tape produces a sound that leads to the change from written code to audible code. A trained operator can recognize the produced sound and translate it to letters without the need for paper tape.

Fun fact: “dot” was known as “dit” because of the sound it makes on the device. Same thing for dash with “dah”. There is also some implementation of Morse code that uses visual signals like light or eye blink.

Each letter has its specific code, and there is no distinction between upper case and lower case.

Here is an illustration of the international version when encoding the text “Morse code”.

Morse code text encoded with morse system

Build a telegraph system with Twilio Programmable Voice

In this tutorial, you will work with the Twilio Programmable Voice API which allows you to make a call and play audio files during the running voice call. The audio can be taken from a web server accessible by Twilio.

We can assemble every sound for each letter to be a unique file sound that can be later played by the Twilio Programmable Voice API.

So here is how our system will work:

  • A text message is written.
  • Each letter in the text message is converted to Morse code.
  • The Morse code is then converted and compiled into one single audio file.
  • The compiled audio file will be streamed by a web server.
  • Create a call with the Twilio Programmable Voice API.
  • The encoded audio message will be streamed through the call using TwiML.

The system can run under any platform that supports Python3.6+. You need to install the required packages in order to build and run it.

Install the necessary requirements

Start by creating a directory named “twilio_morse_code”. It will contain the code for the system.

$mkdir twilio_morse_code

Create a virtual environment to isolate the package for your application. In the root folder of the project, run the following command. It is always better to create a virtualenv so that your application runs in a freshly separated environment.

$python3 -m venv .venv

Activate the virtualenv so you can have a separate library installation in it.

$source .venv/Script/activate

Then install the required libraries.

$pip install twilio pydub

In order to make your app accessible from the internet you need to install Ngrok. It allows you to expose your local service to the internet so that the Twilio Programmable Voice can communicate with it.

Download it from the official website for free: https://ngrok.com/. Install it by using the command line indicated on the website.

Build the Morse code encoder

Start by adding a Python package named ` morse` inside the root directory.

$cd twilio_morse_code
$mkdir morse
$cd morse
$touch __init__.py

You need to translate or encode a given text to a Morse code sound. Each letter has its corresponding Morse code. Create a file named “ code.py” inside the morsepackage and paste the following code that defines the dict:

ONE_UNIT = 0.5
THREE_UNITS = 3 * ONE_UNIT
SEVEN_UNITS = 7 * ONE_UNIT
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': '--..',
    '0': '-----',
    '1': '.----',
    '2': '..---',
    '3': '...--',
    '4': '....-',
    '5': '.....',
    '6': '-....',
    '7': '--...',
    '8': '---..',
    '9': '----.'
}

Note the presence of the “ONE_UNIT” variable initialized with 0.5. This will correspond to the unit time as described above in the Morse code section.

"SEVEN_UNITS" represents space between words and "THREE_UNITS" defines the pause between each letter.

On the other hand, you need Morse code sound for each letter and digit. A prepared list of sounds is available on this GitHub repository from GitHub user cptdeadbones.

Download all of the ogg files and store them inside a subdirectory named “ morse_sound_files” at the same level as the morse package. The directory structure inside “ twilio_morse_code” should look the same as shown below.

folder structure after adding morse_sound_files

Now you need a way to concatenate the sound for each letter to form a final unique sound that can be streamed later. pydub, a Python extension can be utilized here to manipulate audio files.

Create a function “ message_to_morse_sound” that takes a text message string variable and converts it to a Morse code sound file.

The function takes four parameters:

  • a text message string.
  • the base path of the Morse code sounds.
  • the file name of where the generated audio file should be saved.
  • the format of the generated sound file.

Create a new file named encoder.py within the morse subdirectory and copy and paste the following code into the file.

import os
import time
from pydub.playback import play
from pydub import AudioSegment
from .code import CODE, SEVEN_UNITS, ONE_UNIT, THREE_UNITS
def verify_message(message):
    is_message_valid = True
    error = ""
    keys = CODE.keys()
    for char in message:
        if char.upper() not in keys and char != ' ':
            is_message_valid = False
            error = 'Error the character ' + char +' cannot be translated to Morse Code'
            break
    return is_message_valid, error
def message_to_morse_sound(message: str,
                           sound_base_path: str = 'morse_sound_files/',
                           output_file: str = 'output',
                           output_format: str = 'ogg'):
    final_sound = AudioSegment.silent(1)
    for char in message:
        if char == ' ':
            print(' ' * 7)
            time.sleep(SEVEN_UNITS)
            final_sound = final_sound + AudioSegment.silent(SEVEN_UNITS * 1000)
        else:
            print(CODE[char.upper()])
            sound_path = os.path.join(sound_base_path,
                                      char.upper() + '_morse_code.ogg')
            print("Path: ", sound_path)
            sound = AudioSegment.from_ogg(sound_path)
            final_sound = final_sound + sound
            # play(sound)
            time.sleep(THREE_UNITS)
            final_sound = final_sound + AudioSegment.silent(THREE_UNITS * 1000)
    final_sound.export(output_file, format=output_format)
    print(f"morse code written in: {output_file}")

The function verify_message checks if all characters in the message can be translated to Morse code.

Now you have a way to translate a text message to a Morse code audio file. Let’s move to the way to stream it in a phone call.

Make calls with the Twilio Programmable Voice API

You will need to create an instance of Twilio client in order to create a call. In the running call, you will add TwiML instructions to play audio by pointing to your web server URL.

Navigate to the root folder of the application twilio_morse_code and create a file named web_app.py. Note the import of the message_to_morse_sound function from the morse.encoder subdirectory for later use. Copy and paste the contents to the newly created file:

import os
from twilio.rest import Client
from morse.encoder import message_to_morse_sound, verify_message
from config import SOUND_BASE_PATH
account_sid = os.getenv('TWILIO_ACCOUNT_SID')
auth_token = os.getenv('TWILIO_AUTH_TOKEN')
def create_twilio_api_client():
	client = Client(account_sid, auth_token)
	return client
def make_twilio_call(client,
                 	to='',
                 	from_="",
                 	stream_url=''):
	stream_url = stream_url
	call = client.calls.create(
                    	twiml=f'<Response><Play>{stream_url}</Play></Response>',
                    	to=to,
                    	from_=from_
                	)

Navigate to the Twilio console to obtain your TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN. Set the corresponding value in your environment variable.

Accoint SID and auth token on Twilio console

If you are on Linux set the environment variable using /etc/profile so that it can be permanent. Edit /etc/profile with your favorite text editor.

vim /etc/profile

Replace the XXXXX with the appropriate values obtained from the Twilio console.

export TWILIO_ACCOUNT_SID=XXXXX
export TWILIO_AUTH_TOKEN=XXXXX

Build the web server

The web server is the center point of your system because it is where a user enters text to be translated and also where Twilio interacts to retrieve an audio stream.

The Flask framework is used to build the web server. It has the advantage of being lightweight and can give you a way to serve an audio file quickly with static files.

Still in the file web_app.py add the following codes at the end of the file:

from flask import Flask, request
app = Flask('__name__')
@app.route("/hello")
def hello():
    return "hello"
if __name__ == '__main__':
    app.run()

The above code shows the basics of your web server with one route (/hello). It will be used to check that your web server is running by returning a “ hello” text when the web server is running.

At the same level as web_app.py, create a folder named static. It will store the audio file generated by your encoder. Flask can stream any audio file in the static folder automatically in the URL path /static/audio_file.xxx.

It’s time to add a way to interact with the system. For that, the web server needs to have more routes.

First, a route where a user can enter text which will be encoded with Morse code. It is called “tomorseaudio” and can be accessed in URL path /tomorsecode. A “ text” query parameter should be set when accessing this route.

Secondly, create a route where a user decides to send the Morse code to a mobile phone via the Twilio Programmable Voice API. Call it “ sendmorse” and available in “ /sendmorse” URL path. Users should give “to” and “from” query parameters when using this route. The values of to and from should be a valid phone number. Twilio uses E.164 standard, but it is better to use the international format like the following number: +4402077651182.

Add these two functions in web_app.py file after the “hello” function.

@app.route("/tomorseaudio")
def tomorseaudio():
    text = request.args.get("text", "")
    is_valid, error = verify_message(text)
    if not is_valid:
        return error, 400
    if text:
        message_to_morse_sound(text, SOUND_BASE_PATH, "static/output.ogg")
    return f"'{text}' translated to morse code", 200
@app.route("/sendmorse")
def morse_stream():
    to = request.args.get("to", "")
    from_ = request.args.get("from", "")
    base_url = request.base_url
    stream_url = f"{base_url}/static/output.ogg"
    if to and from_:
        client = create_twilio_api_client()
        make_twilio_call(client, to=to, from_=from_, stream_url=stream_url)
        return "audio morse code sent", 200
    return "Query parameter 'to' and 'from' should be a valid phone number", 400

The complete code can be downloaded from this GitHub repository.

Run the Morse code app

Now everything is in place to run the system.

Use the following command to run the web server:

$python web_app.py

Expose the web server with ngrok

If you are running ngrok for the first time, you should create an account and get your auth token. Once you get your auth token, run the following command to add it to your local configuration. Do not forget to replace <token> with your auth token:

$ngrok config add-authtoken <token>

Now run the following command to expose your web server on the internet: ngrok http 5000

You should get something like the below screenshot on your console. Note the ngrok link shown in it.

Test the system by sending a Morse code message

Open the ngrok URL with a web browser by appending /tomorsecode?text=hello to the end of the URL as seen in the example below:

https://your_ngrok_url/tomorsecode?text=hello

This will generate a Morse code audio file for the text “hello”.

Now send it to a friend by specifying your friend's phone number in the query parameter as shown below. Note that it is better to follow the international format for the phone number.

https://your_ngrok_url/sendmorse?to=<your_friend_phone_number>&from=<your_twilio_phone_number>

Be sure to replace the "to" value with your friend's phone number and the "from" value with your Twilio phone number in the international format "+44XXXXXXXXXXX", as seen below:

https://145d-154-126-12-148.ngrok-free.app/sendmorse?to=+44XXXXXXXXXXX&from=+44YYYYYYYYYYY

Your friend should now receive a phone call and hear the Morse code audio message.

What's next for building telegram apps with Programmable Voice?

This article shows that it is still possible to send messages encoded with Morse code like an old telegraph. In our century, it may sound weird to send Morse code with our powerful device and Twilio Programmable Voice API, but it is fun, and it is constantly enriching to learn from the origin of a technology.

Check out these articles to expand your project further:

Aina Rakotoson is a lead Python developer, a dad, a handyman, and a vintage mechanic lover. You can find him on Twitter or on GitHub .