아마존에서 제공하는 Alexa를 raspberry pi에 올려보자

https://github.com/ewenchou/alexa-client

위 URL을 따라가서 지시에 따라 alexa client를 사용해보도록 한다.


test_ask.py 파일은 영어로 된 wav파일을 요청하면 그에 맞는 alexa의 대답을 영어로된 wav파일로 반환해준다.

여기서 입력과 출력을 한글로 변경해보도록 하자.


먼저 한글 음성, 영어 음성을 각각 text로 변환시켜줄 STT와 텍스트를 음성으로 변환 시켜줄 TTS 그리고 번역이 필요하다.

각 부분에 대해 사용할 것은 다음과 같다.

STT : Google Cloud Speech API

TTS : Naver 음성합성 API

번역 : Naver 기계번역 API


Google Cloud Speech API

위 주소의 지시를 따라 debian에서 Google Cloud SDK 환경을 설치한다. 환경이 준비 되었다면 다음 URL을 따라서 Google Cloud Speech API를 사용하는 법에 대해 배워 보고, 제대로 작동되는지 예제를 다뤄보라.

https://cloud.google.com/speech/docs/getting-started


Naver API

TTS 설명 및 예시


Translation 설명 및 예시

https://developers.naver.com/docs/labs/translator/


이제 준비가 다 되었다. 한글로 말하면 한글로 대답해주는 나만의 korean Alexa를 python 코드로 구현해 보자.


korean Alexa

def transcribe_file_kor(speech_file):
    """Transcribe the given audio file."""
    
    speech_client = speech.Client()

    with io.open(speech_file, 'rb') as audio_file:
        content = audio_file.read()
        audio_sample = speech_client.sample(
            content=content,
            source_uri=None,
            encoding='LINEAR16',
            sample_rate_hertz=16000)

    alternatives = audio_sample.recognize('ko-KR')
    for alternative in alternatives:
		return alternative.transcript
		
def transcribe_file_eng(speech_file):
    """Transcribe the given audio file."""
    
    speech_client = speech.Client()

    with io.open(speech_file, 'rb') as audio_file:
        content = audio_file.read()
        audio_sample = speech_client.sample(
            content=content,
            source_uri=None,
            encoding='LINEAR16',
            sample_rate_hertz=16000)

    alternatives = audio_sample.recognize('en-US')
    for alternative in alternatives:
		return alternative.transcript

 이 코드는 Google Cloud Speech API를 사용하여 각각 한글로 된 음성파일을 한글 텍스트로, 영어로 된 음성파일을 영어 텍스트로 변환 시켜주는 코드이다. 



def translateKorToEng(korean_string):
	client_id = "YOUR_CLIENT_ID"
	client_secret = "YOUR_CLIENT_SECRET"
	encText = urllib.parse.quote(korean_string.encode("utf-8"))
	data = "source=ko&target=en&text=" + encText
	url = "https://openapi.naver.com/v1/language/translate"
	request = urllib.request.Request(url)
	request.add_header("X-Naver-Client-Id",client_id)
	request.add_header("X-Naver-Client-Secret",client_secret)
	response = urllib.request.urlopen(request, data=data.encode("utf-8"))
	rescode = response.getcode()
	if(rescode==200):
		response_body = json.loads(response.read().decode('utf-8'))
		translated_eng_str = response_body["message"]["result"]["translatedText"]
		return translated_eng_str
	else:
		print("Error Code:" + rescode)
		

def translateEngToKor(english_string):
	client_id = "YOUR_CLIENT_ID"
	client_secret = "YOUR_CLIENT_SECRET"
	encText = urllib.parse.quote(english_string.encode("utf-8"))
	data = "source=en&target=ko&text=" + encText
	url = "https://openapi.naver.com/v1/language/translate"
	request = urllib.request.Request(url)
	request.add_header("X-Naver-Client-Id",client_id)
	request.add_header("X-Naver-Client-Secret",client_secret)
	response = urllib.request.urlopen(request, data=data.encode("utf-8"))
	rescode = response.getcode()
	if(rescode==200):
		response_body = json.loads(response.read().decode('utf-8'))
		translated_kor_str = response_body["message"]["result"]["translatedText"]
		return translated_kor_str
	else:
		print("Error Code:" + rescode) 

 위 코드는 Naver 기계번역 API를 이용하여 한글을 영어로, 영어를 한글로 번역해주는 코드이다. client_id, client_secret 변수에는 자신이 할당 받은 id 와 secret을 적어 넣으면 된다.



def koreanTextToSpeech(korean_string):
	client_id = "YOUR_CLIENT_ID"
	client_secret = "YOUR_CLIENT_SECRET"
	encText = urllib.parse.quote(korean_string.encode("utf-8"))
	data = "speaker=mijin&speed=0&text=" + encText;
	url = "https://openapi.naver.com/v1/voice/tts.bin"
	request = urllib.request.Request(url)
	request.add_header("X-Naver-Client-Id",client_id)
	request.add_header("X-Naver-Client-Secret",client_secret)
	response = urllib.request.urlopen(request, data=data.encode('utf-8'))
	rescode = response.getcode()
	if(rescode==200):
		print("TTS mp3")
		response_body = response.read()
		with open('response_audio_file_path.mp3', 'wb') as f:
			f.write(response_body)
	else:
		print("Error Code:" + rescode)

 위 코드는 한국어 TTS코드로 Naver 음성합성 API를 이용하여 한글 텍스트 파일을 요청하면 한국어 음성파일을 반환하는 코드이다. 마찬가지로 자신의 client_id 와 client_secret을 넣어주면 된다. "output_audio_file_path.mp3" 에는 음성파일을 반환받기 원하는 경로와 파일명을 정해주면 된다.



