레이블이 라즈베리파이인 게시물을 표시합니다. 모든 게시물 표시
레이블이 라즈베리파이인 게시물을 표시합니다. 모든 게시물 표시

2020년 4월 21일 화요일

JREPL

일을 편리하게 해주는 여러 가지 도구가 있지만, 그런 도구들을 잘 사용하려면 적든 많든 노력이 필요하게 마련이다. 때로는 그런 노력을 하는 것이 너무 부담스러울 때도 있어, 차라리 불편하게 일하는 쪽을 택하기도 한다.
내가 라즈베리 파이 문서 번역을 처음 시작할 때가 그랬던 것 같다. 그 전에 이미 여러 해 동안 번역을 하면서 이런저런 도구를 접해봐서 알고 있었음에도 불구하고, OmegaT도 사용하지 않고 git 클라이언트도 없이 깃허브 사이트에서 텍스트를 복사해서 위키독스 웹 인터페이스에 붙여넣고 무식하게 작업을 했던 것으로 기억한다. 일을 편하게 하자고 골치 아파지는 것이 싫었던 것 같다.

지금은 거의 손을 놓고 있지만, 몇 년이 지난 지금도 원본 문서는 계속 업데이트되고 있어 몇 달에 한 번 정도 들여다보곤 한다. 요즘 작업 방식은 이렇다.
1. GitHub Desktop을 사용해 문서 저장소를 pull(PC에 가져옴)
2. 원본 문서를 번역에 적합한 source 파일로 가공
3. OmegaT에서 번역
4. 번역이 완료된 target 파일을 복사해서 위키독스에 붙여넣기

2번 단계에서 하는 일은 이렇다.
a. 원본 문서의 계층 구조(디렉터리/.../파일명)를 위키독스의 구조(페이지 번호)에 맞게 변환
b. 변환된 계층 구조에 맞춰 문서 내의 하이퍼링크를 수정
원본과 번역본의 계층 구조가 다른 이유는 위키독스에서 페이지를 flat하게 부여하기 때문이다. 이 차이점을 근본적으로 해소하기 위해 다른 서비스로 이전해야 할 지 고민해봤지만, 번역을 거의 다 해놓고 유지보수하는 단계에서 굳이 그럴 필요는 없다고 결론을 내렸다.

문제는 2번 단계를 수작업으로 하다보니 너무 많은 노력이 들어가고, 인적 오류 발생 가능성이 상존한다는 것이었다. 그래서 얼마 전부터 그 부분을 자동화하기 시작했다. 자동화라고 해서 대단한 건 없고, 단순히 COPY 명령을 나열하는 수준으로도 a 작업을 할 수 있다. 그리고 b 작업은 본질적으로 문자열 치환을 하면 되는 것이라서 Windows batch 파일을 작성할 때 쓰이는 간단한 for 문을 사용해 처리했다.

그렇게 사용하다보니 몇 가지 가려운 부분이 생겨서 조금씩 수정을 하게 됐는데, batch 문법을 잘 몰라서 매번 애를 먹었다. Python을 사용할까 하는 생각도 해보고, 일부 과정을 Linux나 Mac 환경으로 옮겨서 하는 것도 생각해보고, cygwin도 생각해봤지만(참, Windows에서 bash가 된다고 했었지) 모두 마음에 들지 않았다. Windows 환경에서 native하게 할 수 있는 방법으로 처리하고 싶었다.

오늘은 PowerShell로 할 수 있는 방법을 찾아서 시도해봤는데 잘 안 됐다. 그 후에 방법을 계속 찾다보니, batch 문법만으로는 근본적인 한계가 있어 JREPL을 사용한다는 사람이 있었다. JREPL은 batch와 JScript가 섞인 하이브리드 스크립트로, 문자열 치환을 제대로 처리해준다. 스크립트 파일 하나만 갖다놓으면 사용할 수 있고, 내가 원하는 일을 모두 제대로 처리해줘서(그렇게까지 만드는 과정이 험난했지만) 아주 마음에 든다.

2017년 9월 30일 토요일

라즈베리 파이 번역서 예제 테스트

라즈베리 파이 번역서의 교정이 진행 중이고, 나는 예제 코드 중에 아직 테스트를 하지 못했거나 실패했던 것들을 들여다보고 있다. 그 중에는 모듈이 없어서 테스트를 못한 것이 있어, 간단히 쇼핑을 했다. (지난 겨울의 쇼핑리스트)

쇼핑 리스트

가치창조기술



테스트

스테핑 모터(코드 10.2, 10.3)

지난번에 테스트했을 땐 EasyDriver에 모터 전원 공급하는 걸 빼먹었던 듯하다. 라즈베리 파이의 GPIO 5V 핀을 사용했더니 잘 된다.



8자리수 7 세그먼트 디스플레이 - MAX7219(코드 10.7)

알리에 주문한 것을 못 받아서 이번에 다시 샀다. 예제가 돌아가기는 하지만, 밝기 조절할 때 숫자가 제멋대로 바뀌어버리고, 100000회 카운트 결과가 100260으로 찍히는 문제가 있다. 전에 원고 검토해주신 분들과 상의해봤지만 정확한 원인은 모르겠고, 모듈의 하드웨어 버그로 짐작할 만한 특징이 보인다.


2017년 3월 3일 금요일

레트로 파이, 3.5mm-컴포지트 케이블 개조

번역


라즈베리 파이 책을 번역한 원고를 출판사에 보냈다.

레트로파이


시간 날 때 게임을 좀 해볼까하고, 레트로파이를 설치하고 게임 롬을 몇 개 넣어두었다. 알리 익스프레스에 주문해둔 게임패드가 도착하기를 기다리고 있다.



3.5mm 4극-컴포지트 변환 케이블 개조


