ラズベリーパイをROMライターにしてみた(Writer編)

前回のReader編に引き続き、今日は、Writer編
設定とか、ROMとラズパイの接続とかは、前回のReader編とまったく同じです。

まずは、Pythonのプログラムから。

#!/usr/bin/env python
#-*- coding: UTF-8 -*-

import smbus
import time
import sys

bus = smbus.SMBus(1)


def write_ROM():
    count=0
    ad=0
    f=open(f_name,'r')
    for m in f:
        l1=m[:-1].split(' ')
        l2=[ l1[0][0:2], l1[0][2:4], l1[0][4:6], l1[0][6:8], l1[1][0:2], l1[1][2:4], l1[1][4:6], l1[1][6:8], l1[2][0:2], l1[2][2:4], l1[2][4:6], l1[2][6:8], l1[3][0:2], l1[3][2:4], l1[3][4:6], l1[3][6:8] ]
        l3=[(count%16)*16,int(l2[0],16),int(l2[1],16),int(l2[2],16),int(l2[3],16),int(l2[4],16),int(l2[5],16),int(l2[6],16),int(l2[7],16),int(l2[8],16),int(l2[9],16),int(l2[10],16),int(l2[11],16),int(l2[12],16),int(l2[13],16),int(l2[14],16),int(l2[15],16) ]
        print l2

        bus.write_i2c_block_data(0x50,ad,l3)
        count+=1
        if count%16 == 0:
            ad+=1
        time.sleep(0.2)

    b1 = (count)*16/256
    b2 = (count)*16%256
    l_count=[count*16,b1,b2]
    print('bytes=%d Bytes1=%x bytes2=%x' %(l_count[0],l_count[1],l_count[2]))
    
    f.close()

if __name__ == '__main__':
    args = sys.argv
    if len(args)<3:
        print('usage: python {} <Device Address(HEX)> <source file name>'.format(__file__))
        exit(0)
    addr=int(args[1],16)
    f_name=args[2]

    write_ROM()

若干、ちからまかせ的な、あまりスマートじゃないスクリプトですが、まぁ、そこは目をつぶっていただいて、
使い方です。
もっぱら、自分の目的のためですので、あまり、汎用的ではありませんが、
4バイトごとに空白をはさんだ1行16バイトのデータファイルを読んで、ROMに書き込みます。
例えば、上記のスクリプトを"i2cromwrite.py" というファイルにしたとしますと、

 $ python i2cromwrite.py 50 HONYOHONYO.txt

とやると、HONYOHONYO.txtというファイルのデータが書き込まれます。
最初の50は、デバイスアドレス 0x50 です。
自分の場合は、WAVファイルをROMに落としたかったので、まず、WAVファイルを用意して、バイナリエディタで開きます。
僕の場合は、xedit.exe というフリーのバイナリエディタを使ったので、データが4バイトごとのブロックで1行16バイト表示されます。 ここから、必要な部分を切り取って、テキストファイルにデータを貼り付けます。
テキストファイル上で必要な加工を行った後、ラズパイに転送し、ROMに書き込むという手順でやっています。 ちなみに必要な加工というのは、最初の行にデータのバイト数を書き込んでいます。 そうすると、PICで読み込むときに、その回数読めばすむので・・・。
この一連の作業が、以下のような感じになります。

まず、WAVファイルを開いて、データ部分を切り取ります。
f:id:manpukukoji:20171027110127p:plain
続いて、メモ帳を開いて、データを貼り付け、最初の行にバイト数を書き込みます。
f:id:manpukukoji:20171027110230p:plain
このファイルをラズパイに送ります。
FTPでも良いし、USBメモリやSDカード経由でも良いし、なんでも良いかと思います。
そして、上記のPythonのコードでROMに書き込みます。
f:id:manpukukoji:20171027110745p:plain
これで、ROMに書き込まれている・・・はずです・・・。

ラズベリーパイをROMライターにしてみた(Reader編)

