아두이노 키보드 제어(아두이노)
아두이노 키보드 제어(아두이노)
- 온라인 가상시뮬레이터 : https://www.tinkercad.com
지난 시간에 아두이노 마이크로 보드를 간단히 테스트를 했으니 이제 본격적으로 키보드 제어를 실험해 봅시다. 아두이도 키보드/마우스 제어를 하기 위한 목적으로 아두이노 마이크로 보드를 구매 했습니다. 오늘은 키보드 제어에 대해서 이야기를 할까 합니다. 프로그램언어를 공부하신분들은 오토키보드, 오토마우스 프로그램을 한번쯤을 사용해 보셨을 꺼에요. 게임을 할 때 오토 매크로 프로그램으로 게임을 자동사냥에 이용을 해보셨던 분도 아마 있을 꺼에요. C언어에서 키관련 핸들러를 이용해서 오토매크로 프로그램을 만들 수 있습니다. 이걸 코딩하기 위해서는 좀 코딩하기도 힘들고 복잡합니다. 저도 예전에 학창시절에 한번 코딩해보고 이제는 코딩 기억도 안나는 그런 것들이 있었지 정도에 기억만 남았네요.
그런데 아두이노에서 키보드 제어를 무지 쉽게 할 수 있는 방법을 제공합니다. 키보드 라이브러리가 오픈소스로 제공되고 단지 여러분들은 함수 몇개만 알고 있으면 쉽게 키보드 제어를 할 수 있습니다. 키보드 제어 코딩이 쉽지 않는데 이렇게 비전공자들도 쉽게 제어할 수 있는 방법을 제공하니 진짜 아두이노는 못하는 것이 없는 만능 싱글보드라고 생각됩니다.
이제 본격적으로 키보드 제어를 해볼까요.
1. 아두이노 키보드 제어
- 사전학습 참조 : https://www.arduino.cc/reference/ko/language/functions/usb/keyboard/ (아두이노공식홈페이지)
위 사전학습으로 읽고 오시면 됩니다. 사전학습 자료를 보시면 8개의 함수가 있는데 이 함수만 알면 키보드 제어를 마음대로 할 수 있습니다. 간단히 함수에 대해 살펴 볼까요.
- 키보드 시작 : Keyboard.begin()
- 키보드 종료 : Keyboard.end()
- 키 누름 : Keyboard.press()
- 키 해제 : Keyboard.release()
- 키 전체 해제 : Keyboard.releaseAll()
- 키 문자열 출력 : Keyboard.print(), Keyboard.println() , Keyboard.write()
함수도 그렇게 어렵지 않죠. 기본적으로 3개의 함수만 알고 있으면 키보드 키를 마음대로 제어할 수 있습니다.
1) 키 제어
키보드의 키를 제어하는 방법을 살펴보도록 하죠.
[기본소스]
#include <Keyboard.h>
void setup(){
Keyboard.begin();
}
void loop(){
if(버턴누름){
Keyboard.press('a'); //키 누름
delay(100);
Keyboard.releaseAll(); //키 해제
delay(200);
}
}
위 소스가 기본 키보드 누르는 동작입니다. press('a')함수는 'a'키 누르는 명령입니다. 이렇게만 코딩하면 a키가 눌려 있는 상태가 됩니다. 키누를 눌렀으면 때는 동작을 해야 겠죠. releaseAll()함수로 현재 눌려진 키들을 전부 해제하는 명령입니다.
즉, "ctrl+n" 키를 누를때 사용하면 좋겠죠.
Keyboard.press(KEY_LEFT_CTRL);
Keyboard.press('n');
delay(100);
Keyboard.releaseAll(); //키 해제
어떤 느낌이신지 아시겠지요.
2) 문자열 제어
아두이노는 미리 만든 문자열을 키보드로 친것과 같이 한번에 출력할 수 있습니다. 느낌으로 표현하자면 Ctrl+V의 느낌이라고 생각하시면 됩니다. 키보드 커서가 있는 위치에 아두이노에 저장된 문자열이 Ctrl+V를 누른 것 같이 문자열을 해당 커서 위치에 출력하게 됩니다.
Keyboard.println("STEEMIT!");
[결과]
어떤 느낌인지 아시겠지요.
3) 키보드 키값
- 출처 : https://github.com/arduino-libraries/Keyboard/blob/master/src/Keyboard.h (mjbcopland)
위 github에 가시면 키보드 키값을 확인하실 수 있습니다. 아니면 아두이노 IDE 라이브러리 폴더에 가셔서 Keyboard.h 파일을 열어보셔서 됩니다.
열어보시면 키보드 키들이 정의 되어 있는데 아래 그림과 같습니다.
위 그림은 알파벳을 제외한 그외 일부 키들에 대한 부분입니다. 알파벳은 그냥 알파벳을 쓰면 되고 나머지 키들은 위에서 정의한 이름으로 키를 사용하시면 됩니다.
2. 아두이노 키보드 제어 회로도
- 준비물 : 스위치버턴 5개, 아두이노 마이크로
- 내용 : 스위치버턴을 아두이노 마이크로 2,3,4,5,6번핀에 순서대로 연결하시오.
지난시간에 보여준 아래 그림은 다시 복습차원으로 보시기 바랍니다.
- 출처 : 아두이노 마이크로 datasheet : https://www.jameco.com/Jameco/Products/ProdDS/2207700.pdf
아두이노 마이크로 핀 연결할 때 핀 번호를 위에 복습차원으로 올린 아두이노 마이크로 정보를 보시고 핀 연결하시면 됩니다.
3. 코딩
스위치버턴을 5개를 키보드 키를 제어하는데 사용 합니다. 처음에 알파벳 a,b,c,d 키와 KEY_RETURN 키로 실험을 하려다가 설계를 2번핀은 인터럽트 핀으로 사용하고 안정장치를 마련하고 나머지 3,4번은 a,b 키, 5번은 KEY_RETURN 키, 6번은 KEY_BACKSPACE 키로 사용하여 최대한 키보드 느낌을 살리는 실험을 하겠습니다.
1) 키보드 안전장치
키보드를 아두이노를 제어를 하게 되면 약간 주의해서 코딩을 해야 합니다. 잘못된 코딩을 하게 되면 무한 루프에 빠져서 키를 무한으로 누르는 문제가 발생 시킬 수 있습니다. 의도치 않게 잘못된 무한 버그가 발생 할 수 있으니 코딩을 할 때 주의해서 코딩을 해야 합니다. 그래서 안전장치로 인터럽트를 이용할까 합니다. loop()함수가 무한 반복 수행 하더라고 중간에 인터럽트 이벤트를 발생시킬 수 있습니다. 그러면 이 이벤트 명령으로 반복 수행을 중단시킬 수 있습니다. 즉, loop()함수에 락을 하나 걸어놓고 문제가 생기면 락을 걸어서 아두이노의 키보드 명령을 중단시키면 문제에 대해서 탈출 할 수 있습니다.
[기본소스]
const byte interruptPin = 2;
boolean state = true;
void setup() {
pinMode(interruptPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(interruptPin), exchange, FALLING);
}
void loop(){
if(state==false){
키보드 명령;
}
}
void exchange() {
state=!state;
}
위 코딩을 보면 state가 false일때만 키보드 명령이 수행되고 키보드 명령이 잘못된 코딩으로 오류로 무한 루프에 빠지게 되면 인터럽트 스위치버턴을 누르면 state는 true 상태가 되고 더이상 키보드 명령을 수행하지 않게 됩니다.
이 안전장치가 어떤 느낌인지 아시겠지요. 나중에 코딩이 완성되면 키보드 안정장치 코딩은 지우셔도 됩니다. 코딩하면서 테스트할 때 혹시 모를 error에 대한 대비책으로 생각하시면 됩니다.
참고로, 아두이노 마이크로에서 선언때 초기값이 state가 true인데 loop()함수 내에서 state의 초기 상태는 false입니다. 아두이노우노에서는 loop()에서도 true 상태를 유지하는데 이상하게 아두이노 마이크로는 인터럽트가 한번 수행하고 들어가는 것 같더군요. 혼동하지 마세요.
2) 키보드 키 실험
- 알파벳(a,b 키), KEY_RETURN, KEY_BACKSPACE 키로 간단히 키보드 테스트
핀번호을 선언 합니다.
const byte Pin_A = 3;
const byte Pin_B = 4;
const byte Pin_Return = 5;
const byte Pin_Backspace = 6;
스위치 버턴 모두 내부풀업모드 방식을 사용하니깐 pinMode()은 다음과 같이 INPUT_PULLUP 모드로 선언 합니다.
pinMode(Pin_A, INPUT_PULLUP);
pinMode(Pin_B, INPUT_PULLUP);
pinMode(Pin_Return, INPUT_PULLUP);
pinMode(Pin_Backspace, INPUT_PULLUP);
'a'키 누름 이벤트 명령은 다음과 같습니다. 내부풀업모드로 초기상태가 HIGH 입니다. 그래서 스위치를 누르면 LOW로 바뀌게 되고 IF문이 참이 되어 키 누름과 해제 명령을 수행하게 됩니다.
if(digitalRead(Pin_A) == LOW){
Keyboard.press('a');
delay(100);
Keyboard.releaseAll();
delay(200);
}
여기서, 일정 딜레이 시간을 둔 이유는 키보드 누름 간격을 주기 위해서 입니다. 딜레이 시간이 없게 되면 너무 빠른 속도로 눌러지기 때문에 a라는 키가 연속으로 눌러지는 현상이 발생합니다. 너무 길레 딜레이를 주면 지연이 발생하고 너무 짧게 딜에이를 주면 연속 키 누름 현상이 발생합니다. 위 시간값은 제 편의상 지정한 값임으로 키 누름 딜레이 시간을 조절해 보세요. 참고로 실제 키보드 보다는 좀 느린 편입니다. 키누름 반응속도가 좀 느린 편이죠. 그런데 너무 짧게 하면은 정상적인 알파벳 입력이 어려우니깐 상황에 따라서 조절하시면 될 듯 싶습니다. 참고로, 게임 조종기로 사용할 경우는 반응속도를 높이기 위해서 딜레이 간격을 줄이는게 좋습니다.
나머지 스위치 버턴도 위와 같이 동일하게 코딩하면 됩니다.
3) 종합소스
위 안전장치 코딩과 키 누름 코딩을 합쳐서 종합해 보면 아래와 같이 코딩을 할 수 있습니다.
#include <Keyboard.h>
const byte Pin_A = 3;
const byte Pin_B = 4;
const byte Pin_Return = 5;
const byte Pin_Backspace = 6;
const byte interruptPin = 2;
boolean state = true;
void setup() {
Serial.begin(9600);
Keyboard.begin();
pinMode(Pin_A, INPUT_PULLUP);
pinMode(Pin_B, INPUT_PULLUP);
pinMode(Pin_Return, INPUT_PULLUP);
pinMode(Pin_Backspace, INPUT_PULLUP);
pinMode(interruptPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(interruptPin), exchange, FALLING);
}
void loop() {
if(state==false){
if(digitalRead(Pin_A) == LOW){
Keyboard.press('a');
delay(100);
Keyboard.releaseAll();
delay(200);
}
if(digitalRead(Pin_B) == LOW){
Keyboard.press('b');
delay(100);
Keyboard.releaseAll();
delay(200);
}
if(digitalRead(Pin_Return) == LOW){
Keyboard.press(KEY_RETURN);
delay(100);
Keyboard.releaseAll();
delay(200);
}
if(digitalRead(Pin_Backspace) == LOW){
Keyboard.press(KEY_BACKSPACE);
delay(100);
Keyboard.releaseAll();
delay(200);
}
}
}
void exchange() {
state=!state;
}
스위치 버턴 누름을 delay()함수로 간단히 테스트 했기 때문에 각 키간의 지연 렉이 좀 발생할 수 있습니다. 시간 간격이 워낙 짧기 때문에 체감이 잘 안될 수 있지만 더 정교하게 제어를 원하시는 분이라면 delay()함수 대신에 저번에 설명한 millis()함수로 이용하여 delay()함수 없이 delay를 제어하는 방법으로 코딩을 수정하면 됩니다.
이 코딩은 단지 동작을 테스트 할 목적의 코딩이기 때문에 복잡한 동작 명령은 생략합니다.
4. 결과
스위치버턴은 말풍선의 순서대로 해당 키보드 키명령을 가지고 있습니다. 해당 스위치 버턴을 해당 명령을 PC에서 수행합니다.
결과는 아래 동영상에서 확인하시면 됩니다.
마무리
완벽하지는 않지만 그래도 키보드 같은 느낌으로 제작이 되었네요. 코딩을 좀 더 정리를 해야 하고 약간 문제되는 부분을 수정 보안해야 하는데 간단한 실험을 하기 위해서 코딩은 수정하지 않았습니다. 그리고, 아두이노 IDE 예제들에서 USB 예제를 보시면 키보드 예제 소스들이 몇가지가 있는데 재밌는 예제들인데 한번 전부 다 테스트 해보셨으면 합니다. 꽤 재밌는 원리가 숨어 있습니다. 예제 중 하나만 설명드리면 아두이노 마이크로가 아두이노 IDE 창에 새로운 창을 띄우게 하고 아두이노 자기 스스로 코딩을 하고 그 코딩을 직접 아두이노 마이크로에 업로드를 시킵니다. 그리고 아두이노 마이크로가 직접 스스로 코딩한 명령을 수행하게 됩니다. 이게 뭘 의미 하냐면 아두이노 마이크로가 자기 스스로 업그레이드를 수행한다는 것을 의미합니다. 만약, 학습능력을 아두이노 마이크로에 코딩되어 있으면 그 학습에 의해서 스스로 자기 자신을 코딩하여 업그레이드가 가능해진다는 의미가 됩니다. 한마디로 아두이노 마이크로가 스스로 진화할 수 있다는 의미와 같습니다.
자기 자신에게 프로그램을 코딩하고 이식한다는 것은 엄청난 원리입니다. 스스로 업그레이드를 할 수 있다는 의미이고 진화를 할 수 있다는 의미와 같습니다. 한번 이 원리를 상상의 나래를 펼쳐 보세요.
(jjangjjangman 태그 사용시 댓글을 남깁니다.)
호출에 감사드립니다! 즐거운 스티밋하세요!
짱짱맨 방문에 감사합니다.
대단하시네요 ..우리아기들이 게임으로 즐기는 구글 코딩이 이런데 쓰이는 거군요!!ㅎㅎ
내년 코딩의무화 교육에도 아두이노 교육이 이루어 질꺼에요.