mdraber
Published © GPL3+

How to build Arduino Laser Morse Code Decoder

In past project I was only transmitting Morse Code. Now I can also read it.

IntermediateFull instructions provided9 hours1,712
How to build Arduino Laser Morse Code Decoder

Things used in this project

Hardware components

Arduino Nano R3
Arduino Nano R3
×1
0.96" OLED 64x128 Display Module
ElectroPeak 0.96" OLED 64x128 Display Module
×2
Laser transmitting module
×1
Laser Sensor Module
×1
5 key keyboard module
×1
MOSFET Transistor, Switching
MOSFET Transistor, Switching
×1

Story

Read more

Schematics

Diagrams for the Transmitter and Receiver

Transmitter -laser powered from external power supply

Code

Laser Morse code transmitter - simplified version

Arduino
It sends text implicitly provided in the code. In this example it sends the whole alfabeth with Unit length 300ms
#define Laser_Pin 2
unsigned long Start; 
unsigned long End; 
unsigned long Signal_Len=0; 
unsigned long Pause_Len=0; 
void setup() { 
  Serial.begin(9600);  
  pinMode(Laser_Pin,INPUT); 
  attachInterrupt(digitalPinToInterrupt(2), Signal_change,CHANGE); 
} 
void Signal_change(){ 
  if(digitalRead(Laser_Pin)==1) {
    Start=millis(); 
    Pause_Len=Start-End;
  }  
  else { 
      
    End=millis(); 
    Signal_Len=End-Start; 
  } 
} 


void loop() { 
  if (Signal_Len>0){ 
    Serial.print("Signal Start:"); 
    Serial.print(Start); 
    Serial.print(" Signal End:"); 
    Serial.print(End); 
    Serial.print(" Signal Duration:"); 
    Serial.println(Signal_Len); 
    Signal_Len=0; 
  }  
    if (Pause_Len>0){ 
    Serial.print("Pause Start:"); 
    Serial.print(Start); 
    Serial.print(" Pause End:"); 
    Serial.print(End); 
    Serial.print(" Pause Duration:"); 
    Serial.println(Pause_Len); 
    Pause_Len=0; 
  }  
}

Laser Receiver - Recognising lenth of laser signals and pause durations

Arduino
Connect the Laser Receiver module to Arduino D2
Load this code and open the serial monitor.
Whenever the sensor detect laser signal it will output its length. It also measures the pauses inbetween the signals
#define Laser_Pin 2
unsigned long Start; 
unsigned long End; 
unsigned long Signal_Len=0; 
unsigned long Pause_Len=0; 
void setup() { 
  Serial.begin(9600);  
  pinMode(Laser_Pin,INPUT); 
  attachInterrupt(digitalPinToInterrupt(2), Signal_change,CHANGE); 
} 
void Signal_change(){ 
  if(digitalRead(Laser_Pin)==1) {
    Start=millis(); 
    Pause_Len=Start-End;
  }  
  else { 
      
    End=millis(); 
    Signal_Len=End-Start; 
  } 
} 

void loop() { 
  if (Signal_Len>0){ 
    Serial.print("Signal Start:"); 
    Serial.print(Start); 
    Serial.print(" Signal End:"); 
    Serial.print(End); 
    Serial.print(" Signal Duration:"); 
    Serial.println(Signal_Len); 
    Signal_Len=0; 
  }  
    if (Pause_Len>0){ 
    Serial.print("Pause Start:"); 
    Serial.print(Start); 
    Serial.print(" Pause End:"); 
    Serial.print(End); 
    Serial.print(" Pause Duration:"); 
    Serial.println(Pause_Len); 
    Pause_Len=0; 
  }  
}

Laser Code transmittor -full version

Arduino
In this version you have text input functionality.
It requires OLED display connected to I2C ports A4 and A5.
And 5 key keybord connected to A2
Check how the functionality works in the Youtube videos linked in the project story
// Mario's Ideas
// Text input using OLED display an 5 Key Keyboard and encoding it to Morse Code

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// Oled display size
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels



