추천글
1. 모양 구상
2. 키캡 구입
3. 디자인
4. 키 배열 ← 여기까지 1단계 : http://www.kbdmania.net/xe/tipandtech/8635141
5. 조립 & 납땜 ← 여기까지 2단계 : http://www.kbdmania.net/xe/tipandtech/8639304
6. 아두이노 프로그래밍 ← 여기까지 3단계
마지막으로 소프트웨어를 만들어 넣어주는 것으로 키보드에 생명을 창조시키겠습니다. 음화화
---
개념적으로 볼 때 첫째 시간에는 우선 키보드 모양을 정한 뒤 키를 약간개의 그룹으로 묶는 일을 했습니다.
둘째 시간에는 앞서 정한 묶음을 실제로 10k옴 저항을 이용해서 줄줄이 엮었습니다. 그림으로 보자면 납땜 하다보니 좌우 반전되어 이런 식이 됐겠죠.
그럼 이번 시간에는 아래 그림처럼 만들 필요가 있습니다.
첫째 시간에서 그린 그림에 둘째 시간에 엮은 순서를 적어둔 상태지요. 이 순서를 적어두면 프로그래밍이 훨씬 쉬워지니 메모해두도록 합시다.
---
지난 시간의 마지막 단계는 "시리얼 모니터"를 이용해서 1023 1023 1023 하는 숫자들이 막 들어오고 키를 누르면 어떤 숫자가 1023보다 낮아지고 그랬었는데요. 예를 들어서 제 경우에는 키보드에 달려있는 실제키 A키를 누르면
이렇게 A2가 210으로 낮아지는걸 볼 수 있습니다. 이거는 물론 우연히 제 경우에 A키가 아날로그2번 묶음에 붙어있었고, 두 번째 키에 붙어있어서 210이 들어온것이니 여러분들이 A키를 누를 때와 숫자의 위치나 값은 다를 수 있습니다. 하지만 이런 식으로 특정 키를 누르면 특정 항목의 값이 변하게 됩니다.
그러면 이거를 프로그래밍 하기 좋게 패턴으로 만들어두도록 합시다.
오늘 한다고 했던 세번째 그림에서 보시면 제 경우 A키는 초록색 키(A2)의 2번째에 있습니다. 그러면 실제 키보드의 캡스락 키를 누르면 A2의 1번 값이 들어오리라는걸 알 수 있습니다. A는 2번째, 탭은 3번째, Q는 4번째, 1은 5번째 하는 식이겠죠.
이걸 순서대로 눌러서 A2값이 어떻게 변하는지 확인해보겠습니다.
1번 : 15
2번 : 210
3번 : 340
4번 : 433
5번 : 504
6번 : 559
7번 : 603
8번 : 640
9번 : 670
10번 : 697
11번 : 720
12번 : 739
안누름 : 1020
+- 2 정도 오르락 내리락 하는데 대충 이런 값이 나온다는 것을 발견했습니다. 이건 물론 제 경우입니다. 지난시간 10k옴인줄 알고 쓴 저항이 사실 9.1k옴이어서 아마 여러분 보다 값이 조금씩 높을 것 같습니다. 아두이노 보드나 여러가지 요인에 따라서 조금씩 다를테니 직접 눌러보셔야합니다.
이번엔 다른 키들이 달려있는 A0쪽을 한 번 재어보겠습니다. 이쪽도 역시 +- 2정도 차이는 있어도 똑같습니다. A0 A1 A2 A9 A10 전부 똑같네요. 헤헤... 저항을 한 가지 종류만 써서 문제 없이 다 달았다면 이렇게 핀 관계 없이 같은 단계를 갖는 값이 받아집니다. 설명할 수 없는 요인에 의해서 아날로그핀마다 값이 다르게 찍힐 수 있는데 각 단계만 구분된다면 문제는 없습니다. 하지만 A0에 붙인 스위치를 눌렀는데 A0이랑 같이 A1도 변한다든지 한다면 땜질을 잘못 하신거니 해당 부분 땜을 다시 살펴보시기 바랍니다.
참고로, 사용한 저항은 10k옴 한 종류인데 저항을 안달은 15에서 -> 하나 달은 210에는 195만큼 값이 차이나는데 점점 달아나갈 수록 값의 차이가 줄어들어서 11개 일땐 720이지만 -> 12개일땐 739로 19밖에 차이가 안나지요. 이래서 '아날로그 핀' 하나에 키를 13~14개밖에 달 수가 없습니다. 그 이상 달면 값 차이가 별로 나지 않아서 키를 구분할 수가 없으니까요. 굳이 키를 더 달고 싶으시면 처음에는 10k옴보다 값이 낮은 3k옴 정도를 사용하기 시작해서 뒤에는 10k옴보다 값이 높은 30k옴 정도를 사용하면 값 차이를 구분할 수 있으니 키를 몇 개 더 연결할 수 있겠습니다만 그 밖의 여러 가지 문제로 인해서 별로 추천하지는 않습니다.
자 아무튼 그럼 이제 각 키들 값도 알았으니 프로그래밍 하는 일만 남았네요. 아무래도 키보드 매니아에는 코딩 하시는 분들이 많이 계실 것 같아서 전혀 배운적이 없는 제가 팁이랍시고 적는게 좀 부끄러워지는데 뭐 그래도...
---
우선 아두이노는 아래와 같은 방식으로 동작합니다.
1. 변수 선언 부분
▷ 시킬게 없으면 생략 가능.
2. 최초 전원 인가시 1회만 동작하는 부분 : void setup() { }
▶ 시킬게 없어도 생략하면 안됨. 틀은 놔둬야됨.
3. 이후 무한히 반복할 부분 : void loop() { }
▶ 시킬게 없어도 생략하면 안됨. 틀은 놔둬야됨.
예를 들어서...
int WAIT = 10;
void setup() {
pinMode(13, OUTPUT);
}
void loop() {
digitalWrite(13, 1);
delay(WAIT);
digitalWrite(13, 0);
delay(WAIT);
}
라고 프로그램을 짰다고 치면
int WAIT = 100; // "WAIT" 라는 이름의 변수(=임시로 값을 기억시켜둘 공간)를 선언
void setup() { // 전원 인가시 최초 1회만 동작할 부분
pinMode(13, OUTPUT); // 13번 핀을 출력 핀으로 사용한다고 선언
} // 전원 인가시 최초 1회만 동작할 부분 끝
void loop() { // 이후 무한히 반복될 부분
digitalWrite(13, 1); // 13번 핀에 디지털신호 1 = 켜짐을 전송
delay(WAIT); // "WAIT" 라는 이름의 변수에 적혀진 숫자만큼 대기
digitalWrite(13, 0); // 13번 핀에 디지털신호 0 = 꺼짐을 전송
delay(WAIT); // "WAIT" 라는 이름의 변수에 적혀진 숫자만큼 대기
} // 무한히 반복될 부분 끝
와 같이 되어, 100ms에 한 번씩 13번 핀에 5볼트가 들어갔다 안들어갔다 반복하는 아두이노가 만들어집니다. 13번 핀에 LED의 +극 다리를 끼우고, -핀에 LED의 -극 다리를 끼우면 깜빡이는 LED가 만들어지는 것입니다.
키보드를 만들 때에도 똑같습니다. 실제로 해보도록 하겠습니다. 앞서 봤던 세번째 키보드 그림을 다시 보겠습니다.
보라색 A0핀을 할 생각입니다. 저는 지금 z - w - esc - 2 - 4 - 6 - y - 8 - i - 0 - 윗방향키 - b 순서로 저항을 붙여놨네요.
그리고 앞서 측정해둔 스위치 단계별 값도...
1번 : 15
2번 : 210
3번 : 340
4번 : 433
5번 : 504
6번 : 559
7번 : 603
8번 : 640
9번 : 670
10번 : 697
11번 : 720
12번 : 739
안누름 : 1020
이제 코딩을 짜면...
void setup() {
pinMode(A0, INPUT_PULLUP);
}
void loop() {
if (analogRead(A0) < 20) {
Keyboard.press('z');
}
else if (analogRead(A0) < 220) {
Keyboard.press('w');
}
else if (analogRead(A0) < 350) {
Keyboard.press(KEY_ESC);
}
else if (analogRead(A0) < 450) {
Keyboard.press('2');
}
else if (analogRead(A0) < 520) {
Keyboard.press('4');
}
else if (analogRead(A0) < 570) {
Keyboard.press('6');
}
else if (analogRead(A0) < 620) {
Keyboard.press('y');
}
else if (analogRead(A0) < 650) {
Keyboard.press('8');
}
else if (analogRead(A0) < 680) {
Keyboard.press('i');
}
else if (analogRead(A0) < 710) {
Keyboard.press('0');
}
else if (analogRead(A0) < 730) {
Keyboard.press(KEY_UP_ARROW);
}
else if (analogRead(A0) < 750) {
Keyboard.press('b');
}
else {
Keyboard.release('z');
Keyboard.release('w');
Keyboard.release(KEY_ESC);
Keyboard.release('2');
Keyboard.release('4');
Keyboard.release('6');
Keyboard.release('y');
Keyboard.release('8');
Keyboard.release('i');
Keyboard.release('0');
Keyboard.release(KEY_UP_ARROW);
Keyboard.release('b');
}
}
짠! 됐습니다. 이렇게 작성하여 아두이노에 업로드 시킵니다. 키 12개를 처리했는데 생각보다 간단하죠? 주석을 달아서 다시 보겠습니다.
void setup() { // 초기 1회 부분
pinMode(A0, INPUT_PULLUP); // A0핀을 입력 핀으로 선언
} // 초기 1회 부분 끝
void loop() { // 무한 반복 부분
if (analogRead(A0) < 20) { // 만약에 A0핀의 아날로그값이 20 이하라면
Keyboard.press('z'); // 'z' 키를 누름
}
else if (analogRead(A0) < 220) { // 20 이하가 아니었다면, 혹시 220 이하라면
Keyboard.press('w'); // 'w' 키를 누름
}
else if (analogRead(A0) < 350) { // 220 이하도 아니었다면, 혹시 350 이하라면
Keyboard.press(KEY_ESC); // KEY_ESC 키를 누름
}
else if (analogRead(A0) < 450) { // 350 이하도 아니었다면, 혹시 450 이하라면
Keyboard.press('2'); // '2' 키를 누름
}
else if (analogRead(A0) < 520) { // 450 이하도 아니었다면, 혹시 520 이하라면
Keyboard.press('4'); // '4' 키를 누름
}
else if (analogRead(A0) < 570) { // 520 이하도 아니었다면, 혹시 570 이하라면
Keyboard.press('6'); // '6' 키를 누름
}
else if (analogRead(A0) < 620) { // 570 이하도 아니었다면, 혹시 620 이하라면
Keyboard.press('y'); // 'y' 키를 누름
}
else if (analogRead(A0) < 650) { // 620 이하도 아니었다면, 혹시 650 이하라면
Keyboard.press('8'); // '8' 키를 누름
}
else if (analogRead(A0) < 680) { // 650 이하도 아니었다면, 혹시 680 이하라면
Keyboard.press('i'); // 'i' 키를 누름
}
else if (analogRead(A0) < 710) { // 680 이하도 아니었다면, 혹시 710 이하라면
Keyboard.press('0'); // '0' 키를 누름
}
else if (analogRead(A0) < 730) { // 710 이하도 아니었다면, 혹시 730 이하라면
Keyboard.press(KEY_UP_ARROW); // KEY_UP_ARROW 키를 누름
}
else if (analogRead(A0) < 750) { // 730 이하도 아니었다면, 혹시 750 이하라면
Keyboard.press('b'); // 'b' 키를 누름
}
else { // 750 이하 조차 아니라면
Keyboard.release('z'); // 'z' 키를 뗌
Keyboard.release('w'); // 'w' 키를 뗌
Keyboard.release(KEY_ESC); // KEY_ESC 키를 뗌
Keyboard.release('2'); // '2' 키를 뗌
Keyboard.release('4'); // '4' 키를 뗌
Keyboard.release('6'); // '6' 키를 뗌
Keyboard.release('y'); // 'y' 키를 뗌
Keyboard.release('8'); // '8' 키를 뗌
Keyboard.release('i'); // 'i' 키를 뗌
Keyboard.release('0'); // '0' 키를 뗌
Keyboard.release(KEY_UP_ARROW); // KEY_UP_ARROW 키를 뗌
Keyboard.release('b'); // 'b' 키를 뗌
}
} // 무한 반복 부분 끝
즉, 예를 들어서 실제 키보드에 A0 핀의 일곱 번째에 연결된 키인 Y 키를 누르면 아까 시리얼 모니터로 봤다시피 A0값이 603 +- 약간의 오차가 뜹니다. 그러면 analogRead(A0) 를 통해서 A0값을 읽었을 때 20 이하인가? 아니오 -> 220 이하인가? -> 아니오 ... 해서 쭉 훑다가 570 이하인가? -> 아니오 -> 620 이하인가? -> 예! 하는 순간이 발생합니다. 그러면 Keyboard.press('y'); 가 'y' 문자를 컴퓨터로 전송해서 y키를 눌렀음(누른겁니다. 눌렀다 뗀게 아니라 누르고 있는 중)을 알리게 됩니다.
이때에 620 이하인가? 에서 답변 예!가 발생했으니 그 이후 "else" 로 시작하는 질문들은 체크하지 않고 건너뛰게 됩니다. 그럼 다시 무한 반복 부분으로 올라와서 analogRead(A0) 을 하겠죠. 아직 손가락이 키를 떼지 않았으면 또다시 620 이하인가? 에서 예! 하게 되고 'y'가 아직 눌려있다고 알려줍니다.
그러다가 손가락을 떼면 A0값은 1020 정도로 회복이 되겠죠. 그럼 620 이하인가? -> 아니오 -> 650 이하인가? -> 아니오 ... 해서 뒷부분을 마저 읽게 됩니다. 750 이하인가? -> 아니오 -> 그것조차 아니라면(else) 부분이 실행되겠지요. 여기 else에서는 A0으로 누를 수 있는 종류의 키들 전부를 손가락 뗐다고 컴퓨터에 전송합니다. 이러면서 손가락이 Y 키를 뗐다는걸 컴퓨터도 알게 됩니다.
평소 아무 키를 누르지 않은 상태에서라면 언제나 else 부분이 실행되겠구요.
A0은 다 했으니 A1도 합니다. 아날로그핀 다섯개(혹은 그 이상) + 디지털핀 네개(혹은 그 이상) 전부 자신의 상황에 맞게 복붙해서 써주면 되겠습니다.
void setup() {
pinMode(A0, INPUT_PULLUP);
pinMode(A1, INPUT_PULLUP);
pinMode(A2, INPUT_PULLUP);
pinMode(A9, INPUT_PULLUP);
pinMode(A10, INPUT_PULLUP);
pinMode(0, INPUT_PULLUP);
pinMode(1, INPUT_PULLUP);
pinMode(2, INPUT_PULLUP);
pinMode(3, INPUT_PULLUP);
}
void loop() {
if (analogRead(A0) < 20) {
}
if (analogRead(A1) < 20) {
}
if (analogRead(A2) < 20) {
}
if (analogRead(A9) < 20) {
}
if (analogRead(A10) < 20) {
}
if (digitalRead(0) == 0) {
}
if (digitalRead(1) == 0) {
}
if (digitalRead(2) == 0) {
}
if (digitalRead(3) == 0) {
}
}
지면 관계상 생략하여 Read 안쪽 내용은 생략합니다. 이런식으로 쭉 써주시면 됩니다. 컨트롤/알트/윈도우/쉬프트 이런 키들은 디지털 핀으로 하셨을텐데 디지털핀은 digitalRead 로 읽으시면 되고 값은 1 = 스위치 뗌, 0 = 스위치 누름 두가지 종류로 들어옵니다.
아참 그리고 아까 위에서 숫자나 문자는 '1', 'a' 하는 식으로 적었는데 그 외에 특수키들 - ESC키나 DELETE키나 CTRL, ALT 등등의 키들도 있지요. 이런 키들은 아두이노 자체에서 특별한 이름을 통해서 지원하거나 아니면 HID Spec에 정의된 키보드 Scancode를 전송하는 방법으로 입력할 수 있습니다.
이건 제가 고생해서 찾아낸 표준 104키 스캔코드를 표준 104키 키보드에 그려넣은 그림입니다. 아두이노에 넣었을 때 올바르게 동작하는 값으로 되어있어서 일반적인 스캔코드와는 값이 약간 다릅니다. 좋은 자료에요 이거... 보시면 KEY_LEFT_CTRL, KEY_RIGHT_SHIFT 처럼 아두이노에서 지원하는 특별한 이름이 있는 키도 있고 206, 92 처럼 스캔코드값으로 된 키도 있습니다. 요거 보시고 넣으시면 되겠습니다.
이렇게 Read한 값에 Keyboard.press와 Keyboard.release를 하는 것으로 키보드가 완성됩니다! 저거 위에꺼 다 하시고 키캡 끼우면 끝입니다. 별거 없죠?
---
그 외
진짜로 벌써 키보드 만들기는 다 끝났습니다. 나머지는 악세사리입니다.
1. 한영/한자나 !@#$^ 특수문자는?
한영은 KEY_RIGHT_ALT, 한자는 KEY_RIGHT_CTRL로 하시면 윈도우즈에서 한영/한자 기능이 별다른 설정 없이 잘 됩니다.
특수문자는 원래 SHIFT가 눌려졌다(Press)면 '1' '2' '3'이 ! @ #으로 저절로 바뀌어 들어가집니다. 스캔코드가 같거든요.
참고로 한글 역시 그냥 'a', 'b', 'c' 하시면 알아서 ㅁ ㅠ ㅊ가 잘 쳐집니다.
---
2. 반복입력은?
위에서는 Keyboard.press, Keyboard.release를 썼습니다. 누를때 눌렀다고하고 뗐을 때 뗐다고 하는건데 이렇게 해주면 운영체제가 알아서 잘 해줍니다. 키를 누르면 일정 시간 동안에는 반복입력을 피하려고 키가 한 번만 눌린 상태가 됐다가 일정시간 이후에는 일부러 꾹 눌렀구나~ 하고 글자를 다다다다 쳐주는거죠.
이거를 옛날 게임기 터보 버튼처럼 그냥 누르고 있으면 지연시간 없이 계속 반복시킬 수도 있는데요, Keyboard.write가 그 기능입니다. press와 release를 동시에 하는 기능입니다. 따라서 별도로 release 할 필요 없이 Keyboard.write('a'); 하면 그냥 a키가 팡 쳐집니다. 다만 실제로는 반복입력되는 속도가 너무 빨라서 타이핑에선 쓰기가 어려운 기능입니다. 키 한번 누르는 것으로 여러 글자나 여러 문장을 입력하게 하는 매크로로 만들 수도 있으니 검색해보시고 응용하실 수 있겠습니다.
---
3. FN키
노트북 키보드에 보통 달려있는 'fn' 키는, 추측하셨겠지만 컴퓨터와 통신하는 키가 아닙니다. 예를 들어서 fn + 1 했을때 F1 키가 눌러지게 하려면 컴퓨터에 FN과 1을 던져주면 컴퓨터가 합성해주는 방식이 아니라는거지요. 키보드에서 합성해서 F1을 컴퓨터로 던져주는겁니다.
이거는 예를 들어서 FN키가 디지털핀 0번에 물려져있고 '1'키가 디지털핀 1번에 물려져있다고 했을 때에
int fn = 0;
void setup() {
pinMode(0, INPUT_PULLUP);
pinMode(1, INPUT_PULLUP);
}
void loop() {
if (digitalRead(0) == 0) {
fn = 1;
}
else {
fn = 0;
}
if (digitalRead(1) == 0) {
if (fn == 1) {
Keyboard.press(KEY_F1);
}
else {
Keyboard.press('1');
}
}
else {
Keyboard.release(KEY_F1);
Keyboard.release('1');
}
}
처럼 Keyboard.press를 쓰는게 아니라 'fn' 이라는 변수를 선언하고, 키가 눌렸을 때 그 값을 0이나 1로 만드는 것으로 키보드 내부에서만 입력했나 안했나를 정한 다음에 FN키와 합성시킬 키들 각각에 'fn' 이라는 변수가 0이라면~ 1이라면~ 으로 구분을 해서 '1'을 넘기거나 KEY_F1을 넘기거나 할 수 있습니다.
---
4. 넘락/캡스락 LED는?
LED 자체는 일단 필요하신 위치에 붙이시고 -극은 다른 모든 키들의 한쪽 다리가 붙어있는 것과 마찬가지로 -극에 붙이시고, +극은 아두이노의 디지털핀 아무데나에 갖다 붙이시구요... 예를 들어서 13번 핀에 붙였다 하면
digitalWrite(13, 0); // 꺼짐
digitalWrite(13, 1); // 켜짐
으로 제어할 수 있습니다. 아두이노는 아주 쩔기 때문에 빛의 밝기 조절도 가능한데요
analogWrite(13, 0); // 꺼짐
analogWrite(13, 255); // 켜짐
처럼 0~255 단계로 빛 밝기를 변화시킬 수 있습니다.
다만 주의하실 점으로 저항 없이 아두이노 핀에 LED를 바로 붙이면 5볼트가 들어가지기 때문에 LED가 녹습니다. 이럴때는 200~300옴 정도의 저항을 하나 붙이시거나 아니면
analogWrite(13, 128);
과 같이 빛 밝기를 128을 넘지 않게 쓰시면 되겠습니다.
근데 이거보다 더 중요한건 넘락과 캡스락이 켜졌는지 꺼졌는지 신호를 컴퓨터에서 받아오는 방법인데요... 이건 아두이노에서 기본으로 지원해주질 않는 기능입니다. !? 그래서 검색을 한참 해보니 외국인이 이 기능을 사용할 수 있게끔 해둔 패치가 있는데요~ 그 패치는 키보드매니아 자료실에서 아두이노로 검색해보시면 제가 올려둔게 있습니다.
해당 패치를 아두이노 1.0.2에 덮어씌우시면 LED 상태를 받아올 수 있는 명령어가 추가됩니다. 1.0.2에만 가능하기 때문에 그 이상 높은 버전의 아두이노에서는 사용하실 수 없습니다.
해당 패치를 덮어씌우신 다음 Keyboard.getLedStatus() 로 현재 LED 상태를 알 수 있는데요, 다꺼짐0 넘락1 캡스락만2 넘락캡스락3 으로 네가지 구분을 받아올 수 있습니다. 예를 들어서
if (Keyboard.getLedStatus() >= 2) { analogWrite(13, 128); }
else { analogWrite(13, 0); }
이렇게 하시면 13번에 연결된 LED를 캡스락 LED로 쓰실 수 있습니다.
---
5. 보다 효율적인 스캔
앞서 설명드린 if문은 analogRead를 매 줄 진행합니다. 요거 할 때 부담(딜레이)이 좀 있습니다. 그래서 키가 많아지고 코드가 복잡해지면 가끔 빠르게 입력하는 키가 씹히거나 할 수 있습니다. 그러니까 이걸 변수로 지정해서 한 번에 몰아서 스캔하는 방법이 있는데요
int AR0 = 0;
void setup() {
pinMode(A0, INPUT_PULLUP);
}
void loop() {
AR0 = analogRead(A0);
if (AR0 > 1000) {
Keyboard.release();
}
else if (AR0 < 20) { }
else if (AR0 < 220) { }
}
처럼 AR0이라고 앞에서 변수를 선언한 다음 1바퀴 돌 때 1번만 스캔하면 전체적인 루프 시간이 빨라집니다. 또 아무 키를 입력하지 않은 " > 1000" 부분을 맨 앞으로 오게 하고, 그 뒤로 키 입력이 생기는 낮은 숫자를 배치하면 평소 아무 키도 안눌렀을 때 if 체크가 많이 생략되어 조금 빨라집니다.
또한 키 체크 하실때에도 예를 들어 (AR0 < 220) 보다는 (AR0 > 205 && AR0 < 215) 처럼 오차범위 내에서 최대한 줄여서 if를 체크하시면 혹시 생길 수 있는 고스트키 현상을 최대한 억제할 수 있습니다.
---
6. 매크로
지금 스위치를 누르면 원하는 글자가 입력되고 스위치를 떼면 입력이 멈추는 장치를 만들었는데 매크로를 지원합니까? 같은 이야기를 해선 곤란하겠죠.
디지털핀0번에 연결된 키를 누르면 메모장을 실행하고, "Hello, world!" 하고 말 한 다음 5초 기다리고서 www.google.com 을 인터넷 익스플로러로 띄우는 기능을 만들어봅시다.
void setup() {
pinMode(0, INPUT_PULLUP); // 0번 핀을 입력핀으로 선언합니다
}
void loop() {
if (digitalRead(0) == 1) { // 0번 핀이 스위치가 안눌려서 5볼트 그대로면
} // 암것도 안합니다
else { // 스위치가 눌리면
Keyboard.releaseAll(); // 일단 혹시 모르니 다른 모든 키를 뗌 처리합니다
Keyboard.press(KEY_LEFT_GUI); // 윈도우 키를 누릅니다
Keyboard.press('r'); // R키를 누릅니다 (윈도우+R = 실행)
Keyboard.releaseAll(); // 키 다시 뗍니다
Keyboard.println("notepad"); // notepad하고 적습니다. 지금 실행창 열린 상태니 이러면 메모장이 실행되겠죠.
// print 하시면 그냥 입력만 되고, println 하시면 입력+엔터 됩니다
delay(500); // 메모장 열리는동안 0.5초간 기다립니다
Keyboard.println("Hello, world!"); // 글자 타이핑합니다
delay(5000); // 5초간 기다립니다
Keyboard.press(KEY_LEFT_GUI); // 윈도우
Keyboard.press('r'); // R
Keyboard.releaseAll(); // 키 떼고
Keyboard.println("iexplore http://www.google.com"); // 익스 실행
}
}
---
도전과제 :
키보드로 마우스 만들기
ALT키와 GUI(Windows) 키 합치기
일반 숫자를 넘버패드 숫자 스캔코드로 입력되게 하기
키를 1초 이내에 한 번 치면 ㄱ, 두 번 치면 ㅋ, 세번 치면 ㄲ로 입력하게 하기
가능합니다.
스위치에 LED를 심으시고 LED -극은 스위치와 마찬가지로 전부 -극에, +극만 싹 모아서 아두이노 디지털 핀 하나 남는거에 붙이신 뒤 특정 키가 눌리면 일단 맨 위에 int key_led = 0; 선언해주고
if (key_led = 0) {
key_led = 1;
analogWrite(LED달린핀번호, 128);
}
else {
key_led = 0;
analogWrite(LED달린핀번호, 0);
}
하는식으로 껐다켰다 하실 수 있습니다. 아두이노 '비틀' 말고 '늘소미노 하나'처럼 디지털 핀이 충분히 있는 보드를 쓰시면 전체-일부키만-다끔 순서대로 돌아가게 하시거나 2색 3색 LED에 다리 각각을 다른 디지털핀에 붙여서 변색을 하실 수도 있습니다.
재미있고 유익한 글입니다.
일반 pcb 기판을 봤을때 배선이 연결을보고, 저항이 없이
그냥 pcb 안에서 스위치들을 연결하는 전선 정도로만 생각하였는데
장터에 나오는 기판들도 pcb안에 각 라인별로 저항을 가지고 있는건지 궁급합니다.
주옥션 풀와이어링 같은걸 보면, 별도의 저항없이
테프론 와이어로 연결하는데 차이가 있는지 궁금하네요 ^^
저도 다른 기성 키보드들의 동작 원리는 잘 모르는데요... 대략 대체로 키보드는 매트릭스 구조로 키를 인식하는 것 같습니다. 예를 들어 가로 10핀 * 세로 10핀의 바둑판 형식의 격자를 쳐놓고 각 교차점마다 스위치를 올려놓은 뒤 모든 선은 한쪽 방향으로만 전기를 흐르게 배치하면 스위치를 눌렀을 때 전기가 통하는 핀을 추려내서 격자에서의 X값과 Y값을 알 수 있으니 해당 키를 호출할 수 있는 형식으로 만들 수도 있습니다. 이렇게 하면 저항 대신 다이오드를 사용하게 되겠지요.
정확히 이러한 원리인지는 모르겠으나 뭐 저항을 쓴 방식으로도 되고~ 더 간단하고~ 다리 갯수도 매트릭스는 최소 구현하려는 키 갯수의 제곱근의 두배(100키라면 10*10, 20개)가 필요한데 저항 방식은 그의 절반 정도로도 가능하니까요. 특히 상용 컨트롤러의 매트릭스 따기는 스위치마다 두 개의 지정된 선을 달아줘야하지만 저항을 쓰면 스위치 다리 두 개 중에 하나는 그냥 다 공통이고, 다른 하나도 몇 개의 키 묶음끼리 저항으로 연결할 뿐 순서나 지정된 핀같은게 없어서 납땜도 수월해집니다.
무엇보다 마이크로 컨트롤러의 프로그래밍을 쉽게 맘대로 할 수 있는 점도 큰 장점입니다. 풀와이어링의 경우는 이미 공장에서 프로그래밍이 된 컨트롤러를 이용하므로 다리 여러개 가운데 정확히 두 개를 특정 키에 맞춰서 붙여줘야하는 번거로움이 있지만 아두이노로 하게 되면 아무 다리나 붙여놓고 나중에 해당 값에 알맞은 키를 지정하면 되니까 제작시에 하드웨어적인 수고가 줄어듭니다. 각종 매크로를 만들어넣거나 키 편집도 가능하고 마우스를 넣을 수도 있고 아무튼 그렇습니다.
기성품 키보드는 '키보드 매트릭스' 로 구성되어 있습니다. 가로 세로 격자 형태의 회로를 짜서 지정된 두 핀이 붙으면 해당 위치의 키가 눌렸다고 체크하는 방식입니다. 제 글에는 저항을 이용한 아날로그-디지털 컨버팅(ADC) 방식으로 구현을 하고 있습니다. 원래는 제가 이 방법을 생각할 때 키보드 매트릭스가 뭔지 몰라서 그냥 무턱대고 저항으로 만들었던거지만 아무튼 저항 방식은 키보드 구현에 필요한 접점 수가 아주 적어지고(=비용절감) 납땜과 같은 제작 수고가 파격적으로 줄어들고 머리를 덜 써도 되므로 키보드 컨트롤러 자작에 효과적이라고 할 수 있겠습니다 ㅎㅎ
그리고 저항으로 아날로그-디지털 컨버팅을 했다고 생각하면 어쩐지 아날로그 같아서 기분이 좋아지거든요 ㅋㅋ
본문에서 사용한 아두이노는 이두이노 레오나르도인데 16MHz로 동작합니다. 이걸로 부족하다면 보드 사이즈가 좀더 커지지만 기능이 훨씬 빵빵한 고성능 아두이노 보드(메가2560, 갈릴레오2 등)를 이용하는 방법도 있습니다. 똑같은 아두이노 플랫폼인데 클럭이 말도 못하게 높거든요
이때 주의하실점은 PWM 출력이 되는 핀(보드에 보시면 ~기호가 인쇄된 핀)과 안되는 핀이 있는데 PWM이 되어야 밝기의 단계 조절이 가능합니다. 안되는 핀에서는 켜고 끄는 것 밖에 안되니 PWM이 안되는 디지털 핀을 키에 할당하고 PWM이 되는 핀을 LED에 주어야 겠지요.
켜고 끄는 코딩은 상황에 따라 다를테니 예를 여기에 적지는 않겠습니다~
음 안그래도 쪽지가 한통 와서 여차저차 더 생각해본 뒤 정확히 말씀드리면요~
본문에 소개된 방법은 기판 없이 손납땜을 쉽게 하기 위해서 10k옴 저항을 스위치간 직렬로 줄줄이 연결하고 있습니다. 그러니까 예를 들어서 세번째 스위치를 누르면 두번째의 10k옴 저항을 거친 뒤에 세번째 스위치의 10k옴 저항까지 하여 10+10 = 20k를 아두이노 보드에 전달하게 됩니다. 이렇게 하는 경우 여기에 LED를 붙이면 저항이 너무 높아서 LED가 켜지질 않습니다.
저항을 직렬이 아니라 병렬로 연결하는 방법도 있습니다. 두번째 스위치(첫 저항)엔 10k옴을, 세번째 스위치(둘째 저항)엔 20k옴을, 네번째 스위치(셋째 저항)엔 30k옴을 각각 따로 붙이는 방법입니다. 키 동작 자체는 앞선 방법과 똑같지만 스위치 서로를 연결하여 땜이 간편한 직렬에 비해 매번 (아날로그 핀에 연결되는) 중앙선에 땜을 해야하며 저항을 스위치마다 구분해서 달아야 하기 때문에 기판없는 손땜에선 귀찮은 방법입니다.
단, 키를 누르고 있을 때 LED도 켜지게 하기 위해서는 저항을 병렬로 써야합니다. 아래 그림을 보시면 위에서부터 1번은 본문과 같은 직렬(기판 없이 손뗌 하기 가장 편리), 2번은 병렬, 그리고 3번은 LED를 달았을 때의 회로도입니다. (3번 회로는 LED 장착으로 인해 저항값이 낮아짐에 주의)
단 이때에는 LED가 불을 켜기 위해 기본적으로 먹는 전기가 있기 때문에 AD값이 반 정도로 줄어드는 관계로 아날로그핀 1개에 물릴 수 있는 키의 개수도 반으로 줄어듭니다. LED가 없으면 1핀당 최대 14개 정도까지 붙일 수 있지만 LED를 달면 그 절반인 7~8개 정도밖에는 붙일 수 없습니다. 늘소미노 하나를 쓰면 아날로그 핀이 9개 있으므로 텐키리스 정도까지는 제작할 수 있게 됩니다. 만약 풀사이즈 키보드를 만들려면 키보드에 USB 허브를 내장하여 아두이노 보드를 두 개 넣고 내부적으로는 키보드를 반으로 쪼개서 두 개로 작업하시면 되겠습니다.
+ 방금 실제로 테스트해본 결과 아두이노 핀을 5V에 220~270옴 저항으로 쇼트시키는 '풀업' 이 필요하며, 각각의 스위치에는 LED와 함께 0(직결) ~ 최대 300옴 까지, 즉 30옴 간격의 저항을 사용하여 최대 10키 묶음이 가능합니다. LED 밝기는 저항이 낮을 수록 밝고(그림의 스위치1, 직결 상황으로 analogRead값 약 500), 저항이 높을 수록 어둡지만(그림의 스위치5, 350옴일때 analogRead 값 930) 육안으로 구분 불가할 정도로 적은 차이였습니다.
아.. 따라 할 수 있을까요??
열손가락이 엄지인 사람이라 정말 재주가 부럽습니다.
잘 읽었습니다. 언젠가는 아마,... 시도해 볼 수 있을거 같네요.
자세한 강좌 감사합니다.
대단하십니다. 저도 아두이노랑 atmega128이랑 cortex-m 시리즈를 공부하고 있는 입장에서 직접 만들어보면 정말 재밌을것 같습니다.
실제로 기계식 키보드에 몇몇 제품에 cortex-m 시리즈가 들어간다고 하더라구요.
키를 입력받을 때 adc 말고 단순 디지털 인풋으로 하면 훨씬 더 빠르게 입력을 받을 수 있지않을까요? 아무래도 꽤 많은 키들의 adc변환을 하려면 단순 디지털 io 핀 값을 읽어오는 것보다 딜레이가 많을 것 같아서요
입력속도는 사실 큰 문제는 아닌 것 같습니다~ 입력속도의 경우 오히려 ADC와 저항을 이용하면 한 핀에 연결된 여러 개의 키를 한 번만 읽고 넘어가므로 루프 속도가 빨라져서 모든 키를 전부 체크하는 것 보다 빨라집니다.
물론 모든 키를 디지털로 연결해도 되고, 그게 동시키입력도 한 방에 해결하는데다 저항같은 별도의 부품이 없어도 되니까 좋긴 합니다만 키 갯수만큼 컨트롤러의 핀이 있어야 하기 때문에 미니키보드라도 최소 60키는 필요하니 다리 갯수가 부족하여 구현이 어려울 것 같습니다. 숫자키패드나 게이밍키패드라면 디지털로 바로 받아도 좋을 것 같습니다.
속도 얘길 다시 하면, 오히려 제가 자작해서 몇 달 써보니, 문제는 아두이노 CPU의 성능인 것 같은데요... 반복입력이 약간 거시기합니다. 그러니까 A키를 누르면 아두이노가 코드를 위에서부터 한줄씩 읽다가 어느줄에서 눌린걸 감지해서 keyboard.press("A")가 되고, 루프를 쭉쭉 돌다가 A키가 떨어져있으면 keyboard.release("A") 를 하는 걸텐데 이 루프 속도가 생각만큼 빠르지가 않은 것 같습니다. A키를 아주 빠르게 연속해서 누르면 release를 감지하기 전에 다시 press가 들어오니 그냥 꾹 누르고 있는 동작으로 처리를 합니다. 일반적인 글자키에서는 같은 키를 연속해서 누르는 경우가 잘 없으니까 사용에 문제가 없지만 딜리트나 백스페이스 키 같은 경우 빠른 속도로 여러번 누르는 경우가 많아서 이때에는 다섯 번 누르면 네 번만 먹히거나 하는 문제가 있습니다.
실제로는 이러한 사용을 하는 키가 몇 개 없기 때문에 해당 키만 press / release가 아니라 write를 이용해서 반복입력시 즉각적으로 처리하게 쓰고 있습니다.
헤에... 아두이노 막강하군요 =ㅁ=... 그런데 클럭이 조금 걱정인데... 아예 라즈베리파이나 arm7 이상으로 본문 강좌처럼 hid 키보드를 만들순 없나요
마지막 강좌이군요..
아주 디테일한 강좌 감사합니다.
혹시 전체 full LED 도 가능한가요??
위에 설명하신 fn키 조합으로 On/off 하면 될듯 한데..
만약 가능하다면 응용해서 특정 키들만 LED On/Off도 가능할듯 할듯 보이는데요..