2016년 5월 13일 금요일

라즈베리 파이에서 systemd를 사용해 부팅시 프로그램을 자동 실행 (Execute script on boot using systemd)




Raspbian 최신버젼이 릴리즈 되면서 cron이나 rc.local을 사용해 파이선 스크립트를 부팅시 자동실행 시키는 데서 고생하기 시작하였다. Raspbian의 부트 순서가 변경되어 이 프로세스들이 다른 시점에서 실행되기 때문인 것으로 보인다. 이것이 얼마나 문제가 되는지는 실행할 파이선 스크립트가 어떤 일을 하고 어떤 리소스를 필요로 하는가에 따라 치명적일 수도 있고 별 문제가 되지 않을 수도 있다.

부팅시 실행될 파이선 스크립트가 실행 시점에서 어떤 시스템 기능을 필요로 한다면 이 문제는 매우 치명적이다. 일반적으로 다음과 같은 것들이 포함된다.
  • 네트웍이 연결되어 있어야 함
  • /home/pi 디렉토리가 마운트 되어 읽을 수 있어야 함
  • 시스템의 시간이 NTP로 업데이트 되어야 함
그래서 커스텀 기능을 실행시키는데 권자오디는 방법이라 여려 리눅스 패키지에서 적용하고 있는 ’systemd’를 사용하기로 했다. systemd는 리눅스 시스템의 설정을 관리하기 위한 소프트웨어 툴로 이전에 동일한 목적으로 사용되던 여러 툴들을 대체하기 위한 목적으로 만들어 졌다.

systemd는 각자의 수준에 따라 매우 쉽거나 또는 매우 공포스러울 수 있다. 여기서는 최소한의 수고로 단지 스크립트 하나를 실행되도록 하는것이 목표이다.

Step 1 - 파이선 스크립트
 
여기서 사용할 예제 스크립트는 /home/pi 디렉토리에 저장되어 있고 이름은 ‘myscript.py’이다.  

Step 2 - Unit 파일 만들기
 
다음으로 해야 할 일은 설정 파일(aka Unit file)을 만들어 systemd에게 언제 무엇을 해야 하는지 알려줘야 한다.

$ sudo vi /lib/systemd/system/myscript.service

에디터를 실행한 다음 아래의 내용을 입력한다.

[Unit] Description=My Script Service
After=multi-user.target

[Service]
Type=idle
ExecStart=/usr/bin/python /home/pi/myscript.py

[Install]
WantedBy=multi-user.target

위의 파일은 “My Script Service”라는 이름의 새 서비스를 정의하고, ‘multi-user’ 환경이 사용 가능해 졌을 때 서비스를 한번만 실행하도록 요구한다. “ExecStart” 파라미터는 실행하길 원하는 명령어를 지정한다. “Type”은 모든 것이 다 로드 된 후에만 ExecStart 명령이 실행되는걸 보장하기 위해 ‘idle’로 설정한다. 예제에 사용한 GPIO 기반의 스크립트는 디폴트인 ‘simple’에서는 동작하지 않는다. 

파일의 패스는 절대주소여야 하고 스크립트의 위치 뿐 아니고 파이선의 위치까지도 완전히 지정해 줘야만 한다.

스크립트의 텍스트 출력을 로그파일에 저장하고 싶으면 ExecStart 라인을 다음과 같이 바꾸면 된다.

ExecStart=/usr/bin/python /home/pi/myscript.py > /home/pi/myscript.log 2>&1

Unit 파일의 퍼미션은 644로 설정해야 한다.

$ sudo chmod 644 /lib/systemd/system/myscript.service

Step 3 - systemd 설정
 
unit파일이 정의되었으므로 systemd에게 부팅시 이것을 사용하도록 알려줘야 한다.

$ sudo systemctl daemon-reload
$ sudo systemctl enable myscript.service

이제 시스템을 리부트 시키면 myscript.py가 자동으로 실행될 것이다.

$ sudo reboot

Step 4 - 서비스 상태 확인
 
서버스의 상태는 다음 명령으로 확인해 볼 수 있다.

$ sudo systemctl status myscript.service

2016년 5월 12일 목요일

라즈베리 파이를 비콘으로 사용하기 (Using Raspberry Pi as a Beacon)

독립적으로 비콘 모듈을 사용할 수도 있지만 이미 라즈베리 파이를 가지고 있다면 별도의 비콘 모듈 없이 라즈베리 파이를 비콘으로 설정해 사용할 수 있다.