// Array holding all Morse code letter dot dash combinations
char MorseCode[26][6] = {
  {'.','-','x','x','x','A'},
  {'-','.','.','.','x','B'},
  {'-','.','-','.','x','C'},
  {'-','.','.','x','x','D'},
  {'.','x','x','x','x','E'},
  {'.','.','-','.','x','F'},
  {'-','-','.','x','x','G'},
  {'.','.','.','.','x','H'},
  {'.','.','x','x','x','I'},
  {'.','-','-','-','x','J'},
  {'-','.','-','x','x','K'},
  {'.','-','.','.','x','L'},
  {'-','-','x','x','x','M'},
  {'-','.','x','x','x','N'},
  {'-','-','-','x','x','O'},
  {'.','-','-','.','x','P'},
  {'-','-','.','-','x','Q'},
  {'.','-','.','x','x','R'},
  {'.','.','.','x','x','S'},
  {'-','x','x','x','x','T'},
  {'.','.','-','x','x','U'},
  {'.','-','-','x','x','W'},
  {'.','.','.','-','x','V'},
  {'.','-','-','.','x','X'},
  {'-','.','-','-','x','Y'},
  {'-','-','.','.','x','Z'},
  };

//Potentiometer PIN A1
  int Keyboard=A2;

// Variables capturing current and newly calculated position on the letter board (9x3 - 27 postions)
  int New_X=0;
  int Old_X=0;
  int New_Y=0;
  int Old_Y=0;
  
// Variable capturing output from Keyboard pin (Values 0 1023)
  int Key_read=0;
  int Prev_Key_read=1023;
  boolean Key_pressed=false; 

// String variable holding the text to transmit
  String To_Transmit="";

//Unit length
  int unit=300;

// Length of the text to transmit
  int To_Transmit_Length=0;


// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
  #define OLED_RESET     4 // Reset pin # (or -1 if sharing Arduino reset pin)
  Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);


// Used for displaying Leter board
char Letters[3][9]={"ABCDEFGHI",
                    "JKLMNOPQR",
                    "STUVWXYZ_"};


// Play/Display Morse code representation of the letter
void Play_Letter (char Letter){
  // searching in MorseCode array for the corresponding letter
  if (Letter=='_') delay(5*unit); else {
    for (int j=0; j<26; j++){
      if (MorseCode[j][5]==Letter)
        // if the right letter is detected run Play_Dot_Dash for . or -
        for (int k=0; k<4;k++){
          if (MorseCode[j][k]!='x') Play_Dot_Dash(MorseCode[j][k]);
        }
    }
    delay(2*unit);
  }
}

// Playing/Displaying . or -

void Play_Dot_Dash(char sign){
  if (sign=='.'){
    analogWrite(A3, 255);
    delay(unit);
    analogWrite(A3,0);
    delay(unit);
  }
  if (sign=='-'){
    analogWrite(A3, 255);
    delay(3*unit);
    analogWrite(A3, 0);
    delay(unit);
  }
}

void setup() {
  Serial.begin(9600);
  
  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }

  // Show initial display buffer contents on the screen --
  // the library initializes this with an Adafruit splash screen.
  display.display();
  delay(2000); // Pause for 2 seconds
  
  // Clear the buffer
  display.clearDisplay();
  display.display();
  
  // Display filled in rect in the top section of the display when To_Transfer would be output
  display.fillRect(0, 0, 128, 15, SSD1306_INVERSE);
  display.drawRect(110, 2, 16, 12, SSD1306_BLACK);
  display.setTextSize(1);            
  display.setTextColor(SSD1306_BLACK);  
  display.setCursor(113,4); 
  display.println("OK");
  display.display();
  // Display Letter Board 3 rows 9 character in each row 
  display.setTextSize(2);            
  display.setTextColor(SSD1306_WHITE);       
  for (int j=0; j<3;j++){
    for (int i=0; i<9;i++){
       display.setCursor(i*12+2*i+1,j*16+17);           
       display.println(Letters[j][i]);
       delay(10);
     display.display();
    }
  }

  // Highlight character A by displaying Inverse rect at first position
  display.fillRect(0, 16, 12, 16, SSD1306_INVERSE);
  display.display();
  
}