def askToAlexa():
    alexa = AlexaClient()
    input = 'your_request_audio_path.wav'
    save_to = 'your_response_audio_path.mp3'
    alexa.ask(input, save_to=save_to)
    print "Response from Alexa saved to {}".format(save_to) 

 마지막으로 영어 음성파일을 Alexa에게 전달하고 그에 대한 답변을 음성파일로 받는 함수이다.  input과 save_to에 각각 자신의 요청파일과 반환받기 원하는 파일의 경로와 파일명을 넣으면 된다. 


이제 조각 들을 모두 모았으니 하나로 합쳐보자!


# -*- coding: utf-8 -*-
import io
import sys
import os
import json
from six.moves import urllib
from alexa_client import AlexaClient
from google.cloud import speech

def transcribe_file_kor(speech_file):
    """Transcribe the given audio file."""
    
    speech_client = speech.Client()

    with io.open(speech_file, 'rb') as audio_file:
        content = audio_file.read()
        audio_sample = speech_client.sample(
            content=content,
            source_uri=None,
            encoding='LINEAR16',
            sample_rate_hertz=16000)

    alternatives = audio_sample.recognize('ko-KR')
    for alternative in alternatives:
		return alternative.transcript
		
def transcribe_file_eng(speech_file):
    """Transcribe the given audio file."""
    
    speech_client = speech.Client()

    with io.open(speech_file, 'rb') as audio_file:
        content = audio_file.read()
        audio_sample = speech_client.sample(
            content=content,
            source_uri=None,
            encoding='LINEAR16',
            sample_rate_hertz=16000)

    alternatives = audio_sample.recognize('en-US')
    for alternative in alternatives:
		return alternative.transcript
		
		
def translateKorToEng(korean_string):
	client_id = "YOUR_CLIENT_ID"
	client_secret = "YOUR_CLIENT_SECRET"
	encText = urllib.parse.quote(korean_string.encode("utf-8"))
	data = "source=ko&target=en&text=" + encText
	url = "https://openapi.naver.com/v1/language/translate"
	request = urllib.request.Request(url)
	request.add_header("X-Naver-Client-Id",client_id)
	request.add_header("X-Naver-Client-Secret",client_secret)
	response = urllib.request.urlopen(request, data=data.encode("utf-8"))
	rescode = response.getcode()
	if(rescode==200):
		response_body = json.loads(response.read().decode('utf-8'))
		translated_eng_str = response_body["message"]["result"]["translatedText"]
		return translated_eng_str
	else:
		print("Error Code:" + rescode)
		

def translateEngToKor(english_string):
	client_id = "YOUR_CLIENT_ID"
	client_secret = "YOUR_CLIENT_SECRET"
	encText = urllib.parse.quote(english_string.encode("utf-8"))
	data = "source=en&target=ko&text=" + encText
	url = "https://openapi.naver.com/v1/language/translate"
	request = urllib.request.Request(url)
	request.add_header("X-Naver-Client-Id",client_id)
	request.add_header("X-Naver-Client-Secret",client_secret)
	response = urllib.request.urlopen(request, data=data.encode("utf-8"))
	rescode = response.getcode()
	if(rescode==200):
		response_body = json.loads(response.read().decode('utf-8'))
		translated_kor_str = response_body["message"]["result"]["translatedText"]
		return translated_kor_str
	else:
		print("Error Code:" + rescode)
	
	
	
def koreanTextToSpeech(korean_string):
	client_id = "YOUR_CLIENT_ID"
	client_secret = "YOUR_CLIENT_SECRET"
	encText = urllib.parse.quote(korean_string.encode("utf-8"))
	data = "speaker=mijin&speed=0&text=" + encText;
	url = "https://openapi.naver.com/v1/voice/tts.bin"
	request = urllib.request.Request(url)
	request.add_header("X-Naver-Client-Id",client_id)
	request.add_header("X-Naver-Client-Secret",client_secret)
	response = urllib.request.urlopen(request, data=data.encode('utf-8'))
	rescode = response.getcode()
	if(rescode==200):
		print("TTS mp3")
		response_body = response.read()
		with open('korean_alexa.mp3', 'wb') as f:
			f.write(response_body)
	else:
		print("Error Code:" + rescode)
	
	
"""This test will send a pre-generated audio file in the `tests` directory
to AVS, and saves it as `/tmp/test1.mp3`."""
def askToAlexa():
    alexa = AlexaClient()
    input = 'after_srate_conv.wav'  #'{}/1.wav'.format(TESTS_PATH)
    save_to = 'response_alexa.mp3'
    alexa.ask(input, save_to=save_to)
    print "Response from Alexa saved to {}".format(save_to)