라즈베리 파이 3의 경우 무선랜/블루투스가 기본 내장되어 있어 별도의 추가 하드웨어가 필요 없어 더 경제적이다. 라즈베리 파이 1/2의 경우는 USB타입의 블루투스 동글을 추가해 주면 된다.


 비콘은 특별한 것이 아니고 BLE(Bluetooth Low Energy)의 advertising packet 안에 옵션 필드인 Manufacturer Specific Data 에 특정 값을 집어넣어 주기적으로 advertising 하는 것이다. 그러므로 이 필드에 들어갈 값을 알아야 한다.

필드의 구조는 다음과 같다.

  • ID (uint8_t) : 이 값은 항상 0x02
  • Data Len (uint8_t) : payload 나머지 부분의 길이 → 0x15 (21 bytes)
  • 128-bit UUID (uint8_t[16]) : 128-bit 식별자
  • Major (uint16_t) : Major value
  • Minor (uint16_t) : Minor value
  • TX Power (uint8_t) : RSSI 기반으로 거리를 추정하기 위해 사용됨
 예를 들어 다음은 유효한 iBeacon payload의 값이다.


블루투스 표준에 의하면 Company Identifier가 Manufacturer Specific Data 필드 앞에 와야만 한다. 여기서 사용할 Apple의 company identifier는 0x004C 이다.

라즈베리 파이가 비콘 데이터를 보내게 하려면 몇가지 툴을 설치해 줘야만 한다. 여기서 핵심은 Bluez(리눅스 블루투스 스택)이고 부가적으로 libusb와 몇개의 helper library가 필요하다.

순서대로 필요한 툴들을 설치, bluez를 다운받아 컴파일 및 설치 해 주면 된다.

pi@raspberrypi ~ $ sudo apt-get install libusb-dev libdbus-1-dev libglib2.0-dev libudev-dev libical-dev libreadline-def

pi@raspberrypi ~ $ sudo mkdir bluez
pi@raspberrypi ~ $ cd bluez
pi@raspberrypi ~ $ sudo wget www.kernel.org/pub/linux/bluetooth/bluez-5.11.tar.xz
 
pi@raspberrypi ~ $ sudo unzx bluez-5.11.tar.xz 
pi@raspberrypi ~ $ sudo tar xvf bluez-5.11.tar
pi@raspberrypi ~ $ cd bluez-5.11
pi@raspberrypi ~ $ sudo ./configure --disable-systemd
pi@raspberrypi ~ $ sudo make
pi@raspberrypi ~ $ sudo make install



설치가 완료되었으면 블루투스 동글을 꼽고 재부팅 시켜주면 된다.

재부팅이 완료되면 먼저 시스템에 설치된 디바이스 목록을 확인한다.


pi@raspberrypi ~ $ hciconfig
hci0:  Type: BR/EDR    Bus: USB
       BD Address: 00:1A:7D:DA:71:12 ACL MTU: 310:10 SCO MTU: 64:8
       DOWN
       RX bytes: 495 acl:0 sco:0 events:21 errors: 0
       TX bytes: 89 acl:0 sco:0 commands: 21 errors: 0
pi@raspberrypi ~ $

만일 모든것이 정상적으로 설정되어 있으면 위와 같이 hci 디바이스가 보이게 된다. 이제 다음 명령으로 디바이스를 활성화 시켜 준다. 또한 advertising을 할 때 문제를 일으킬 수도 있으므로 device scanning은 꺼 준다.

pi@raspberrypi ~ $ sudo hciconfig hci0 up 
pi@raspberrypi ~ $ sudo hciconfig hci0 leadv3
pi@raspberrypi ~ $ sudo hciconfig hci0 noscan

이제 다시 hciconfig 명령을 수행해 보면 'DOWN' 대신 'UP  RUNNING'이 보여야 한다.

pi@raspberrypi ~ $ hciconfig
hci0:  Type: BR/EDR    Bus: USB
       BD Address: 00:1A:7D:DA:71:12 ACL MTU: 310:10 SCO MTU: 64:8
       UP RUNNING
       RX bytes: 1008 acl:0 sco:0 events:45 errors: 0
       TX bytes: 236 acl:0 sco:0 commands: 45 errors: 0
pi@raspberrypi ~ $

이제 마지막으로 해 줘야 할 일은 advertising data에 beacon 값을 넣어주는 것이다. 다음과 같이 명령을 입력해 준다. (좀 길지만 한 줄에 다 입력해 준다.)


pi@raspberrypi ~ $ sudo tools/hcitool -i hci0 cmd 0x08 0x0008 1E 02 01 1A 1A 
FF 4C 00 02 15 E2 0A 39 F4 73 F5 4B C4 A1 2F 17 D1 AD 07 A9 61 00 00 00 00 C8 00

