Last month, I created a morse code generator. It accepts user input from the console, translates it into morse code, and blinks an LED to “transmit” the message.
I decided to build on that a bit, adding a button to the circuit that allows me to generate morse code from a button click. The clicks are read in by a GPIO pin, and interpreted by a Python script.
Defining the Rules
We should always figure out what a program is going to do before we start writing it, so here are a few rules to guide us:
- Dots and dashes will be entered using the rules on timing outlined in “What is Morse Code?“
- An acceptable tolerance will be built into the timing, since it’s difficult to keep an exact rhythm.
- Blink a blue LED to the rhythm of the “base time”, to help with timing.
- Interpret dots and dashes using International Morse Code (IMC)
- The message separator prosign AR ·-·-· will indicate the end of the message, after which the script will display its interpretation.
- When a dot or dash is timed correctly, blink green LED 3x; otherwise, blink red 3x
- When a dot/dash sequence (indicated by a gap equal to 3 dots) is interpreted
- as a valid letter/number, blink a green LED 3x,
- as invalid/unrecognized, blink a red LED 3x and discard the sequence.
- as a valid letter/number, blink a green LED 3x,
That might not be everything, but it does give us a general direction to run.
Designing the Circuit
Now let’s decide what we need in a circuit, based on the rules we just laid out.
- A button to “transmit” dots and dashes.
- A line from 3.3v, thru a 220Ω resistor, to one side of the button (let’s call it side 1).
- A line from the other side of the button (side 2) to pin 31 (GPIO 6).
- A 10kΩ pulldown resistor from pin 31 to ground. (more on pulldown resistors)
- A yellow LED and 220Ω resistor from side 2 of the button, to ground.
- A red LED and 220Ω resistor connecting pin 36 (GPIO 16) to ground.
- A green LED and 220Ω resistor connecting pin 32 (GPIO 12) to ground.
- A blue LED and 220Ω resistor connecting pin 11 (GPIO 17) to ground.
Here’s the kind of layout I planned out.
In retrospect, that resistor connecting the cathode side of the yellow LED to ground won’t hurt, but it’s unnecessary, since there’s already a resistor connecting 3.3v to the button.
And here are a few pictures of the actual board after I wired it up:
Writing the Script
Here’s the python script. Make sure you copy InternationalMorseCode.py from GitHub too, and place it in the same directory you’re running this script from.
|
|
Hopefully most of this is self-explanatory, maybe with a little bit of studying the code. I’ll address a few points though. If you have questions about the rest of it, leave a comment and I’ll try to clarify it.
Metronome
If you’re unfamiliar with a metronome, it’s just a device that makes a regular beat or sound, to mark rhythm. I added an LED that blinks one cycle (on and off) per “base time”. All the code does is look at the current state of the LED (on or off), and flips it.
GPIO.output(11, not GPIO.input(11))
The t.daemon
piece causes it stop running the thread when the program ends. Otherwise, the light just keeps on blinking!
Success or Failure
The signal_to_user
method simply takes a pin and turns it on and off a few times, rapidly. That gives us the flashing green and red LEDs.
Note that both of these methods run in a separate thread, so as not to freeze up the main thread that our program is running on. You can read more about threading in Python here.
Detecting Button Clicks
The only thing we’re interested in is when a button was pressed and is then released, or vice versa. It’s possible, even with a pulldown resistor, to occasionally detect two presses or two releases in a row. Some of that is even due to the button itself… I’ve seen the duplicate events more often when I don’t push the button as forcefully, probably causing something inside to float between connected and disconnected a few times really quickly.
You can apply a “bouncetime” when you setup the pin, which tells it to ignore duplicate button presses that are really close together. But I preferred to just detect it and correct it myself, which is what I’m doing in intercept_morse_code
with the last_edge
stuff.
More Resources
Courses
If you’re interested, Coursera has a series of courses on Python, two of which are called Getting Started with Python and Python Data Structures. There’s another course I’ve started working through too, called Interfacing with the Raspberry Pi.
Reference Card
Here’s a reference card from toptechboy.com that shows what the pins do on the Pi. If you don’t have a cobbler that plugs into your breadboard, and you have to wire up individual GPIO pins on the Pi to your breadboard, you’ll want to keep this handy.
Hardware
If you want any of the accessory hardware you saw here, including the T-shaped cobbler and cable (it’s very handy to not have to wire up all the individual GPIO pins!), you can pick up the same set I did, called the CanaKit Raspberry Pi GPIO Breakout Board Bundle.
I wouldn’t suggest it if I didn’t like it. It’s affordable (in keeping with the spirit of the whole Pi movement), has good reviews, and I’ve used most of the parts in it now and haven’t had a problem with a single one.
Helpful Links
- Simple Guide to the RPi GPIO Header and Pins
- International Morse Code | Morse Code World
- Python Lists | GeeksforGeeks
- Manage Concurrent Threads (Python)
Final Thoughts
This project led me down the path of detecting the pin edges (whether the button is pressed or not, 1 or 0, on or off, high or low), and other pin-related concepts like bounce time. I wrote more about what I learned.
Quick note about buttons. When you use one on your breadboard for the first time, it might feel like it only goes so far. Be sure to give it a good firm push so it’s flush with the breadboard, otherwise it won’t come into contact like it should. Mine looked like it was in at first, but wasn’t registering clicks very well.