아두이노 Pro Micro 매크로 키보드, 마우스


* 경고 *

해당 포스팅은 교육적인 목적입니다. 절대 악용, 남용 하면 아니되옵니다.

 

안녕하세요 라이프온룸 입니다. !!ㅎㅎ 

오늘은 아두이노 Pro Micro로 들어온 시리얼 데이터로 매크로 Keyboard, Mouse 를 만들어 보겠습니다.

준비물은 간단합니다. Arduino Pro Micro 만 있으면 되요 ㅎㅎ

아두이노 Pro Micro는 Arduino Leonardo 호환 보드로 Atmega32u4 칩에 프로그램이 가능합니다. 그래서 시리얼 HID 모두 가능하지요 ! 다만 5V 버전과 3.3V 이 존재합니다. 5V 버전의 경우 16MHz로 동작하는데 아래 보면 오실레이터 부분에 5V인 경우에는 16 이라는 숫자가 보일 거고 3.3V 일 경우에는 8 이라는 숫자가 보일 겁니다. 

Arduino Pro Micro 5V and 3.3V version

주의할 점은 아두이노 IDE에서 보드를 잘 못 선택하고 스케치를 업로드 하면 벽돌이 될 수 있습니다…. 저의 경우 3.3V 의 부트로더가 Lily Pad Arduino Usb 였는데 Arduino/Genuino Micro 로 스케치 업로드 했다가 바로 벽돌 됐습니다. ㅜㅜ 복구하는 방법은 아래 URL을 참고 하세요. 간단히 설명드리자면 RST 와 GND를 두번 쇼트 시키고 8초 안에 빈 스케치를 주입하시면 됩니다.

https://learn.sparkfun.com/tutorials/pro-micro–fio-v3-hookup-guide/troubleshooting-and-faq

여튼 그래서 무슨 보드를 쓰는지 잘 숙지하고 있어야 하고 스케치 업로드 할 때 주의하셔야 합니다.  Pro Micro는 잘 못 하면 벽돌이 잘 된다고 하네요 ㅜㅜ 

 

저의 경우 5V 버전으로 작업 했는데요 아두이노를 연결하면 아래처럼 Leonardo 라고 인식합니다. 이렇게 인식이 되면 일단 정상 동작 하는 겁니다. 

1. 아두이노 코드 

자 그럼 이제 먼저 아두이노 코드를 짜볼게요 

/*
/mMo/12,12|
/mlC/p|
/mlC/r|
/mKey/hello|
*/

#include "Mouse.h"
#include "Keyboard.h"
#include "HID.h"

int x,y = -1;

void setup() {
  Serial.begin(9600);

  // Sends a clean report to the host. This is important on any Arduino type.
  Keyboard.begin();
  Mouse.begin();
}

void loop() {
  if (Serial.available())
  {
    String mouseString = Serial.readStringUntil('|');
    mouseString.trim();
    if (mouseString.charAt(0) == '/')
    {
      // send like this - "/mMo/5,5"
      if (mouseString.substring(1, 4).equals(F("mMo")) == true)
      {
            //-------------------------------   
            int slashIdx = mouseString.indexOf('/', 1);
            if (slashIdx > 0)
            {
              int commaIdx = mouseString.indexOf(',', slashIdx + 1);
              if (commaIdx > 0)
              {
                x = mouseString.substring(slashIdx + 1, commaIdx).toInt();
                y = mouseString.substring(commaIdx + 1).toInt();
                
              }
              else
              {
                x = mouseString.substring(slashIdx + 1).toInt();
                y = 0;
              }
              Mouse.move(x, y, 0);
              //Serial.print(x);Serial.print(':');Serial.println(y);
            }
            //-------------------------------         
            Serial.println(F("move"));
      }
      else if (mouseString.substring(1, 4).equals(F("mlC")) == true)
      {
            //-------------------------------   
            int slashIdx = mouseString.indexOf('/', 1);
            if (slashIdx > 0)
            {
              char action = mouseString.substring(slashIdx + 1)[0];
              if ((action == 'p') && (!Mouse.isPressed(MOUSE_LEFT)))
              {
                Mouse.press(MOUSE_LEFT);
                Serial.println(F("Pre"));
              }
              else if ((action == 'r') && (Mouse.isPressed(MOUSE_LEFT)))
              {
                Mouse.release(MOUSE_LEFT);
                Serial.println(F("Rls"));
              }
            }
            else
            {
              Mouse.press();
              delay(400);
              Mouse.release();
              Serial.println(F("Click"));
            }
            //-------------------------------         
      }
      else if (mouseString.substring(1, 4).equals(F("mrC")) == true)
      {
            //-------------------------------   
            int slashIdx = mouseString.indexOf('/', 1);
            if (slashIdx > 0)
            {
              char action = mouseString.substring(slashIdx + 1)[0];
              if ((action == 'p') && (!Mouse.isPressed(MOUSE_RIGHT)))
              {
                Mouse.press(MOUSE_RIGHT);
                Serial.println(F("Pre"));
              }
              else if ((action == 'r') && (Mouse.isPressed(MOUSE_RIGHT)))
              {
                Mouse.release(MOUSE_RIGHT);
                Serial.println(F("Rls"));
              }
            }
            else
            {
              Mouse.press(MOUSE_RIGHT);
              delay(400);
              Mouse.release(MOUSE_RIGHT);
              Serial.println(F("Click"));
            }
      }
      else if (mouseString.substring(1, 5).equals(F("mKey")) == true)
      {
            //-------------------------------   
            int slashIdx = mouseString.indexOf('/', 1);
            if (slashIdx > 0)
            {
              
              Keyboard.print(mouseString.substring(slashIdx + 1));
            }
            //-------------------------------         
            
            Serial.println(F("Key"));
      }      
      
    }
  }
}

 

 