위의 값들 중에서 FF는 Manufacturer Specific Data의 시작을 알려 주고, 4C 00은 Apple의 Company ID(0x004C), 그리고 나머지 부분부터 C8까지가 beacon의 payload이다.

이제 라즈베리 파이는 비콘으로 동작하게 된다. 확인해 보려면 iOS7 이후 버젼을 OS를 사용하는 아이폰/아이패드/아이팟 터치 또는 안드로이드 폰에 beacon toolkit 또는 유사한 앱을 설치해 실행하면 된다.

앱을 시작해 listen 모드로 들어가면 아래와 유사한 화면을 볼 수 있을 것이다.



이제 동작하는건 확인했지만 라즈베리 파이를 부팅할 때 마다 매번 위의 명령을 입력하기 힘드니 스크립트를 사용하는것이 편하다.

pi@raspberrypi ~ $ cat /home/pi/beacon.sh

#!/bin/bash
# ref: http://www.theregister.co.uk/Print/2013/11/29/feature_diy_apple_ibeacons/
set -x
# inquiry local bluetooth device
#hcitool dev
export BLUETOOTH_DEVICE=hci0
#sudo hcitool -i hcix cmd <OGF> <OCF> <No. Significant Data Octets> <iBeacon Prefix> <UUID> <Major> <Minor> <Tx Power> <Placeholder Octets>

#OGF = Operation Group Field = Bluetooth Command Group = 0x08
#OCF = Operation Command Field = HCI_LE_Set_Advertising_Data = 0x0008
#No. Significant Data Octets (Max of 31) = 1E (Decimal 30)
#iBeacon Prefix (Always Fixed) = 02 01 1A 1A FF 4C 00 02 15

export OGF="0x08"
export OCF="0x0008"
export IBEACONPROFIX="1E 02 01 1A 1A FF 4C 00 02 15"

#uuidgen  could gerenate uuid
export UUID="4a 4e ce 60 7e b0 11 e4 b4 a9 08 00 20 0c 9a 66"

export MAJOR="00 02"
export MINOR="00 01"
export POWER="C5 00"


# initialize device
sudo hciconfig $BLUETOOTH_DEVICE up
# disable advertising
sudo hciconfig $BLUETOOTH_DEVICE noleadv
# stop the dongle looking for other Bluetooth devices
sudo hciconfig $BLUETOOTH_DEVICE noscan

sudo hciconfig $BLUETOOTH_DEVICE pscan


sudo hciconfig $BLUETOOTH_DEVICE leadv

# advertise
sudo hcitool -i $BLUETOOTH_DEVICE cmd 0x08 0x0008 $IBEACONPROFIX $UUID $MAJOR $MINOR $POWER
sudo hcitool -i $BLUETOOTH_DEVICE cmd 0x08 0x0006 A0 00 A0 00 00 00 00 00 00 00 00 00 00 07 00
sudo hcitool -i $BLUETOOTH_DEVICE cmd 0x08 0x000a 01

echo "complete"


pi@raspberrypi ~ $

/home/pi/beacon.sh 라는 파일을 만들어 위의 파란색 부분의 내용을 넣어주면 된다. 위의 스크립트 중에 hcitool cmd 0x08 0x0006 a0 00 a0 00 .. 에서 첫번째/두번째 바이트 (0xa0 0x00)은 minimum interval, 세번째/네번째 바이트(0xa0 0x00)은 maximum interval이다. 0xa0 0x00은 0x00a0 (big endian)이 되므로 10진수로는 160이 된다. BLE에서 시간 granularity는 0.625ms이므로 최소/최대 인터벌은 모두 160 * 0.625ms = 100ms가 된다. 즉 매 0.1초마다 한번씩 비콘 신호를 advertising하게 된다.

파일을 만들었으면 chmod 명령으로 실행 퍼미션을 추가해 준다.

pi@raspberrypi ~ $ chmod +x beacon.sh

이제부터는 ./beacon.sh 만 입력해 주면 라즈베리 파이가 비콘으로 동작한다.

