Components and supplies
Resistor 330 ohm
LED (generic)
Arduino UNO
Photo resistor
Resistor 1k ohm
Project description
Code
Decoder
arduino
1// Configuration 2// Minimum tested unit length was 10ms and it works reliably with cheap light resistor. 3const int UNIT_LENGTH = 100; 4const int BUFFER_SIZE = 5; 5 6 7enum class Signal: byte { 8 NOISE = 0, 9 DIT = 1, 10 DAH = 2, 11 ELEMENTGAP = 3, 12 GAP = 4, 13 LONGGAP = 5 14}; 15 16struct MorseCodeElement { 17 Signal m_signal; 18 unsigned long m_duration; 19}; 20 21class MorseCodeBuffer { 22 int m_size; 23 int m_head; 24 int m_tail; 25 MorseCodeElement* m_buffer; 26 27public: 28 MorseCodeBuffer(int size) { 29 // Use extra element to distinguish empty vs full. 30 size++; 31 32 m_size = size; 33 m_head = 0; 34 m_tail = 0; 35 m_buffer = new MorseCodeElement[size]; 36 } 37 38 bool Enqueue(MorseCodeElement element) { 39 int new_tail = (m_tail + 1) % m_size; 40 41 // Is full? 42 if (new_tail == m_head) { 43 return false; 44 } 45 46 m_tail = new_tail; 47 m_buffer[m_tail] = element; 48 49 return true; 50 } 51 52 bool TryDequeue(MorseCodeElement* element) { 53 // Is empty? 54 if (m_head == m_tail) { 55 return false; 56 } 57 58 *element = m_buffer[m_head]; 59 m_head = (m_head + 1) % m_size; 60 return true; 61 } 62 63 int GetCount() { 64 if (m_head == m_tail) { 65 return 0; 66 } 67 68 return (m_tail - m_head + m_size) % m_size; 69 } 70}; 71 72class AdaptiveLogicLevelProcessor { 73 int m_sensorMinValue = 1023; 74 int m_sensorMaxValue = 0; 75 int m_sensorMedianValue = 511; 76 unsigned long m_sensorCalibrationTime = 0; 77 bool m_calibrated; 78 79public: 80 AdaptiveLogicLevelProcessor() { 81 m_sensorMinValue = 1023; 82 m_sensorMaxValue = 0; 83 m_sensorMedianValue = 511; 84 m_sensorCalibrationTime = 0; 85 } 86 87 bool process(int sensorValue, int* digitalInputValue) { 88 unsigned long currentTime = millis(); 89 90 // Re-calibrate sensor value range 91 if (currentTime - m_sensorCalibrationTime > 5000) { 92 if (m_sensorMinValue < m_sensorMaxValue) { 93 94 if (m_sensorMaxValue - m_sensorMinValue > 20) { 95 m_sensorMedianValue = m_sensorMinValue + (m_sensorMaxValue - m_sensorMinValue) / 2; 96 m_calibrated = true; 97 } else { 98 Serial.println(); 99 Serial.print("Unreliable LOW/HIGH: "); 100 Serial.print(m_sensorMinValue); 101 Serial.print(' '); 102 Serial.print(m_sensorMaxValue); 103 Serial.println(); 104 m_calibrated = false; 105 } 106 } 107 108 m_sensorMaxValue = 0; 109 m_sensorMinValue = 1023; 110 m_sensorCalibrationTime = currentTime; 111 } 112 113 if (m_sensorMinValue > sensorValue) { 114 m_sensorMinValue = sensorValue; 115 } 116 117 if (m_sensorMaxValue < sensorValue) { 118 m_sensorMaxValue = sensorValue; 119 } 120 121 if (!m_calibrated) { 122 return false; 123 } 124 125 *digitalInputValue = sensorValue > m_sensorMedianValue ? HIGH : LOW; 126 return true; 127 } 128}; 129 130class MorseCodeElementProcessor { 131 unsigned long m_previousTime = 0; 132 int m_previousSignal = LOW; 133 134 int m_oneUnitMinValue; 135 int m_oneUnitMaxValue; 136 int m_threeUnitMinValue; 137 int m_threeUnitMaxValue; 138 int m_sevenUnitMinValue; 139 int m_sevenUnitMaxValue; 140 141public: 142 MorseCodeElementProcessor(int unitLengthInMilliseconds) { 143 m_oneUnitMinValue = (int)(unitLengthInMilliseconds * 0.5); 144 m_oneUnitMaxValue = (int)(unitLengthInMilliseconds * 1.5); 145 146 m_threeUnitMinValue = (int)(unitLengthInMilliseconds * 2.0); 147 m_threeUnitMaxValue = (int)(unitLengthInMilliseconds * 4.0); 148 149 m_sevenUnitMinValue = (int)(unitLengthInMilliseconds * 5.0); 150 m_sevenUnitMaxValue = (int)(unitLengthInMilliseconds * 8.0); 151 } 152 153 bool process(int newSignal, MorseCodeElement* element) { 154 unsigned long currentTime = millis(); 155 unsigned long elapsed; 156 bool shouldBuffer = false; 157 158 element->m_signal = Signal::NOISE; 159 160 // If previous status was OFF and now it is ON 161 if (m_previousSignal == LOW && newSignal == HIGH) { 162 elapsed = currentTime - m_previousTime; 163 element->m_duration = elapsed; 164 165 if (m_sevenUnitMinValue <= elapsed) { 166 element->m_signal = Signal::LONGGAP; 167 shouldBuffer = true; 168 } else if (m_threeUnitMinValue <= elapsed && elapsed <= m_threeUnitMaxValue) { 169 element->m_signal = Signal::GAP; 170 shouldBuffer = true; 171 } else if (m_oneUnitMinValue <= elapsed && elapsed <= m_oneUnitMaxValue) { 172 element->m_signal = Signal::ELEMENTGAP; 173 shouldBuffer = true; 174 } else { 175 element->m_signal = Signal::NOISE; 176 shouldBuffer = true; 177 } 178 179 m_previousSignal = HIGH; 180 m_previousTime = currentTime; 181 } else if (m_previousSignal == HIGH && newSignal == LOW) { 182 elapsed = currentTime - m_previousTime; 183 element->m_duration = elapsed; 184 185 if (m_threeUnitMinValue <= elapsed && elapsed <= m_threeUnitMaxValue) { 186 element->m_signal = Signal::DAH; 187 shouldBuffer = true; 188 } else if (m_oneUnitMinValue <= elapsed && elapsed <= m_oneUnitMaxValue) { 189 element->m_signal = Signal::DIT; 190 shouldBuffer = true; 191 } else { 192 element->m_signal = Signal::NOISE; 193 shouldBuffer = true; 194 } 195 196 m_previousSignal = LOW; 197 m_previousTime = currentTime; 198 } 199 200 return shouldBuffer; 201 } 202}; 203 204class MorseCodeProcessor { 205 private: 206 static const int TREE_SIZE = 255; 207 static constexpr char tree[TREE_SIZE] = { 208 '\\0', '\\0', '\\0', '5', '\\0', '\\0', '\\0', 'H', '\\0', '\\0', '\\0', '4', '\\0', '\\0', '\\0', 'S', 209 '\\0', '\\0', '$', '\\0', '\\0', '\\0', '\\0', 'V', '\\0', '\\0', '\\0', '3', '\\0', '\\0', '\\0', 'I', 210 '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', 'F', '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', 'U', 211 '\\0', '?', '\\0', '\\0', '\\0', '_', '\\0', '\\0', '\\0', '\\0', '\\0', '2', '\\0', '\\0', '\\0', 'E', 212 '\\0', '\\0', '\\0', '&', '\\0', '\\0', '\\0', 'L', '\\0', '"', '\\0', '\\0', '\\0', '\\0', '\\0', 'R', 213 '\\0', '\\0', '\\0', '+', '\\0', '.', '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', 'A', 214 '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', 'P', '\\0', '@', '\\0', '\\0', '\\0', '\\0', '\\0', 'W', 215 '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', 'J', '\\0', '\\'', '\\0', '1', '\\0', '\\0', '\\0', '\\0', 216 '\\0', '\\0', '\\0', '6', '\\0', '-', '\\0', 'B', '\\0', '\\0', '\\0', '=', '\\0', '\\0', '\\0', 'D', 217 '\\0', '\\0', '\\0', '/', '\\0', '\\0', '\\0', 'X', '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', 'N', 218 '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', 'C', '\\0', ';', '\\0', '\\0', '\\0', '!', '\\0', 'K', 219 '\\0', '\\0', '\\0', '(', '\\0', ')', '\\0', 'Y', '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', 'T', 220 '\\0', '\\0', '\\0', '7', '\\0', '\\0', '\\0', 'Z', '\\0', '\\0', '\\0', '\\0', '\\0', ',', '\\0', 'G', 221 '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', 'Q', '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', 'M', 222 '\\0', ':', '\\0', '8', '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', 'O', 223 '\\0', '\\0', '\\0', '9', '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', '0', '\\0', '\\0', '\\0' 224 }; 225 226 bool m_error; 227 int m_start; 228 int m_end; 229 int m_index; 230 Signal m_previousInput; 231 232 void reset() { 233 m_error = false; 234 m_start = 0; 235 m_end = TREE_SIZE; 236 m_index = (m_end - m_start) / 2; 237 } 238 239 public: 240 MorseCodeProcessor() { 241 reset(); 242 m_previousInput = Signal::NOISE; 243 } 244 245 bool process(Signal input, char* output) { 246 bool completed = false; 247 248 if (!m_error && input == Signal::DIT) { 249 if (m_start == m_index) { 250 m_error = true; 251 } else { 252 m_end = m_index; 253 m_index = m_start + (m_end - m_start) / 2; 254 } 255 } else if (!m_error && input == Signal::DAH) { 256 if (m_end == m_index) { 257 m_error = true; 258 } else { 259 m_start = m_index + 1; 260 m_index = m_start + (m_end - m_start) / 2; 261 } 262 } else if (input == Signal::GAP || input == Signal::LONGGAP) { 263 completed = !m_error && tree[m_index] != 0; 264 265 if (completed) { 266 output[0] = tree[m_index]; 267 output[1] = '\\0'; 268 269 if (input == Signal::LONGGAP) { 270 output[1] = ' '; 271 output[2] = '\\0'; 272 } 273 } 274 275 reset(); 276 } 277 278 m_previousInput = input; 279 280 return completed; 281 } 282}; 283 284constexpr char MorseCodeProcessor::tree[]; 285 286MorseCodeBuffer buffer(BUFFER_SIZE); 287MorseCodeProcessor morseCodeProcessor; 288AdaptiveLogicLevelProcessor logicLevelProcessor; 289MorseCodeElementProcessor morseCodeElementProcessor(UNIT_LENGTH); 290 291 292/************************************************************ 293 * Timer interrupt function to process analog signal 294 ************************************************************/ 295SIGNAL(TIMER0_COMPA_vect) { 296 cli(); 297 298 int digitalInputValue; 299 300 if (logicLevelProcessor.process(analogRead(A0), &digitalInputValue)) { 301 MorseCodeElement element; 302 303 if (morseCodeElementProcessor.process(digitalInputValue, &element)) { 304 buffer.Enqueue(element); 305 } 306 } 307 308 sei(); 309} 310 311void setup() { 312 Serial.begin(9600); 313 314 // Sets up a timer interrupt to be called for every millisecond 315 cli(); 316 OCR0A = 0xAF; 317 TIMSK0 |= _BV(OCIE0A); 318 sei(); 319} 320 321/************************************************************ 322 * Helper function to dequeue an item from the buffer safely 323 ************************************************************/ 324bool TryDequeueSafe(MorseCodeElement* element) { 325 // Dequeue item from the buffer while disabling interrupt 326 // so that it doesn't corrupt buffer status 327 cli(); 328 bool result = buffer.TryDequeue(element); 329 sei(); 330 331 return result; 332} 333 334char* output = new char[3]; 335 336void loop() { 337 MorseCodeElement element; 338 339 // Drain buffer 340 while (TryDequeueSafe(&element)) { 341 if (element.m_signal == Signal::DIT) { 342 Serial.print("."); 343 } else if (element.m_signal == Signal::DAH) { 344 Serial.print("-"); 345 } 346 347 if (morseCodeProcessor.process(element.m_signal, output)) { 348 Serial.print('('); 349 Serial.print(output); 350 Serial.print(')'); 351 } 352 353 if (element.m_signal == Signal::LONGGAP) { 354 Serial.println(); 355 } 356 } 357}
Encoder
arduino
1#define DEBUG 2 3const int UNIT_LENGTH = 100; 4const int DIT_LENGTH = UNIT_LENGTH * 1; 5const int DAH_LENGTH = UNIT_LENGTH * 3; 6const int ELEMENT_GAP = UNIT_LENGTH * 1; 7const int SHORT_GAP = UNIT_LENGTH * 2; 8const int MEDIUM_GAP = UNIT_LENGTH * 4; 9 10const char* codeset[] = { 11 /* ! */ "-.-.--", 12 /* " */ ".-..-.", 13 /* # */ NULL, 14 /* $ */ "...-..-", 15 /* % */ NULL, 16 /* & */ ".-...", 17 /* ' */ ".----.", 18 /* ( */ "-.--.", 19 /* ) */ "-.--.-", 20 /* * */ NULL, 21 /* + */ ".-.-.", 22 /* , */ "--..--", 23 /* - */ "-....-", 24 /* . */ ".-.-.-", 25 /* / */ "-..-.", 26 /* 0 */ "-----", 27 /* 1 */ ".----", 28 /* 2 */ "..---", 29 /* 3 */ "...--", 30 /* 4 */ "....-", 31 /* 5 */ ".....", 32 /* 6 */ "-....", 33 /* 7 */ "--...", 34 /* 8 */ "---..", 35 /* 9 */ "----.", 36 /* : */ "---...", 37 /* ; */ "-.-.-.", 38 /* < */ NULL, 39 /* = */ "-...-", 40 /* > */ NULL, 41 /* ? */ "..--..", 42 /* @ */ ".--.-.", 43 /* A */ ".-", 44 /* B */ "-...", 45 /* C */ "-.-.", 46 /* D */ "-..", 47 /* E */ ".", 48 /* F */ "..-.", 49 /* G */ "--.", 50 /* H */ "....", 51 /* I */ "..", 52 /* J */ ".---", 53 /* K */ "-.-", 54 /* L */ ".-..", 55 /* M */ "--", 56 /* N */ "-.", 57 /* O */ "---", 58 /* P */ ".--.", 59 /* Q */ "--.-", 60 /* R */ ".-.", 61 /* S */ "...", 62 /* T */ "-", 63 /* U */ "..-", 64 /* V */ "...-", 65 /* W */ ".--", 66 /* X */ "-..-", 67 /* Y */ "-.--", 68 /* Z */ "--..", 69 /* [ */ NULL, 70 /* \*/ NULL, 71 /* ] */ NULL, 72 /* ^ */ NULL, 73 /* _ */ "..--.-" 74 }; 75 76const char* getCode(char c) { 77 // To uppercase if needed 78 if ('a' <= c && c <= 'z') { 79 c = c - ('a' - 'A'); 80 } 81 82 if ('!' <= c && c <= '_') { 83 return codeset[c - '!']; 84 } 85 86 return NULL; 87} 88 89void setup() { 90 pinMode(LED_BUILTIN, OUTPUT); 91 Serial.begin(19200); 92} 93 94 95void dit() 96{ 97#ifdef DEBUG 98 Serial.print("."); 99#endif 100 digitalWrite(LED_BUILTIN, HIGH); 101 delay(DIT_LENGTH); 102 digitalWrite(LED_BUILTIN, LOW); 103 delay(ELEMENT_GAP); 104} 105 106void dah() 107{ 108#ifdef DEBUG 109 Serial.print("-"); 110#endif 111 digitalWrite(LED_BUILTIN, HIGH); 112 delay(DAH_LENGTH); 113 digitalWrite(LED_BUILTIN, LOW); 114 delay(ELEMENT_GAP); 115} 116 117void letter() 118{ 119#ifdef DEBUG 120 Serial.print(" "); 121#endif 122 delay(SHORT_GAP); 123} 124 125void space() 126{ 127#ifdef DEBUG 128 Serial.println(); 129#endif 130 delay(MEDIUM_GAP); 131} 132 133void play(const char * input) { 134 int inputLength = strlen(input); 135 136 for (int i = 0; i < inputLength; i++) { 137 char c = input[i]; 138 139 if (c == ' ') { 140 space(); 141 } 142 else { 143 const char* code = getCode(c); 144 145 if (code == NULL) { 146 continue; 147 } 148 149 int codeLength = strlen(code); 150 151 for (int j = 0; j < codeLength; j++) { 152 if (code[j] == '.') { 153 dit(); 154 } 155 else if (code[j] == '-') { 156 dah(); 157 } 158 } 159 160 letter(); 161 } 162 } 163} 164 165const char* text = "Hello World "; 166 167// the loop function runs over and over again forever 168void loop() { 169 play(text); 170}
Encoder
arduino
1#define DEBUG 2 3const int UNIT_LENGTH = 100; 4const int DIT_LENGTH = UNIT_LENGTH * 1; 5const int DAH_LENGTH = UNIT_LENGTH * 3; 6const int ELEMENT_GAP = UNIT_LENGTH * 1; 7const int SHORT_GAP = UNIT_LENGTH * 2; 8const int MEDIUM_GAP = UNIT_LENGTH * 4; 9 10const char* codeset[] = { 11 /* ! */ "-.-.--", 12 /* " */ ".-..-.", 13 /* # */ NULL, 14 /* $ */ "...-..-", 15 /* % */ NULL, 16 /* & */ ".-...", 17 /* ' */ ".----.", 18 /* ( */ "-.--.", 19 /* ) */ "-.--.-", 20 /* * */ NULL, 21 /* + */ ".-.-.", 22 /* , */ "--..--", 23 /* - */ "-....-", 24 /* . */ ".-.-.-", 25 /* / */ "-..-.", 26 /* 0 */ "-----", 27 /* 1 */ ".----", 28 /* 2 */ "..---", 29 /* 3 */ "...--", 30 /* 4 */ "....-", 31 /* 5 */ ".....", 32 /* 6 */ "-....", 33 /* 7 */ "--...", 34 /* 8 */ "---..", 35 /* 9 */ "----.", 36 /* : */ "---...", 37 /* ; */ "-.-.-.", 38 /* < */ NULL, 39 /* = */ "-...-", 40 /* > */ NULL, 41 /* ? */ "..--..", 42 /* @ */ ".--.-.", 43 /* A */ ".-", 44 /* B */ "-...", 45 /* C */ "-.-.", 46 /* D */ "-..", 47 /* E */ ".", 48 /* F */ "..-.", 49 /* G */ "--.", 50 /* H */ "....", 51 /* I */ "..", 52 /* J */ ".---", 53 /* K */ "-.-", 54 /* L */ ".-..", 55 /* M */ "--", 56 /* N */ "-.", 57 /* O */ "---", 58 /* P */ ".--.", 59 /* Q */ "--.-", 60 /* R */ ".-.", 61 /* S */ "...", 62 /* T */ "-", 63 /* U */ "..-", 64 /* V */ "...-", 65 /* W */ ".--", 66 /* X */ "-..-", 67 /* Y */ "-.--", 68 /* Z */ "--..", 69 /* [ */ NULL, 70 /* \*/ NULL, 71 /* ] */ NULL, 72 /* ^ */ NULL, 73 /* _ */ "..--.-" 74 }; 75 76const char* getCode(char c) { 77 // To uppercase if needed 78 if ('a' <= c && c <= 'z') { 79 c = c - ('a' - 'A'); 80 } 81 82 if ('!' <= c && c <= '_') { 83 return codeset[c - '!']; 84 } 85 86 return NULL; 87} 88 89void setup() { 90 pinMode(LED_BUILTIN, OUTPUT); 91 Serial.begin(19200); 92} 93 94 95void dit() 96{ 97#ifdef DEBUG 98 Serial.print("."); 99#endif 100 digitalWrite(LED_BUILTIN, HIGH); 101 delay(DIT_LENGTH); 102 digitalWrite(LED_BUILTIN, LOW); 103 delay(ELEMENT_GAP); 104} 105 106void dah() 107{ 108#ifdef DEBUG 109 Serial.print("-"); 110#endif 111 digitalWrite(LED_BUILTIN, HIGH); 112 delay(DAH_LENGTH); 113 digitalWrite(LED_BUILTIN, LOW); 114 delay(ELEMENT_GAP); 115} 116 117void letter() 118{ 119#ifdef DEBUG 120 Serial.print(" "); 121#endif 122 delay(SHORT_GAP); 123} 124 125void space() 126{ 127#ifdef DEBUG 128 Serial.println(); 129#endif 130 delay(MEDIUM_GAP); 131} 132 133void play(const char * input) { 134 int inputLength = strlen(input); 135 136 for (int i = 0; i < inputLength; i++) { 137 char c = input[i]; 138 139 if (c == ' ') { 140 space(); 141 } 142 else { 143 const char* code = getCode(c); 144 145 if (code == NULL) { 146 continue; 147 } 148 149 int codeLength = strlen(code); 150 151 for (int j = 0; j < codeLength; j++) { 152 if (code[j] == '.') { 153 dit(); 154 } 155 else if (code[j] == '-') { 156 dah(); 157 } 158 } 159 160 letter(); 161 } 162 } 163} 164 165const char* text = "Hello World "; 166 167// the loop function runs over and over again forever 168void loop() { 169 play(text); 170}
Decoder
arduino
1// Configuration 2// Minimum tested unit length was 10ms and it works 3 reliably with cheap light resistor. 4const int UNIT_LENGTH = 100; 5const int 6 BUFFER_SIZE = 5; 7 8 9enum class Signal: byte { 10 NOISE = 0, 11 DIT = 12 1, 13 DAH = 2, 14 ELEMENTGAP = 3, 15 GAP = 4, 16 LONGGAP = 5 17}; 18 19struct 20 MorseCodeElement { 21 Signal m_signal; 22 unsigned long m_duration; 23}; 24 25class 26 MorseCodeBuffer { 27 int m_size; 28 int m_head; 29 int m_tail; 30 MorseCodeElement* 31 m_buffer; 32 33public: 34 MorseCodeBuffer(int size) { 35 // Use extra 36 element to distinguish empty vs full. 37 size++; 38 39 m_size = size; 40 41 m_head = 0; 42 m_tail = 0; 43 m_buffer = new MorseCodeElement[size]; 44 45 } 46 47 bool Enqueue(MorseCodeElement element) { 48 int new_tail 49 = (m_tail + 1) % m_size; 50 51 // Is full? 52 if (new_tail == m_head) 53 { 54 return false; 55 } 56 57 m_tail = new_tail; 58 m_buffer[m_tail] 59 = element; 60 61 return true; 62 } 63 64 bool TryDequeue(MorseCodeElement* 65 element) { 66 // Is empty? 67 if (m_head == m_tail) { 68 return false; 69 70 } 71 72 *element = m_buffer[m_head]; 73 m_head = (m_head + 1) % 74 m_size; 75 return true; 76 } 77 78 int GetCount() { 79 if (m_head 80 == m_tail) { 81 return 0; 82 } 83 84 return (m_tail - m_head 85 + m_size) % m_size; 86 } 87}; 88 89class AdaptiveLogicLevelProcessor { 90 91 int m_sensorMinValue = 1023; 92 int m_sensorMaxValue = 0; 93 int m_sensorMedianValue 94 = 511; 95 unsigned long m_sensorCalibrationTime = 0; 96 bool m_calibrated; 97 98public: 99 100 AdaptiveLogicLevelProcessor() { 101 m_sensorMinValue = 1023; 102 m_sensorMaxValue 103 = 0; 104 m_sensorMedianValue = 511; 105 m_sensorCalibrationTime = 0; 106 } 107 108 109 bool process(int sensorValue, int* digitalInputValue) { 110 unsigned long currentTime 111 = millis(); 112 113 // Re-calibrate sensor value range 114 if (currentTime 115 - m_sensorCalibrationTime > 5000) { 116 if (m_sensorMinValue < m_sensorMaxValue) 117 { 118 119 if (m_sensorMaxValue - m_sensorMinValue > 20) { 120 m_sensorMedianValue 121 = m_sensorMinValue + (m_sensorMaxValue - m_sensorMinValue) / 2; 122 m_calibrated 123 = true; 124 } else { 125 Serial.println(); 126 Serial.print("Unreliable 127 LOW/HIGH: "); 128 Serial.print(m_sensorMinValue); 129 Serial.print(' 130 '); 131 Serial.print(m_sensorMaxValue); 132 Serial.println(); 133 134 m_calibrated = false; 135 } 136 } 137 138 m_sensorMaxValue 139 = 0; 140 m_sensorMinValue = 1023; 141 m_sensorCalibrationTime = currentTime; 142 143 } 144 145 if (m_sensorMinValue > sensorValue) { 146 m_sensorMinValue 147 = sensorValue; 148 } 149 150 if (m_sensorMaxValue < sensorValue) { 151 m_sensorMaxValue 152 = sensorValue; 153 } 154 155 if (!m_calibrated) { 156 return false; 157 158 } 159 160 *digitalInputValue = sensorValue > m_sensorMedianValue ? HIGH 161 : LOW; 162 return true; 163 } 164}; 165 166class MorseCodeElementProcessor { 167 168 unsigned long m_previousTime = 0; 169 int m_previousSignal = LOW; 170 171 int 172 m_oneUnitMinValue; 173 int m_oneUnitMaxValue; 174 int m_threeUnitMinValue; 175 176 int m_threeUnitMaxValue; 177 int m_sevenUnitMinValue; 178 int m_sevenUnitMaxValue; 179 180public: 181 182 MorseCodeElementProcessor(int unitLengthInMilliseconds) { 183 m_oneUnitMinValue 184 = (int)(unitLengthInMilliseconds * 0.5); 185 m_oneUnitMaxValue = (int)(unitLengthInMilliseconds 186 * 1.5); 187 188 m_threeUnitMinValue = (int)(unitLengthInMilliseconds * 2.0); 189 190 m_threeUnitMaxValue = (int)(unitLengthInMilliseconds * 4.0); 191 192 m_sevenUnitMinValue 193 = (int)(unitLengthInMilliseconds * 5.0); 194 m_sevenUnitMaxValue = (int)(unitLengthInMilliseconds 195 * 8.0); 196 } 197 198 bool process(int newSignal, MorseCodeElement* element) { 199 200 unsigned long currentTime = millis(); 201 unsigned long elapsed; 202 bool 203 shouldBuffer = false; 204 205 element->m_signal = Signal::NOISE; 206 207 208 // If previous status was OFF and now it is ON 209 if (m_previousSignal == 210 LOW && newSignal == HIGH) { 211 elapsed = currentTime - m_previousTime; 212 213 element->m_duration = elapsed; 214 215 if (m_sevenUnitMinValue 216 <= elapsed) { 217 element->m_signal = Signal::LONGGAP; 218 shouldBuffer 219 = true; 220 } else if (m_threeUnitMinValue <= elapsed && elapsed <= m_threeUnitMaxValue) 221 { 222 element->m_signal = Signal::GAP; 223 shouldBuffer = true; 224 225 } else if (m_oneUnitMinValue <= elapsed && elapsed <= m_oneUnitMaxValue) { 226 227 element->m_signal = Signal::ELEMENTGAP; 228 shouldBuffer = true; 229 230 } else { 231 element->m_signal = Signal::NOISE; 232 shouldBuffer 233 = true; 234 } 235 236 m_previousSignal = HIGH; 237 m_previousTime 238 = currentTime; 239 } else if (m_previousSignal == HIGH && newSignal == LOW) { 240 241 elapsed = currentTime - m_previousTime; 242 element->m_duration = elapsed; 243 244 245 if (m_threeUnitMinValue <= elapsed && elapsed <= m_threeUnitMaxValue) 246 { 247 element->m_signal = Signal::DAH; 248 shouldBuffer = true; 249 250 } else if (m_oneUnitMinValue <= elapsed && elapsed <= m_oneUnitMaxValue) { 251 252 element->m_signal = Signal::DIT; 253 shouldBuffer = true; 254 } 255 else { 256 element->m_signal = Signal::NOISE; 257 shouldBuffer = true; 258 259 } 260 261 m_previousSignal = LOW; 262 m_previousTime = currentTime; 263 264 } 265 266 return shouldBuffer; 267 } 268}; 269 270class MorseCodeProcessor 271 { 272 private: 273 static const int TREE_SIZE = 255; 274 static constexpr 275 char tree[TREE_SIZE] = { 276 '\\0', '\\0', '\\0', '5', '\\0', '\\0', '\\0', 277 'H', '\\0', '\\0', '\\0', '4', '\\0', '\\0', '\\0', 'S', 278 '\\0', '\\0', 279 '$', '\\0', '\\0', '\\0', '\\0', 'V', '\\0', '\\0', '\\0', '3', '\\0', '\\0', '\\0', 280 'I', 281 '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', 'F', '\\0', '\\0', 282 '\\0', '\\0', '\\0', '\\0', '\\0', 'U', 283 '\\0', '?', '\\0', '\\0', '\\0', 284 '_', '\\0', '\\0', '\\0', '\\0', '\\0', '2', '\\0', '\\0', '\\0', 'E', 285 '\\0', 286 '\\0', '\\0', '&', '\\0', '\\0', '\\0', 'L', '\\0', '"', '\\0', '\\0', '\\0', '\\0', 287 '\\0', 'R', 288 '\\0', '\\0', '\\0', '+', '\\0', '.', '\\0', '\\0', '\\0', 289 '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', 'A', 290 '\\0', '\\0', '\\0', '\\0', 291 '\\0', '\\0', '\\0', 'P', '\\0', '@', '\\0', '\\0', '\\0', '\\0', '\\0', 'W', 292 293 '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', 'J', '\\0', '\\'', '\\0', 294 '1', '\\0', '\\0', '\\0', '\\0', 295 '\\0', '\\0', '\\0', '6', '\\0', '-', 296 '\\0', 'B', '\\0', '\\0', '\\0', '=', '\\0', '\\0', '\\0', 'D', 297 '\\0', 298 '\\0', '\\0', '/', '\\0', '\\0', '\\0', 'X', '\\0', '\\0', '\\0', '\\0', '\\0', 299 '\\0', '\\0', 'N', 300 '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', 'C', 301 '\\0', ';', '\\0', '\\0', '\\0', '!', '\\0', 'K', 302 '\\0', '\\0', '\\0', 303 '(', '\\0', ')', '\\0', 'Y', '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', 'T', 304 305 '\\0', '\\0', '\\0', '7', '\\0', '\\0', '\\0', 'Z', '\\0', '\\0', '\\0', '\\0', 306 '\\0', ',', '\\0', 'G', 307 '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', 308 'Q', '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', 'M', 309 '\\0', ':', 310 '\\0', '8', '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', '\\0', 311 '\\0', 'O', 312 '\\0', '\\0', '\\0', '9', '\\0', '\\0', '\\0', '\\0', '\\0', 313 '\\0', '\\0', '0', '\\0', '\\0', '\\0' 314 }; 315 316 bool m_error; 317 318 int m_start; 319 int m_end; 320 int m_index; 321 Signal m_previousInput; 322 323 324 void reset() { 325 m_error = false; 326 m_start = 0; 327 m_end 328 = TREE_SIZE; 329 m_index = (m_end - m_start) / 2; 330 } 331 332 public: 333 334 MorseCodeProcessor() { 335 reset(); 336 m_previousInput = Signal::NOISE; 337 338 } 339 340 bool process(Signal input, char* output) { 341 bool completed 342 = false; 343 344 if (!m_error && input == Signal::DIT) { 345 if 346 (m_start == m_index) { 347 m_error = true; 348 } else { 349 m_end 350 = m_index; 351 m_index = m_start + (m_end - m_start) / 2; 352 } 353 354 } else if (!m_error && input == Signal::DAH) { 355 if (m_end == m_index) 356 { 357 m_error = true; 358 } else { 359 m_start = m_index 360 + 1; 361 m_index = m_start + (m_end - m_start) / 2; 362 } 363 } 364 else if (input == Signal::GAP || input == Signal::LONGGAP) { 365 completed 366 = !m_error && tree[m_index] != 0; 367 368 if (completed) { 369 output[0] 370 = tree[m_index]; 371 output[1] = '\\0'; 372 373 if (input == Signal::LONGGAP) 374 { 375 output[1] = ' '; 376 output[2] = '\\0'; 377 } 378 379 } 380 381 reset(); 382 } 383 384 m_previousInput 385 = input; 386 387 return completed; 388 } 389}; 390 391constexpr char MorseCodeProcessor::tree[]; 392 393MorseCodeBuffer 394 buffer(BUFFER_SIZE); 395MorseCodeProcessor morseCodeProcessor; 396AdaptiveLogicLevelProcessor 397 logicLevelProcessor; 398MorseCodeElementProcessor morseCodeElementProcessor(UNIT_LENGTH); 399 400 401/************************************************************ 402 403 * Timer interrupt function to process analog signal 404 ************************************************************/ 405SIGNAL(TIMER0_COMPA_vect) 406 { 407 cli(); 408 409 int digitalInputValue; 410 411 if (logicLevelProcessor.process(analogRead(A0), 412 &digitalInputValue)) { 413 MorseCodeElement element; 414 415 if (morseCodeElementProcessor.process(digitalInputValue, 416 &element)) { 417 buffer.Enqueue(element); 418 } 419 } 420 421 sei(); 422} 423 424void 425 setup() { 426 Serial.begin(9600); 427 428 // Sets up a timer interrupt to be called 429 for every millisecond 430 cli(); 431 OCR0A = 0xAF; 432 TIMSK0 |= _BV(OCIE0A); 433 434 sei(); 435} 436 437/************************************************************ 438 439 * Helper function to dequeue an item from the buffer safely 440 ************************************************************/ 441bool 442 TryDequeueSafe(MorseCodeElement* element) { 443 // Dequeue item from the buffer 444 while disabling interrupt 445 // so that it doesn't corrupt buffer status 446 cli(); 447 448 bool result = buffer.TryDequeue(element); 449 sei(); 450 451 return result; 452} 453 454char* 455 output = new char[3]; 456 457void loop() { 458 MorseCodeElement element; 459 460 461 // Drain buffer 462 while (TryDequeueSafe(&element)) { 463 if (element.m_signal 464 == Signal::DIT) { 465 Serial.print("."); 466 } else if (element.m_signal 467 == Signal::DAH) { 468 Serial.print("-"); 469 } 470 471 if (morseCodeProcessor.process(element.m_signal, 472 output)) { 473 Serial.print('('); 474 Serial.print(output); 475 Serial.print(')'); 476 477 } 478 479 if (element.m_signal == Signal::LONGGAP) { 480 Serial.println(); 481 482 } 483 } 484}
Downloadable files
Encoder
Encoder
Encoder
Encoder
Decoder
Decoder
Comments
Only logged in users can leave comments