if __name__ == '__main__':

	# 환경 변수 설정
	os.system("export GOOGLE_APPLICATION_CREDENTIALS=\"Your_Google_Service_Account_Key_Path.json\"")
	
	# 인코딩 설정
	reload(sys)
	sys.setdefaultencoding('utf-8')
	
	# 녹음
	os.system("arecord -D plughw:1,0 -f S16_LE -c1 -r16000 -d 3 input.wav")
	print("recording complete!")
	
	# STT 한글
	input_kor_str = transcribe_file_kor('input.wav')
	print(input_kor_str)
	
	# 한글을 영어로 번역
	input_eng_str = translateKorToEng(unicode(input_kor_str))
	print(input_eng_str)
	
	# 영어 TTS
	os.system("espeak -w before_srate_conv.wav \""+input_eng_str+"\"")
	os.system("sox before_srate_conv.wav -r 16000 after_srate_conv.wav")
	print("TTS and srate convert completed")
	
        # alexa
	askToAlexa()
	
	# 영어음성 STT
	os.system("sox response_alexa.mp3 -r 16000 response_alexa_converted.wav")
	output_eng_str = transcribe_file_eng('response_alexa_converted.wav')
	print(output_eng_str)
	
	# 영어문장을 한글로 번역
	output_kor_str = translateEngToKor(output_eng_str).encode("utf-8")
	print(output_kor_str)
	
	# 한글 TTS
	koreanTextToSpeech(unicode(output_kor_str))
	
	# 한글 음성 출력
	os.system("mpg321 korean_alexa.mp3") 

자 이제 한글로 얘기하면 한글로 대답해주는 코드가 완성되었다. 미국 대통령이 누군지 물어보자.







Raspberry Pi로 LED를 제어해 보자

T 코블러를 이용해서 GPIO를 제어해 보자.

먼저 T 코블러는 다음과 같이 생긴 녀석으로 Pi의 GPIO핀들을 쉽게 사용할 수 있기 때문에 사용된다. 다음 그림 들을 보고 T 코블러를 연결하자.

빨간 색선이 위와 같은 곳에 위치하도록!


브레드 보드에 연결!



T 코블러를 통해 나온 핀들은 다음과 같다



WiringPi 설치

GPIO를 다루기 위해 WiringPi를 설치하자
$ sudo apt-get update
$ sudo apt-get upgrade
$ git clone git://git.drogon.net/wiringPi
$ cd wiringPi
$ git pull origin
$ ./build 

설치 후

$ gpio readall 

명령을 실행시키면 아래와 같은 출력이 나온다.

자신의 핀들의 상태를 알 수 있다.


LED 제어하기

아래와 같은 회로로 LED가 연결되어 있다고 해보자.

GPIO 23번을 통해 LED를 제어해보자.

$ gpio –g mode 23 output

먼저 23번 핀을 output 모드로 변경한 후

$ gpio –g write 23 1

23번 핀을 high로 만들면 LED가 켜진다!

$ gpio –g write 23 0

23번 핀을 low로 만들면 LED가 꺼진다.



스위치 읽기

아래와 같은 회로로 스위치가 연결되어 있다고 해보자.


GPIO 21번의 위에 연결된 저항은 핀 자체의 풀업 저항이기 때문에 우리가 회로상에 연결하는 저항은 아니다. 회로 구성을 보면 스위치가 눌려 있기 전에는 GPIO 21번 핀이 high이고, 스위치가 눌리면 GPIO 21번 핀이 low 가 된다는걸 알고 지나가자.


$ gpio –g mode 21 input

먼저 21번 핀을 input 모드로 변경한 후

$ gpio –g mode 21 up

21번 핀을 pull-ip 상태로 만든다.

$ gpio –g read 21

21번 핀의 값을 읽어 high일 때 1을 low일 때 0을 반환한다.



이제 다음 예제들을 bash script를 통해 풀어보자.

예제 1) 도미노

LED 4개와 스위치 1개를 이용해, 스위치를 눌렀을 때 LED가 도미노처럼 한개 씩만 켜지도록 한다. 처음엔 다 꺼져있는 상태이고, 마지막 LED가 켜지면 모두 끄고 끝이난다.



domino.sh

#!/bin/bash
gpio_arr=(5 6 13 19)
for i in "${gpio_arr[@]}"
do
   gpio -g mode $i output
   gpio -g write $i 0
done
gpio -g mode 21 input
gpio -g mode 21 up

while true; do
   sw=`gpio -g read 21`
   if [ $sw -eq 0 ]; then
      for i in "${gpio_arr[@]}"
      do
         gpio -g write $i 1
         sleep 0.5
         gpio -g write $i 0
      done
   fi
done



예제 2) 369 LED


스위치를 눌렀을 때 예제 1번과 비슷한 동작을 한다. 100번 움직이는데, 3,6,9가 들어간 번째의 움직임에서는 LED를 키지 않는다.



LED369.sh