pi@raspberrypi ~ $ ./beacon.sh
+ export BLUETOOTH_DEVICE=hci0
+ BLUETOOTH_DEVICE=hci0
+ export OGF=0x08
+ OGF=0x08
+ export OCF=0x0008
+ OCF=0x0008
+ export 'IBEACONPROFIX=1E 02 01 1A 1A FF 4C 00 02 15'
+ IBEACONPROFIX='1E 02 01 1A 1A FF 4C 00 02 15'
+ export 'UUID=4a 4e ce 60 7e b0 11 e4 b4 a9 08 00 20 0c 9a 66'
+ UUID='4a 4e ce 60 7e b0 11 e4 b4 a9 08 00 20 0c 9a 66'
+ export 'MAJOR=00 01'
+ MAJOR='00 01'
+ export 'MINOR=00 00'
+ MINOR='00 00'
+ export 'POWER=C5 00'
+ POWER='C5 00'
+ sudo hciconfig hci0 up
+ sudo hciconfig hci0 noleadv
+ sudo hciconfig hci0 noscan
+ sudo hciconfig hci0 pscan
+ sudo hciconfig hci0 leadv
+ sudo hcitool -i hci0 cmd 0x08 0x0008 1E 02 01 1A 1A FF 4C 00 02 15 4a 4e ce 60 7e b0 11 e4 b4 a9 08 00 20 0c 9a 66 00 01 00 00 C5 00
< HCI Command: ogf 0x08, ocf 0x0008, plen 32
  1E 02 01 1A 1A FF 4C 00 02 15 4A 4E CE 60 7E B0 11 E4 B4 A9
  08 00 20 0C 9A 66 00 01 00 00 C5 00
> HCI Event: 0x0e plen 4
  01 08 20 00
+ sudo hcitool -i hci0 cmd 0x08 0x0006 A0 00 A0 00 00 00 00 00 00 00 00 00 00 07 00
< HCI Command: ogf 0x08, ocf 0x0006, plen 15
  A0 00 A0 00 00 00 00 00 00 00 00 00 00 07 00
> HCI Event: 0x0e plen 4
  01 06 20 0C
+ sudo hcitool -i hci0 cmd 0x08 0x000a 01
< HCI Command: ogf 0x08, ocf 0x000a, plen 1
  01
> HCI Event: 0x0e plen 4
  01 0A 20 0C
+ echo complete
complete 

pi@raspberrypi ~ $

이제 라즈베리 파이를 비콘으로 사용하려면 부팅될 때 마다 자동으로 위의 스크립트가 실행되도록 설정해 주면 된다.

라즈베리 파이에서 특정 프로그램이 부팅될 때 실행되게 하는 방법은 몇가지가 있지만 /etc/rc.local을 수정하는 방법 또는 cron을 사용하는 방법은 문제가 있다. 라즈베리 파이에서 블루투스가 활성화 된 이후에 위의 스크립트가 실행되어야만 하는데 두가지 방법은 스크립트가 먼저 실행되어 버리기 때문에 비콘으로 동작하지 못한다.

그러므로 systemd를 사용해 준다. 먼저 /lib/systemd/system 디렉토리에 두개의 파일을 만들어 준다. 각각 파일의 내용은 아래와 같다.

pi@raspberrypi ~ $ cat /lib/systemd/system/beacon.service
[Unit]
Description=Beacon service
After=bluetooth.target

[Service]
Type=forking
ExecStart=/home/pi/beacon.sh
ExecReload=/home/pi/beacon.sh

[Install]
WantedBy=bluetooth.target


pi@raspberrypi ~ $ cat /lib/systemd/system/beacon.target
#  This file is part of systemd.
#
#  systemd is free software; you can redistribute it and/or modify it
#  under the terms of the GNU Lesser General Public License as published by
#  the Free Software Foundation; either version 2.1 of the License, or
#  (at your option) any later version.

[Unit]
Description=Beacon
Documentation=man:systemd.special(7)
StopWhenUnneeded=yes


pi@raspberrypi ~ $ sudo systemctl daemon-reload
pi@raspberrypi ~ $ sudo systemctl enable beacon.service
Synchronizing state for beacon.service with sysvinit using update-rc.d...
Executing /usr/sbin/update-rc.d beacon defaults
Executing /usr/sbin/update-rc.d beacon enable


pi@raspberrypi ~ $

이제 시스템을 리부팅 하면 항상 자동으로 비콘이 시작된다.





아이폰에서 Beecon 앱으로 확인해 본 결과이다. 스크립트에서 설정한 UUID(4a4e......9a66) , Major(2), Minor(1) 를 확인할 수 있다.






2016년 5월 9일 월요일

슈퍼캡을 사용해 라즈베리 파이용 간이 UPS 만들기

