Raspberry Pi 日記 (part2)

長嶋 洋一


2013年7月5日(金)

さて金曜日、ゼミの日である。 この日は前日トライしてイマイチだったおいちゃん作曲(0:40-1:00のたった20秒間の改訂)に朝から没頭して、とりあえず1限の終わりに試作版を完成しておいちゃんに送った。 そして2限にはゼミで、まずリュ君の「鉄板に振動モジュールを取り付けて振動させ、その上の砂の共鳴パターンを見る」という実験の続報を検討した。 このように なかなかに面白い進展をしていて、自動計測記録するMaxパッチの可能性を提案した。 土佐谷さんの「飛沫傘」もまた、アマゾンで仕入れた現物であれこれ可能性が広がってきた。 森川さんがお休みということで、昼前にはゼミミーティングも終わり、いよいよ午後はRaspberry Piタイムとなった。 ちょうどお昼にはおいちゃんから「これでOK」メイルも届き(^_^)、今回の作曲も無事に完了である。

さて、昨日はRaspberry PiのGPIOにSUACboardを接続して、 このように 64個のLEDの点灯制御まで成功した。 SUACboardに付いているスイッチングAC電源アダプタの入力電圧が「100-240V」でなく「100-120V」とあったので、Sketchingに持参するのに交換が必要か・・・と思って調べたら、アメリカは110-120Vまで、という事で交換も不要と確認できた。

そこでいよいよ、64個のLEDのPWM制御であるが、これには「PWMパラメータの外部正制御」が必須となってくる。 「2013年7月1日(月)」に実験したように、RAMディスクの手法によって、外部のプロセスから64個のPWMパラメータ(128文字)を受け取るルートも確立したが、そのパラメータの送り手がRaspberry Pi内の他のプログラム、というのは淋しい。 Propellerでやったように、ここはホストのMacのMaxから刻々と変化させて送ってやりたい。 そこで検索してみると、やはり同じことを考える人は多いのだろう、まさにズバリ Control Raspberry Pi with another Program というページがあった。 そしてそこで出て来たのはやはり、OSCだった。 OSCについては、以前にふーみんメイルで、

PythonのOSCは,
	sudo apt-get install python-txosc
で入ります.単純なやつは
	http://opensoundcontrol.org/implementation/python-simple-osc
にもあります.
Pd用のものは
	sudo apt-get install pd-osc
です.
と紹介されていた。 単純で十分、いよいよ久しぶりのOSCである。 OSCは GDS (global delayed session) Music の研究でやっていて、ここで開発したシステムはヤマハの受託研究として作り、僕が筆頭発明者として米国特許登録されているのだ。 そしてなんと、「2013年6月18日(火)」の日記のところで、以下のように書いていた。
「GPIO」のゲットとインストールだけでなく、ふーみんに教わった「sudo apt-get install puredata」と「sudo apt-get install python-txosc」と「sudo apt-get install pd-osc」をエラー無しに完了させ、さらにThingmのBlibk(1)もついでにダウンロードしてみた。
つまり、Python用のOSCはもう既に、3台のRaspberry Piにインストールされている筈なのである。 そこでまず Python - Simple OSC に行った。 SimpleOSCはOSCをPython用にラップしたもので、以下のように使え、プラットフォームを問わないという。
import osc
osc.init()
osc.sendMsg( '/test', 999)
これは簡単そうである。 そこでリンクから SimpleOSC module 0.3.2 の記述を読むと、この「SimpleOSC」を走らせるためには、まず pyOSC に行って、「pyOSC」をインストールせよ、という事である。 そして、学生など初学者のためのAPIとしてSimpleOSCをデザインしたので、エキスパートはpyOSCそのものを使え、という。 そのために pyOSCのサンプルプログラム も用意しているのだという。 そこでとりあえず、このサンプルに加えて、zip形式で SimpleOSC_0.3.2.zip をダウンロードして、 ここ から pyOSC-0.3.5b-5294.zip とをダウンロードしてみると、その中身は以下のようなものであった。