以前買い置きしていたI2CのROM 24C512 が手元にあり、これを使おうと思ったのだけど、データを書き込むのがめんどくさい。そのためにAruduinoとかPICとかで書き込んでも良いのだけど、PCで作ったデータファイルをROMに書き込むとなると、PCでデータ作って、書き込み用のプログラム作って、シリアルでArduinoに送り、Arduino側でシリアルとI2Cのプログラム作って・・・って、あー、もう、めんどくせー! となるので、なんか、良い方法はないかいな? と、考えてたら、
おー!ラズパイがあった!
ラズパイだと、I2Cもいけるし、ファイルも簡単に読み書きできる!
と、いうわけで、ラズベリーパイで24C512 を読み書きするための、ROM Reader/Writer を試してみた。
f:id:manpukukoji:20171027100652j:plain
と、言っても、ハードウェアは、ブレッドボードにROMを置いて、ラズパイとジャンパー線4本でつなぐだけ。
I2Cのプルアップも、なぜか、必要なかった。・・・ラズパイ側でプルアップされているのかな???
あとは、Pythonでチョコチョコッと・・・。
使ったラズパイは、RasPi3で、OSはRaspbian with Pixcelってやつ。 比較的新しい方だと思う。I2Cのバスも”1”になってました。

古いやつだと、I2Cを使うのに、i2c-tools とか、smbus とかのPythonのライブラリをインストールする必要があったようだけど、僕の場合、何もしなくてもすでに入っていたみたい。ラズパイの設定でインターフェースのタブでI2Cを有効にしておけば、それだけで問題なく使えました。
このあたりは、検索するといろいろ詳しく説明されたサイト(I2C表示器が多いけど同じです。)があるので、省略するとして、こちらでは、いきなりROMをつないで読むところからです。
例えば、このあたりのサイトで、丁寧に説明されています。
Raspberry Pi で I2C を使用する準備 – 或る阿呆の記