#!/bin/bash
gpio_arr=(19 5 6 13)
for i in "${gpio_arr[@]}"
do
   gpio -g mode $i output
   gpio -g write $i 0
done
gpio -g mode 21 input
gpio -g mode 21 up

while true; do
   sw=`gpio -g read 21`
   if [ $sw -eq 0 ]; then
      for i in {1..100}
      do
         if [[ "$i" == *"3"* ]] || [[ "$i" == *"6"* ]] || [[ "$i" == *"9"* ]]; then
            sleep 0.5
         else
            gpio -g write ${gpio_arr[$(($i % 4))]} 1
            sleep 0.5
            gpio -g write ${gpio_arr[$(($i % 4))]} 0
         fi
      done
   fi
done



예제 2-2) 369 LED - python script

bash로 짯던 369 LED를 python으로 짜보자

python 에서는 GPIO를 RPI.GPIO모듈을 이용해서 제어한다. 그의 메소드는 아래와 같다


메소드
기능
GPIO.setmode(GPIO.BOARD)
GPIO.setmode(GPIO.BCM)
핀 번호를 라즈베리파이 보드(BOARD) 번호로 참조
BCM(Broadcom chip-specific pin numbers)모드
GPIO.setup(pin, GPIO.IN)
GPIO.setup(pin, GPIO.OUT)
핀을 입력으로 설정
핀을 출력으로 설정
GPIO.output(pin, GPIO.(True/HIGH))
GPIO.output(pin, GPIO.(Flase/LOW)
디지털 출력을 True/HIGH로 설정
디지털 출력을 Flase/LOW로 설정
GPIO.input(pin)
디지털 값을 읽음
GPIO.cleanup()
GPIO 모듈의 점유 리소스를 해제
GPIO.VERSION
RPi.GPIO 모듈의 버전값을 갖는 변수
이를 이용하여 369 LED를 python script로 구현하여 보자.


#!/usr/bin/python2

import RPi.GPIO as GPIO
from time import sleep

GPIO.setmode(GPIO.BCM)
GPIO.setup(21, GPIO.IN, pull_up_down=GPIO.PUD_UP)
gpio_arr=[19,5,6,13]
for gpio in gpio_arr:
	GPIO.setup(gpio, GPIO.OUT)
	GPIO.output(gpio, False)

def my_callback(channel):
	for i in range(1,101):
		num = str(i)
		if '3' in num or '6' in num or '9' in num:
			sleep(0.5)
		else:
			GPIO.output(gpio_arr[i%4], True)
			sleep(0.5)
			GPIO.output(gpio_arr[i%4], False)
	 
GPIO.add_event_detect(21, GPIO.FALLING, callback=my_callback, bouncetime=300)
try:
	while True:
		sleep(1)
	
except KeyboardInterrupt:
	pass
GPIO.cleanup() 





Pi camera 사용하기


먼저 라즈베리에서 camera 설정을 해주자
$ sudo raspi-config

<Interfacing Options> - <Camera> - <Yes>


그리고 재부팅을 해주자

$ sudo reboot


Pi camea module 연결하기



카메라 연결 부를 위와 같이 열어 준다.


카메라 모듈을 위와 같은 방향으로 연결하고 하얀 캡을 다시 꾹 눌러준다.



타임랩스 촬영하기

터미널 창에 다음과 같은 명령어를 입력한다.
$ raspistill -hf -vf -t 30000 -tl 1000 -o image%04d.jpg

-hf  : 좌우반전

-vf  : 상하반전

-t   : 몇 millsecond 동안 촬영

-th  : 몇 millsecond 마다 촬영할 것인지

파일 명은 image[4자리 숫자(0000~9999)].jpg 로 촬영하게 된다.



PI에 USB 마운트하기

라즈베리 파이의 micro sd 카드 용량이 작다면 usb를 사용하자. 파이에 usb를 연결한 후
$ sudo -i

루트 권한으로 변경 후

# blkid

위 명령어로 연결된 장비를 확인한다.


/dev/mmcblk0: PTUUID="fc7fe1c0" PTTYPE="dos"
/dev/mmcblk0p1: LABEL="boot" UUID="7EF5-979D" TYPE="vfat" PARTUUID="fc7fe1c0-01"
/dev/mmcblk0p2: UUID="139ca52c-d45e-44ca-8b8c-12c0af0c9cf5" TYPE="ext4" PARTUUID="fc7fe1c0-02"
/dev/sda1: UUID="D40C-337A" TYPE="vfat"


위에서 /dev/sda1는 연결된 장비이며 UUID 와 TYPE을 적어두자.

# nano /etc/stab

부팅시에 자동으로 마운트 되게 해보자


proc            /proc           proc    defaults          0       0
/dev/mmcblk0p1  /boot           vfat    defaults          0       2
/dev/mmcblk0p2  /               ext4    defaults,noatime  0       1


위와 같이 목록이 뜰 것인데, 아래줄은 추가하자

UUID=D40C-337A  /camera         vfat    defaults          0       0


UUID는 마운트할 기기의 UUID이고, /camera는 마운트 위치이다. 임의로 설정하되, 해당 디렉토리를 만들어 놓자. 세번째는 파일 형식으로 아까 확인한 vfat형식을 적자. 그 뒤는 기타 설정이므로 위와 같이 적으면 무리없이 사용할 수 있다. 이후에 재부팅을 하면 USB가 제대로 마운트 되는 것을 확인할 수 있다.



이미지를 동영상으로~

타임랩스를 촬영하는 법과 파이에서 USB를 사용하는 법을 알았으니 찍어둔 사진으로 동영상을 만들어보자. 여러 방법들이 있지만, MakeAVI 프로그램을 이용해보도록한다.

http://makeavi.sourceforge.net/

이 링크를 따라 MakeAVI를 다운받자. 압축을 풀고 파일을 실행한다.



위와 같이 실행하면 jpg 파일을 avi 파일로 바꾸어 준다.



Raspberry Pi 부팅 시에 자동으로 파이썬 스크립트 실행하기

실행 시킬 파이썬 스크립트를 준비한 후 다음을 따라해보자.
나는 전에 만들었던 인터넷 라디오 및 음악 플레이어 python 스크립트를 사용하였다.


먼저 /home/pi/.profile을 연다.
$ nano ~/.profile


그리고 맨 마지막 줄에 부팅시에 자신이 실행되길 원하는 bash 명령을 입력한다.

......
(sleep 10 && /usr/bin/python /home/pi/myscript.py)&

절대경로를 써줘야 하기 때문에 python 과 실행할 스크립트 .py의 경로를 제대로 써준다.

&& 은 앞의 명령을 끝낸 후 뒤의 명령을 실행하고

&   은 앞의 명령을 백그라운드에서 실행하고 그 다음 명령을 같이 실행한다.

10초를 sleep 한 후 파이썬 명령을 내리는 이유는 bash명령이 부팅 후에 실행되는 것이 아니라, 부팅 도중에 동시에 실행 된다. 따라서 파이썬 스크립트가 경우에 따라 제대로 실행되지 않는 결과가 나타날 수 있기 때문에 10초를 sleep 후에 파이썬 코드를 실행 시킨다.

위의 코드 대신 자신이 부팅시에 실행되길 원하는 bash 명령이 있다면 바꾸어보자.


아래는 음악 및 인터넷 라디오 플레이어를 부팅과 동시에 실행시킨 영상이다.



저번에 PiFaceCAD를 이용해 만들었던 음악플레이어에 이어서 인터넷 라디오 스트리머를 만들어보자.

먼저 PiFaceCAD를 위한 SW설치, IR Remote 설정, Configuring LIRC 는 저번 음악플레이어 만들기와 동일하니 혹시 안했다면 아래 링크에 가서 한번 해보자.


2017/03/29 - [Raspberry] - PiFaceCAD를 이용해 MusicPlayer 만들기



인터넷 라디오 스트리밍하기

인터넷 라디오 스트리밍을 위해 mplayer를 사용할 것이기 때문에 설치해 주자.
$ sudo apt-get install mplayer

간단하게 다음 코드를 입력해보자.

$ mplayer mms://211.181.136.136/livefm

여러 줄로 뭐라고 뜨며 라디오가 재생된다면 성공이다. 인터넷 라디오는 환경에 따라 되는 주소도 아닌 주소도 있기 때문에 아래의 링크 혹은 검색을 통해 더 많은 주소들을 찾아보자.


http://changjhyun.tistory.com/35


mplayer 사용하기

mplayer 를 위와 같은 방식이 아닌 slave모드로 켜면 mplayer 가 실행되는 도중 command를 읽어 들여 일시정지나 볼륨조정 같은 설정을 할 수 있다. 또는 FIFO 파일을 통해 mplayer를 조종할 수 있다.

$ mkfifo /tmp/mplayer-control $ mplayer -slave -input file=/tmp/mplayer-control mms://211.181.136.136/livefm

위와 같이 mplayer를 실행 시키면 fifo 파일을 읽어 mplayer를 조종할 수 있게 된다.


위 명령을 실행시킨 후 다른 터미널 창을 하나 더 켜서 다음 명령어를 입력해보자

$ echo "pause" > /tmp/mplayer-control


다른 터미널에서 실행되던 mplayer가 일시 정지하는가? 일시정지 했을 것이다. 이것으로 fifo파일로 mplayer를 조종 할 수 있는것을 확인할 수 있다, 위에서 사용한 pause 함수 말고도 mplayer가 slave 모드로 실행 되었을 때 사용할 수 있는 함수들이 많다. 따라서 아래 링크를 따라가 documents를 확인하여 사용할 함수들을 알아보도록 한다.


http://www.mplayerhq.hu/DOCS/tech/slave.txt




파이썬에서 mplayer 실행시키기

python에서 mplayer를 실행시키는 것 뿐만아니라, python코드에서 터미널에 명령을 내리고 싶다면 어떻게 해야할까? 파이썬의 라이브러리에서 이와 같은 경우를 위해 os.system 함수를 제공하고 있다. os.system함수의 인자로 문자열을 주면, 해당 문자열을 터미널에 치는 것과 같은 결과가 나타나게 된다.


터미널 창에 mplayer 명령을 치는 것과 같은 효과가 있다.



 따라서 mplayer를 조종할 fifo 파일에 보낼 echo 명령도 os.system 함수를 통해 구현하면 된다. 그러나 코드안에서 mplayer를 실행시켰다 해도 mplayer가 실행되는 동안에는 코드가 os.system("mplayer ...")의 다음 줄로 넘어가지 못한다. 따라서 mplayer를 실행시키는 동안 스레드를 통해 echo 명령을 내려 mplayer를 조종하도록 구현하겠다.



이를 바탕으로 인터넷 라디오 스트리머를 구현해 보았다.

curNum = 0

channelLink = ["mms://211.181.136.136/livefm", "mms://114.108.140.39/magicfm_live",\
				"mms://live.febc.net/LiveFm","mms://vod.ysmbc.co.kr/YSFM",\
				"mms://121.254.230.3/FMLIVE","mms://210.96.79.102/Incheon"]
channelName = ["MBC", "SBS","SeoulFBEC","YeosooMBC","JIBS","TBN"]
cad.lcd.write('CH : '+channelName[curNum])

path = '/tmp/mplayer-control'
pin = -1
remote = -1
mute = False
play = True
volume = 50

def radioMainFunc():
	global pin
	global remote
	global volume
	global play
	global mute
	
	f = open(path,'w')
	if mute:
		f.write('volume 0 1\n')
	else:
		f.write('volume '+ str(volume) +' 1\n')
	f.close()
	
	play = True;
	curCh = 0
	cad.lcd.clear()
	writeBottomLine()
	cad.lcd.write('CH : '+channelName[curCh])

	while True:
		showTime()
		# play control
		if pin == 2 or remote == 2:		# pause and unpause
			os.system('echo \"pause\" > /tmp/mplayer-control')
			if play:
				play = False;
			else:
				play = True;
			writeBottomLine()
			pin = -1
			remote = -1
		elif pin == 3 or remote == 3:	# next channel
			curCh = (curCh + 1) % len(channelLink)
			os.system('echo \"loadfile '+channelLink[curCh] +'\" > /tmp/mplayer-control')
			cad.lcd.clear()
			writeBottomLine()
			cad.lcd.write('CH : '+channelName[curCh])
			pin = -1
			remote = -1
		elif pin == 1 or remote == 1:	# prev channel
			if curCh == 0:
				curCh = len(channelLink)-1
			else:
				curCh -= 1
			os.system('echo \"loadfile '+channelLink[curCh] +'\" > /tmp/mplayer-control')
			cad.lcd.clear()
			writeBottomLine()
			cad.lcd.write('CH : '+channelName[curCh])
			pin = -1
			remote = -1
		# volume control
		elif pin == 6 or remote == 6:	# volume down
			while True:
				volume -= 3
				if volume < 0:
					volume = 0
				os.system('echo \"volume '+ str(volume) +' 1\" > /tmp/mplayer-control')
				updateVolume();
				sleep(0.2)
				remote = -1
				if not cad.switches[6].value:
					break
			pin = -1
		elif pin == 7 or remote == 7:	# volume up
			while True:
				volume += 3
				if volume > 100 :
					volume = 100
				os.system('echo \"volume '+ str(volume) +' 1\" > /tmp/mplayer-control')
				updateVolume();
				sleep(0.2)
				remote = -1
				if not cad.switches[7].value:
					break
			pin = -1
		elif pin == 5 or remote == 5:	# mute
			if mute:
				os.system('echo \"volume '+ str(volume) +' 1\" > /tmp/mplayer-control')
				mute = False;
			else:
				os.system('echo \"volume 0 1\" > /tmp/mplayer-control')
				mute = True;
			updateVolume();
			pin = -1
			remote = -1
		elif pin == 4 or remote == 4: 
				pin = -1
				remote = -1
				os.system('echo \"quit\" > /tmp/mplayer-control')
				return

main = threading.Thread(target=radioMainFunc)
main.start()

os.system('mplayer -slave -input file='+path+' '+channelLink[0])

 radioMainFunc() 이외의 함수는 저번 음악플레이어를 만들었을 때 사용한 함수들을 그대로 사용하였다. 인터럽트 또한 이전과 같은 방식으로 작성하고 사용하였다. PiFace CAD의 버튼과 리모콘도 거의 같은 역할을 한다. 달라진 점이라고는 스레드를 사용한 것과 mplayer를 조종할 때 fifo파일과, echo 명령을 사용한다는 것이다.


인터럽트 반환 값이 2일 때 : echo "pause" > [path]  명령을 통해 일시정지 및 재생

1,3일 때 : echo "loadfile" [url] > [path] 명령을 통해 이전채널, 다음채널 스트리밍

5,6,7일 때 : echo "volume" <value> 1  > [path] 명령을 통해 볼륨 조정(5: 음소거 및 해제, 6: 감소, 7: 증가)

4  일 떄 : echo "quit"] > [path]  명령을 통해 mplayer 종료




 이 코드와 저번에 작성한 음악플레이어 코드를 병합 및 수정하여 라디오와 음악플레이어를 선택하여 들을 수 있는 플레이어를 구현하였다.