初学者向けAPIという「SimpleOSC」の「simpleOSC.py」Pythonソースを見ると、以下であり、定義が並んでいるだけだった。 これはまさにAPIで、今回、使いたいものではなくて、Pythonベースで何かソフトウェアを作る場合にOSCと橋渡しするもののようである。

try :
    from OSC import OSCServer, ThreadingOSCServer, ForkingOSCServer, OSCClient, OSCMessage, OSCBundle
except :
    print "Warning!!! you must have pyOSC installed -> https://trac.v2.nl/wiki/pyOSC"
    
import threading

client = 0
server = 0
st = 0

def printing_handler(addr, tags, data, source):
    print "---"
##    print "received new osc msg from %s" % getUrlStr(source)
    print "with addr : %s" % addr
    print "typetags :%s" % tags
    print "the actual data is : %s" % data
    print "---"

def initOSCClient(ip='127.0.0.1', port=9000) :
    global client
    client = OSCClient()
    client.connect( (ip,port) )
    
def initOSCServer(ip='127.0.0.1', port=9001, mode=0) :
    """ mode 0 for basic server, 1 for threading server, 2 for forking server
    """
    global server, st

    if mode == 0 :
        server = OSCServer( (ip ,port) ) # basic
    elif mode == 1 : 
        server = ThreadingOSCServer( (ip ,port) ) # threading
    elif mode == 2 :
        server = ForkingOSCServer( (ip ,port) ) # forking

    server.addDefaultHandlers()

def startOSCServer() :
    print "Registered Callback-functions are :"
    for addr in server.getOSCAddressSpace():
        print addr
    st = threading.Thread( target = server.serve_forever )
    st.start()

def setOSCHandler(address="/print", hd=printing_handler) :
    server.addMsgHandler(address, hd) # adding our function

def closeOSC() :
    if client is not 0 : client.close()
    if server is not 0: server.close() 
    if st is not 0: st.join()

def reportOSCHandlers() :
    print "Registered Callback-functions are :"
    for addr in server.getOSCAddressSpace():
        print addr
    
def sendOSCMsg( address='/print', data=[] ) :
    m = OSCMessage()
    m.setAddress(address)
    for d in data :
        m.append(d)
    client.send(m)

def createOSCBundle(address) : # just for api consistency
    return OSCBundle(address)
    
def sendOSCBundle(b):
    client.send(b)

def createOSCMsg(address='/print', data=[]) :
    m = OSCMessage()
    m.setAddress(address)
    for d in data :
        m.append(d)
    return m
これに対して、「pyOSC_examples」にたった二つだけ入っているサンプルは名前そのままの「basic_send.py」と「basic_receive.py」であり、「basic_send.py」の中身は以下で、まさに、これが欲しかったものである。(^_^)
""" sending OSC with pyOSC
https://trac.v2.nl/wiki/pyOSC
example by www.ixi-audio.net based on pyOSC documentation
"""

import OSC
import time, random

## the most basic ##
client = OSC.OSCClient()
msg = OSC.OSCMessage()
msg.setAddress("/print")
msg.append(1234)
client.sendto(msg, ('127.0.0.1', 9000)) # note that the second arg is a tupple and not two arguments

## better practice ##
client = OSC.OSCClient()
client.connect( ('127.0.0.1', 9000) ) # note that the argument is a tupple and not two arguments
msg = OSC.OSCMessage() #  we reuse the same variable msg used above overwriting it
msg.setAddress("/print")
msg.append(4321)
client.send(msg) # now we dont need to tell the client the address anymore

## in mode detail ##

# tupple with ip, port. i dont use the () but maybe you want -> send_address = ('127.0.0.1', 9000)
send_address = '127.0.0.1', 9000

# OSC basic client
c = OSC.OSCClient()
c.connect( send_address ) # set the address for all following messages

# single message
msg = OSC.OSCMessage()
msg.setAddress("/print") # set OSC address
msg.append(44) # int
msg.append(4.5233) # float
msg.append( "the white cliffs of dover" ) # string

c.send(msg) # send it!