ROMは、ATMELの24C512 です。ですので、他のROMで同じようにいくわけではないと思います。秋月電子通商のサイトで見ると、300円(http://akizukidenshi.com/catalog/g/gI-00561/)だけど、同様の24LC512って言うのもある。こちらは、価格もほぼ半額(http://akizukidenshi.com/catalog/g/gI-02524/)だし、同じように使えるのではないかと思うのだけど、現物を持っていないので、試しておらず、わからないので、あしからず・・・。
ラズパイとの接続は、以下のようにつないだ。

RaspberryPi        24C512
Pin 01  3V    ---   Pin 8
Pin 03  SDA   ---   Pin 5
Pin 05  SCL   ---   Pin 6
Pin 09  GND   ---   Pin 4

f:id:manpukukoji:20171027074550p:plain

Frizingの図は、電源部分、勘違いしやすいので、V+とGND、間違わないように十分注意してね。 ・・・自己責任でよろしく!
上記以外のROMのピンはそのまま(float)ですが、デバイスアドレスを指定するような場合には、アドレスに応じてプルアップすると良いようです。7ピンは、ライトプロテクトなので、書き込みする場合には、そのままか、GNDに落とします。
接続できたら、ラズパイで確認します。

$ i2cdetect -y 1

と、やると、特にアドレスピンを設定していなければ、50 と表示されるはずです。
表示されなければ、ROMが認識されていないかもしれません。
あとは、以下のPythonプログラムを走らせるとROMが読めると思います。
プログラム中の"bus=smbus.SMBus(1)" のところ、もし、バスが0の場合には、添え字を"(0)"にしてくださいね。
ポイントは、24C512は、アドレスが、2バイトなので、smbusのライブラリでは、本来の使い方では、1バイトしか書けないみたいで、"write_i2c_block_data でデータ部分の最初のデータに2バイト目のアドレスを書くことによって、2バイト分のアドレスを指定しているみたいです。

#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import smbus
import sys

bus = smbus.SMBus(1)
addr = 0
adr1=0
adr2=0
startaddr = 0
endaddr = 0

def read_ROM():
    l1=[]
    print('%4s : %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X ' %('Adr',0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15) )
    print('-'*54)
    for i in range(startaddr/256,endaddr/256+1):       
        for j in range(16):
            for k in range(16):
                bus.write_i2c_block_data(addr,i,[16*j+k])
                l1.append(bus.read_byte(addr))
        
            print('%4X : %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X ' %(256*i+16*j,l1[0],l1[1],l1[2],l1[3],l1[4],l1[5],l1[6],l1[7],l1[8],l1[9],l1[10],l1[11],l1[12],l1[13],l1[14],l1[15]) )

            l1=[]

if __name__ == '__main__':
    args = sys.argv
    if len(args) < 4 :
        print('usage: python {} <Device Address(HEX)> <Start Address(HEX)> <End Address(HEX)>'.format(__file__))
        exit(0)
    addr = int(args[1],16)
    startaddr = int(args[2],16)
    endaddr = int(args[3],16)
    
    read_ROM()

使い方は、
このスクリプトのファイル名に続いて、デバイスアドレス(50) 読み込みの開始アドレス 読み込み終了アドレス となります。
例えば、"i2cromread.py" という名前で保存し、デバイスは、0x50、読み込み開始アドレスは、0x200、終了アドレス0x21F とすると、以下のようになります。 ただし、表示は、256バイトごとのブロックで表示されるので、要らない部分も表示されるかと思いますが、そこんとこ、よろしく・・・。

$ python i2cromread.py 50 200 21F

f:id:manpukukoji:20171027090206p:plain
と、言うわけで、Writer編は、次回に・・・。

ラズベリーパイとUSBカメラでビデオキャプチャしてタイムラプス動画作成

一定時間ごとに写真をとって、定点観察したいと思い、ラズベリーパイとUSBカメラでビデオキャプチャしてみた。
ラズベリーパイは、PythonOpenCVで一定時間ごとにビデオキャプチャするスクリプトを作って、キャプチャした映像をFTPWindowsに取ってきて、タイムラプス動画をWindows上で作成した。
で、とりあえず、できたのがこの動画

ラズベリーパイとUSBカメラでビデオキャプチャしてタイムラプス動画を作成

ま、やり方は、CRON使う手とか、いろいろあると思うけど、とりあえず、手っ取り早くPython上で全部まかなって、凝った仕組みは何もないけど、まぁ、目的は達成できているので、こんなもんかな・・・?
OpenCV使うと、こんなことも、簡単にできちゃうんですね。 
おそるべし、OpenCV
自分では、一からこんなのとても作れるとは思えない・・・。 ありがたいこってす・・・。
Pythonスクリプトは、以下のようなもの。
エラー処理もしていないし、保存先のフォルダも固定だし、ファイル名も固定です。
スクリプトのおいてあるフォルダの下に、Imgというフォルダを作ると、そこに、CapXXXX.jpgというファイルができていきます。 XXXXのところには、キャプチャしたときの日付と時間になります。例えば、2017年10月07日の9時15分00秒だと、Cap20171001091500.jpg となります。 このままだと、180秒(3分)ごとにキャプチャされます。
for文 でごにょごにょしているのは、いきなりキャプチャすると、なぜか真っ黒だったので、苦肉の策です。
もっと、ちゃんとしたスマートな方法があるのでしょうが、まぁ、こんなにちょこっとのスクリプトなんで、良いか・・・。
ってなもんです。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import cv2
import datetime
from time import sleep

cap = None
count=0
camera_port = 0   # USBカメラのデバイスは0 (/dev/video0)
cap = cv2.VideoCapture(camera_port)

while(True):
  for i in range(10):
    ret, im = cap.read()
    if ret==True:
        if i==9:
            tm=datetime.datetime.today().strftime("%Y/%m/%d %X")
            ftm=datetime.datetime.today().strftime("%Y%m%d%H%M")
            cv2.putText(im,tm,(20,450),cv2.FONT_HERSHEY_PLAIN,2,(255,255,0))
            file_name = "./Img/Cap" + ftm + ".jpg"
            print (file_name)
            cv2.imwrite(file_name, im)
            print("File written")
            sleep(180)

cap.release()
cv2.destroyAllWindows()

ラズベリーパイ2にコマンドボタンをつけてみた

ラズベリーパイにスイッチをつけて、GPIOからシャットダウンしたり、リブートしたりしている人がいる。
確かに、リモートで接続していると、接続が切れてしまったりすると、どうしようもなくなって、やむなく、”えいっ!”っと、電源スイッチを切るのだけど、気の小さい僕には、精神衛生上、はなはだ、よろしくない。
と、言うわけで、シャットダウンスイッチをつけてみた。
せっかくなので、同時に、アプリケーションをスタートしたり、ストップしたりするコマンドボタンも作ってみた。
このコマンドボタンは、監視カメラ用に走らせている、motion のプログラムを起動したり、停止させたりするために利用している。 まぁ、コマンドスイッチ自体は、Python のプログラムしだいで、どのようにも利用できる。
ボタンは、前回のRTCの基盤のあいたところにスイッチを並べてみた。LED点灯用のピンも設定したが、途中で面倒になり、実装は、していない。 /^^;)

