Skip to main content Link Search Menu Expand Document (external link)

Embedded Programming – Morse Code Decoder

Table of contents
  1. Embedded Programming – Morse Code Decoder
    1. Group Assignment for Embedded Programming
    2. Programming Designed Board
      1. Button Debouncing
    3. Morse Code Decoder
      1. Code Snippets

Group Assignment for Embedded Programming

compare the performance and development workflows for other architectures.

shown in Week 6 of EECS Group Assignments.

Programming Designed Board

As shown in the Testing and Debugging section of Week 4 | PCB Design, I already used the Atmel JTAG debugger to burned bootloader onto the designed coin-sized programmable board. I also tried the serial port communication demo, and the basic interaction of the LED and button.

The only hardware modification I made when debugging this is replacing the wrong 1MΩ (1004) resistor with the corret smaller one 1kΩ (1001), as shown in the photo below.

image
Board with wrong resistor (1MΩ or 1004)
image
Fixed board with correct resistor (1kΩ or 1001)

One thing I learned (or recalled to be exact) from this mistake is that the SMD resistor code follows scientific notation, where the last digit is the order of magnitude, and the previous ones are significant digits. For example,

\[\bm{100{\color{blue} 4}} = 100\times 10^{\color{blue} 4} = 1\text{M}\Omega \\ \bm{100{\color{blue} 1}} = 100\times 10^{\color{blue} 1} = 1\text{K}\Omega\]

Button Debouncing

The software modification I made is that I added debounce to the button with referencing to Arduino debounce doc, as shown in the code snippet below.

led_flip_button_debounced.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// led_flip_button_debounced.ino
const int pin_led = 4; // Pin of the LED
const int pin_btn = 2; // Pin of the button
int led_curr = HIGH; // current status of the LED
int btn_curr;        // current status of the button
int btn_prev = HIGH; // previous status of the button
// debounce variables | borrowed from https://www.arduino.cc/en/Tutorial/BuiltInExamples/Debounce
unsigned long lastDebounceTime =  0; // last time the output was toggled
unsigned long debounceDelay    = 80; // debounce delay time in ms [originally 50 ms; increased to improve robustness to noise]
void setup() {
  pinMode(pin_led, OUTPUT);
  pinMode(pin_btn, INPUT);
  digitalWrite(pin_led, led_curr); // initial LED status
}
void flip_led() {
  led_curr = digitalRead(pin_led);
  digitalWrite(pin_led, !led_curr);
}
bool button_pressed() {
  int pressed = false;
  int val = digitalRead(pin_btn);
  if (val != btn_prev){ // start to record debounce time [ms]
    lastDebounceTime = millis();
  }
  // only if recorded debounce time exceeds the threshold, and
  // button switched from HIGH to LOW, button is pressed
  if( (lastDebounceTime - millis()) > debounceDelay ) {
    if (val != btn_curr) {
      btn_curr = val;
      if (btn_curr == LOW) {
        pressed = true;
      }
    }
  }
  btn_prev = val;
  return pressed;
}

void loop() {
  if (button_pressed()) {
    flip_led();
  }
}

Morse Code Decoder

Since I already did a basic switch interaction between the LED and button, I decided to make a Morse code decoder. The idea is to use the button as the Morse code input, and the microchip will get corresponding decoded character and output in the serial port. The LED is used to indicate the status of the button input.

The programmed board in action is shown in the image and video below.
image

The user inputs the Morse code of “HELLOWORLD”, and the serial port outputs the decoded characters.

As you can see that the button is still really challenging to control in practice. This might becuase the button is too small, and the user has to press it really hard to get a good signal, which causes noise with different distributions. This would remain as future work to improve the user experience.

From this practice, I learned that UI/UX design is super non-trivial, especially when the user is interacting with the hardware directly.

Update in Week 8 [Nov 15, 2022]

After using the internal pull-up resistor, that is replacing Line #18 of the code with pinMode(pin_btn, INPUT_PULLUP); as suggested by Neil. The button is super easy to use now and Morse Code Decoder is really robust.

Code Snippets

morsecode.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
// morsecode.ino
const int pin_led = 4; // pin of the LED
const int pin_btn = 2; // pin of the button
int ledState = HIGH;         
int buttonState = HIGH;             
int lastButtonState = HIGH;  
const long debounceDelay = 100; // debounce delay time in ms [originally 50 ms; increased to improve robustness to noise]
const long pauseInterval = 350;  // pause interval [ms]
long signalLength =   0;  // length of the signal
long pauseTime    =   0;  // pause time
const String dot  = "·";
const String dash = "—";
String morse = "";
bool newChar = false;
bool newLine = false;
void setup() {
  Serial.begin(0);
  pinMode(pin_btn, INPUT);
  pinMode(pin_led, OUTPUT);
  while(digitalRead(pin_btn));
  Serial.println("Morse Code Decoder:");
}
void loop() {
  buttonState = digitalRead(pin_btn);
  if (!buttonState && !lastButtonState) { // button pressing status - gathering signal
    ++signalLength; 
  }
  else if(buttonState && !lastButtonState){ // button released - send signal (dot or dash)
    if (signalLength>debounceDelay && signalLength<2*pauseInterval) {
      morse = morse + dot;
    } 
    else if (signalLength>2*pauseInterval) {
      morse = morse + dash;
    }
    signalLength = 0; 
    digitalWrite(pin_led, LOW); 
  }
  else if (!buttonState && lastButtonState) { // button just pressed - reset signal
    pauseTime = 0;
    digitalWrite(pin_led, HIGH);  
    newChar = true;
    newLine = true;
  }
  else if (buttonState && lastButtonState) {  
    ++pauseTime;
    if (( pauseTime>4*pauseInterval ) && (newChar)) { 
      decoder(morse);
      newChar = false;
      morse = "";
    }
    if ((pauseTime>20*pauseInterval) && (newLine)) {
      Serial.println();
      newLine = false;
    }
  }
  lastButtonState = buttonState;
  delay(1);
}
void decoder(String message)   {                                 
  if (message == "·—")
    Serial.print("A");
  else if (message == "—···")  
    Serial.print("B");
  else if (message == "—·—·")  
    Serial.print("C");
  else if (message == "—··")  
    Serial.print("D");
  else if (message == "·")  
    Serial.print("E");
  else if (message == "··—·")  
    Serial.print("F");
  else if (message == "——·")  
    Serial.print("G");
  else if (message == "····")  
    Serial.print("H");
  else if (message == "··")  
    Serial.print("I");
  else if (message == "·———")  
    Serial.print("J");
  else if (message == "—·—")  
    Serial.print("K");
  else if (message == "·—··")  
    Serial.print("L");
  else if (message == "——")  
    Serial.print("M");
  else if (message == "—·")  
    Serial.print("N");
  else if (message == "———")  
    Serial.print("O");
  else if (message == "·——·")  
    Serial.print("P");
  else if (message == "——·—")  
    Serial.print("Q");
  else if (message == "·—·")  
    Serial.print("R");
  else if (message == "···")  
    Serial.print("S");
  else if (message == "—")  
    Serial.print("T");
  else if (message == "··—")  
    Serial.print("U");
  else if (message == "···—")  
    Serial.print("V");
  else if (message == "·——")  
    Serial.print("W");
  else if (message == "—··—")  
    Serial.print("X");
  else if (message == "—·——")  
    Serial.print("Y");
  else if (message == "——··")  
    Serial.print("Z");
  else 
    Serial.print(message);
    
  Serial.print(" ");
  message = ""; 
}