# bundle : few messages sent together
# use them to send many different messages on every loop for instance in a game. saves CPU and it is faster
bundle = OSC.OSCBundle()
bundle.append(msg) # append prev mgs
bundle.append( {'addr':"/print", 'args':["bundled messages:", 2]} ) # and some more stuff ...
bundle.setAddress("/*print")
bundle.append( ("no,", 3, "actually.") )

c.send(bundle) # send it!

# lets try sending a different random number every frame in a loop

try :
    seed = random.Random() # need to seed first 
    
    while 1: # endless loop
        rNum= OSC.OSCMessage()
        rNum.setAddress("/print")
        n = seed.randint(1, 1000) # get a random num every loop
        rNum.append(n)
        c.send(rNum)
        time.sleep(5) # wait here some secs

except KeyboardInterrupt:
    print "Closing OSCClient"
    c.close()
    print "Done"        
「basic_receive.py」の中身は以下で、まさに、これが欲しかったものである(^_^)。 これで判明したのは、とりあえず「SimpleOSC」は不要のようなので、この2本のサンプルを使うとして、まずは「pyOSC」をインストールすればいい、という事である。 同時に二つをインストールして良くないことが起きた事は少なくない(^_^;)ので、この作戦で行こう。
""" receiving OSC with pyOSC
https://trac.v2.nl/wiki/pyOSC
example by www.ixi-audio.net based on pyOSC documentation
"""

import OSC
import time, threading

# tupple with ip, port. i dont use the () but maybe you want -> send_address = ('127.0.0.1', 9000)
receive_address = '127.0.0.1', 9000

# OSC Server. there are three different types of server. 
s = OSC.OSCServer(receive_address) # basic
##s = OSC.ThreadingOSCServer(receive_address) # threading
##s = OSC.ForkingOSCServer(receive_address) # forking

# this registers a 'default' handler (for unmatched messages), 
# an /'error' handler, an '/info' handler.
# And, if the client supports it, a '/subscribe' & '/unsubscribe' handler
s.addDefaultHandlers()

# define a message-handler function for the server to call.
def printing_handler(addr, tags, stuff, source):
    print "---"
    print "received new osc msg from %s" % OSC.getUrlStr(source)
    print "with addr : %s" % addr
    print "typetags %s" % tags
    print "data %s" % stuff
    print "---"

s.addMsgHandler("/print", printing_handler) # adding our function

# just checking which handlers we have added
print "Registered Callback-functions are :"
for addr in s.getOSCAddressSpace():
    print addr

# Start OSCServer
print "\nStarting OSCServer. Use ctrl-C to quit."
st = threading.Thread( target = s.serve_forever )
st.start()

try :
    while 1 :
        time.sleep(5)

except KeyboardInterrupt :
    print "\nClosing OSCServer."
    s.close()
    print "Waiting for Server-thread to finish"
    st.join() ##!!!
    print "Done"
・・・ということで、「pyOSC」のフォルダにある「REAMDE.txt」を開くと、なるほどこれは このように 長大であった(^_^;)。 ただし欲しいのは、最後の以下の部分の情報である。
INSTALLING
    To install, simply run
    
    $ sudo ./setup.py install
    
    and provide your password.
    That's it. After this, you can use the 'pyOSC' module in your python-scripts by doing
    
    import OSC
    
DOCUMENTATION
    To get help, run
    
    $ pydoc OSC
    
    or, from within a python-interpreter
    
    >>> import OSC
    >>> help(OSC)
    
TESTING
    The file 'OSC.py' contains an OSC-testing program. 
    Please have a good look at the source for this program
    (the 'if __name__ == "__main__":' block at the end of the file)
    for an example on how to use this module.
    
    To get help on how to invoke the test-program, run
    
    $ python OSC.py --help
    
    Usage: OSC.py [options]

    OSC.py OpenSoundControl-for-Python Test Program

    Options:
      -h, --help		show this help message and exit
      -l LISTEN, --listen=LISTEN
      			listen on given host[:port]. default = '0.0.0.0:2222'
      -s SENDTO, --sendto=SENDTO
			send to given host[:port]. default = '127.0.0.1:2222'
      -t, --threading	Test ThreadingOSCServer
      -f, --forking	Test ForkingOSCServer
      -u, --usage	show this help message and exit