スイッチは、前回のRTCモジュールを組んだときの基板のあいたところにヘッダピンとともに並べてみた。
f:id:manpukukoji:20160723214957j:plain
Paithonのスクリプトは、以下のような感じで組んでみた。

#!/usr/bin/python
#coding: UTF-8
import time
import RPi.GPIO as GPIO
import os

System_Reboot=5
System_Shutdown=6
Monitor_Start=13
Monitor_Stop=19
pin_out=21

def SysRBoot_callback(gpio_pin):

	GPIO.output(pin_out, True)
	print "Reboot now!"
	os.system("sudo shutdown -r now")

def SysShut_callback(gpio_pin):

	GPIO.output(pin_out, True)
	print "Shutdown now!"
	os.system("sudo shutdown -h now")

def MStart_callback(gpio_pin):

	if os.path.exists("/var/run/motion/motion.pid"):
		time.sleep(1)
	else:
		os.system("sudo service motion start")
		print "motion start"

def MStop_callback(gpio_pin):

	if os.path.exists("/var/run/motion/motion.pid"):
		os.system("sudo service motion stop")
		print "motion stop"

GPIO.setmode(GPIO.BCM)
#GPIO.cleanup()
GPIO.setup(System_Reboot,GPIO.IN,pull_up_down=GPIO.PUD_UP)
GPIO.setup(System_Shutdown,GPIO.IN,pull_up_down=GPIO.PUD_UP)
GPIO.setup(Monitor_Start,GPIO.IN,pull_up_down=GPIO.PUD_UP)
GPIO.setup(Monitor_Stop,GPIO.IN,pull_up_down=GPIO.PUD_UP)
GPIO.setup(pin_out,GPIO.OUT)
GPIO.add_event_detect(System_Reboot,GPIO.RISING)
GPIO.add_event_detect(System_Shutdown,GPIO.RISING)
GPIO.add_event_detect(Monitor_Start,GPIO.RISING)
GPIO.add_event_detect(Monitor_Stop,GPIO.RISING)
GPIO.add_event_callback(System_Reboot,SysRBoot_callback)
GPIO.add_event_callback(System_Shutdown,SysShut_callback)
GPIO.add_event_callback(Monitor_Start,MStart_callback)
GPIO.add_event_callback(Monitor_Stop,MStop_callback)

try:
	while True:
		time.sleep(1)
except KeyboardInterrupt:
	GPIO.cleanup()

RasPi2にRTC(RX-8564) をつけてみる

f:id:manpukukoji:20160723215217j:plainラズベリーパイ2は、時計を持っていないので、シャットダウンすると再立ち上げの際に時刻をセットしなおさないと最後にシャットダウンした時間からの時刻になってしまう。 もちろん、インターネットに接続されていると、ネットワークからNTPで時刻をセットするので問題ないのだけれども、スタンドアロンで使うときには、毎回時刻をセットしなければ正しい時刻が得られない。
と、言うわけで、電源を落としても時刻を保持できるようにRTCモジュールを接続した。
すでに、RTCの接続については、沢山のサイトで説明されているので、あちこち参考にさせていただいた。
今回、僕が使ったのは、マルツで売っている MRX-8564 といいうモジュールでエプソンのRX-8564LC というICが乗っかっている。 これに、ボタン電池をくっつけて、ラズパイにI2Cで接続。
基盤作成は、コマンド起動用のスイッチとあわせて、こんな感じで作ってみた。
f:id:manpukukoji:20160723212535j:plain