from pygame import mixer # Load the required library
import pifacecad
import time
import datetime
import threading
import os


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

# bitmap
bmp_note = pifacecad.LCDBitmap([2,3,2,2,14,30,12,0])
bmp_play = pifacecad.LCDBitmap([0,8,12,14,12,8,0,0])
bmp_pause = pifacecad.LCDBitmap([0,27,27,27,27,27,0,0])
bmp_clock = pifacecad.LCDBitmap([0,14,21,23,17,14,0,0])
bmp_vol1 = pifacecad.LCDBitmap([1,3,15,15,15,3,1,0])
bmp_vol2 = pifacecad.LCDBitmap([8,16,0,24,0,16,8,0])

cad.lcd.store_custom_bitmap(0, bmp_note)
cad.lcd.store_custom_bitmap(1, bmp_play)
cad.lcd.store_custom_bitmap(2, bmp_pause)
cad.lcd.store_custom_bitmap(3, bmp_clock)
cad.lcd.store_custom_bitmap(4, bmp_vol1)
cad.lcd.store_custom_bitmap(5, bmp_vol2)

# music
song = ['We Don\'t Talk Anymore.mp3','Closer.mp3','Shape Of You.mp3','Starving.mp3','The Ocean.mp3'\
		,'Happy.mp3','Daft Punk.mp3', 'Chandelier.mp3','The Greatest.mp3', 'Lean On.mp3']