3.5mm 4극-컴포지트 변환 케이블을 개조했다. 라즈베리 파이 2 또는 3을 브라운관 TV에 연결하는 데에 쓰기 위해서이다. 라즈베리 파이 1에는 HDMI 단자 외에 비디오 출력용 컴포지트 단자가 있지만(이어폰 단자는 음성 출력용), 라즈베리 파이 2 또는 3에는 컴포지트 단자 대신 3.5mm 4극 이어폰 단자를 통해 음성과 영상을 함께 출력하게 되어있다. 그런데, 가지고 있던 변환 케이블은 극성의 배열이 맞지 않아서 쓸 수가 없었다. 변환 케이블을 새로 구매하려고 봐둔 것이 있기는 했지만, 멀티미터와 전기인두를 갖고 있으니 써먹어야겠다는 생각이 들었다. TV의 음성 입력이 하나뿐이기 때문에 컴포지트 단자 중에서 흰색과 노란색만 연결하고, 빨간색은 빼고 만들었다.






참고





2017년 1월 26일 목요일

GPS 모듈 테스트

AliExpress에서 구입한 GPS 모듈(NEO-6M)을 라즈베리 파이에 연결하여 테스트해보았다.
상품 설명은 새것이라고 되어있지만, 물건을 받아보니 스티커와 안테나가 깨끗하지 않아 중고품 같은 느낌이 들었다. 접점은 깨끗하고, 핀이 붙어있지 않다.


우선, 동봉된 핀을 납땜해서 붙였다. 전기 인두를 쓰는 김에, 다른 부품들도 핀을 붙였다.


테스트를 해보니 실내에서는 신호가 잘 잡히지 않는다. 휴대폰 충전하는 외장 배터리에 연결해서 베란다에서 다시 테스트했다.


$ sudo apt install gpsd-clients
$ sudo systemctl stop serial-getty@ttyAMA0    # RPi 3에서 필요
$ sudo gpsmon /dev/ttyAMA0



참고




2016년 12월 20일 화요일

RPi VNC 해상도 변경

라즈베리 파이에 기본으로 포함된 RealVNC를 통해 접속해보니 화면 해상도가 낮아서 불편했다. 헤드레스일 때 기본 해상도가 640×480인 모양이다. 검색을 통해 알아낸 방법은 약간 의외였다.

/boot/config.txt에서 hdmi_로 시작하는 항목 세 개를 다음과 같이 편집하는 것이다.

hdmi_force_hotplug=1
hdmi_group=2
hdmi_mode=16

각 항목의 의미에 대해서는 문서를 참조.

2016년 12월 6일 화요일

쇼핑 리스트

번역 중인 책에 나온 전자부품들을 구입하느라 며칠 동안 디바이스마트와 알리익스프레스에서 열심히 쇼핑을 했다.


주문내역


구입처 별로 비싼 물건부터. 가격은 변동될 수 있음.

디바이스마트



해외구매 상품 때문에 전체적인 배송이 늦어지는 것이 싫어서, 해외구매 목록의 전체 삭체 버튼을 눌렀다가 국내상품까지 모두 지워지는 바람에 울 뻔 했다. 다행히 맥북에 띄워둔 브라우저에 주문 내역이 남아있어서 다시 주문할 수 있었다.


알리익스프레스




중고나라


  • XBee 시리즈 2



아직 못다 산 것들





알리익스프레스 결제


디바이스마트는 네이버 페이로 결제 완료. 알리에서는 주문 후 결제를 마치지 못했다. 신용카드는 없애버린 지 오래이고, 체크 카드는 얼마 전에 교체발급하면서 실수로 해외결제가 안되는 카드를 받았기 때문이다. 이참에 달러화로 해외 결제 시 수수료를 물지 않는다는 '하나 글로벌페이' 체크카드를 만들어볼까 싶다(해외 결제 체크카드 수수료 및 수수료를 덜 낼 수 있는 카드들 참조).

여러 업체에서 주문했고, 배송 기간도 제각각이라 물건이 도착할 때마다 테스트해보면 될 듯. 이번 달까지 초벌 번역을 끝내고 1~2월은 테스트와 퇴고를 하며 보내야겠다.


라즈비안 SSH


라즈비안의 최신 버전에서는 SSH 서버가 디폴트로 disable된다는 소식(SSH - 위키독스 라즈베리 파이 문서 참조).

2016년 11월 13일 일요일

TV 리모콘으로 OpenELEC 조작

IR(적외선) 센서를 라즈베리 파이에 연결하여 TV 리모콘으로 OpenELEC을 조작하도록 해보았다.

RPi와 IR 센서(늘솜 NS-GPIO-04)는 지인에게 얻은 것이고, 리모콘은 VHS/DVD 콤보를 중고로 구입할 때 딸려온 것인데 짝이 맞지 않아서인지 작동이 되지 않아 못쓰고 있었다.

IR 센서 연결


센서 모듈은 조립이 된 상태로 받은 것인데, 센서가 기판의 앞면에 붙어있어서 RPi 2에 꽂는데에 문제가 없다. IR 센서는 RPi의 GPIO18에 연결된다(데이터시트).


디바이스 트리 오버레이 추가


RPi에 IR 센서를 연결하였으면 config.txt 파일을 편집해야 한다. OpenELEC에서는 /flash 디렉터리에 있는데, 파일시스템이 읽기 전용으로 마운트되어 있기 때문에 편집하기 위해서는 쓰기가 가능하도록 마운트해야 한다. SSH로 접속하여 다음의 명령을 실행한다.

openelec:/flash # mount | grep flash
/dev/mmcblk0p6 on /flash type vfat (ro,noatime,fmask=0022,dmask=0022,codepage=437,iocharset=ascii,shortname=mixed,utf8,errors=remount-ro)
openelec:/flash # mount -o remount,rw /flash
openelec:/flash # mount | grep flash
/dev/mmcblk0p6 on /flash type vfat (rw,noatime,fmask=0022,dmask=0022,codepage=437,iocharset=ascii,shortname=mixed,utf8,errors=remount-ro)

config.txt에 한 줄을 추가하고 리부트한다.