じつをいうと、最初、ラズパイの電源からのラインにダイオードを入れてなかったので、RTCをつなぐと、いきなりラズパイのLEDが点灯したので、ラズパイが起動したのかと思って、びっくりした! さすがにボタン電池では、起動するところまでは行かなかったみたいだけど、LEDくらいは、点くんだね。 まぁ、当たり前っちゃ、あたりまえ何だけど・・・。
あわてて、こちらにもダイオードを追加したしだいです。

ラズベリーパイの設定は、以下のサイトを参考にさせてもらった。
http://tomosoft.jp/design/?p=5812
http://news.mynavi.jp/articles/2014/08/21/raspberry-pi4/002.html
http://wp.developapp.net/?p=3362

これらのサイトに詳しく説明されているので改めて説明するまでも無いけど、
僕の場合、最後の rc.local を編集して、自動起動するところで、ひっかかった。
確かに、設定をしてあるのだけれども、シャットダウンしてしばらくしてから立ち上げると、正しい時刻が設定されていない。 が、マニュアルで設定して、hwclock を表示させると、正しい時刻を表示するので、RTC自体は、ちゃんと動いているみたい。
つまり、立ち上げのときにちゃんと設定されていないみたいなので、もう一度、rc.local を良く見てたら、ちゃんと、以下のように書いてある。
“In order to enable or disable this script just change the execution bits.
By default this script does nothing”
「このスクリプトを有効/無効にするには、実行ビットを書き換えてね。
デフォルトでは、このスクリプトは、なんにもせんからね。」
と、いうことで、
$ sudo chmod +x /etc/rc.local
と、やったら、ちゃんと立ち上がりで時刻がRTCから設定されるようになった。

ラズパイも、ハードもOSもどんどんアップデートされているので、先にやった人のときは、必要なかった事が必要になってたり、逆に、必要だったことが必要なかったり、編集すべき設定ファイルが変わっていたりで、なかなか、簡単にはいかないもんですね…。

ネスカフェバリスタとラズベリーパイで顔を認識したら、ナンパするバリスタ君にしてみた

ネスカフェバリスタラズベリーパイを接続してUSBカメラからキャプチャーした画像をOpenCVを使ったPythonプログラムで顔認識させ、人の顔が認識されたら、”コーヒー飲まない?”と、お誘いするバリスタ君にしてみた。
youtu.be
 
バリスタとラズパイ以外の使用機材は、以下のようなもの
  USBカメラ:iBUFFALO BSW20KM15
  WiFiアダプタ:WLI-UC-GNU2
  セルフパワーのUSBハブ
ラズベリーパイには、Python用のOpenCVをインストール。
以下のサイトを参考にさせていただいた。
Raspberry Piで画像処理ライブラリ”OpenCV”使って”顔認識”試してみた: EeePCの軌跡

上記の参考サイトを見ながら、OpenCVとサンプルをインストール

 $ sudo apt-get install libopencv-dev
 $ sudo apt-get install python-opencv

サンプルは、上記参考サイトにあるように、face.xmlとfacedetect.py をコピーし、まずは、顔認識をテストしてみる。
が、以前にも書いたが、これまで使っていたtightVNCserver では、うまく表示させることができなかったので、x11VNC というソフトをインストール。 ( $ sudo apt-get install x11vnc )
が、そのままでは、解像度が低く、VNCで画面の一部しか表示されなかったので、

 $ sudo nano /boot/config.txt