ここでまず、以下のようにMax6でUDPで7001と7002の2つのポートで、OSCメッセージを出すものと受けて表示するものを用意した。 これにより、もしRaspberry Piから何かが届けば、こちらで確認できる。

そしてここでフト思ったのが、この「pyOSC」のディレクトリ以下の一式、さらに「basic_send.py」と「basic_receive.py」の2つのサンプルを、いちいちrcpでRaspberry Piに転送するのは面倒だなぁ・・・という事だった。 Linuxでは「*.tar.z」とか「*.tar.gz」とかいう圧縮形式が昔からあるが、考えてみればWindowsと違うMacのzip形式も、中身はUnixなのである。 そこで試しに、「pyOSC」のディレクトリ内に「basic_send.py」と「basic_receive.py」も入れて、これをMacのcompressでzipにして、rcpからRaspberry Piに転送して、ダメ元で「unzip」というのをしてみると、なんと以下のようにちゃんと解凍できて、無事に「setup.py」でインストールが完了した。 「__MACOSX」という、幽霊のようなフォルダも解凍によって出来るが、これは「rm -r __MACOSX」で中身ごと消去できるので問題ない。

Last login: Fri Jul  5 07:24:55 on console
nagasm-Mac-mini:~ nagasm$ ssh pi@172.16.65.62
pi@172.16.65.62's password: 
Linux raspberrypi 3.6.11+ #456 PREEMPT Mon May 20 17:42:15 BST 2013 armv6l

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: Sun Jun 23 15:51:40 2013

pi@raspberrypi ~ $ ls
Desktop     RPi.GPIO-0.1.0  bcm2835-1.25  blink_autorun  ocr_pi.png    pyusb-1.0.0-a1  test.c
GPIO_clear  a.out           blink1-tool   null           python_games  pyusb-1.0.0a3   twilight.png

pi@raspberrypi ~ $ rcp nagasm@172.16.65.31:Desktop/pyOSC.zip .
Password:
pyOSC.zip                                                         100%   38KB  38.0KB/s   00:00    

pi@raspberrypi ~ $ ls -l
合計 1160
drwxr-xr-x 2 pi   pi    4096  6月 18 23:18 Desktop
-rwsr-xr-x 1 root pi    9355  6月 22 17:19 GPIO_clear
drwxr-xr-x 7 pi   pi    4096  6月 18 04:31 RPi.GPIO-0.1.0
-rwxr-xr-x 1 pi   pi   37081  6月 23 15:50 a.out
drwxr-xr-x 5 pi   pi    4096  6月 22 17:21 bcm2835-1.25
-rwsr-xr-x 1 root pi 1020394 11月 28  2012 blink1-tool
-rwxr-xr-x 1 pi   pi    5170  6月 22 09:02 blink_autorun
-rw-r--r-- 1 pi   pi       0  6月 23 16:59 null
-rw-r--r-- 1 pi   pi    5781  2月  3 05:07 ocr_pi.png
-rw-r--r-- 1 pi   pi   38910  6月 23 17:00 pyOSC.zip
drwxrwxr-x 2 pi   pi    4096  7月 20  2012 python_games
drwxr-xr-x 6 pi   pi    4096 12月 29  2010 pyusb-1.0.0-a1
drwxr-xr-x 5 pi   pi    4096  6月 18 23:09 pyusb-1.0.0a3
drwxr-xr-x 2 pi   pi    4096  7月  5  2013 test
-rwxr-xr-x 1 pi   pi    1458  6月 23 15:50 test.c
-rw-r--r-- 1 pi   pi   20123  5月 31 11:45 twilight.png

pi@raspberrypi ~ $ unzip pyOSC.zip
Archive:  pyOSC.zip
   creating: pyOSC/
  inflating: pyOSC/basic_receive.py  
  inflating: pyOSC/basic_send.py     
  inflating: pyOSC/lesser.txt        
  inflating: pyOSC/OSC.py            
   creating: __MACOSX/
   creating: __MACOSX/pyOSC/
  inflating: __MACOSX/pyOSC/._OSC.py  
  inflating: pyOSC/PKG-INFO          
  inflating: pyOSC/README.txt        
  inflating: pyOSC/setup.py          
  inflating: __MACOSX/._pyOSC