openelec:/flash # tail -1 config.txt
dtoverlay=lirc-rpi


리모콘 테스트


modprobe를 실행하고,

openelec:~ # modprobe lirc_rpi

lircd 프로세스를 종료한 다음,

openelec:~ # killall lircd

다음의 명령을 실행하고 리모콘을 눌러본다.

openelec:~ # mode2 -d /dev/lirc0
space 16777215
pulse 4528
space 4428
pulse 598
...

리모콘의 버튼을 눌렀을 때 출력이 발생하는 것으로 보아, 리모콘이 작동한다는 것을 알 수 있다.


리모콘 기록


irrecord를 실행하여 리모콘의 버튼 하나하나에 이름을 붙인다.

openelec:~ # irrecord --device=/dev/lirc0 /storage/.config/lircd.conf


irrecord의 지시를 따라 키보드와 리모콘을 누르다보면 다음과 같은 파일이 만들어진다.  /etc/lirc/lircd.conf.rpi 파일과 http://lirc.sourceforge.net/remotes/lg/6710CDAL01G 를 참고했다.

openelec:~/.config # more lircd.conf

# Please make this file available to others
# by sending it to 
#
# this config file was automatically generated
# using lirc-0.9.1-git(default) on Sun Nov 13 02:57:53 2016
#
# contributed by
#
# brand:                       LG
# model no. of remote control: 6710CMAM03C
# devices being controlled by this remote:
#

begin remote

  name  LG_6710CMAM03C2
  bits           16
  flags SPACE_ENC|CONST_LENGTH|REPEAT_HEADER
  eps            30
  aeps          100

  header       4518  4441
  one           580  1653
  zero          580   535
  ptrail        579
  repeat        580  1652
  pre_data_bits   16
  pre_data       0x808
  gap          107643
  toggle_bit_mask 0x0

      begin codes
          KEY_POWER                0x7887
          KEY_MUTE                 0xF807
          KEY_VOLUMEUP             0xE817
          KEY_VOLUMEDOWN           0x6897
          KEY_SLEEP                0x43BC
          KEY_1                    0x827D
          KEY_2                    0x42BD
          KEY_3                    0xC23D
          KEY_4                    0x22DD
          KEY_5                    0xA25D
          KEY_6                    0x629D
          KEY_7                    0xE21D
          KEY_8                    0x12ED
          KEY_9                    0x926D
          KEY_0                    0xD22D
          KEY_UP                   0x20DF
          KEY_DOWN                 0x728D
          KEY_LEFT                 0x609F
          KEY_RIGHT                0xE01F
          KEY_ENTER                0xA05F
          KEY_MENU                 0xB24D
          KEY_STOP                 0x708F
          KEY_CHANNELUP            0xC837
          KEY_CHANNELDOWN          0x48B7
          KEY_RECORD               0xA857
          KEY_STOP                 0x708F
          KEY_FASTFORWARD          0x8877
          KEY_REWIND               0x08F7
          KEY_PLAY                 0xB04F
          KEY_BACK                 0x30CF
      end codes

end remote

리부트하면 OpenELEC에서 리모콘을 사용할 수 있다. 키보드나 스마트폰 앱으로 조작하는 것보다 훨씬 편하다.



참고



2016년 11월 5일 토요일

랜 선으로 두 대의 RPi 연결

랜 선 하나만으로 두 대의 라즈베리 파이를 연결할 수 있는지 실험해보았다. 결과는 성공이다.



랜 케이블로 연결된 두 대의 라즈베리 파이의 호스트명은 각각 rpi1과 rpi3이다. rpi1에는 HDMI와 키보드, 마우스를 연결하고 micro USB 전원을 연결했다. rpi3은 헤드레스로, USB-TTL 케이블을 통해 노트북의 USB 포트에서 전원을 공급받으며, 노트북에서 콘솔을 띄웠다.

rpi3에서 ifconfig로 IP 주소를 확인한 후,
pi@rpi3:~$ ifconfig eth0
eth0      Link encap:Ethernet  HWaddr b8:27:eb:74:7d:c9
          inet addr:169.254.218.16  Bcast:169.254.255.255  Mask:255.255.0.0
          inet6 addr: fe80::bbe0:b9f7:d1f3:1222/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:88 errors:0 dropped:0 overruns:0 frame:0
          TX packets:133 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:21761 (21.2 KiB)  TX bytes:34334 (33.5 KiB)

rpi1에서 rpi3으로 ping 테스트를 해보니 잘 된다.
pi@rpi1 ~ $ ping -c 3 169.254.218.16
PING 169.254.218.16 (169.254.218.16) 56(84) bytes of data.
64 bytes from 169.254.218.16: icmp_req=1 ttl=64 time=0.578 ms
64 bytes from 169.254.218.16: icmp_req=2 ttl=64 time=0.483 ms
64 bytes from 169.254.218.16: icmp_req=3 ttl=64 time=0.544 ms

--- 169.254.218.16 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2000ms
rtt min/avg/max/mdev = 0.483/0.535/0.578/0.039 ms

SSH 접속도 잘 된다.
pi@rpi1:~$ ssh pi@169.254.218.16
The authenticity of host '169.254.218.16 (169.254.218.16)' can't be established.
ECDSA key fingerprint is 78:7a:83:49:ae:18:91:45:91:37:03:ca:fa:d3:14:f5.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '169.254.218.16' (ECDSA) to the list of known hosts.
pi@169.254.218.16's password:

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Sat Nov  5 13:10:27 2016 from rpi1.local
pi@rpi3:~$

IP 주소를 입력하는 대신 호스트명을 사용하기 위해, rpi1에 있는 /etc/hosts 파일의 끝에 rpi3의 IP 주소를 추가했다.
pi@rpi1 ~ $ tail -1 /etc/hosts
169.254.218.16  rpi3

