Morse code is an ancient tongue spoken by the hooded figures who worship a terrifying obsidian pillar in the sunken city of – oh, wait. Sorry! Wrong blog.
We’ve briefly looked over producing Morse code and making music. What if we’re receiving Morse code now? Today, we’ll be teaching the Arduino to translate Morse code back into English and use a microphone at the same time.
What you’ll need:
- Arduino Uno
- Wires, male on one end and female on the other
- USB Cable
- Microphone
- PC/Laptop connection
The circuit
Let’s take a brief look at the circuit we’ll be playing with:
We’re also going to want to connect the whole thing into our computer so we can upload code. Remember, all the code can be found href=’http://github.com/marginallyclever/arduinostarterkit’ target=’_blank’>here. Today, we’ll be looking at the folder called “arduinoListenToMorse”.
Volume Graph
First step we’re going to take is a checking step – we’re going to make sure our microphone works exactly how it’s supposed to work. I’ll make a graph of the volume picked up by the microphone for you to see.
#define THRESHOLD (0) #define MAX_SAMPLES (5) int top=0; int samples[MAX_SAMPLES]; int si=0; int mi=0; int total=0; char *graph[11] = { "----------", "*---------", "**--------", "***-------", "****------", "*****-----", "******----", "*******---", "********--", "*********-", "**********", }; void setup() { Serial.begin(57600); for(int i=0;i<MAX_SAMPLES;++i) { samples[i]=0; } } void loop() { int volume=analogRead(0); // average the samples total -= samples[si]; samples[si] = volume; total += samples[si]; if( mi < MAX_SAMPLES ) mi++; si = (si+1) % MAX_SAMPLES; int average = total / mi; // remember the loudest sound, for scaling the graph if( top < average ) top = average; // scale the volume from threshold to top, inclusive. int x = 10.0 * (float)(average-THRESHOLD)/(float)(top-THRESHOLD); if(x<0) x=0; if(x>10) x=10; // print it out Serial.print(millis()); Serial.print('\t'); Serial.print(top); Serial.print('\t'); Serial.println(graph[x]); delay(25); }The graph comes out like this:
25 42 ********** 51 42 ********** 76 42 ********** 102 42 ********** 128 42 ********** 153 42 ********** 179 42 ********** 204 42 ********** 230 42 ********** 256 42 ********** 281 42 ********** 307 42 ********** 332 42 ********** 358 42 ********** 384 42 ********** 409 42 ********** 435 42 ********** 460 42 ********** 487 42 ********** 513 42 ********** 538 42 ********** 564 42 ********** 589 42 ********** 615 42 ********** 641 42 ********** 666 42 ********** 692 42 ********** 717 42 ********** 743 42 ********** 769 42 ********** 794 42 ********** 820 42 ********** 845 42 ********** 871 42 ********** 897 42 ********** 922 42 ********** 948 42 ********** 973 42 ********** 1000 42 ********** 1026 42 ********** 1051 42 ********** 1077 42 ********** 1102 42 ********** 1128 42 ********** 1154 42 ********** 1179 42 ********** 1205 42 ********** 1230 42 ********** 1257 42 ********** 1283 42 ********** 1308 42 ********** 1334 42 ********** 1359 42 ********** 1385 42 ********** 1411 42 ********** 1436 42 ********** 1462 42 ********** 1487 42 ********** 1514 42 ********** 1540 42 ********** 1565 42 ********** 1591 42 ********** 1616 42 ********** 1642 42 ********** 1668 42 ********** 1693 42 ********** 1719 42 ********** 1744 42 ********** 1770 42 ********** 1797 42 ********** 1822 42 ********** 1848 42 ********** 1873 42 ********** 1899 42 ********** 1925 42 ********** 1950 42 ********** 1976 42 ********** 2001 42 ********** 2027 42 ********** 2054 42 ********** 2079 42 ********** 2105 42 ********** 2130 42 ********** 2156 42 ********** 2182 42 ********** 2207 42 ********** 2233 42 ********** 2258 42 ********** 2284 42 ********** 2311 42 ********** 2336 42 ********** 2362 42 ********** 2387 42 ********** 2413 42 ********** 2439 42 ********** 2464 42 ********** 2490 42 ********** 2515 42 ********** 2541 42 ********** 2568 42 ********** 2593 42 ********** 2619 42 ********** 2644 42 ********** 2670 42 ********** 2696 42 ********** 2721 42 ********** 2747 42 ********** 2772 42 ********** 2798 42 ********** 2825 42 ********** 2850 42 ********** 2876 42 ********** 2902 42 ********** 2927 42 ********** 2953 42 ********** 2978 42 ********** 3004 42 ********** 3030 42 ********** 3055 42 ********** 3081 42 ********** 3107 42 ********** 3133 42 ********** 3159 42 ********** 3184 42 ********** 3210 42 ********** 3235 42 ********** 3261 42 ********** 3287 42 ********** 3312 42 ********** 3338 42 ********** 3364 42 ********** 3390 42 ********** 3416 42 ********** 3441 42 ********** 3467 43 ********** 3492 43 ********** 3518 43 ********** 3544 43 ********** 3569 43 ********** 3595 43 ********** 3621 43 ********** 3647 43 ********** 3673 43 ********** 3698 43 ********** 3724 43 ********** 3749 43 ********** 3775 43 ********** 3801 43 ********** 3826 43 **********If we take a look at it, it’s obvious to see that the volume in a quiet room is ~42. I’m going to set the threshold volume to 44 and sing something into the microphone.
4006 208 ---------- 4032 208 ---------- 4059 208 ---------- 4084 208 ---------- 4110 208 ---------- 4135 208 ---------- 4161 208 ---------- 4187 208 ---------- 4213 208 ---------- 4239 208 ---------- 4264 208 ---------- 4290 208 ---------- 4316 208 ---------- 4341 208 ---------- 4367 208 *--------- 4393 208 *--------- 4419 208 *--------- 4445 208 *--------- 4470 208 ---------- 4496 208 ---------- 4521 208 ---------- 4547 208 ---------- 4574 208 ---------- 4599 208 ---------- 4625 208 **-------- 4651 208 **-------- 4676 208 ***------- 4702 208 ***------- 4728 208 ***------- 4754 208 *--------- 4780 208 *--------- 4805 208 ---------- 4831 208 ---------- 4856 208 ---------- 4882 208 ---------- 4909 208 ---------- 4934 208 ---------- 4960 208 ---------- 4985 208 ---------- 5011 208 ---------- 5037 208 ---------- 5063 208 ---------- 5089 208 ---------- 5114 208 ---------- 5140 208 ---------- 5166 208 ---------- 5191 208 ---------- 5217 208 ---------- 5243 208 ---------- 5269 208 ---------- 5295 208 *****----- 5320 208 ******---- 5346 208 ******---- 5371 208 ******---- 5398 208 ********-- 5424 208 ***------- 5449 208 ******---- 5475 208 *******--- 5500 208 *******--- 5526 208 ******---- 5552 208 ******---- 5578 208 *--------- 5604 208 *--------- 5629 208 *--------- 5655 208 ---------- 5681 208 ---------- 5706 208 ---------- 5732 208 ---------- 5758 208 **-------- 5784 208 **-------- 5810 208 **-------- 5835 208 **-------- 5861 208 **-------- 5886 208 ---------- 5913 208 ---------- 5939 208 ---------- 5964 208 ---------- 5990 208 ---------- 6016 208 ---------- 6041 208 ---------- 6067 208 ---------- 6093 208 ---------- 6119 208 ---------- 6145 208 ---------- 6170 208 ---------- 6196 208 ---------- 6221 208 ---------- 6248 208 ---------- 6274 208 ---------- 6299 208 ---------- 6325 208 ***------- 6350 208 ***------- 6376 208 ***------- 6402 208 ***------- 6428 208 ***------- 6454 208 ---------- 6479 208 ---------- 6505 208 ---------- 6531 208 ---------- 6556 208 ---------- 6582 208 ---------- 6608 208 ---------- 6634 208 ---------- 6660 208 ---------- 6685 208 ---------- 6711 208 ---------- 6736 208 ---------- 6762 208 ---------- 6789 208 ---------- 6814 208 ---------- 6840 208 ---------- 6865 208 ---------- 6891 208 ---------- 6917 208 ---------- 6943 208 ---------- 6969 208 ---------- 6994 208 ---------- 7020 208 ---------- 7046 208 ---------- 7071 208 ---------- 7097 208 ---------- 7123 208 ---------- 7149 208 ---------- 7175 208 ---------- 7200 208 ---------- 7226 208 ---------- 7251 208 ---------- 7277 208 ---------- 7304 208 ---------- 7329 208 ---------- 7355 208 ---------- 7380 208 ---------- 7406 208 ---------- 7432 208 ---------- 7458 208 ---------- 7484 208 ---------- 7510 208 *--------- 7535 208 *****----- 7561 208 *****----- 7586 208 *****----- 7612 208 *****----- 7639 208 ****------ 7664 208 **-------- 7690 208 **-------- 7715 208 **-------- 7741 208 **-------- 7767 208 **-------- 7793 208 ---------- 7819 208 ***------- 7844 208 **-------- 7870 212 ********** 7896 220 ********** 7921 392 ********** 7947 412 ********** 7973 412 *********- 7999 412 ******---- 8025 412 ******---- 8050 412 *--------- 8076 412 ---------- 8101 412 ---------- 8128 412 ---------- 8154 412 ---------- 8179 412 ---------- 8205 412 ***------- 8230 412 ***------- 8256 412 **-------- 8282 412 ****------ 8308 412 ****------ 8334 412 ***------- 8359 412 ***------- 8385 412 ***------- 8411 412 ---------- 8436 412 *--------- 8462 412 ---------- 8488 412 **-------- 8514 412 **-------- 8540 412 **-------- 8565 412 ***------- 8591 412 ****------ 8616 412 *****----- 8643 412 *****----- 8669 412 *****----- 8694 412 ****------ 8720 412 ***------- 8745 412 ---------- 8771 412 ---------- 8797 412 ---------- 8823 412 ---------- 8849 412 ---------- 8875 412 *--------- 8900 412 *--------- 8926 412 *--------- 8951 412 **-------- 8978 412 **-------- 9004 412 *--------- 9029 412 *--------- 9055 412 *--------- 9080 412 ---------- 9106 412 ---------- 9132 412 ---------- 9158 412 ---------- 9184 412 ---------- 9209 412 ---------- 9235 412 ---------- 9261 412 ---------- 9286 412 ---------- 9313 412 ***------- 9338 412 ***------- 9364 412 ***------- 9390 412 ***------- 9415 412 ***------- 9441 412 ---------- 9466 412 ---------- 9493 412 ---------- 9519 412 ---------- 9544 412 ---------- 9570 412 ---------- 9595 412 ---------- 9621 412 **-------- 9648 412 ***------- 9673 412 ***------- 9699 412 **-------- 9724 412 **-------- 9750 412 **-------- 9776 412 **-------- 9801 412 ***------- 9828 412 ***------- 9853 412 ***------- 9879 412 *--------- 9905 412 *--------- 9930 412 ----------Great! I can see the volume.
Okay, so after I sang into the microphone, we got a bunch of volumes with stars and dashes, right? Yours will probably look different, but we get the same meaning behind it – which is that we can see the volume. Louder? More stars. Quieter? Less.
In my first version, the volume was high-low-high-low over and over again. The Arduino is so fast that it’s listening to the silence between pulses that our ears can’t even detect. Picture this – how do you tell the Arduino to “listen for the lengths of a beep” if the Arduino thinks “each and every beep is actually a lot of super short pulses bunched together”?
Let’s figure out stuff regarding the volume, first. To get around this I tried to smooth the input by averaging several samples in a row. We’re going to hit File > Examples > Analog > Smoothing. It makes nice “waves” in the graph that has no interruptions.
Off and On
Okay, let’s build a bit more into the code. I’d like the Arduino to report when there is and isn’t a beep. So, I’m going to wrap the output every 100 characters so that the Serial Monitor is still readable as the data is coming in.
// use a microphone to listen for morse code, then decypher the // message. #define THRESHOLD (44) #define MAX_SAMPLES (5) int top=0; int samples[MAX_SAMPLES]; int si=0; int mi=0; int total=0; int c=0; void setup() { Serial.begin(57600); for(int i=0;i<MAX_SAMPLES;++i) { samples[i]=0; } } void loop() { int volume=analogRead(0); total -= samples[si]; samples[si] = volume; total += samples[si]; if( mi < MAX_SAMPLES ) mi++; si = (si+1) % MAX_SAMPLES; int average = total / mi; if( top < average ) top = average; int x = 10.0 * (float)(average-THRESHOLD)/(float)(top-THRESHOLD); if(x<0) x=0; if(x>10) x=10; if(x>1) Serial.print('*'); else Serial.print(' '); c++; if(c>100) { // wrap c=0; Serial.println(); } delay(5); }When I play the Morse code into the microphone, this is what the output gave:
***************************************************************************************************** ***************************************************************************************************** ***************************************************************************************************** ************************************************************** ************ * ***** ********** // these three short beeps are when the sending Arduino reset, because // the speaker is on pin 13. // there is a brief silence and then the message begins. ********************************************************* ** ********************* ***** *************** * ******************** *********************** ******************** ** ********************************************************** ********************* ************************************************************ ************ ********* ************************************* *********************** ********************************************************* ********************************************************** ****************** ****************** ********************************************************* ************************************************************* ****** *************** ******************************* ****************************** ******************** **************** ****************************************** ********************* ********************* ********************* ********************* ************ ********************************************** ********************* ******************** ************************************************ ********** ********************************************************** ************************************************************ ********************* ******************** ****** **************************************************** ******************** ********************* ****************************************** **************** ********************* ********************* *********************** ********************************************************* ** ********************* ********************************************************* ********************* ********** ************************************************ ******************** ********************************************************** ********************* ********************* ********* ************ ******************** ****************** ** ********************* ******************** *********************** ****************************** ****************************** ***************** **** ******************** ********************* ************************************************************ ************ ******** ******************** *********************** *********************** ************************************* ********************* * ******** ************************************************* ********************* ******************** ******************* * ********************************************* *************** ******************** ********************* ******************** ************************************************************ **** ***************** ******************** ******************** **************************************************** ******** ******************** ************************************* ******************** *********************** ************************* ***********************************Okay, wow. A load of stuff that I can’t understand. What’s important to note here is that there are distinct sections of short and long. Let’s ‘clean it up in a text editor.
I copied everything into Sublime Text 2 and immediately took off the starting noise – the large block of stars before the message actually begins. I used regular expressions to find and delete all “\n” characters, then replaced all []+ with “\n”. We end up with a much more organized (albeit still unreadable) text:
*********************************************************** ********************* ******************** * ******************** *********************** ******************** ************************************************************ ********************* ************************************************************ ********************* ************************************************************ ********************************************************* ********************************************************** ****************** ****************** ********************************************************* ************************************************************* ********************* ************************************************************* ******************** ********************************************************** ********************* ********************* ********************* ********************* ********************************************************** ********************* ******************** ********************************************************** ********************************************************** ************************************************************ ********************* ******************** ********************************************************** ******************** ********************* ********************************************************** ********************* ********************* *********************** ********************************************************* *********************** ********************************************************* ********************* ********************************************************** ******************** ********************************************************** ********************* ********************* ********************* ******************** ******************** ********************* ******************** *********************** ************************************************************ ********************* ******************** ********************* ************************************************************ ******************** ******************** *********************** ************************************************************ ********************* * ********************************************************* ********************* ******************** ******************** ************************************************************ ******************** ********************* ******************** ************************************************************ ********************* ******************** ******************** ************************************************************ ******************** ********************************************************* *********************** ************************************************************Check out our code again. We see that the delay between each stars is 5ms. Taking a look at our text, we see that the longer lines are about 60 stars – 300ms. A shorter line is about 20-22 stars. Let’s say 100ms. This pretty much matches what I’d expect according to the standard for Morse code, and it also tells me how fast the transmitter is transmitting.
There appear to be some odd outliers, such as 1 star lines, so let’s ignore all lines that are less than, say, four stars. So, I delete all 4 star or lower lines, then used regular expressions to replace [*]{17} with “~”. I’m going to delete all the rest of the [*]s. Then, I’m going to replace all “~~~” with “-”. Replace remaining “~”s with “.”. Delete the rest of the “\n” characters.
Okay! We’ve managed to condense the long text into the following:
-…..-.-.—..–.-.-….-..—..-..-…-.-.-.-……..-…-…-.-…-…-…-.-.-
Dashes and dots. Morse code! Now, we have to figure out where the letters and words are – or more specifically, where each one starts and ends.
Automaton
Let’s go through the whole manual process and make it automatic. Programming!
#define THRESHOLD (44) #define MAX_SAMPLES (5) #define BAUD (100.0) #define WAIT (5.0) #define AVG_SHORT (BAUD*1.0/WAIT) #define AVG_LONG (BAUD*3.0/WAIT) #define AVG_NEWWORD (BAUD*7.0/WAIT) #define MINIMUM (AVG_SHORT/4.0) int top=0; int samples[MAX_SAMPLES]; int si=0; int mi=0; int total=0; int c=0; int is_on=0; void setup() { Serial.begin(57600); for(int i=0;i<MAX_SAMPLES;++i) { samples[i]=0; } } void loop() { findOnOffEvent(); } void findOnOffEvent() { int volume=analogRead(0); total -= samples[si]; samples[si] = volume; total += samples[si]; if( mi < MAX_SAMPLES ) mi++; si = (si+1) % MAX_SAMPLES; int average = total / mi; if( top < average ) top = average; int x = 10.0 * (float)(average-THRESHOLD)/(float)(top-THRESHOLD); if(x<0) x=0; if(x>10) x=10; if(x>1) { // noise! if(is_on==0) { // noise has just started. if( c > MINIMUM ) { // Was the silence a new word or a new letter? if( c > (AVG_NEWWORD+AVG_SHORT)/2.0 ) { Serial.println(); } else if( c > (AVG_LONG+AVG_SHORT)/2.0 ) { Serial.print(' '); } } // remember noise started is_on=1; c=0; } } else { // silence! if(is_on==1) { // silence is new if( c > MINIMUM ) { // Was the noise a long or a short? if( c > (AVG_LONG + AVG_SHORT)/2.0 ) { Serial.print('-'); } else { Serial.print('.'); } } // remember silence started is_on=0; c=0; } } c++; delay(WAIT); }This code splits everything up, so we get this output when we play it:
- .... . -.-. --- ..- -. -.-. .. .-.. --- ..-. .-. .. -.-. -.- ... .... .- ... -.. . -.-. .. -.. . -.. .-.-.-Check it by hand! Do we get the same thing after we decode it?
Translator
Finally, I think I have everything in place and I can turn dashes and dots back into english letters.
#define THRESHOLD (44) #define MAX_SAMPLES (5) #define BAUD (100.0) #define WAIT (5.0) #define AVG_LONG (BAUD*3.0/WAIT) #define AVG_SHORT (BAUD*1.0/WAIT) #define AVG_NEWWORD (BAUD*7.0/WAIT) #define MINIMUM (AVG_SHORT/4.0) #define MAX_PATTERN (64) #define NUM_CODES (54) // 0 10 20 30 40 50 // 0123456789012345678901234567890123456789012345678901234 static const char *letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,?'!/()&:;=+-_\"$@"; // each code represents a character. // 1 represents a dot and 0 represents a dash. // the order of these codes matches the order of the characters in // *letters. static const char *codes[NUM_CODES] = { "10", // A, codes[0] "0111", // B, codes[1] "0101", // C "011", // D "1", // E "1101", // F "001", // G "1111", // H "11", // I "1000", // J "010", // K "1011", // L "00", // M "01", // N "000", // O "1001", // P "0010", // Q "101", // R "111", // S "0", // T "110", // U "1110", // V "100", // w "0110", // x "0100", // y "0011", // z "00000", // 0 "10000", // 1 "11000", // 2 "11100", // 3 "11110", // 4 "11111", // 5 "01111", // 6 "00111", // 7 "00011", // 8 "00001", // 9 "101010", // . "001100", // , "110011", // ? "100001", // ' "010100", // ! "01101", // / "01001", // ( "010010", // ) "10111", // & "000111", // : "010101", // ; "01110", // = "10101", // + "01110", // - "110010", // _ "101101", // " "1110110", // $ "100101", // @, codes[54] }; int top=0; int samples[MAX_SAMPLES]; int si=0; int mi=0; int total=0; int c=0; int is_on=0; char pattern[MAX_PATTERN]; int pi=0; void setup() { Serial.begin(57600); for(int i=0;i<MAX_SAMPLES;++i) { samples[i]=0; } for(int i=0;i<MAX_PATTERN;++i) { pattern[i]=0; } } void loop() { int volume=analogRead(0); total -= samples[si]; samples[si] = volume; total += samples[si]; if( mi < MAX_SAMPLES ) mi++; si = (si+1) % MAX_SAMPLES; int average = total / mi; if( top < average ) top = average; int x = 10.0 * (float)(average-THRESHOLD)/(float)(top-THRESHOLD); if(x<0) x=0; if(x>10) x=10; if(x>1) { // noise! if(is_on==0) { // noise has just started. if( c > MINIMUM ) { // Was the silence a new word or a new letter? if( c > (AVG_NEWWORD+AVG_SHORT)/2.0 ) { pattern[pi]=0; findLetter(); // new word, extra \n Serial.println(); // start counting - and . all over again. pi=0; } else if( c > (AVG_LONG+AVG_SHORT)/2.0 ) { pattern[pi]=0; findLetter(); // start counting - and . all over again. pi=0; } } // remember noise started is_on=1; c=0; } } else { // silence! if(is_on==1) { // silence is new if( c > MINIMUM ) { // Was the noise a long or a short? if( c > (AVG_LONG + AVG_SHORT)/2.0 ) { // long Serial.print('-'); pattern[pi++]='0'; } else { // short Serial.print('.'); pattern[pi++]='1'; } } // remember silence started is_on=0; c=0; } } c++; delay(WAIT); } // pattern contains the received longs and shorts, // saved as 1s and 0s. Find the matching code in the list // then find the matching printable character. // print '?' if nothing is found void findLetter() { int i,j; // go through all the codes for(i=0;i<NUM_CODES;i++) { // check if code[i] matches pattern exactly. if(strlen(pattern) == strlen(codes[i]) && strcmp(pattern,codes[i])==0) { // match! Serial.print(' '); Serial.println(letters[i]); return; } } Serial.print('?'); }We’ve got everything split up, so let’s take a look at the final output.
- T .... H . E -.-. C --- O ..- U -. N -.-. C .. I .-.. L --- O ..-. F .-. R .. I -.-. C -.- K ... S .... H .- A ... S -.. D . E -.-. C .. I -.. D . E -.. D .-.-.- .I’m really enjoyed the first season of Rick & Morty. I hope the rest are at least as strange.
Questions
- Want to seriously challenge yourself? use a large sample rate and no smoothing to find the frequency of each beep. Build an electric tuning fork for a friend’s piano!
I have a question, the distance between lines on existing graphvolume for how many seconds / ms ?,
I see the code
In Sketch 1…
“delay (25);
} [/ Code]”
In Sketch 2…
“delay (5);
} [/ Code]”
In Sketch 3…
“delay (WAIT);
} [/ Code]”
“if it could set the distance between lines on the graph through the code?