# radio
path = '/tmp/mplayer-control'
channelLink = ["mms://211.181.136.136/livefm", "mms://114.108.140.39/magicfm_live",\
				"mms://live.febc.net/LiveFm","mms://vod.ysmbc.co.kr/YSFM",\
				"mms://121.254.230.3/FMLIVE","mms://210.96.79.102/Incheon"]
channelName = ["MBC", "SBS","SeoulFBEC","YeosooMBC","JIBS","TBN"]


pin = -1		# PiFaceCAD button
remote = -1		# IR remote control button
volume = 50		# current volume
play = True		# current playing status (playing : True, pause: False)
mute = False	# current mute status
curSong = 0		# current song number
stridx = 0		# current printed song string index

def update_pin_text(event):
	 global pin
	 pin = event.pin_num

def print_ir_code(event):
	 global remote
	 remote = int(event.ir_code)
	 
def showTime():		# print current hour and minute
	now = datetime.datetime.now()
	nowTime = now.strftime('%H:%M')
	cad.lcd.set_cursor(10,1)
	cad.lcd.write_custom_bitmap(3)
	cad.lcd.write(nowTime)
	cad.lcd.set_cursor(0,0)
	
def writeVolume():	# print current volume
	global volume
	global mute
	cad.lcd.set_cursor(0,1)
	cad.lcd.write_custom_bitmap(4)
	cad.lcd.write_custom_bitmap(5)
	if mute:
		cad.lcd.write('mute')
	else:
		cad.lcd.write(str(volume).rjust(4))
	
