[Arduino] - 시리얼 통신을 이용한 디스플레이 오토피벗 구현 (피버시노 v1.0)



  • 시작하며
인텔 그래픽을 사용하는 컴퓨터는 ctrl + alt + 방향키로 디스플레이 화면을 돌릴 수 있다.
하지만 지포스 같은 외장그래픽 사용자는 컨트롤 패널에서 직접 설정해주어야만 가능하다.
모니터를 물리적으로 회전시키면 디스플레이 화면도 회전시켜주는 무언가가 필요하다.

그래서 생각한 것이 피버시노 프로젝트

이 프로젝트의 목적은 피벗 모니터의 디스플레이 화면을 자동으로 회전 시키는 것에 있다.

물론 시중에 이러한 오토피벗 기능을 내장한 모니터가 판매되고 있지만 전문가용이거나 대부분 고가의 제품들이다. 이 장비를 이용하면 오토피벗 기능이 없는 저렴한 모니터에서 추가적인 설정 없이 모니터를 돌리기만 해도 아두이노가 대신 디스플레이를 회전시켜준다.

아두이노, 가속도센서, 파이썬 만으로 구현 할 수 있으며 소형화도 가능하다.

  • 하드웨어
이 프로젝트에서 사용한 가속도 센서는 MMA7361이다.
사용방법이 궁금하다면 라이브러리에 예제가 포함되어있다.
아두이노 우노에 적층 할 수 있도록 만들었다.


각 핀의 이름은 다음과 같다.
5V, 3V3, GND, g-Selec(10pin), selfTest(12pin)
x(A0pin), y(A1pin), z(A2pin), Sleep(13pin), 0g(11pin),
전원은 5V를 사용하였다.
본인이 사용할 핀들만 연결해주어도 된다.

  • 아두이노 코드
이 프로그램은 현재 상태를 가속도 센서로 감지하여 변화하였을때 시리얼 값을 출력한다.

작동원리는 P[0]에는 과거의 값이 P[1]에는현재의 값이 지속적으로 저장된다.
이 두 값을 아두이노가 반복적으로 비교 하여 시리얼 신호를 보낼지 말지를 결정한다.

이러한 구조를 가지게 된 이유는 상태가 변화하지 않고 있을 때에도 신호를 보내게 되면 수신측(컴퓨터)에서 지속적으로 처리를 해주어야 하기 때문에 자원의 낭비가 발생하게된다.
자원의 낭비는 곧 성능 하락으로 이어지기 때문에 필요한 상황에서만 작동하도록 해야한다.

예를 들어 현재 y값을 RotateMonitor 함수에 대입하면 상태에 따라 결과 값이 나온다.
그 값을 "Idle" 이라 하자

1. "Idle" 은 P[0]에 저장이 된다.

2. 현재 i = 0이기 때문에 P[i]와 P[0]은 둘다 "Idle", 조건문 실행 X

3. P[1]에 현재 상태 값을 저장하고, i = 1이 된다.
이 작업을 P[i]와 P[0]이 다른 상태 값을 가질때 까지 반복한다.

4. 모니터를 왼쪽으로 회전시킨다.

5. 현재 i 값은 1이고 P[1]의 상태 값은 "Idle" 이다. 
하지만 현재 모니터를 왼쪽으로 회전시켰기 때문에 P[0]의 상태 값은 "Left"가 된다.

6. P[i]와 P[0]의 상태 값이 서로 다르기 때문에 조건문이 실행된다.
다시 i = 0이 된다.

7. 상태 값이 시리얼 포트로 상태 값을 전송된다.

위의 과정을 반복하는 프로그램이며, 이 후의 과정은 컴퓨터에서 진행된다.