void Highlight_letter(int X, int X_Old, int Y, int Y_Old){
  // When position changes 
  // Draw the inverse rect in the Old_pos to deactivate  the highlight in the old spot
  // Draw the inverse rect to Highlite the new spot

  // Displaying Inverse rect in a new position to highlight
  display.fillRect(X*12+2*X, Y*16 +16, 12, 16, SSD1306_INVERSE);
  
  // Displaying Inverse rect in the old positon to unhighlight
  display.fillRect(X_Old*12+2*X_Old, Y_Old*16 +16, 12, 16, SSD1306_INVERSE);
  
  display.display();
}


void loop() {
  Key_read =analogRead(Keyboard);
  if (Prev_Key_read>1000 and Key_read<1000){
    Key_pressed=true;
    if (Key_read<10 and Old_X>0) New_X=Old_X-1; 
    if (Key_read>160 and Key_read<170 and Old_X<9) New_X=Old_X+1;
    if (Key_read>25 and Key_read<35 and Old_Y>-1) New_Y=Old_Y-1;
    if (Key_read>80 and Key_read<90 and  Old_Y<2 ) New_Y=Old_Y+1;
    if (Key_read>350 and Key_read<360) {
     if (New_Y!=-1){
       To_Transmit=To_Transmit + Letters[New_Y][New_X];
       To_Transmit_Length++;
       display.setTextSize(1);
       display.setCursor(3,1);
       display.setTextColor(BLACK     );
       display.fillRect(0, 0, 100, 15, SSD1306_WHITE);
       display.println(To_Transmit);
       display.display();
     }
     else{   
       for (int i=0; i<To_Transmit.length();i++ ){
         Play_Letter(To_Transmit.charAt(i));
       }
      }
     }     

    if (New_Y==-1 and Old_Y==0){
        display.fillRect(110, 2, 16, 12, SSD1306_INVERSE); 
        display.fillRect(Old_X*12+2*Old_X, Old_Y*16 +16, 12, 16, SSD1306_INVERSE); 
    }
    if (New_Y==0 and Old_Y==-1){
        display.fillRect(110, 2, 16, 12, SSD1306_INVERSE); 
        display.fillRect(New_X*12+2*New_X, New_Y*16 +16, 12, 16, SSD1306_INVERSE);
        Prev_Key_read=Key_read;
        Old_X=New_X;
        Old_Y=New_Y;;
    }
    if ((Old_X!=New_X or Old_Y!=New_Y) and Old_Y!=-1 ){
        if (New_Y!=-1 )Highlight_letter (New_X,Old_X,New_Y,Old_Y);
        Old_X=New_X;
        Old_Y=New_Y;
    }
  }
  
  display.display();
  Prev_Key_read=Key_read; 
      
}

Laser Morse Code Receiverv - simplified verison

Arduino
It receognises signals decodes them to text and displays the result in Serial Monitor
String rcvd="";
String Message="";
int tol=10; 
int unit=300;
#define LaserPin 2

unsigned long Lastrun=0;

unsigned long Start; 
unsigned long End; 
unsigned long Signal_Len=0; 
unsigned long Pause_Len=0; 

boolean TransferInProgress=false;

struct MorseCodeSignal{
  char Letter; 
  String Signal;
};

MorseCodeSignal MorseCode[26] = {
  {'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',"--.."}
  };


void setup() {
  Serial.begin(9600);
  attachInterrupt(digitalPinToInterrupt(2), Signal_change,CHANGE); 
}

void Signal_change(){ 
  if (millis()-Lastrun>10){
  if(digitalRead(LaserPin)==1) {
    Start=millis(); 
    Pause_Len=Start-End;
    if (TransferInProgress==false) {TransferInProgress=true;rcvd="";}
  }   
  else { 
    End=millis(); 
    Signal_Len=End-Start; 
  } 
 
  Lastrun=millis();
  }
} 

char Match_Letter(String input){
  char Matched_letter;
  for (int i=0;i<26;i++){
    if(MorseCode[i].Signal==rcvd) Matched_letter=MorseCode[i].Letter;
  }
  return Matched_letter;
}