그리고, SSH 접속을 할 때마다 패스워드를 입력하지 않기 위해 다음과 같이 키를 생성 및 복사했다.
$ ssh-keygen
$ ssh-copy-id pi@rpi3
$ ssh-add

지금까지 한 일은 아래의 영상과 같은 것을 만들어 보기 위한 준비 과정이었다. 소스 코드는 여기에 있다.

2016년 10월 31일 월요일

RPi에서 음성 인식하기

라즈베리 파이에서 USB 마이크와 PocketSphinx라는 소프트웨어를 이용하여 음성인식을 시험해보았다. 이 글에서는 마이크를 사용하여 소리를 녹음해보고, 음성인식에 필요한 소프트웨어를 설치한 다음, 음성을 인식하는지 테스트해보고, 그것을 이용해서 다른 명령을 실행시키는 과정을 다룬다.

USB 마이크


구입과 설치


USB 마이크의 구입과 설치에 대해서는 Raspberry Pi에서 소리 입력 및 녹음하기를 참조. 아래와 같은 명령으로 소리를 녹음하고 재생할 수 있으면, 다음으로 진행할 수 있다(이 글에서는 소리의 녹음을 위해 ALSA 프레임워크를 사용한다).

$ arecord -D plughw:1,0 -d 5 test.wav
$ aplay test.wav


USB 마이크를 기본 녹음 장치로 설정


USB 마이크를 기본 녹음 장치로 만들어두는 것이 편리할 것이다.

$ more ~/.asoundrc
pcm.!default {
    type asym
    playback.pcm {
        type plug
        slave.pcm "hw:0,0"
    }
    capture.pcm {
        type plug
        slave.pcm "hw:1,0"
    } 
}


홈 디렉터리에 위와 같이 .asoundrc를 작성한다. 이제 arecord를 실행할 때 -D 옵션을 생략해도 된다.

$ arecord -d 5 test.wav


음성인식


의존 패키지 설치


PocketSphinx를 설치하기에 앞서, 필요한 패키지들을 먼저 설치한다.

sudo apt install libasound2-dev autoconf libtool bison swig python-dev python-pyaudio
sudo pip install gevent grequests


SphinxBase와 PocketSphinx 설치


SphinxBase를 설치한다.

git clone git://github.com/cmusphinx/sphinxbase.git
cd sphinxbase
./autogen.sh
make
sudo make install
cd ..

PocketSphinx를 설치한다.

git clone git://github.com/cmusphinx/pocketsphinx.git
cd pocketsphinx
./autogen.sh
make
sudo make install
cd ..

ldconfig를 실행.

sudo ldconfig


사전 만들기


말뭉치(corpus)를 텍스트 파일로 작성한다. 아래의 말뭉치는 구글 캘린더를 조작하는 명령과 관련된 것이다.

$ more corpus.txt 
day
week
month
4
agenda
refresh
next
previous
today

언어 모델과 사전을 만들 차례이다. 위에서 만든 말뭉치 파일을 http://www.speech.cs.cmu.edu/tools/lmtool-new.html 페이지의 Upload a sentence corpus file:에서 선택한 다음, "COMPILE KNOWLEDGE BASE" 버튼을 누르면 잠시 후에 다운로드 페이지로 이동한다. .lm과 .dic 파일을 다운로드하거나, .tgz로 압축된 파일을 다운로드하여 압축을 푼다. 파일들을 적당한 위치로 옮기고, 필요하다면 이름도 바꾼다.

음성인식 테스트


다음과 같이 pocketsphinx_continuous 명령을 실행하여 음성을 인식하는지 테스트해본다. -lm과 -dict 옵션에는 .lm 파일과 .dic 파일을 각각 지정한다. USB 마이크가 디폴트 녹음 장치로 설정되지 않았다면, -adcdev plughw:1,0 옵션을 추가하여 실행한다.

$ pocketsphinx_continuous -hmm /usr/local/share/pocketsphinx/model/en-us/en-us -lm cal.lm -dict cal.dic -samprate 16000/8000/48000 -inmic yes
INFO: pocketsphinx.c(152): Parsed model-specific feature parameters from /usr/local/share/pocketsphinx/model/en-us/en-us/feat.params
Current configuration:
[NAME]   [DEFLT]  [VALUE]
-agc   none  none
-agcthresh  2.0  2.000000e+00
-allphone    
-allphone_ci  no  no
-alpha   0.97  9.700000e-01

...

INFO: ps_lattice.c(1441): Joint P(O,S) = -144424 P(S|O) = -24085
INFO: ngram_search.c(872): bestpath 0.00 CPU 0.000 xRT
INFO: ngram_search.c(875): bestpath 0.00 wall 0.001 xRT
NEXT
INFO: continuous.c(275): Ready....
^C

위에서는 음성을 NEXT라고 인식한 것을 볼 수 있다.

음성인식 결과를 이용하여 다른 명령 실행하기

pocketsphinx_continuous를 사용하여 구글 캘린더(이전 글 참조)를 조작하는 셸 스크립트를 다음과 같이 작성해보았다.

#!/bin/sh

cd /home/pi/calendar

pocketsphinx_continuous -hmm /usr/local/share/pocketsphinx/model/en-us/en-us \
    -lm cal.lm -dict cal.dic -samprate 16000/8000/48000 -inmic yes | \
    xargs -L1 sh -c 'echo "$*" | head -c1 | tr A-Z a-z | \
    xargs xdotool search -onlyvisible -class "chromium" windowactivate type' -

위에서 pocketsphinx_continuous는 마이크로 입력된 음성을 분석해서 그에 해당하는 단어를 출력하는 것을 반복한다. 그러면, 출력이 있을 때마다 파이프와 xargs를 통해 head, tr, xdotool이 실행된다. 'NEXT'를 인식하였다면, 첫 글자 'N'을 취해서, 소문자인 'n'으로 변환하고, Chromium 창에서 'n'을 타이핑하는 것이다. 구글 캘린더는 키보드 입력으로 'n'이 들어오면 다음(next)으로 이동한다.
자동 실행을 위해 다음과 같은 systemd unit 파일을 ~/.config/systemd/user/pocketsphinx.service에 작성했다.

