레이블이 PiFaceCAD인 게시물을 표시합니다. 모든 게시물 표시
레이블이 PiFaceCAD인 게시물을 표시합니다. 모든 게시물 표시

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년 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월 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)