아래 명령예시 입니다. 마우스 이동, 좌클릭, 우클릭, Keyboard 타자를 지원 합니다. 

  • /mMo/12,12| – 마우스를 현위치에서 12, 12, 만큼 움직임
  • /mlC/p| – 현재 좌표 마우스 누르기
  • /mlC/r| – 현재 좌표 마우스 때기
  • /mKey/hello| – hello 그대로 Keyboard 입력 

요 상태에서 아래 설정으로 스케치를 업로드를 합니다. (5V 16MHz 사용 중)

  • 보드 : Arduino/Genuino Micro
  • 포트 :COM21 (Arduino/Genuino Micro)

업로드 후 시리얼 모니터를 열고 위 예시를 시험해보세요 !! 명령이 성공하면 명령 종류에 따라서 String을 Serial로 Return 해 줍니다. ㅎ

2. Python Code 

그럼 윈도우에서 Python Code로 키보드 ‘F4’를 누르면 3초 뒤 “Hello”를 출력하는 예제를 해보겠습니다. 우선 아래 라이브러리를 설치하고 갈게요 

pip install pyserial
pip install keyboard
pip install mouse

 

설치를 하셨으면 serialFunc.py 라는 파일을 만들고 아래 코드를 입력해 줍니다. 

import serial
import time


# /mMo/12,12|
# /mlC|
# /mlC/p|
# /mlC/r|

# /mrC|
# /mrC/p|
# /mrC/r|

# /mKey/keyinput|


class ExternalHID:
    ser = 0
    def __init__(self, comport):
        try:
            self.ser = serial.Serial(comport, 9600, timeout=1)

        except Exception as e:
            print(str(e))

    def disconnectSerial(self):
        self.ser.close()

    def checkSerial(self):
        if self.ser == 0:
            print('Serial Not available')
            return False
        else:
            return True

    def mouseMove(self, x, y):
        if not self.checkSerial():
            return False

        moveCommand = '/mMo/%d,%d|' % (x, y)
        self.ser.write(str.encode(moveCommand))
        rsp = self.ser.readline()
        if rsp.strip() == b'move':
            return True
        else:
            return False

    def mouseClick(self, button):
        if not self.checkSerial():
            return False

        if button == 'left':
            moveCommand = '/m%sC|' % ('l')
        elif button == 'right':
            moveCommand = '/m%sC|' % ('r')
        else:
            return False
        self.ser.write(str.encode(moveCommand))
        rsp = self.ser.readline()
        if rsp.strip() == b'Click':
            print('HID Click Success')
            return True
        else:
            return False

    def keyboardInput(self, keyinput):
        if not self.checkSerial():
            return False

        moveCommand = '/mKey/%s|' % keyinput
        self.ser.write(str.encode(moveCommand))
        rsp = self.ser.readline()
        if rsp.strip() == b'Key':
            return True
        else:
            return False

    def mousePress(self, button):
        if not self.checkSerial():
            return False
        print(str(button))
        if button == 'left':
            moveCommand = '/m%sC/p|' % ('l')
        elif button == 'right':
            moveCommand = '/m%sC/p|' % ('r')
        else:
            return False

        self.ser.write(str.encode(moveCommand))
        rsp = self.ser.readline()
        if rsp.strip() == b'Pre':
            return True
        else:
            return False

    def mouseRelease(self, button):
        if not self.checkSerial():
            return False
        print(str(button))
        if button == 'left':
            moveCommand = '/m%sC/r|' % ('l')
        elif button == 'right':
            moveCommand = '/m%sC/r|' % ('r')
        else:
            return False

        self.ser.write(str.encode(moveCommand))
        rsp = self.ser.readline()
        if rsp.strip() == b'Rls':
            return True
        else:
            return False