[Unit]
Description=PocketSphinx
After=graphical.target

[Service]
ExecStart=/home/pi/calendar/start-pocketsphinx.sh
Environment=DISPLAY=:0
Restart=no

[Install]
WantedBy=default.target

참고





2016년 10월 20일 목요일

RPi 벽걸이 캘린더

라즈베리 파이와 구글 캘린더를 이용하여 벽걸이 캘린더를 만드는 프로젝트가 있다. 요즘 수요일마다 참석하는 스터디 모임에서 일차적인 목표로 삼은 과제이다.

얼마 전에 구입한 RPi 3 Model B를 이용하여 이를 구현해보았다. Raspbian의 새로운 데스크톱 환경인 PIXEL에 기본으로 포함되는 Chromium 웹 브라우저를 사용했다.

Chromium에서 구글 캘린더를 열면 모바일 페이지가 뜨는 버그가 있는데, URL을 https://calendar.google.com/calendar/render로 변경함으로써 데스크톱용 캘린더 페이지를 열 수 있다(스터디에서는 버그를 피하기 위해 Chromium 대신 iceweasel(Firefox ESR) 브라우저를 설치하는 방법을 택했다. 따라서 아래의 내용을 참고하되 iceweasel에 맞도록 수정이 필요하다). Chromium에서 키보드의 F11 키를 누르면 전체 화면으로 바뀐다. 캘린더 보기는 "월"을 선택한다.

명령행에서 다음의 명령을 실행하면, Chromium을 전체화면으로 실행시키면서 구글 캘린더를 "월" 보기로 보여준다.
chromium-browser --kiosk "https://calendar.google.com/calendar/render#main_7|month"

이제, RPi를 켤 때마다 캘린더가 뜨도록 할 차례이다. 스터디에서는 ~/.config/lxsession/LXDE/autostart 파일을 이용하려고 했지만, 원격 접속의 문제인지 아니면 PIXEL로 바뀐 것 때문인지는 몰라도 예상대로 잘 되지 않는 것 같았다. 집에 와서 검색을 통해 다음과 같은 방법을 찾았다.
  1. 위의 명령을 실행하는 start-kiosk.sh 파일을 pi 사용자의 홈(~) 디렉터리에 만든다.
  2. 다음과 같은 설정으로 ~/.config/systemd/user/kiosk.service 파일을 만든다.
    [Unit]
    Description=Kiosk
    After=graphical.target
    
    [Service]
    ExecStart=/bin/bash /home/pi/start-kiosk.sh
    Environment=DISPLAY=:0
    Restart=always
    
    [Install]
    WantedBy=default.target
  3. 다음의 명령을 실행한다.
    systemctl --user enable kiosk
    systemctl --user start kiosk
  4. sudo reboot 명령으로 테스트해본다. Chromium이 제대로 종료되지 않은 것에 대한 팝업이 뜨는데, 이것을 숨기려면 start-kiosk.sh 파일의 chromium-brower 명령에 옵션을 추가한다.
    chromium-browser --kiosk --disable-session-crashed-bubble --disable-infobars "https://calendar.google.com/calendar/render#main_7|month"
  5. 화면이 꺼지거나 화면보호기가 뜨지 않도록 하려면 start-kiosk.sh에서 Chromium 실행 전에 다음과 같은 명령을 실행하도록 해준다.
    xset -dpms; xset s off

참고





2016년 10월 18일 화요일

Raspberry Pi 잡설

Raspberry Pi 관련 몇 가지 소소한 발견.

RPi에 외장 HDD 연결

  • RPi 1 Model B에서는 USB 포트의 출력이 약해서 외장형 HDD 사용에 문제가 있고, 가능하다 하더라도 포트가 2개 밖에 없기 때문에 다른 USB 장치를 꽂을 여유가 없다. 외장 HDD를 사용하기 위해서는 별도의 전원을 가진 USB 허브가 필요하다.
  • RPi 3 Model B에 외장형 HDD를 직접 물려보았더니 잘 동작한다. 핫 플러깅을 했는데도 문제가 없었다. 전원은 2.5 A 어댑터로부터 RPi의 마이크로 USB에 공급.
  • RPi 1 Model B+에서도 가능할지 모른다.

OpenELEC


  • RPi에서 쓰던, OpenELEC을 설치한 마이크로 SD 카드를 그대로 RPi 3에 넣고 전원을 넣어보니 부팅이 되는 듯 메시지가 올라가다가 멈춘다. 업데이트를 해줘야 쓸 수 있다.
  • OpenELEC에서 TV를 볼 수 있다! 댓글의 채널 목록을 받아서 테스트해보니, 현재 안 나오는 채널도 많지만, 아직 나오는 채널도 많다.
    www.todayhumor.co.kr/board/view.php?table=jisik&no=201727
  • KODI 재시작 명령
    systemctl restart kodi

디스플레이

영상을 감상할 때 종횡비 4:3의 브라운관 TV에 연결하면 위아래가 남고, WFHD(21:9) 모니터에서는 좌우가 남는다. 영상을 감상하는 데에는 역시 16:9 모니터가 최적일 것 같다. 그런 용도가 아니라 하더라도, RPi를 사용할 때 이런저런 신경을 쓰지 않고 편하게 사용할 수 있는 디스플레이 장치는 HDMI 단자가 있고 스피커가 내장된, 종횡비 16:9의 모니터라는 결론.



2016년 10월 14일 금요일

USB-TTL 케이블로 RPi에 연결

이번에 구입한 라즈베리파이 스타터 키트에는 USB to TTL 케이블이 포함되어 있다. 이것을 이용하면 네트워크가 되지 않는 상황에서도 직렬 연결을 통해 라즈베리 파이(RPi)와 통신할 수 있어 든든하다(명령행에서 WiFi를 설정할 수 있다).