以下の部分を修正(#をはずす)
  #framebuffer_width = 1280
    #framebuffer_height = 720

と、設定を少しいじくる。
これで、なんとか顔認識ができるようになった。
いろいろなサイトで紹介されているようにモニター画面に映った顔の部分に赤い四角で枠が入るあれ、です。
このサンプルをもとに、顔が認識されたら、シリアル通信でAquesTalkPicoという音声合成ICにメッセージを送ってお話させます。
シリアル通信で発声させるのは、前回
ラズベリーパイからシリアル通信で音声合成ICを発声させてみた - 満腹居士 七転八倒の記
でも書いたが、ラズパイのPin#01, 06, 08 からICにつなぐ。
プログラムは、facedetect.py のオリジナルの部分も含めて、以下のようにした。

#!/usr/bin/python
"""
This program is demonstration for face and object detection using haar-like features.
The program finds faces in a camera image or video stream and displays a red box around them.

Original C implementation by:  ?
Python implementation by: Roman Stanchak, James Bowman
"""
import sys
import cv2.cv as cv
from optparse import OptionParser
import time
import serial
# Parameters for haar detection
# From the API:
# The default parameters (scale_factor=2, min_neighbors=3, flags=0) are tuned
# for accurate yet slow object detection. For a faster operation on real video
# images the settings are:
# scale_factor=1.2, min_neighbors=2, flags=CV_HAAR_DO_CANNY_PRUNING,
# min_size=<minimum possible face size

min_size = (20, 20)
image_scale = 2
haar_scale = 1.2
min_neighbors = 2
haar_flags = 0
count_facecheck = 0

con=serial.Serial('/dev/ttyAMA0', 9600, timeout=10)

def detect_and_draw(img, cascade):
    global count_facecheck
    # allocate temporary images
    gray = cv.CreateImage((img.width,img.height), 8, 1)
    small_img = cv.CreateImage((cv.Round(img.width / image_scale),
                               cv.Round (img.height / image_scale)), 8, 1)

    # convert color input image to grayscale
    cv.CvtColor(img, gray, cv.CV_BGR2GRAY)

    # scale input image for faster processing
    cv.Resize(gray, small_img, cv.CV_INTER_LINEAR)

    cv.EqualizeHist(small_img, small_img)

    if(cascade):
        t = cv.GetTickCount()
        faces = cv.HaarDetectObjects(small_img, cascade, cv.CreateMemStorage(0),
                                     haar_scale, min_neighbors, haar_flags, min_size)
        t = cv.GetTickCount() - t
        # print "detection time = %gms" % (t/(cv.GetTickFrequency()*1000.))
        if faces:
            for ((x, y, w, h), n) in faces:
                # the input to cv.HaarDetectObjects was resized, so scale the
                # bounding box of each face and convert it to two CvPoints
                pt1 = (int(x * image_scale), int(y * image_scale))
                pt2 = (int((x + w) * image_scale), int((y + h) * image_scale))
                cv.Rectangle(img, pt1, pt2, cv.RGB(255, 0, 0), 3, 8, 0)
                print "detection time = %gms" % (t/(cv.GetTickFrequency()*1000.))
            print count_facecheck
            count_facecheck += 1
            if count_facecheck == 5:
              con.write("? ko-hi'- nomima/se'nka?\r")

            elif count_facecheck == 50:
              con.write("? ne'e ne'e, ko-hi'- nomo'uyo\r")

            elif count_facecheck == 100:
              con.write("ko-hi'- noma'naika na'-\r")

            elif count_facecheck == 300:
              count_facecheck = 0

        else:
                count_facecheck = 0

    cv.ShowImage("result", img)

if __name__ == '__main__':

#    global count_facecheck
    parser = OptionParser(usage = "usage: %prog [options] [filename|camera_index]")
    parser.add_option("-c", "--cascade", action="store", dest="cascade", type="str", help="Haar cascade file, default %default", default = "../data/haarcascades/haarcascade_frontalface_alt.xml")
    (options, args) = parser.parse_args()

    cascade = cv.Load(options.cascade)

    if len(args) != 1:
        parser.print_help()
        sys.exit(1)

    input_name = args[0]
    if input_name.isdigit():
        capture = cv.CreateCameraCapture(int(input_name))
    else:
        capture = None

    cv.NamedWindow("result", 1)

    width = 320 #leave None for auto-detection
    height = 240 #leave None for auto-detection

    if width is None:
        width = int(cv.GetCaptureProperty(capture, cv.CV_CAP_PROP_FRAME_WIDTH))
    else:
        cv.SetCaptureProperty(capture,cv.CV_CAP_PROP_FRAME_WIDTH,width)

    if height is None:
        height = int(cv.GetCaptureProperty(capture, cv.CV_CAP_PROP_FRAME_HEIGHT))
    else:
        cv.SetCaptureProperty(capture,cv.CV_CAP_PROP_FRAME_HEIGHT,height)

    if capture:
        frame_copy = None

        while True:

            frame = cv.QueryFrame(capture)
            if not frame:
                cv.WaitKey(0)
                break
            if not frame_copy:
                frame_copy = cv.CreateImage((frame.width,frame.height),
                                            cv.IPL_DEPTH_8U, frame.nChannels)

#                frame_copy = cv.CreateImage((frame.width,frame.height),
#                                            cv.IPL_DEPTH_8U, frame.nChannels)

            if frame.origin == cv.IPL_ORIGIN_TL:
                cv.Copy(frame, frame_copy)
            else:
                cv.Flip(frame, frame_copy, 0)

            detect_and_draw(frame_copy, cascade)

            if cv.WaitKey(10) >= 0:
                break
    else:
        image = cv.LoadImage(input_name, 1)
        detect_and_draw(image, cascade)
        cv.WaitKey(0)

    cv.DestroyWindow("result")

発声部分のプログラムは、簡単なのですぐわかると思うが、
顔を認識した回数をカウントして、何回かになると発声するようにしている。
最初は、「コーヒー飲みませんか?」 と、お誘いする。
2回目は、「ねぇ、ねぇ、コーヒー飲もうよ!」と、ちょっと、うざい感じで・・・。
3回目は、「コーヒー飲まないかなぁ~?」と、ちょっと、未練たらしく・・・。

3回発声すると、リセットされて、しばらく間をとる。3回の間に顔認識がはずれた時にも、リセットされて、また、一回目の発声からはじまる。
てな、具合。

別に、バリスタなくても良いんじゃないかって?
あっ、気付いちゃった?
まぁ、そうなんだけど、コミュニケーションとるのに、何か、それなりのネタがいるじゃないですか。
だから、バリスタで入れるコーヒーをネタにナンパをしかけるという作戦で・・・。

これまでの一連の試みで、人が近くに来たのを認識し、「コーヒーはいかが?!」と、声をかけて、音声認識で返事を受け取って、コーヒーを入れる。と、いう、サービスがほぼ自動化できるようになった。
これに、手足をつけてロボット化すると、給仕ロボットができちゃいそうですね。

ラズベリーパイからシリアル通信で音声合成ICを発声させてみた

これまで、音声合成IC(Aques talk pico)で発声させるのにプリセットのメッセージをラズパイのGPIOからコントロールしていたが、ラズパイから直接シリアル通信で発生させるようにセットしてみた。
f:id:manpukukoji:20160306114053j:plain
プリセットメッセージを使うと、メッセージ数にもよるが、GPIOのピンを3つ、4つ使うことになるけどシリアル通信で直接メッセージを送ると、送信だけで良いのでGPIO14のピン1本で事足りるので節約になるね。
シリアル通信のための設定は、いろいろなサイトで説明してくれているが、なんと、今回、僕の環境では、何もしなくてもそのまま通信できちゃったよ???

これまでのWebの情報だとシリアル通信するためには、以下の3つが必要だそうです。
1.シリアルコンソールのdisable化
2./boot/cmdline.txt の編集
3./etc/inittab の編集

僕の環境は、
Raspberry Pi2
NOOBS ver. 1.7.0. (Raspbian Kernel 4.1.17)
になっているが、上記の設定をしなくてもできちゃうみたい・・・?!

ちなみに、NOOBSのバージョンは、もう、1.8.0になっていた。
で、現在の/boot/cmdline.txt は、以下のようになっていた。
/etc/inittab は、そもそも、はじめから見つからなかった。

pi@raspberrypi:~$ cat /boot/cmdline.txt
dwc_otg.lpm_enable=0 console=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p7 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait

ラズパイとATP3011との接続は、電源用の3.3VとGround、ラズパイのTxd Pin#08(GPIO14) の3本で接続
回路図は、こちら
f:id:manpukukoji:20160306113408j:plain
写真では、IR受信用のセンサとリモコンコード解読用のPICが付いているが、今回は、使わないので回路図からは、はずしてある。

簡単な発声プログラムは、Pythonで以下のようなプログラムを書いてみた。

pi@raspberrypi:~ $ cat wrserial.py
import serial
import time

def main():
    con=serial.Serial('/dev/ttyAMA0', 9600, timeout=10)
    print con.portstr
    time.sleep(1)
    str="? ohayo-\r\n"
    con.write(str)

if __name__ == '__main__':
    main()

これで、

 $sudo python wrserial.py

と、やると、”おはよー”と挨拶してくれるはず・・・。