Sep 18, 2018 - 시리얼통신으로 자바와 아두이노 연동하기

개인적인 취미로 숫자 키보드를 만들고 싶은데,
가지고 있는 아두이노는 UNO R3 와 UNO CH430 이 두기종.

아두이노 키보드를 만들기위한 미니 아두이노도 구매했는데 분실해버리는 바람에,
현재 2개의 기종으로 숫자 키보드를 만들려고 합니다.

참고주소
위의 참고주소를 가면, 친절하게 배선도와, 아두이노 소스코딩을 제공해줍니다.

다만 문제는 제가 가지고 있는 기종이 키보드와 같은 하드웨어 인식이 안되는 R3와 CH430 인게 문제였네요.

방법은,
참고주소1
참고주소2
DFU (Device Firmware Update)을 처리해야합니다.

CH430은 모르겠지만 R3는 저렇게 해서 가능하다고 하여
jumper 로 앞단자를 가리고,
순정 아두이노 펌웨어와, 아두이노 키보드 펌웨어 이렇게 2개를 처리해야한다고 하네요.

Arduino-keyboard-0.3.hex  
Arduino-usbserial-uno.hex  

펌웨어하는 방법도, 쉽지 않은데, 벽돌이 되면….
게다가 Arduino-keyboard-0.3.hex 적용하면,
아두이노 코딩 업로드가 안된다는 글까지 보게 되었습니다.

그래서, 꼭 내가 하드웨어로 키보드 인식을 해서 쓸 것인지에 대한 고찰과 함께,
결론이 그냥 나는 텐키리스 키보드를 사면,

숫자키가 허전해서, 3D 프린터로 숫자 키보드를 하나 만들고 싶고,
이미 가지고 있는 우노들을 이용하고 싶은 것뿐이더군요.

그래서 인숙한 자바 Serial 통신방법을 찾게 되었습니다. 참고주소

참고 주소에서는
TxRx라이브러리를 받고난 뒤 압축을 풀고
RXTXcomm.jar는 %JAVAHOME%lib에 넣어주시고
rxtxSerial.dll는 %JAVAHOME%bin에 넣으라고 가이드를 주었는데,

저는 귀찮아서 java는 바로 프로젝트에서 import 받아서 처리했고, rxtxSerial.dll 파일을 windows에 system32 폴더에 넣었습니다.

아두이노는 핀을 총 5개 사용했고 (연습삼아서… 나중에 매트릭스 구조로 다시 짜야함..)

#include <Boards.h>
#include <FirmataConstants.h>
#include <FirmataDefines.h>
#include <FirmataMarshaller.h>
#include <FirmataParser.h>

#define PIN_NUM3 3
#define PIN_NUM4 4
#define PIN_NUM5 5
#define PIN_NUM6 6
#define PIN_NUM7 7

int state = 1;

void setup()
{
  Serial.begin(9600);
  pinMode(PIN_NUM3, INPUT);
  pinMode(PIN_NUM4, INPUT);
  pinMode(PIN_NUM5, INPUT);
  pinMode(PIN_NUM6, INPUT);
  pinMode(PIN_NUM7, INPUT);

  Serial.write(INPUT); // Send keypress
  // enable internal pull-ups
  digitalWrite(PIN_NUM3, 1);
  digitalWrite(PIN_NUM4, 1);
  digitalWrite(PIN_NUM5, 1);
  digitalWrite(PIN_NUM6, 1);
  digitalWrite(PIN_NUM7, 1);

  delay(200);
}

void loop()
{
  state = digitalRead(PIN_NUM3);
  if (state != 1) {
    Serial.write('a'); // Send keypress
    delay(200);
  }

  state = digitalRead(PIN_NUM4);
  if (state != 1) {
    Serial.write('b'); // Send keypress
    delay(200);
  }

  state = digitalRead(PIN_NUM5);
  if (state != 1) {
    Serial.write('c'); // Send keypress
    delay(200);
  }

  state = digitalRead(PIN_NUM6);
  if (state != 1) {
    Serial.write('d'); // Send keypress
    delay(200);
  }

  state = digitalRead(PIN_NUM7);
  if (state != 1) {
    Serial.write('e'); // Send keypress
    delay(200);
  }
}

해당 값을 받아 처리하는 자바 클래스도 간단하게 구현하였습니다.

package aduino;