디바이스마트에서 구입한 것과 동일한 것으로 보이는 제품이 Adafruit에도 있어서 좀 더 자세한 정보를 알 수 있었다. USB 쪽 몸체가 검정색으로 된 것은 PL2303HX 칩셋을 쓴 구형 제품이고, 2014년 이후 제품은 파란색 몸체에 PL2303TA 칩셋이 들어있다고 한다. Windows 8과 10까지 지원하는 최신 드라이버에 대한 링크도 있다.

검정, 흰색, 녹색 선을 각각 6, 8, 10번 핀에 꽂는다. 빨간 선을 2번 핀에 꽂으면 전원 공급도 가능한데, 그때에는 마이크로 USB에는 전원을 연결하지 말아야 한다.


그리고, RPi의 /boot/config.txt에 UART를 사용하겠다는 설정이 있어야 하고, raspi-config에는 직렬 연결을 통해 로그인할 수 있도록 설정되어 있어야 한다(여기에서 삽질을 했다).

/boot/config.txt
...
enable_uart=1






참고


2016년 10월 13일 목요일

RJ with PIXEL 설치 및 한글 설정

라즈베리 파이 3 모델 B를 구입했다(디바이스마트의 스타터 키트).

노트북에 Raspbian Jessie with PIXEL(2016-09-23) 이미지를 내려받아Win32DiskImager로 마이크로 SD 카드에 기록한 다음, 마이크로 SD 카드를 RPi에 꽂고, 모니터와 키보드・마우스도 연결하고서 RPi의 전원을 넣으니 금세 부팅이 되어 GUI 데스크탑으로 들어온다.

Raspberry Pi Configuration에서 몇 가지 설정을 하고, 리부트. 한 가지 주의할 점은, Localisation 탭에서 WiFi Country를 KR로 변경하면 안 된다.

라즈비안을 업그레이드하고,

$ sudo apt update
$ sudo apt upgrade


펌웨어를  업데이트하고,

$ sudo rpi-update


한글이 제대로 표시되도록 글꼴을 설치하고,

$ sudo apt install ttf-unfonts-core


한글 입력을 위해 fcitx-hangul도 설치했다.

$ sudo apt install fcitx-hangul

리부트.

$ sudo reboot