void loop() {

 if (TransferInProgress){
   if (Signal_Len>0){ 
      if(Signal_Len>=(unit- unit/tol) and Signal_Len<=(unit+unit/tol)) {rcvd+='.';} 
      if(Signal_Len>=(3*unit- unit/tol) and Signal_Len<=(3*unit+unit/tol)) rcvd+='-'; 
     // if(Signal_Len<(unit- unit*tolerance/100) or (Signal_Len>(unit+unit*tolerance/100)and Signal_Len<(3*unit- unit*tolerance/100)) or Signal_Len>(3*unit+unit*tolerance/100) )
      
      Signal_Len=0; 
    }  
    if (Pause_Len>0){  
      
      if(Pause_Len>(unit- unit/tol) and Signal_Len<(unit+unit/tol)) {;}
      if(Pause_Len>(3*unit- unit/tol) and Signal_Len<(3*unit+unit/tol)) {Message+=Match_Letter(rcvd);Serial.print(Match_Letter(rcvd));Serial.print(rcvd);rcvd="";}
      //if(Pause_Len>(5*unit- unit*tolerance/100) and Signal_Len<(5*unit+unit*tolerance/100)) {Message+=Match_Letter(rcvd);Message+=" ";Serial.print(Message);Serial.println(rcvd);rcvd="";}
      Pause_Len=0;
    }  
    if((millis()-End)>8*unit and TransferInProgress){Serial.print(Match_Letter(rcvd));Serial.println(rcvd);Message+=Match_Letter(rcvd);Serial.println(Message);rcvd="";TransferInProgress=false;Message="";}
 }
}

Laser Morse Code Receiver - Full version

Arduino
It reads Laser Signals , decodes then into text and displays the result on OLED display
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#include <avr/pgmspace.h>

const char alfabeth[] PROGMEM = {"ABCDEFGHIJKLMNOPQRSTUVWXYZ"};

const char morse_A[] PROGMEM = ".-";
const char morse_B[] PROGMEM = "-...";
const char morse_C[] PROGMEM = "-.-.";
const char morse_D[] PROGMEM = "-..";
const char morse_E[] PROGMEM = ".";
const char morse_F[] PROGMEM = "..-.";
const char morse_G[] PROGMEM = "--.";
const char morse_H[] PROGMEM = "....";
const char morse_I[] PROGMEM = "..";
const char morse_J[] PROGMEM = ".---";
const char morse_K[] PROGMEM = "-.-";
const char morse_L[] PROGMEM = ".-..";
const char morse_M[] PROGMEM = "--";
const char morse_N[] PROGMEM = "-.";
const char morse_O[] PROGMEM = "---";
const char morse_P[] PROGMEM = ".--.";
const char morse_Q[] PROGMEM = "--.-";
const char morse_R[] PROGMEM = ".-.";
const char morse_S[] PROGMEM = "...";
const char morse_T[] PROGMEM = "-";
const char morse_U[] PROGMEM = "..-";
const char morse_V[] PROGMEM = "...-";
const char morse_W[] PROGMEM = ".--";
const char morse_X[] PROGMEM = "-..-";
const char morse_Y[] PROGMEM = "-.--";
const char morse_Z[] PROGMEM = "--..";

const char *const morse_code[] PROGMEM = {morse_A, morse_B, morse_C, morse_D, morse_E, morse_F,
                                          morse_G, morse_H, morse_I, morse_J, morse_K, morse_L,
                                          morse_M, morse_N, morse_O, morse_P, morse_Q, morse_R,
                                          morse_S, morse_T, morse_U, morse_V, morse_W, morse_X,
                                          morse_Y, morse_Z};

char buffer[5];
#define OLED_RESET     -1// Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(128, 64, &Wire, OLED_RESET);

String rcvd="";
String Message="";
char Matched_Letter;
int tol=10; 
int unit=300;
#define LaserPin 2
int letter_index=-1;

unsigned long Lastrun=0;

unsigned long Start; 
unsigned long End; 
unsigned long Signal_Len=0; 
unsigned long Pause_Len=0; 

boolean TransferInProgress=false;

struct MorseCodeSignal{
  char Letter; 
  String Signal;
};




void setup() {
  Serial.begin(9600);
  attachInterrupt(digitalPinToInterrupt(2), Signal_change,CHANGE); 
  
   // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }
 
  display.display();
  delay(2000); // Pause for 2 seconds

    // Clear the buffer
  display.clearDisplay();
   display.fillRect(0,0,128,16,SSD1306_WHITE);
   

  display.display();
}