if __name__ == "__main__":

    import keyboard as key
    import time

    ser = ExternalHID('COM21')


    def printHello():
        state = False
        returnList = []
        while True:
            val = key.is_pressed('F4')
            if state != val:
                if val == True:
                    time.sleep(3)
                    ser.keyboardInput('Hello')
                state = val


    while True:
        time.sleep(0.001)
        printHello()

 

이 코드는 아두이노에서 정의한 Keyboard, Mouse 제어 스트링을 Serial 통신으로 보내주는 역할을 합니다. 다양한 함수를 만들어 놨으니 한번 사용해 보세요 !

코드를 입력 했으면 Pycharm 혹은 Cmd 에서 실행시킨 뒤 메모장에 마우스를 위치시키고  ‘F4’ 를 눌러보세요 ! 그럼 약 3초 뒤에 Hello 라는 단어가 메모장에 써질 겁니다. 

이쯤에서 로아 낚시 매크로 시리즈를 보고 오신 분이 계신다면 이게 뭔 뻘짓이지 ? 그냥 ser.keyboardInput(‘Hello’) 대신 key.write(‘Hello’) 쓰면 되는거 아닌가? 라는 아주 합리적인 의문을 가지실 수 있습니다. 

하지만 후자는 S/W 적인 방법입니다. 게임에 적용 할 때면 안 먹히는 경우가 꽤 있습니다. 특히 게임 상에서 마우스 이벤트로 특정 버튼을 누르려 할 때 안되는 경우가 종종 있습니다.(물론 이건 제 지식이 짧아서 일 수 있습니다… ㅜㅜ) 

이 포스트의 하드웨어 메크로는 가능데스죠 ㅋㅋ 악용은 금지 입니다. ..

오늘의 포스팅은 여기까지 입니다. 모두들 좋은 밤 되세요 ㅎㅎㅎ

 

You may also like...

11 Responses

  1. 박준형 댓글:

    좋은 정보 감사합니다. 즐겨찾기 등록해두었다가 나중에 한번 시도해보겠습니다^^

  2. 양옥석 댓글:

    엑자일 물약 자동으로 먹게끔 할려면 위에 아두이노를 무조건 설치해야 하나요 ?? 컴맹이어서 앞부분 따라하다가 바로 막혀서 새벽까지 글만 읽어보고 또 봐도 무슨 말인지 왜 안돼는지도 모르고 막막 하네요.라이브러리며 코드를 어디에다가 설치를 해야하는지도 잘 모르겠구요 ㅠㅠ . 처음 예시처럼 화면캡쳐하고 설명 해주시면 안될까요 ??

  3. 중학생 댓글:

    아두이노 프로 마이크로 컴퓨터랑 연결하면 “bluetooth 및 기타 디바이스”에서 Leonardo로 잡히는거까지는 됬습니다.
    아두이노 ide에서는 보드랑 포트가 Arduino/Genuino Micro로 안뜨고 Leonardo로 잡히는데 어떻게 하죠?

  4. 학생 댓글:

    아두이노 프로 마이크로 컴퓨터랑 연결하면 “bluetooth 및 기타 디바이스”에서 Leonardo로 잡히는거까지는 됬습니다.
    아두이노 ide에서는 보드랑 포트가 Arduino/Genuino Micro로 안뜨고 Leonardo로 잡히는데 어떻게 하죠?

  5. 학생 댓글:

    아두이노 프로 마이크로 컴퓨터랑 연결하면 “bluetooth 및 기타 디바이스”에서 Leonardo로 잡히는거까지는 됬습니다.
    아두이노 ide에서는 보드랑 포트가 Arduino/Genuino Micro로 안뜨고 Leonardo로 잡히는데 어떻게 하죠?

  6. 궁금합니다 댓글:

    마우스 이동이 상대좌표이동말고는 없는데.. 검색을해보니 절대좌표를 사용할 수 있더라구요 혹시 절대좌표에 대한것도 강좌로 올려주실수 있을까요?

  7. 최광자 댓글:

    잘보고있습니다. 요즘 활동이 없으셔서 댓글을 달까 말까 고민하다가 질문이 있어 이렇게 댓글을 남겨봅니다.
    우선 질문에 앞서 이런 유익한 글을 남겨주셔서 감사합니다.
    현재 ser.keyboardInput(‘Hello’)를 입력하였을경우 정상적으로 Hello가 잘 작동됩니다.
    혹시 방향키를 꾹 누르는 작동을 하고싶은데 방법이 있을런지싶어 이렇게 질문을 드립니다 .

  8. 최광자 댓글:

    ….