RPi에 직접 연결한 키보드를 사용할 때 영국식 키보드 배열이 되어서 기호 입력에 문제가 있었다(@와 #이 아닌 따옴표와 파운드 기호가 입력됨). Fcitx 설정에서 Input Method에 "키보드 - 영어(미국)"을 추가하고, Hangul 입력기의 Keyboard layout을 "한국어 - 한국어(101/104키 호환)"으로 설정하여 해결했다.

참고



2016년 10월 12일 수요일

RPi에 WN725N 드라이버 설치

라즈베리 파이 모델 B에서 패키지 업그레이드와 펌웨어 업데이트를 했다.

pi@raspberrypi ~ $ sudo apt-get update
pi@raspberrypi ~ $ sudo apt-get upgrade
pi@raspberrypi ~ $ sudo rpi-update
pi@raspberrypi ~ $ sudo reboot
pi@raspberrypi ~ $ uname -a
Linux raspberrypi 4.4.23+ #913 Tue Oct 4 13:56:56 BST 2016 armv6l GNU/Linux
pi@raspberrypi ~ $ sudo vcgencmd version
Oct 11 2016 12:23:39
Copyright (c) 2012 Broadcom
version c242c9606727d307232bde9c153938ab0b05c12d (clean) (release)

무선 랜 동글(WN725N)이 동작하지 않는다. ifconfig -a 명령을 실행해보니, 지금까지 사용하던 wlan0이 보이지 않는다.

아래의 방법으로 드라이버를 재설치하여 해결했다. 라즈베리 파이가 인터넷에 연결된 상태에서 가능한 방법이라, Wi-Fi를 통해 인터넷에 접속된 노트북에 랜 선으로 연결하여 작업했다.

pi@raspberrypi ~ $ wget https://dl.dropboxusercontent.com/u/80256631/install-wifi.tar.gz
--2016-10-12 20:36:22--  https://dl.dropboxusercontent.com/u/80256631/install-wifi.tar.gz
Resolving dl.dropboxusercontent.com (dl.dropboxusercontent.com)... 108.160.172.5
Connecting to dl.dropboxusercontent.com (dl.dropboxusercontent.com)|108.160.172.5|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 3674 (3.6K) [application/octet-stream]
Saving to: `install-wifi.tar.gz.1'

100%[=====================================================>] 3,674       --.-K/s   in 0s

2016-10-12 20:36:35 (7.95 MB/s) - `install-wifi.tar.gz.1' saved [3674/3674]

pi@raspberrypi ~ $ tar xzf install-wifi.tar.gz
pi@raspberrypi ~ $ ./install-wifi

Your current kernel revision = 4.4.23+
Your current kernel build    = #913

Checking for a wifi module to determine the driver to install.

Your wifi module is Bus 001 Device 004: ID 0bda:8179 Realtek Semiconductor Corp.

And it uses the 8188eu driver.

Checking for a new 8188eu wifi driver module for your current kernel.
There is a driver module available for this kernel revision.
Downloading the 8188eu driver.
Installing the 8188eu driver.

참고

2016년 8월 2일 화요일

GitHub의 변경내역 비교하기

2015년 10월부터 라즈베리 파이 재단의 공식 문서를 번역하여 wikidocs.net에 게재하고 있다. 문서는 꾸준히 갱신되기 때문에, GitHub의 저장소에서 커밋 로그를 2-3일마다 확인하여 번역본에도 적용하고 있다(수정 사항이 많거나 바쁠 때에는 원문만 먼저 적용해두고 차차 번역해 나가는 식으로 작업한다). 수정 사항이 많지 않을 때에는 크게 문제가 되지 않지만, 은근히 시간을 많이 뺐는 작업이다. 게다가, 요즘 들어 여러 가지 일로 바빠서 신경을 쓰지 못하다보니, 보름 정도 변경 사항이 쌓여버렸다.

"15일 동안의 변경 사항을 쉽게 확인할 수 없을까?"하는 궁금증이 생겨 잠시 검색해보니, 아주 좋은 방법이 있다.

저장소에 대한 https 경로의 끝에 /compare를 붙이는 것이다.

https://github.com/raspberrypi/documentation/compare

그러면 Compare changes라는 화면이 보인다. 친절하게도, 하루 전의 master와 비교할 수 있는 예제도 링크해놓았다.

https://github.com/raspberrypi/documentation/compare/master@%7B1day%7D...master

1day를 15day로 바꾸면, 보름 동안의 변경사항을 확인.

https://github.com/raspberrypi/documentation/compare/master@%7B15day%7D...master

커밋은 48회 이루어졌지만, 실제 변경된 파일은 19개 밖에 되지 않으므로 쫓아가기가 한결 수월해졌다.


2016년 4월 17일 일요일

AV 시스템 셋업


피겨 스케이트 비디오를 여러 개 구한 것을 계기로, 거실 겸 침실에 AV 시스템을 꾸몄다.

왼쪽 아래부터 시계 방향으로 - 지난 달에 만오천원 주고 산 삼성 20인치 TV. 얼마 뒤에 3만원에 구입하고 LG 서비스센터에서 수리한 VHS/DVD 콤보. OpenELEC을 설치한 라즈베리 파이 모델 B. 아이패드 1세대와 키보드 독. JBL 블루투스 스피커(키보드 독의 이어폰 단자에 유선 연결).



2016년 2월 25일 목요일

웹 브라우저에서 Raspberry Pi 조작하기

스터디의 이번 과제는, 웹브라우저에서 Raspberry Pi의 주소로 접근하여 텍스트필드에 문자열을 입력하면, 그 문자열을 PiFaceCAD에 출력하는 것이었다. 서버는 node.js를 사용하여 구현하기로 했다.

node.js의 웹프레임워크인 express 모듈을 사용했고, PiFaceCAD를 다루는 Python 코드를 호출하기 위해 child_process 모듈을 사용했다.

server.js

var express = require('express');
var app = express();

app.use(express.static('public'));

app.get('/', function (req, res) {
   res.sendFile( __dirname + "/" + "form.html" );
})

app.get('/lcd_write', function (req, res) {
   text = "\"" + req.query.text + "\""
   var exec = require('child_process').exec;
   function puts(error, stdout, stderr) { console.log(stdout) }
   cmd = "python3 -c \"import sys, pifacecad; pifacecad.PiFaceCAD().lcd.write(sys.argv[1])\""
   exec(cmd + " " + text, puts);
   console.log(text);
   res.end(text);
})

var server = app.listen(8000, function () {

  var host = server.address().address
  var port = server.address().port

  console.log("Example app listening at http://%s:%s", host, port)

})

form.html

<html>
<body>
<form action="/lcd_write" method="GET">
Input text here: <input type="text" name="text">  <br>
<input type="submit" value="Submit">
</form>
</body>
</html>


2016년 2월 22일 월요일

Raspberry Pi에 node.js 설치

Raspberry Pi(Raspbian Wheezy)에 node.js를 설치해서 테스트해보았다. 강좌(http://pyrasis.com/nodejs/nodejs-HOWTO)를 따라서 콘솔 메시지로 Hello World까지는 출력을 했지만, express 모듈을 설치하고 사용하는 것이 그리 순탄치 않다.

npm ERR! Error: failed to fetch from registry: express

위와 같은 메시지가 나타나며 패키지 설치에 실패한다든지, 세그멘테이션 오류가 발생한다든지 하는 문제가 생긴다.

다음의 명령으로 패키지를 제거한 뒤에,

sudo apt-get purge nodejs npm

다음의 명령으로 4.2.4 버전을 다시 설치했다.

wget http://nodejs.org/dist/v4.2.4/node-v4.2.4-linux-armv6l.tar.gz
cd /usr/local
sudo tar xzvf ~/node-v4.2.4-linux-armv6l.tar.gz --strip=1


참고

2016년 2월 14일 일요일

Raspberry Pi MP3 Player

Raspberry Pi와 PiFaceCAD를 사용하여 MP3 플레이어를 만들어보았다. 이것도 역시 스터디 숙제. 음악 파일의 재생을 위해 pygame.mixerpygame.mixer.music을 사용했다.



소스 코드:

#!/usr/bin/python3

from pygame import mixer
import time

class Player:
    def __init__(self):
        self.isOn = True
        self.isPlaying = False
        self.isPaused = False

        show("MP3 Player")
        mixer.init()
        self.musicVolume = round(mixer.music.get_volume(), 1)
        import glob
        self.musicFiles = tuple(sorted(glob.glob("*.mp3")))
        self.musicNum = 0
        filename = self.musicFiles[self.musicNum]
        show(filename + "\nReady")

    def listen(self, sw_id, func, always=False):
        if cad.switches[sw_id].value == 1 and (always or self.isOn):
            while cad.switches[sw_id].value == 1: pass
            print('self.%s()' % func)
            exec('self.%s()' % func)

    def playPause(self):
        if self.isPlaying:
            self.pause()
        else:
            if self.isPaused:
                self.resume()
            else:
                self.play()

    def play(self):
        filename = self.musicFiles[self.musicNum]
        mixer.music.load(filename)
        self.musicTitle = filename
        show(self.musicTitle + "\nPlaying")
        mixer.music.play()
        self.isPlaying = True

    def pause(self):
        self.isPlaying = False
        mixer.music.pause()
        show(self.musicTitle + "\nPaused")
        self.isPaused = True

    def resume(self):
        self.isPlaying = True
        show(self.musicTitle + "\nPlaying")
        mixer.music.unpause()
        self.isPaused = False

    def prev(self):
        self.musicNum = (self.musicNum - 1) % len(self.musicFiles)
        self.play()

    def next(self):
        self.musicNum = (self.musicNum + 1) % len(self.musicFiles)
        self.play()

    def randomPlay(self):
        import random
        self.musicNum = random.choice(range(len(self.musicFiles)))
        self.play()

    def volumeUp(self):
        self.setVolume("up")

    def volumeDown(self):
        self.setVolume("down")

    def setVolume(self, action):
        min, max, delta = 0.0, 1.0, 0.1
        vol = mixer.music.get_volume()
        vol = round(vol, 1)

        if action is "up" and vol < max:
            vol = round(vol + delta, 1)
            mixer.music.set_volume(vol)
            show(self.musicTitle + "\nvol: " + str(vol))
            self.musicVolume = vol
        elif action is "down" and vol > min:
            vol = round(vol - delta, 1)
            mixer.music.set_volume(vol)
            show(self.musicTitle + "\nvol: " + str(vol))
            self.musicVolume = vol
        elif action is "mute":
            if vol == min:
                vol = self.musicVolume
                mixer.music.set_volume(vol)
                show(self.musicTitle + "\nvol: " + str(vol))
            else:
                vol = min
                mixer.music.set_volume(vol)
                show(self.musicTitle + "\nMuted")
        else:
            pass

    def mute(self):
        self.setVolume("mute")

    def onOff(self):
        if self.isOn:
            if self.isPlaying:
                mixer.music.stop()
            show('Bye...')
            time.sleep(2)
            cad.lcd.backlight_off()
            time.sleep(1)
            cad.lcd.clear()
            self.isOn = False
        else:
            self.__init__()

import pifacecad

cad = pifacecad.PiFaceCAD()
cad.lcd.backlight_on()
cad.lcd.cursor_off()
cad.lcd.blink_off()

def show(text):
    cad.lcd.clear()
    cad.lcd.write(text)

p = Player()

while True:
    p.listen(0, 'playPause')
    p.listen(1, 'prev')
    p.listen(2, 'next')
    p.listen(3, 'randomPlay')
    p.listen(4, 'onOff', True)
    p.listen(5, 'mute')
    p.listen(6, 'volumeDown')
    p.listen(7, 'volumeUp')

2016년 2월 10일 수요일

PiFaceCAD

올해 초부터 우분투 한국 사용자모임에서 시작한 Raspberry Pi 스터디에 매주 나가고 있다. Pi에서 node.js를 구동하여 웹을 통해 이런저런 장치를 제어하는 것을 목표로 하는데, 작업의 편의를 위해 PiFaceCAD(PiFace Control and Display)라는 제품을 구입해서 사용하기로 했다. 들고다니다가 망가뜨리지 않을까 걱정이 되어, Raspberry Pi에 PiFaceCAD를 꽂은 채로 넣을 수 있는 케이스도 함께 샀다.

PiFaceCAD에 친숙해지기 위해 Python으로 스코어보드를 만들어보는 숙제가 있었다. LCD에 두 팀의 점수를 보여주는 기능을 하며, 각 팀의 점수를 올리기 위한 스위치 한 개 씩과, 점수를 리셋하는 용도의 스위치 한 개를 사용한다.


위의 영상과 같이 동작하며, 내가 짠 코드는 다음과 같다. (먼저 셋업이 필요하다)

#!/usr/bin/python3

import pifacecad

cad = pifacecad.PiFaceCAD()
cad.lcd.backlight_on()

def show():
    cad.lcd.clear()
    cad.lcd.write('A team:' + str(score[0]) + '\n'
                  + 'B team:' + str(score[1]))

score = [0, 0]
show()

while True:
    if cad.switches[0].value == 1:
        score[0] = score[0] + 1
        while cad.switches[0].value == 1: pass
        show()
    elif cad.switches[1].value == 1:
        score[1] = score[1] + 1
        while cad.switches[1].value == 1: pass
        show()
    elif cad.switches[4].value == 1:
        score = [0, 0]
        while cad.switches[4].value == 1: pass
        show()

스위치를 한 번 누르는 동안 여러 점이 올라가는 것을 막기 위해, 손을 뗄 때까지 pass시키는 작은 while 문을 넣었다. 다른 사람들은 time.sleep()을 사용해서 처리했다. 이때, 일반적으로 0.25 초 정도의 sleep이 적당하다고 한다.

작성해놓고 보니 while 블록에서 중복으로 나타나는 것들을 정리하고 싶어져서 아래와 같이 바꾸었다.

#!/usr/bin/python3

import pifacecad

cad = pifacecad.PiFaceCAD()
cad.lcd.backlight_on()

def show():
    cad.lcd.clear()
    cad.lcd.write('A team:' + str(score[0]) + '\n'
                  + 'B team:' + str(score[1]))

def listen(i, func):
    global score
    if cad.switches[i].value == 1:
        if func == 'add': score[i] = score[i] + 1
        elif func == 'reset': score = [0, 0]
        while cad.switches[i].value == 1: pass
        show()

score = [0, 0]
show()

while True:
    listen(0, 'add')
    listen(1, 'add')
    listen(4, 'reset')

다음의 코드는, 위쪽에 붙은 로커 스위치를 테스트해보기 위해 수정한 버전이다.

#!/usr/bin/python3

import pifacecad

cad = pifacecad.PiFaceCAD()
cad.lcd.backlight_on()

def show():
    cad.lcd.clear()
    cad.lcd.write('A team:' + str(score[0]) + '\n'
                  + 'B team:' + str(score[1]))

def listen(i, func, team=None):
    global score
    if cad.switches[i].value == 1:
        if func == 'add': score[team] = score[team] + 1
        elif func == 'reset': score = [0, 0]
        while cad.switches[i].value == 1: pass
        show()

score = [0, 0]
show()

while True:
    listen(5, 'reset')
    listen(6, 'add', 0)
    listen(7, 'add', 1)