pi@raspberrypi ~ $ ls
Desktop         __MACOSX      blink1-tool    ocr_pi.png  python_games    test
GPIO_clear      a.out         blink_autorun  pyOSC       pyusb-1.0.0-a1  test.c
RPi.GPIO-0.1.0  bcm2835-1.25  null           pyOSC.zip   pyusb-1.0.0a3   twilight.png

pi@raspberrypi ~ $ ls -l
合計 1168
drwxr-xr-x 2 pi   pi    4096  6月 18 23:18 Desktop
-rwsr-xr-x 1 root pi    9355  6月 22 17:19 GPIO_clear
drwxr-xr-x 7 pi   pi    4096  6月 18 04:31 RPi.GPIO-0.1.0
drwxrwxr-x 3 pi   pi    4096  7月  5  2013 __MACOSX
-rwxr-xr-x 1 pi   pi   37081  6月 23 15:50 a.out
drwxr-xr-x 5 pi   pi    4096  6月 22 17:21 bcm2835-1.25
-rwsr-xr-x 1 root pi 1020394 11月 28  2012 blink1-tool
-rwxr-xr-x 1 pi   pi    5170  6月 22 09:02 blink_autorun
-rw-r--r-- 1 pi   pi       0  6月 23 17:01 null
-rw-r--r-- 1 pi   pi    5781  2月  3 05:07 ocr_pi.png
drwxr-xr-x 2 pi   pi    4096  7月  5  2013 pyOSC
-rw-r--r-- 1 pi   pi   38910  6月 23 17:00 pyOSC.zip
drwxrwxr-x 2 pi   pi    4096  7月 20  2012 python_games
drwxr-xr-x 6 pi   pi    4096 12月 29  2010 pyusb-1.0.0-a1
drwxr-xr-x 5 pi   pi    4096  6月 18 23:09 pyusb-1.0.0a3
drwxr-xr-x 2 pi   pi    4096  7月  5  2013 test
-rwxr-xr-x 1 pi   pi    1458  6月 23 15:50 test.c
-rw-r--r-- 1 pi   pi   20123  5月 31 11:45 twilight.png

pi@raspberrypi ~ $ rm -r __MACOSX
pi@raspberrypi ~ $ cd pyOSC

pi@raspberrypi ~/pyOSC $ ls
OSC.py  PKG-INFO  README.txt  basic_receive.py  basic_send.py  lesser.txt  setup.py

pi@raspberrypi ~/pyOSC $ sudo ./setup.py install
running install
running build
running build_py
creating build
creating build/lib.linux-armv6l-2.7
copying OSC.py -> build/lib.linux-armv6l-2.7
running install_lib
copying build/lib.linux-armv6l-2.7/OSC.py -> /usr/local/lib/python2.7/dist-packages
byte-compiling /usr/local/lib/python2.7/dist-packages/OSC.py to OSC.pyc
running install_egg_info
Writing /usr/local/lib/python2.7/dist-packages/pyOSC-0.3.5b_5294.egg-info

pi@raspberrypi ~/pyOSC $ 
これで準備は終わりである。 とりあえずREADME.txtの最後に以下のようにあったので、「pyOSC」のディレクトリにいる状態で「./OSC.py」とやってみた。 すると、出るわ出るわ、膨大に こんなメッセージ が出た。 とりあえず、OSCサーバも走って、特にエラーが無いことだけは判った。(^_^;)
TESTING
    The file 'OSC.py' contains an OSC-testing program. 
    
    Usage: OSC.py [options]

    Options:
      -h, --help		show this help message and exit
      -l LISTEN, --listen=LISTEN
      			listen on given host[:port]. default = '0.0.0.0:2222'
      -s SENDTO, --sendto=SENDTO
			send to given host[:port]. default = '127.0.0.1:2222'
      -t, --threading	Test ThreadingOSCServer
      -f, --forking	Test ForkingOSCServer
      -u, --usage	show this help message and exit