void DrawSequence(int index, String instr){
  int offset_X;
  int offset_Y;
  offset_X=9+int(index/6)*43;
  offset_Y=17+(index-int(index/6)*6)*8;
  display.fillRect(offset_X-2,offset_Y,2,6,SSD1306_WHITE);
  for (int i=0; i<instr.length();i++ ){
    if(instr.charAt(i)=='.'){
      display.fillRect(offset_X,offset_Y,4,6,SSD1306_WHITE);
      display.fillRect(offset_X,offset_Y+2,2,2,SSD1306_BLACK);
      offset_X+=4;
    }
    if(instr.charAt(i)=='-'){
      display.fillRect(offset_X,offset_Y,8,6,SSD1306_WHITE);
      display.fillRect(offset_X,offset_Y+2,6,2,SSD1306_BLACK);
      offset_X+=8;
    }
  }
  display.display();
}

void DrawLetter(int index, char Letter){
  int offset_X;
  int offset_Y;
  offset_X=int(index/6)*43;
  offset_Y=16+(index-int(index/6)*6)*8;
  display.setCursor(offset_X,offset_Y); 
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.println(Letter);  
  display.display();
}

void DrawMessage(String TextIn){
  if (TextIn.length()>0){
    display.fillRect(0,0,128,16,SSD1306_WHITE);
    display.setCursor(3,1); 
    display.setTextSize(2);
    display.setTextColor(SSD1306_BLACK);
    display.println(TextIn);
    display.display();
  }
}

void Signal_change(){ 
  if (millis()-Lastrun>10){
  if(digitalRead(LaserPin)==1) {
    Start=millis(); 
    Pause_Len=Start-End;
    if (TransferInProgress==false) {TransferInProgress=true;rcvd="";}
  }   
  else { 
    End=millis(); 
    Signal_Len=End-Start; 
  } 
 
  Lastrun=millis();
  }
} 

char Match_Letter(String input){
  char Matched_letter;  
  String Sequence;
  for (int i = 0; i < 26; i++) {
    strcpy_P(buffer, (char *)pgm_read_word(&(morse_code[i])));  // Necessary casts and dereferencing, just copy.
    Sequence=buffer;
    if (Sequence==input) Matched_letter=pgm_read_byte_near(alfabeth+i);
  }
  return Matched_letter;
}

void loop() {

 if (TransferInProgress){
   if (Signal_Len>0){ 
      if(Signal_Len>=(unit- unit/tol) and Signal_Len<=(unit+unit/tol)) {rcvd+='.';DrawSequence(letter_index,rcvd);} 
      if(Signal_Len>=(3*unit- unit/tol) and Signal_Len<=(3*unit+unit/tol)) {rcvd+='-';DrawSequence(letter_index,rcvd);}; 
     // if(Signal_Len<(unit- unit*tolerance/100) or (Signal_Len>(unit+unit*tolerance/100)and Signal_Len<(3*unit- unit*tolerance/100)) or Signal_Len>(3*unit+unit*tolerance/100) )
      
      Signal_Len=0; 
    }  
    if (Pause_Len>0){  
      
      if(Pause_Len>(unit- unit/tol) and Signal_Len<(unit+unit/tol)) {;}
      if(Pause_Len>(3*unit- unit/tol) and Signal_Len<(3*unit+unit/tol)) {
        Matched_Letter=Match_Letter(rcvd);
        Message+=Matched_Letter;
        DrawMessage(Message);
        Serial.print(Matched_Letter);
        DrawLetter(letter_index,Matched_Letter);
        display.display();
        Serial.println(rcvd);
        rcvd="";
        letter_index++;
        Matched_Letter="";
      }
      Pause_Len=0;
    }  
    if((millis()-End)>8*unit and TransferInProgress){
        Matched_Letter=Match_Letter(rcvd);
        Message+=Matched_Letter;
        DrawMessage(Message);
        display.display();
        Serial.print(Matched_Letter);
        DrawLetter(letter_index,Matched_Letter);
        display.display();
        Serial.println(rcvd);
        rcvd="";
        //letter_index=0;
        TransferInProgress=false;
        Message="";
        Pause_Len=0;
      }
 }
}

Credits

mdraber

mdraber

49 projects • 68 followers

Comments