Arduino UNO로 모스 부호 송수신기 만들기 - 레이저와 조도 센서 활용
부품 및 소모품
| Arduino UNO |
2 |
| LED (범용) |
2 |
| 포토레지스터 (조도 센서) |
1 |
| 부저 |
1 |
프로젝트 소개
통신 시스템 수업에서 학생들이 시각화와 체험을 통해 배우게 하고 싶었습니다. 석기 시대부터 통신의 필요성, 언어와 문법, 신뢰성 있는 통신, 신호, 메시지 등을 실험으로 다뤘습니다.
이 과정에서 시각 통신용 자체 언어를 정의하게 되었고, 그곳에서 모스 부호를 고안했습니다!
모스 부호를 일일이 외우는 건 힘들므로 기계가 필요했습니다. 주어진 문자열을 모스 부호로 인코딩하고, 모스 부호 스트림을 다시 문자열로 디코딩하는 작은 프로그램을 작성하기로 했습니다.
모스 부호표 및 이진 트리
문자별 모스 부호는 다음 표를 참조했습니다.
모스 부호를 문자로 역변환하기 위해 이진 트리로 구성했습니다. '점(.)'은 왼쪽 서브트리, '대시(-)'는 오른쪽 서브트리로 이동합니다.
이를 이용해 문자열을 모스 부호로, 모스 부호를 문자열로 빠르게 변환할 수 있었습니다.
먼저 C#으로 개념 증명(PoC) 프로그램을 작성했습니다.
출력 예시
Hello World .... . .-.. .-.. --- .-- --- .-. .-.. -..
하지만 통신은 양방향이어야 하므로, 인코딩된 모스 부호를 다른 장치로 전송해 거기서 디코딩하고 싶었습니다. 두 개의 Arduino 보드로 송신기와 수신기를 만들었습니다.
송신기 (Transmitter)
송신기 보드에는 Keyes 레이저 다이오드를 연결했습니다. 저렴하고 구하기 쉽습니다. 제 거리에서는 3V 입력이 충분했지만 5V면 최소 200m까지 도달합니다.
송신기는 USB 시리얼로 컴퓨터와 연결됩니다. 시리얼 포트에서 문자 단위로 데이터를 읽어 해당 문자의 모스 부호를 찾아 '점'과 '대시' 빛 신호로 생성합니다.
50ms 펄스를 기본 단위로, '점'은 50ms 빛, '대시'는 150ms 빛을 냅니다. 각 점/대시 사이 50ms 무음, 문자 사이 100ms 무음을 둡니다.
송신기 코드
const char* MorseTable[] = {
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // space, !, ", #, $, %, &, '
NULL, "-.-.--", ".-..-.", NULL, NULL, NULL, NULL, ".----.", // ( ) * + , - . /
"-.--.", "-.--.-", NULL, ".-.-.", "--..--", "-....-", ".-.-.-", "-..-.", // 0-7
"-----", ".----", "..---", "...--", "....-", ".....", "-....", "--...", // 8-9 : ; < = > ?
"---..", "----.", "---...", "-.-.-.", NULL, "-...-", NULL, "..--..", // @ 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 [ \ ] ^ _
"-..-", "-.--", "--..", NULL, NULL, NULL, NULL, "..--.-", // ' a b c d e f g
NULL, ".-", "-...", "-.-.", "-..", ".", "..-.", "--.", // h i j k l m n o
"....", "..", ".---", "-.-", ".-..", "--", "-.", "---", // p q r s t u v w
".--.", "--.-", ".-.", "...", "-", "..-", "...-", ".--", // x y z { | } ~ DEL
"-..-", "-.--", "--..", NULL, NULL, NULL, NULL, NULL
};
int dotLength = 50;
int dashLength = dotLength * 3;
void setup() {
pinMode(13, OUTPUT);
Serial.begin(9600);
}
void loop() {
char ch;
if (Serial.available()) {
ch = Serial.read();
flashDashDot(MorseTable[ch]);
delay(dotLength * 2);
}
}
void flashDashDot(const char *morseCode) {
int i = 0;
while (morseCode[i] != 0) {
if (morseCode[i] == '.') dot();
else if (morseCode[i] == '-') dash();
i++;
}
}
void dot() {
digitalWrite(13, HIGH);
delay(dotLength);
digitalWrite(13, LOW);
delay(dotLength);
}
void dash() {
digitalWrite(13, HIGH);
delay(dashLength);
digitalWrite(13, LOW);
delay(dotLength);
}
수신기 (Receiver)
수신기 Arduino에는 LDR(조도 센서), 피에조 부저, LED를 연결했습니다. LED와 부저는 오디오/비디오 피드백용으로 필수는 아니지만 작동 확인에 좋습니다.
수신기도 컴퓨터에 연결해 결과 문자를 읽습니다. 루프에서 아날로그 포트 4에 연결된 LDR로 빛을 감지합니다. 주변광 차단을 위해 긴 튜브로 감쌌습니다. 레이저 빛이 LDR에 닿으면 값이 높아지고 스피커와 LED를 켭니다. 꺼지면 끕니다.
빛 지속 시간을 계산해 '점'인지 '대시'인지 판별합니다. 16MHz 아트멜 프로세서에서 약 400 사이클이 한 펄스에 해당했습니다.
수신기 코드
// 모스 부호 이진 트리
// 왼쪽 자식: 2n+1, 오른쪽 자식: 2n+2
// <.|-> .E -T .I -A .N -M ...
const char MorseTree[] = {
'\0','E','T','I','A','N','M','S','U','R','W','D','K','G','O',
'H','V','F','U','L','A','P','J','B','X','C','Y','Z','Q','\0','\0',
'5','4','\0','3','\0','\0','\0','2','\0','\0','+','\0','\0','\0','\0','1',
'6','=','/','\0','\0','\0','(','\0','7','\0','\0','\0','8','\0','9','0',
'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','?','_','\0','\0','\0',
'\0','"','\0','\0','.','\0','\0','\0','\0','@','\0','\0','\0','\0','\0','\0',
'-','\0','\0','\0','\0','\0','\0','\0','\0',';','!','\0',')','\0','\0','\0',
'\0','\0',',','\0','\0','\0','\0',':','\0','\0','\0','\0','\0','\0','\0'
};
int val = 0; // LDR 값
int ctrHigh = 0; // HIGH 지속 카운트
int ctrLow = 0; // LOW 지속 카운트
int codePtr = 0; // 트리 포인터
int dotLen = 400; // 점 길이 기준
void setup() {
Serial.begin(9600);
Serial.print(codePtr);
}
void loop() {
val = analogRead(4);
if (val >= 10) { // 빛 감지됨
ctrHigh++;
ctrLow = 0;
digitalWrite(13, HIGH);
tone(9, 1000);
} else { // 빛 꺼짐
ctrLow++;
if (ctrHigh >= dotLen && ctrHigh < dotLen * 2) {
Serial.print(".");
codePtr = (2 * codePtr) + 1; // 왼쪽 자식
} else if (ctrHigh >= dotLen * 2) {
Serial.print("-");
codePtr = (2 * codePtr) + 2; // 오른쪽 자식
} else {
if (ctrLow == dotLen * 2) { // 문자 간격
Serial.print(MorseTree[codePtr]);
codePtr = 0;
}
}
ctrHigh = 0;
digitalWrite(13, LOW);
noTone(9);
}
}
수신기 출력 예시
....H.E.-..L.-..L---O.--W---O.-.R.-..L-..D-.-.--!
회로도
모스 부호 수신기 회로: morsereceiver_1JpG6jZ6yv.fzz
모스 부호 송신기 회로: morsetransmitter_sOKvrIbOv3.fzz