def updateVolume():
	global volume
	cad.lcd.set_cursor(2,1)
	if mute:
		cad.lcd.write('mute')
	else:
		cad.lcd.write(str(volume).rjust(4))
	
	
def writeBottomLine():	#print LCD bottom line
	global play
	showTime()
	writeVolume()
	cad.lcd.set_cursor(7,1)
	if play:
		cad.lcd.write_custom_bitmap(1)
	else:
		cad.lcd.write_custom_bitmap(2)
	cad.lcd.set_cursor(0,0)
	
	
def writeTopLine():		#print LCD top line and rotate when length > 16
	global curSong
	global stridx
	
	cad.lcd.set_cursor(0,0)
	str = song[curSong]
	if len(str)>16:
		str_rotate = (str+'    '+str)[stridx:stridx+15]
		stridx += 1
		if stridx == len(str)+4:
			stridx = 0
		cad.lcd.write(str_rotate)
	else :
		cad.lcd.write(str)
	
def musicMainfunc():
	global pin
	global remote
	global volume
	global play
	global mute
	global curSong
	global stridx
	
	curSong = 0
	pin = -1
	remote = -1
	
	mixer.init()
	mixer.music.set_volume(float(volume)/100)
	while True:
		if curSong == len(song):
			curSong = 0
		cad.lcd.clear()
		cad.lcd.blink_off()
		cad.lcd.cursor_off()
		mixer.music.load(song[curSong])
		mixer.music.play()
		writeTopLine()
		count_t = time.time()
		while mixer.music.get_busy() == True:
			writeBottomLine()
			if time.time()-count_t > 1:	# writeTopLine every 1 second
				writeTopLine()
				count_t = time.time()
			# song control
			if pin == 1 or remote == 1:	# prev song
				curSong-=2
				if curSong==-2:
					curSong=len(song)-2
				pin = -1
				remote = -1
				stridx = 0
				break;
			elif pin == 3 or remote == 3:	# next song
				pin = -1
				remote = -1
				stridx = 0
				break;
				
			elif pin == 2 or remote == 2:	# play and pause
				if play:
					mixer.music.pause()
					play = False
				else:
					mixer.music.unpause()
					play = True
				pin = -1
				remote = -1
					
			# volume control
			elif pin == 6 or remote == 6:	# volume down
				while True:
					volume -= 3
					if volume < 0:
						volume = 0
					mixer.music.set_volume(float(volume)/100)
					updateVolume();
					time.sleep(0.2)
					if not cad.switches[6].value:
						break
				pin = -1
				remote = -1
			elif pin == 7 or remote == 7:	# volume up
				while True:
					volume += 3
					if volume > 100:
						volume = 100
					mixer.music.set_volume(float(volume)/100)
					updateVolume();
					time.sleep(0.2)
					if not cad.switches[7].value:
						break
				pin = -1
				remote = -1
			elif pin == 5 or remote == 5:	# mute
				if mute:
					mixer.music.set_volume(float(volume)/100)
					mute = False
				else:
					mixer.music.set_volume(0)
					mute = True
				pin = -1
				remote = -1
			elif pin == 4 or remote == 4:	# quit
				pin = -1
				remote = -1
				mixer.quit()
				return
		curSong+=1
		
		