아두이노같이 OS/파일 시스템이 없는 경우는 갑자기 전원이 끊어져도 크게 망가질 부분은 없다. 하지만 라즈베리 파이의 경우 SD 카드에 파일시스템을 관리하고 있기 때문에 정상적으로 shutdown을 하지 않고 갑자기 전원이 나가는 경우 최악의 경우 파일시스템이 망가져 다음번에 정상 부팅이 안 될 가능성이 있다.

그래서 서버를 운영하는데 필수장비 중에 하나가 UPS(Uninterruptible Power Supply)이다.



 내부 동작 원리는 다음과 같다.


정상적으로 전기가 공급되고 있으면 배터리를 충전하면서 동시에 UPS에 연결되어 있는 장비에 전원을 공급해 준다. 하지만 정전이 되면 배터리에 충전되어 있는 전기를 이용해 장비들에 전원을 공급해주게 된다.

정전이 되면 매우 짧은 시간에 배터리에서 전원이 공급되므로 연결되어 있는 장비들은 끊어지지 않고 계속 전력을 공급받을 수 있게 된다. 물론 배터리에 무한정의 전기를 저장할 수 없으므로 짧은 시간 (서버등이 정상적으로 shutdown을 할 수 있는 시간 정도)만 전원을 공급해 줄 수 있다. 그러므로 위의 그림에는 생략되어 있지만 USB 또는 시리얼 통신 등을 통해 정전이 되었다는걸 서버에 알려 줘 안전하게 shutdown 할 수 있게 해 주는 기능을 가지고 있다.

위와 같은 일반적인 상용 UPS는 무겁고 가격도 비싸며 처음 배터리에 충전되는데에도 꽤 시간이 걸린다.

그래서 라즈베리 파이에 사용할 수 있도록 배터리 대신 슈퍼 캐패시터를 사용해 간이 UPS를 만들어 보았다. 10F  용량의 슈퍼캐패시터를 사용했기 때문에 라즈베리 파이 3의 경우 대략 약 1분 정도의 시간동안 전력을 공급해 줄 수 있다.

회로 구성은 다음과 같다.

회로도는 아래에 있다. 대부분의 슈퍼캡은 내압이 2.7V이기 때문에 내압을 5V 이상으로 올리기 위해 두개의 슈퍼캡을 직렬로 연결해 주었다.

PowerIn 쪽에 5V 전원 아답터를 연결해 주고 PowerOut 쪽을 라즈베리 파이의 전원 커넥터에 연결해 주면 된다. 그리고 PwrFail 커넥터(J3)의 +3V3은 라즈베리 파이 커넥터의 17번 핀, PwrFail은 커넥터의 11번 핀(GPIO17)에 연결해 주면 된다.

정상적으로 전원이 공급되면 PwrFail 신호가 'H'를 유지하지만 전원 공급이 끊어지고 슈퍼 캐패시터에서 전원이 공급되면 PwrFail 신호가 'L'로 바뀌게 된다. 그러므로 라즈베리 파이에서 GPIO17번 핀을 감시하다 'L'가 되면 곧바로 shutdown 명령을 수행하면 정전이 되더라도 정상 종료가 되어 파일 시스템이 망가질 일이 없어진다.


먼저 동작 테스트를 위해 위의 회로를 간단하게 만능기판 위에 꾸며 보았다. 위의 사진에서 아래쪽의 작은 LED가 PwrFail 에 연결된 LED로 전원이 공급되고 있으면 불이 켜지고, 전원이 끊어지면 불이 꺼지게 된다. 큰 LED와 DC 모터는 PowerOut에 연결해 놓은 부하(load)이다. 여기서는 라즈베리 파이 대신 전력을 사용하게 된다.


위의 동작 비디오를 보면 처음에는 작은 LED에 빨간 색 불이 켜져 있다. 즉 전원이 공급되고 있다는 것이다. 전원이 공급되고 있으니 당연히 큰 LED에 불이 들어오고 모터가 돌고 있다. 하지만 약 17초 정도쯤에서 빨간색 LED가 꺼진다. 즉 전원 공급이 중단 된 것이다. 하지만 파란색 LED는 계속 켜져 있고 모터도 계속 돌고 있다. 이 시점에서는 외부에서 전원 공급은 없지만 슈퍼 캐패시터에 충전되어 있는 전기로 출력 단자 쪽으로 전기를 공급하고 있는 것이다. 비디오는 짧게 끊었지만 약 3~4분 정도까지 모터가 회전을 지속하였다.

이제 회로 동작 검증은 끝났고 다음번 포스트에서는 직접 라즈베리 파이에 연결한 다음 전원 공급이 끊어질 때 부터 어느 정도까지 버틸 수 있는지 테스트 해 볼 것이다.