#include <AcceleroMMA7361.h>
#define commandLine_OPEN "Rotate <"
#define commandLine_CLOSE ">"
AcceleroMMA7361 accelero;
const char * P[2];
const char * RotateMonitor(int x);
int y;
int i = 0;
void setup()
{
  Serial.begin(9600);
  accelero.begin(13121110, A0, A1, A2);   //(Sleep, selpTest, 0g, g-Select, x, y, z
  accelero.setARefVoltage(5);                   //sets the AREF voltage to 5V
  accelero.setSensitivity(LOW);                //sets the sensitivity
  accelero.calibrate();
}
void loop()
{
  y = accelero.getYAccel();
  
  P[0= RotateMonitor(y);
  if(P[i] != P[0]) {
    Serial.print(commandLine_OPEN);
    Serial.print(RotateMonitor(y)); 
    Serial.println(commandLine_CLOSE);
    i = 0;
    }
  else { 
    P[1= RotateMonitor(y);
    i = 1;
  }
  delay(500);
}
const char * RotateMonitor(int y){
  if(y < -60) { return "Left"; }            //270 degrees
  else if(y > 60) { return "Right"; }       //90  degrees
  else { return "Idle"; }                   //0  degrees
}
cs
  • 파이썬 코드
시작하기 앞서 파이썬이 컴퓨터에 설치 되어 있어야 하며, 시리얼 통신을 이용하기 때문에
먼저 터미널을 이용해 pyserial도 설치해야한다.

pip install pyserial

이 프로그램은 아두이노 코드 과정중 7번에서 전송받은 상태 값을 전달 받아 컴퓨터의 디스플레이를 적절한 각도로 회전시켜준다.

COM_Port= ["포트"] 이자리에 본인의 아두이노가 연결된 포트를 적어준다.
Monitor_ROTATE = {"Right":"90", "Idle":"0", "Left":"270"}
자신이 정한 상태 값에 따른 회전 각도를 정해준다.

사실 이 프로그램이 단독적으로 디스플레이 화면을 회전시켜주는 것은 아니며 display.exe라는 응용프로그램을 추가로 사용한다.

display.exe의 사용법
ex) "C:\Users\UserName\Desktop\display.exe" /rotate:90

즉 파이썬 코드는 상황에 맞는 커맨드 명령어를 실행하는 메크로 프로그램이다.
command = "C:\저장경로\display.exe /rotate:" + Monitor_ROTATE[direction]
저장경로에 본인의 경로를 적으면 된다.

import serial
import string
import time
from subprocess import call
import traceback
def initSerial(device_Port):
    serialFromArduino = serial.Serial(device_Port, 9600, timeout=1, xonxoff=False, rtscts=False, dsrdtr=False)
    serialFromArduino.flushInput()
    serialFromArduino.flushOutput()
    return serialFromArduino
def waitForSerialInit():
    COM_Port = ["COM9"]
    while True:
        for device_Port in COM_Port:
            try:
                serialFromArduino = initSerial(device_Port)
                print("device found on " + device_Port)
                return serialFromArduino
            except Exception:
                print("Failed to device on " + device_Port)
        time.sleep(5)
Monitor_ROTATE = {"Right":"90""Idle":"0""Left":"270"}
commandLine_OPEN = "Rotate <"
commandLine_CLOSE = ">"
serialFromArduino = waitForSerialInit()
while True:
    try:
        line = serialFromArduino.readline().decode("utf-8")
    except Exception:
        print("Fuck: ")
        traceback.print_exc()
        print("Something wrong, Check your device !")
        time.sleep(5)
        print("trying to initialise serial...")
        serialFromArduino = waitForSerialInit()
        continue
    if line == "":
        continue
    print("line: " + line)
    if line.find(commandLine_OPEN) == 0:
        
        direction = line.replace(commandLine_OPEN,"")
        direction = direction[0:direction.find(commandLine_CLOSE)]
        print("direction: " + direction)
        if direction in Monitor_ROTATE:
            print("Monitor Rotate: " + Monitor_ROTATE[direction])
            command = "C:\Rotate\display.exe /rotate:" + Monitor_ROTATE[direction]
            print("running: " + command)
            call(command, shell=True)
        else:
            print("invalid direction: " + direction)
            print("ignoring")
cs

파이썬 프로그램을 실행 프로그램으로 만들면 사용하기 더 편해진다.
터미널을 이용해 Pyinstaller를 설치한다

pip install pyinstaller

Pyinstaller 사용법
ex) pyinstaller --onefile "파일.py"



작업이 완료되면 dist폴더에 파일.exe이 생성된다.

아두이노를 연결하고 PC에서 파이썬 프로그램을 실행하면 다음과같이 작동한다.


만약 콘솔창이 뜨는것이 싫다면 다음과 같이 --noconsole 옵션을 추가하면된다.

pyinstaller --onefile --noconsole "파일.py"


  • 마치며
이제 모니터를 돌려서 사용할때 디스플레이 설정에서 일일이 바꿔가며 사용할 필요없이 자동으로 화면이 회전된다.

이 앞으로 개발 방향은 3축의 가속도 센서를 모두 이용해 모니터가 책상위 평면에 있을 때에도 화면회전이 가능하도록 하거나 변화량을 감지해 좀더 성능과 정밀도를 높이는 것이다.

또한 display.exe 프로그램도 디스플레이 회전 기능만 있는 것이 아니기 때문에 보다 더 다양한 기능을 구현 할 수 있다.



  • 업데이트 버전
=====================================================
피버시노 v1.0
피버시노 v1.1
피버시노 v1.1.1
피버시노 v1.1.2
피버시노 v1.1.3


Comments

  1. 이게 연결한 핀을 찾을수없다고 뜨는데요?

    ReplyDelete
  2. 다 되는데 화면만 안돌아가네여 display를 깔았는데 32랑 64가 있더라고여 전 c드라이브에 Program Files 에 넣어서 "C:\Program Files\display.exe /rotate:" + Monitor_ROTATE[direction 이렇게 썼는데 안되네요

    ReplyDelete
    Replies
    1. 파이썬 프로그램에서 아두이노와 시리얼 통신이 정상적으로 이루어지는지 확인해보세요. 경로가 정확하다면 display.exe 실행 부분이 문제가 아니라 통신문제일 수도 있습니다.
      그리고 제 소스코드를 그대로 사용하셨다면 신경쓰셔야 할 변수는 display.exe 프로그램 경로와 COM 포트 넘버입니다.
      참고하세요! :7

      Delete
    2. This comment has been removed by the author.

      Delete
  3. 멋지십니다. 저는 안드로이드폰 Dock에 있는 HDMI Output 단자를 이용해 오래된 모니터에 연결해서 음악을 듣는(보는) 용도로 사용하는데 당연히 모니터는 피벗이 안됩니다. 이 경우는 어떤방식으로 구현이 가능할까요? 역시 아두이노로 가능할까요?

    ReplyDelete

Post a Comment

좋은하루되세요. ^^

Popular posts from this blog

[Python] - 블루투스 모듈 HC-06의 MAC 주소를 이용하여 통신 포트 찾기

[POE] - 패스 오브 엑자일 획득키 F 의 사용법 (Path of Exile)

[Arduino] - HC-06를 이용해 PC 와 Arduino 블루투스 연결

[Raspberry Pi] - 라즈베리 파이에서 멀티미디어 감상에 적절한 비디오 세팅