def radioMainFunc():	# Internet radio streaming player
	global pin
	global remote
	global volume
	global play
	global mute
	
	f = open(path,'w')
	if mute:
		f.write('volume 0 1\n')
	else:
		f.write('volume '+ str(volume) +' 1\n')
	f.close()
	
	play = True;
	curCh = 0
	cad.lcd.clear()
	writeBottomLine()
	cad.lcd.write('CH : '+channelName[curCh])

	while True:
		showTime()
		# play control
		if pin == 2 or remote == 2:		# pause and unpause
			os.system('echo \"pause\" > /tmp/mplayer-control')
			if play:
				play = False;
			else:
				play = True;
			writeBottomLine()
			pin = -1
			remote = -1
		elif pin == 3 or remote == 3:	# next channel
			curCh = (curCh + 1) % len(channelLink)
			os.system('echo \"loadfile '+channelLink[curCh] +'\" > /tmp/mplayer-control')
			cad.lcd.clear()
			writeBottomLine()
			cad.lcd.write('CH : '+channelName[curCh])
			pin = -1
			remote = -1
		elif pin == 1 or remote == 1:	# prev channel
			if curCh == 0:
				curCh = len(channelLink)-1
			else:
				curCh -= 1
			os.system('echo \"loadfile '+channelLink[curCh] +'\" > /tmp/mplayer-control')
			cad.lcd.clear()
			writeBottomLine()
			cad.lcd.write('CH : '+channelName[curCh])
			pin = -1
			remote = -1
		# volume control
		elif pin == 6 or remote == 6:	# volume down
			while True:
				volume -= 3
				if volume < 0:
					volume = 0
				os.system('echo \"volume '+ str(volume) +' 1\" > /tmp/mplayer-control')
				updateVolume();
				time.sleep(0.2)
				remote = -1
				if not cad.switches[6].value:
					break
			pin = -1
		elif pin == 7 or remote == 7:	# volume up
			while True:
				volume += 3
				if volume > 100 :
					volume = 100
				os.system('echo \"volume '+ str(volume) +' 1\" > /tmp/mplayer-control')
				updateVolume();
				time.sleep(0.2)
				remote = -1
				if not cad.switches[7].value:
					break
			pin = -1
		elif pin == 5 or remote == 5:	# mute
			if mute:
				os.system('echo \"volume '+ str(volume) +' 1\" > /tmp/mplayer-control')
				mute = False;
			else:
				os.system('echo \"volume 0 1\" > /tmp/mplayer-control')
				mute = True;
			updateVolume();
			pin = -1
			remote = -1
		elif pin == 4 or remote == 4: 	# quit
				pin = -1
				remote = -1
				os.system('echo \"quit\" > /tmp/mplayer-control')
				return
			

def mainFunc():
	global volume
	listener = pifacecad.SwitchEventListener(chip=cad)
	for i in range(8):
		listener.register(i, pifacecad.IODIR_FALLING_EDGE, update_pin_text)
	listener.activate()

	remote_listener = pifacecad.IREventListener(prog='myprogram')
	for i in range(8):
		remote_listener.register(str(i), print_ir_code)
	remote_listener.activate()

	radioMain = threading.Thread(target=radioMainFunc)
	while True:
		musicMainfunc()
		radioMain = threading.Thread(target=radioMainFunc)
		os.mkfifo(path)
		radioMain.start()
		os.system('mplayer -slave -input file='+path+' '+channelLink[0])
		radioMain.join()
		os.remove(path)
	
mainFunc()


1. 이전 곡 & 이전 채널        <-  KEY_CHANNELDOWN

2. 재생 및 일시정지            <-  KEY_OK

3. 다음 곡 & 다음 채널        <-  KEY_CHANNELUP

4. 음악, 라디오 모드 변경     <-  KEY_MENU

5. 음소거 및 해제               <-  KEY_MUTE

6. 볼륨 낮추기                   <-  KEY_VOLUMEUP

7. 볼륨 올리기                   <-  KEY_VOLUMEDOWN


위와 같이 수정 하였다. 아래 데모를 보고 제대로 실행되는지 확인해 보자.







+ Recent posts