しかしこれではあまりに情報が膨大なので、ホームディレクトリに戻って、サンブルの「basic_send.py」と「basic_receive.py」を改変した「test.py」をrcpして走らせる、という、これまでの方法をとる事にした。 もちろん、Max6のパッチがモニタしているポート番号に変更しての実験である。 「## the most basic ##」とか「## better practice ##」は読んだだけで判ったので飛ばして、「## in mode detail ##」の、「# single message」を選んだ。 すると以下のように、あっさりとRaspberry PiからOSCメッセージがMacのMax6バッチに飛んだ。 「the white cliffs of dover」というメッセージは、世界の名曲「Many Rivers to Cross」の歌詞であろうか。 ちょっと起動してからの遅れが気になるが、まずは開通である(^_^)。
#!/usr/bin/python

import OSC
import time

send_address = '172.16.65.31', 7001
c = OSC.OSCClient()
c.connect( send_address )

msg = OSC.OSCMessage()
msg.setAddress("/print") # set OSC address
msg.append(44) # int
msg.append(4.5233) # float
msg.append( "the white cliffs of dover" ) # string
c.send(msg) # send it!

とりあえず今回の用途では、Raspberry PiはOSCでメッセージを送るよりも(こちらの用途はたぶん生存確認用)、OSCのメッセージを受ける方である。 そこで、上のプログラムを「test1.py」とリネームして保管し、次にOSC受信用の「test.py」を作ってみた。 そしてRaspberry Piにrcpして「python ./test.py」で実行させると、以下のようにOSC受信も無事に出来た。 これは基本形で、受信したメッセージタイプを表示するだけで、コールバック関数が記述されていないので何も進展しないが、ここまで来れば「道具だて」の確認として、とりあえずは十分である。

#!/usr/bin/python

import OSC
import time, threading

receive_address = '172.16.65.62', 7003
s = OSC.OSCServer(receive_address)
s.addDefaultHandlers()

# Start OSCServer
print "\nStarting OSCServer. Use ctrl-C to quit."
st = threading.Thread( target = s.serve_forever )
st.start()

try :
    while 1 :
        time.sleep(5)

except KeyboardInterrupt :
    print "\nClosing OSCServer."
    s.close()
    print "Waiting for Server-thread to finish"
    st.join() ##!!!
    print "Done"

MaxでもProcessingでもSuperColliderでも意外に混乱した(^_^;)ので、ここでOSCのアドレスとポートの定義について整理しておくことにした。 とりあえずはRaspberry Pi側の設定を中心に記述する。

この最後の部分を整理していて気付いたが、まだOSCのメッセージ受信は「道半ば」であった(^_^;)。 そこで「pyOSC」の中にあった、長大な 「OSC.py」 の冒頭に以下のように書かれていたので、これを調べてみることにした。
This module contains an OpenSoundControl implementation (in Pure Python), 
based (somewhat) on the good old 'SimpleOSC' implementation 
by Daniel Holth & Clinton McChesney.

This implementation is intended to still be 'Simple' to the user, but much more 
complete (with OSCServer & OSCClient classes) and much more powerful
(the OSCMultiClient supports subscriptions & message-filtering,
OSCMessage & OSCBundle are now proper container-types)
そしてここから延々と、イベントハンドラって何だっけ・・・という、遠い記憶をなぞる謎解きの旅となった。 色々と試しているうちに18時となり、ハムスターの餌やりのために帰宅する時間となって、この作業は週末に持ち越しとなった。 とりあえず、OSCはRaspberry Pi62号機で動作が確認できたので、以下のように61号機と63号機のためにさらにシェルウインドウを開いて、62号機で作業したコマンドのコピペで無事に3台ともOSCのインストール、そして実際に「test1.py」を走らせてMax6にOSCが届いていることをを確認した。 いろいろあったが、収穫と進展もあったということで、今日はおしまいである。


「Raspberry Pi日記」トップに戻る