import gnu.io.CommPort;
import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class Serial
{
    public Serial()
    {
        super();
    }

    void connect ( String portName ) throws Exception
    {
        CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(portName);
        if ( portIdentifier.isCurrentlyOwned() )
        {
            System.out.println("Error: Port is currently in use");
        }
        else
        {
            //클래스 이름을 식별자로 사용하여 포트 오픈
            CommPort commPort = portIdentifier.open(this.getClass().getName(),2000);

            if ( commPort instanceof SerialPort )
            {
                //포트 설정(통신속도 설정. 기본 9600으로 사용)
                SerialPort serialPort = (SerialPort) commPort;
                serialPort.setSerialPortParams(9600,SerialPort.DATABITS_8,SerialPort.STOPBITS_1,SerialPort.PARITY_NONE);

                //Input,OutputStream 버퍼 생성 후 오픈
                InputStream in = serialPort.getInputStream();
                OutputStream out = serialPort.getOutputStream();

                 //읽기, 쓰기 쓰레드 작동
                (new Thread(new SerialReader(in))).start();
                (new Thread(new SerialWriter(out))).start();

            }
            else
            {
                System.out.println("Error: Only serial ports are handled by this example.");
            }
        }     
    }

    /** */
    //데이터 수신
    public static class SerialReader implements Runnable
    {
        InputStream in;

        public SerialReader ( InputStream in )
        {
            this.in = in;
        }

        public void run ()
        {
            byte[] buffer = new byte[1024];
            int len = -1;
            try
            {
                while ( ( len = this.in.read(buffer)) > -1 )
                {
                	String temp = new String(buffer,0,len);
                    if ("a".equals(temp)) {
                    	Process oProcess = new ProcessBuilder("notepad.exe").start();
                    } else if ("b".equals(temp)) {
                    	Process oProcess = new ProcessBuilder("calc.exe").start();
                    } else if ("c".equals(temp)) {
                    	Process oProcess = new ProcessBuilder("control").start();
                    } else if ("d".equals(temp)) {
                    	Process oProcess = new ProcessBuilder("D:\\workset\\eclipse-jee-photon-R-win32-x86_64\\eclipse\\eclipse.exe").start();
                    } else if ("e".equals(temp)) {
                    	Process oProcess = new ProcessBuilder("C:\\Program Files\\JetBrains\\IntelliJ IDEA 2018.2.3\\bin\\idea64.exe").start();
                    }
                }
            }
            catch ( IOException e )
            {
                e.printStackTrace();
            }            
        }
    }

    /** */
    //데이터 송신
    public static class SerialWriter implements Runnable
    {
        OutputStream out;

        public SerialWriter ( OutputStream out )
        {
            this.out = out;
        }

        public void run ()
        {
            try
            {
                int c = 0;
                while ( ( c = System.in.read()) > -1 )
                {
                    this.out.write(c);
                }                
            }
            catch ( IOException e )
            {
                e.printStackTrace();
            }            
        }
    }

    public static void main ( String[] args )
    {
        try
        {
            (new Serial()).connect("COM4"); //입력한 포트로 연결
        }
        catch ( Exception e )
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

변경되는 port 에 대한 변경값 입력하는 방법을 args로 받아 처리하면서,
별도의 jar로 실행하는 방법만 고민하면, 텐키리스 사용하면서 숫자 키보드를 만들 수 있을 거 같습니다.

Sep 1, 2018 - 자동 배포 SVN HOOKS

개인 Team 프로젝트(?) 진행을 하는데, php 밖에 모른다고 하는 팀원 때문에, php 로 개발 중입니다.

이 팀원이 요구사항이 많은 사람입니다. 첫번째 요구사항은, 요구사항을 local host 에서 테스트할 수 없으니 서버에 바로 반영하겠다라고 했습니다.

당연히 테스트가 안된 소스를 운영에 올릴 수는 없고, 현업을 해야하니,
서버에 바로 반영할 수 없고 git 설치로 별도의 형상관리를 두어 local test 후에, 서버에 반영해주겠다고했으나,

git은 해본적없고 하고싶지 않으니 svn을 해달라고 합니다.

두번째 요구사항인 svn 을 설정했더니 실서버에 바로 반영을 못한다면 개발서버를 구축해달라고했습니다.
그개발서버의 전제조건이, 커밋을 하면 바로 적용될 수 있는 개발서버입니다.

처음에는 젠킨스를 설치하고 뭔가하려고 했는데,
simple is best 라고, 그냥 hooks 기능을 사용하여 처리하려고 합니다.

SVN 에서 강제로 특정 경로로 export 하는 명령어입니다.

/home/svn/hooks

hooks 폴더의 post-commit 파일을 만들고,
해당 파일의 맨앞에 썻던,

svn export --force svn://url /home/svn/gnuboard5/txt

를 적용합니다.

훅(hook) 은 특정 이벤트가 발생하면 동작하는 프로그램을 의미하고, post-commit 은 커밋이 발생했을 때를 의미합니다. 훅 은 동작 방식에 따라 크게 2가지로 나눌 수 있습니다.

첫 번째는 이벤트가 처리되기전에 수행되는 pre-훅 으로, pre-훅 을 이용하면 커밋전에 커밋 메시지가 정책에 맞는지, 파일이 추가될 경우 파일형식이 저장소 관리 규칙에 맞는지 등을 검사하여 규칙에 어긋나면 커밋을 거부하게 동작할 수 있습니다.

다른 하나는 이벤트가 완전히 끝난후에 수행되는 post 훅 입니다. 이것을 이용하면 커밋이 완료되면 프로젝트 팀원들에게 메일을 보내거나,
지속적인 통합 서버에 통보하여 커밋된 내용을 업데이트하여 자동으로 빌드를 수행하거나 이슈 관리에서 커밋 내역을 인덱싱하는 등의 작업을 수행할 수 있습니다.

java 의 경우에는 별도의 shell 로 빼서, export 후 build 를 하거나 jar 로 묻거나 하는 별도의 컴파일이 필요하지만,
jsp 나 php 의 경우는 해당 경로에 바로 올려서 처리가 가능하므로,
간단하게 처리해서 제공해주었습니다.


참조


Aug 27, 2018 - GCP 에 SVN 설치해보자

GCP 설정

  • Compute Engine 에서 VM 인스턴스 등록 제일 싼게 (us-east1 : 사우스캐롤라이나) 로 초소형(0.6G) 메모리에 10GB 사용시, 4.28 달러만 사용합니다. 부팅디스크는 CentOS 7(x86_64 built on 20180815)로 설정했습니다.

옵션은 HTTP, HTTPS 둘다 트래픽 허용으로 연습합니다. (SVN 으로 연결 후 해당 페이지까지 띄우기 하려고 합니다.)

등록이 완료되면 바로 처리해야할 것이 3가지 있습니다.

  • 메타데이터에 ssh_key 등록 (로그인 때문에)
  • 고정 IP 신청
  • root 비밀번호 설정
VPC 네트워크 > 외부 IP 주소

고정 IP 는 VPC 네트워크 > 외부 IP 주소가서 아이피를 고정으로 신청해야합니다. 이미 고정된 주소가 있고 인스턴스에서 사용하지 않으면 초당 금액이 나간다고 합니다. ㅎㄷㄷ

Compute Engine > 메타데이터 > SSH

Compute Engine > 메타데이터 > SSH 키를 등록합니다. SSH 키를 생성하고 등록하는 법은 다 알테니, 해당 부분은 지나갑니다. 저는 Xshell 로 했고, 등록한 ssh_key 뒤에 한칸뛰고 접속할 아이디를 bymin 으로 정했습니다. 로그인시에는 bymin 으로 접속합니다.

sudo passwd

sudo passwd

로그인을 한 후에 root 권한의 비밀번호를 설정합니다.

ETC

FTP 접속 설치
yum -y install vsftpd
vi /etc/vsftpd/vsftpd.conf
yum install htop : HTOP 이라는 모니터링 툴
yun install java : 자바를 설치하자! (나중에 쓰겠지..)

이제 SVN 설치! 실제로 GIT 을 사용하고 싶었으나, 해당 서버를 형상관리로 쓰고 싶어하는 지인분이 GIT은 모르겠고 SVN 으로 하고 싶다고, 그리고 PHP 를 띄우고 싶다고 해서….

svn 설치 (git 쓰고싶다)

yum install subversion

svn 의 repository 를 설정할 폴더를 변경합니다.

vi /etc/sysconfig/svnserve

# OPTIONS is used to pass command-line arguments to svnserve.
# 
# Specify the repository location in -r parameter:
OPTIONS="-r /home/svn"

/home/svn 으로 변경했습니다.

svnadmin create --fs-type fsfs repos

/home/svn 으로 이동하여 repos 라는 폴더를 만듭니다. 해당 폴더를 만들면 폴더 안에는,

drwxrwxrwx. 2 root root  54 Aug 26 08:34 conf
drwxrwsrwx. 6 root root 253 Aug 26 09:14 db
-rwxrwxrwx. 1 root root   2 Aug 26 08:31 format
drwxrwxrwx. 2 root root 231 Aug 26 08:31 hooks
drwxrwxrwx. 2 root root  41 Aug 26 08:31 locks
-rwxrwxrwx. 1 root root 229 Aug 26 08:31 README.txt

위와같은 파일이 만들어집니다. 권한은 보지 말아주세요. ㅠㅜ 권한오류가 발생해서 계속 원인파악하다가 권한을 저렇게 바꾸었습니다;

svn 제타위키 그 이후의 설정은 svn 제타위키를 보는 것이 더 낫습니다.

svnserve.conf 수정하여,

[general]
anon-access = none
auth-access = write
password-db = passwd
authz-db = authz

위와같이 등록합니다. 기존 주석된 부분을 빼는 걸로 해결하셔도 되며, 주석을 푸실때, anon-access = read 로 되어있는 부분을 none 으로만 바꿔주시면됩니다.

passwd와 authz 파일을 사용하도록 했으니 해당 파일도 수정합니다.

authz 는 모든 권한을 부여하도록,

[/]
*=rw           

절대 경로의 rw 를 주었으며, passwd는 아이디=패스워드 형태로 등록하시면됩니다.

[users]
testuser1 = P@ssw0rd
testuser2 = P@ssw0rd

서비스 시작은 다음과 같습니다.

service svnserve start

start, stop, restart 등으로 제어하도록 합니다.

다 작업을 하고 커밋을 하려고 했을때 발생한 오류는 2건이었습니다. 첫번째는 방화벽 문제입니다.

GCP 설정을 하면 방화벽이 자동으로 설정되는데요. svn://ip 로 접속하면 svn의 defaul 포트인 3690 을 열어주셔야합니다.

VPC 네트워크 > 방화벽 규칙에, 수신에 3690을 추가해줍니다.

2번째가 Permission Denied 문제였는데요.

chmod -R go+w /home/svn/

서버에 모든 하위 권한에 접속권한을 부여해줘봤습니다…. 그래도 안됩니다. 구글링을 해보니, /etc/sysconfig/selinux 파일을 여셔서 SELINUX=enforcing를 SELINUX=disable로 바꾸신 뒤 재부팅 하면 된다고 해서, 해봤습니다.

재부팅하니 잘되네요.

shutdown [옵션] [시간] [메시지]
-t n : 경고 메세지를 보낸 후 n초 후에 kill 시그널을 보낸다.
-h : shutdown시 halt를 실행하게 한다.
-n : 디스크 동기화 동작의 수행을 금지한다.
-r : 시스템을 재부팅한다.
-f : 다음 부팅시 파일시스템 검사를 하지 않는다.
-c : 이미 예약되어 있는 shutdown을 취소한다. 이 옵션을 둔다면 시간인수는 줄 수 없다.
      하지만 메시지는 사용자들에게 줄 수 있다.
-k : 모든 동작을 제대로 수행하지만, 실제로 시스템을 종료하지는 않는다.
시스템 종료시 가장 자주 사용되는 방식 : shutdown -h now
시스템 재부팅시 자장 자주 사용되는 방식 : shutdown -r now
시스템 종료 예약 : shutdown -h 10 (10분 후에 시스템을 종료한다.)

음, SELinux 는 Linux의 보안을 강화해 주는 보안 강화 커널이고 zero-day 공격 및 buffer overflow 등 어플리케이션 취약점으로 인한 해킹을 방지해 주는 핵심 구성요소라고 하네요. 특정 서비스가 SELinux 때문에 동작하지 않는다면 SELinux 를 끄기 보다는 해당 서비스가 SELinux 하에서 잘 동작하도록 설정을 수정하는걸 권장한다고 하는데, 우선은 SVN 적용을 하려고 한 것이고, 여기에 mysql 도 등록하고 외부에서 접속시킬건데, 이 부분에 계속 문제가 생길 것으로 보여, disable 처리했습니다.


참조