Raspberry Pi 日記 (part2)

長嶋 洋一


2013年6月13日(木)

「Raspberry Pi日記」がスタートして約1ヶ月となり、HTMLファイルも250KBほどになった。 あれはどこだったっけ・・・と捜すのも難儀してきたので、ここで一念発起、朝イチから1時間ほど作業して、トップページから個々の日に飛べるように全面改装した。 一応、次の区切りまでは同じように「連続バージョン」として書き継いで行くが、分量が増えればまた分割する。 あと1ヶ月ほどでSketching2013に行くので、そこまではRaspberry PiとPropellerとArduinoなどに明け暮れるので、今後のドキュメントとしてきちんと残しておきたいのである。

そして1限に「しゃみーず」の3人がやってきて、 このように 作業して、三味線のボディに配線用の穴をあけた。 また昨日までの 「センサ→Arduino→XBee」→「XBee→Max」 を見せるとともに、今後、Flashで「しゃみーず」のプログラムを作るために、Gainerにダミーのボリューム3個を付けたものを作って貸し出した。 これで、僕の方のGainerが完成するまで、ストップする事なく開発が出来るわけである。

そして午後には、学生はコース演習室で、僕は研究室でそれぞれ進めて、 こんな感じに 進展した。 ArduinoがXBeeで受信してGainerに渡す、という基板を作り、初めてArduinoでの「シリアル受信」をやってみたら、何かは出て来たが、まだバグがある(^_^;)が、明日にはなんとか解決したい。 そしてさらに、業者に発注していた こんなもの が届き、あれこれ進んで一日が終わった。

2013年6月14日(金)

この日は2-3限に、4回生の「メディア造形総合演習」の中間進捗報告会があったが、それを挟んで1限と3限で、遂に「しゃみーず」のためのシステムの部分が完成してしまった(^_^)。 写真だけでなく、今後の活用のために、技術的な情報を全てまとめて、わざわざこのために、何もなく作っていた回路図も描いてスキャンした。 これである

そしてYouTube動画まで上げた後でまだ5限の時間が余っていたので、昨日到着した これ を開梱してみることにした。 以下のようにアッサリとした基板とコネクタだけだが、これによってRaspberry Piは現実世界と結びつく、まさに「Raspberry Pi物理コンピューティング」のインターフェースなのである。

まぁ、Raspberry Pi用の汎用インターフェース基板というだけでこの基板だけでは何もしないのだが、いきなりコネクタをはんだ付けする前に、せっかくなので このサイト のリンクをチェックする事にした。 とりあえずこの拡張基板は「Slice_Pi基板」と呼ぶことにしよう。

するとさっそく、ページの下の方の「Documentation」のところから、 Slice_Piマニュアル(PDF) をゲットしたので印刷した。 さらにリンクとして、以下ようなページがある事が判明した。

長期的な展望としては、Raspberry Piで物理コンピューティングをするのに、Pythonだけでプログラミングするつもりは無いが、とりあえずはPythonでやることになるのだろう、とここまで(山口往復の新幹線内とかで)Pythonを勉強してきたので、まさに絶好のタイミングである(^_^)。 とりあえず、今日はあと1時間ほどなので、上記の5つのリンク先をざっと眺めてメモとして残すことにした。 重要そうなところを後回しにしているので、順不動である。

Build document のページは、単に「Slice_Pi基板」のアップ写真が並び、あとは 回路図 が置かれているだけのPRページだった。 ちゃんとDIP間隔のユニバーサル基板パターンだけでなくて、XBeeの2mm間隔のパターンも載っているので、ここは「Raspberry PiでXBee」が超カンタンに実現できるのだ。

review of the board のページは、「Slice_Pi基板」にXBeeを載せて、ここからRaspberry PiのファームウェアをWiFiでアップロードする、という流れを紹介していた。 しかし、既に研究室のRaspberry Piは固定IPアドレスを割り振り、VNC経由で自在にファームウェアを吸い上げることも出来るので、ここでの記事で参考になるとすれば、XBeeとのやりとり部分ぐらいになるだろう。

review of the board のページはこのメーカのWebではなくて、Raspberry Piの拡張基板のいろいろを紹介しているプログであり、その最初の例として「Slice_Pi基板」が挙げられている、というだけである。 以下の写真でコネクタの取り付け方向が判る、という以外にメリットは無いので、今後ここをアクセスする事は無いだろう。

そして残りの2つのWebページは重要である。 Getting started with Raspberry Pi GPIO and Python ではまず、「Installing RPi.GPIO」で、「apt-get」でなく、Unixのtar.gzファイルをゲットして、これを解凍して、「sudo python setup.py install」ということでPythonで記述されたセットアッププログラムでRPi.GPIOがインストールできるらしい。 これはなかなか楽しそうである。(^_^)

Raspberry Pi GPIO Inputs in Python のページはその続きのようで、PythonでLEDを点滅させ、入力について解説して、入出力のPythonサンプルまで行くようである。 しかし、既にPythonを見慣れているので(まだ全くプログラミングしていないが(^_^;))、最後にあった以下のプログラムはとても良く読める。 この程度のプログラムではまだ何も出来ないのだが、入口としてはこれでほぼ完備、ということのようだ。

#!/usr/bin/env python
from time import sleep
# get the GPIO Library
import RPi.GPIO as GPIO
 
# setup some names references to the LED's and buttons
# red     = pin 18
# yellow  = pin 16
# green   = pin 15
# blue    = pin 13
 
# using an list to hold the led numbers
leds = [13, 15, 16, 18]
# the input buttons
up = 12
down = 11
 
# setup the pins as output and input as needed
# looping over the list 
for n in leds:
    GPIO.setup(n, GPIO.OUT)
 
GPIO.setup(up, GPIO.IN)
GPIO.setup(down, GPIO.IN)
# turn off all but the blue LED
GPIO.output(leds[3], True)
GPIO.output(leds[2], True)
GPIO.output(leds[1], True)
GPIO.output(leds[0], False)
 
#state trackers
level = 0
oldlevel = level
 
while 1:
    if GPIO.input(up):
        #up button pressed
        if level < 3:
            level += 1
    elif GPIO.input(down):
        #down button pressed
        if level > 0:
            level -= 1
    if oldlevel != level:
        # turn off last led and turn on next one
        GPIO.output(leds[oldlevel], True)
        GPIO.output(leds[level], False)
        # update state tracker
        oldlevel = level
        # sleep for a bit so button press is counted only once
        sleep(1)
この週末は、明日はいよいよ「40虎」(オープンキャンパスに向けた新入生プロジェクト)の勉強会「SUAC探検」などがあり、晩にもお楽しみの予定があるので進めないが、来週から、ぼちぼちRaspberry Piに戻ることにしよう。

2013年6月17日(月)

新しい週が始まった。5月はゴールデンウイークがあり、7月にも海の日があるが、6月は祝日も無く梅雨の時期で、学生は課題などに追われてあまり好きな季節ではないかもしれないが、コンスタントに仕事を進められるこの時期は嫌いではない。 今年はNIMEをスキップしていて静かな6月である。 Sketching2013の主催者であるMike Kuniavskyからは、今年の参加者に以下のようなメイルが届いた。 このメイルの末尾には、Googleやゼロックスに次いで、インテルもSketching2013のスポンサーに加わったとの情報もあった。 いよいよあと1ヶ月、参加者は皆んな、世界先端のスリリングな仕事を持って集まってくるので、こちらも負けずにあれこれ用意して臨みたい。
Our digital design tools help us think of things we would never have 
considered, before the tool gave us the ability to think of them (as per 
the ideas of distributed cognition). As these tools move into the cloud, 
these thoughts become increasingly social and the tools create a shared 
understanding of what's being designed, an understanding simultaneously 
shaped by the capabilities of the tools and the knowledge and skills of 
the people using them. Then these shared ideas have to become real, they 
have to be projected from virtual approximations to real designs, and 
everything changes again. The ability to instantiate ideas as actual 
objects still falls short of our tools' ability to imagine them.

Sketching in Hardware this year will be about these interfaces: between 
people and their digital tools, between people and each other, and between 
virtual tools and physical tools. How do we project ideas from one to the other?

BRINGING NEW TECHNOLOGY?
+++++++++++++++++++++++
Are you bringing new things to show off? Please do, and please let me 
know. I'm making space in the schedule for a kind of "Science Fair" style 
that will give everyone an opportunity to get some in-depth experience 
with everyone else's new cool stuff.
If you're considering bringing something to show and aren't sure if it's 
ready, just bring it and show what you have. Having nearly complete 
projects is as exciting as having the finished thing. But do let me know.
さて、先週新しくゲットしたMacBookAir(これは今後、「Air2号機」とでも呼ぶことにしよう)の環境設定もあらかた出来たので、インストールしたVNCやターミナルの環境設定を確認するところからRaspberry Piを再開することにした。 まずは以下のように、新しく仕入れた小型LANハブとLANリールと改造ACアダプタでコンパクトに組み上げた。

そして、Raspberry Piとコンタクトするのが初めてのAir2号機からターミナル経由で通信してみると、以下のようにそれぞれ「初めてのSSH」でも何でもなく開通した。 つまり、以前にあった「MacとのSSH問題」はやはり、Raspberry Piの方でMacとSSHをしていない場合に発生する模様である。 「MacとSSHで繋がった」というような履歴情報がRaspberry Piにいったん書かれてしまえば、初めてのMacからのSSHで、「-X (X11)」という謎のおまじないは不要であるらしい。



Last login: Mon Jun 17 07:23:23 on console
MacBookAir-2:~ nagashima$ ls
Desktop		Downloads	Movies		Pictures	Sites
Documents	Library		Music		Public

MacBookAir-2:~ nagashima$ ssh pi@172.16.65.61
The authenticity of host '172.16.65.61 (172.16.65.61)' can't be established.
RSA key fingerprint is 4c:f8:4d:b3:f1:db:31:80:b6:74:19:94:38:e1:06:4d.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '172.16.65.61' (RSA) to the list of known hosts.
pi@172.16.65.61'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: Fri May 31 16:07:30 2013

pi@raspberrypi ~ $ ls
Desktop  ocr_pi.png  python_games  twilight.png
pi@raspberrypi ~ $ exit
ログアウト
Connection to 172.16.65.61 closed.

MacBookAir-2:~ nagashima$ ssh pi@172.16.65.62
The authenticity of host '172.16.65.62 (172.16.65.62)' can't be established.
RSA key fingerprint is 4c:f8:4d:b3:f1:db:31:80:b6:74:19:94:38:e1:06:4d.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '172.16.65.62' (RSA) to the list of known hosts.
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: Fri May 31 16:42:30 2013

pi@raspberrypi ~ $ ls
Desktop  check.txt  mess.txt  ocr_pi.png  python_games  twilight.png
pi@raspberrypi ~ $ rm check.txt
pi@raspberrypi ~ $ ls
Desktop  mess.txt  ocr_pi.png  python_games  twilight.png
pi@raspberrypi ~ $ rm mess.txt
pi@raspberrypi ~ $ ls
Desktop  ocr_pi.png  python_games  twilight.png
pi@raspberrypi ~ $ exit
ログアウト
Connection to 172.16.65.62 closed.

MacBookAir-2:~ nagashima$ ssh pi@172.16.65.63
The authenticity of host '172.16.65.63 (172.16.65.63)' can't be established.
RSA key fingerprint is 4c:f8:4d:b3:f1:db:31:80:b6:74:19:94:38:e1:06:4d.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '172.16.65.63' (RSA) to the list of known hosts.
pi@172.16.65.63'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: Fri May 31 17:27:53 2013

pi@raspberrypi ~ $ ls
Desktop  ocr_pi.png  python_games  twilight.png
pi@raspberrypi ~ $ exit
ログアウト
Connection to 172.16.65.63 closed.
MacBookAir-2:~ nagashima$
・・・とここまで順調だったが、ここでトラブルが発生した。 以下のように、3台のRaspberry Piのうち、IPアドレス「172.16.65.61」のマシン(これも今後のために「61号機」とか呼ぶことにしよう)の基板上のLEDが他の2台と異なり、どうやら静かに死んでいる模様である(^_^;)。 新しいAir2号機からVNCのアドレス登録とともにVNC接続すると、62号機と63号機はちゃんと応答したものの、61号機はVNCの接続待ちのままストップした。 最初はSSHで接続できたが、この状態だとSSHでも繋がらなくなった。

これはつまり、Raspberry Piに入れていたSDカード、つまりシステムのストレージがdirtyになった、という事だろう。 本来であれば、 ここでやった方法 に従って、SSHをONにして、さらにVNCまでインストールされたSDカードのイメージファイルを作ってあるので、これを再度、SDカードに書き込めばいいのだが、せっかくなので、別の方法をとってみる事にした。 ここでやった方法 に準じるが、以下の作戦である。

イメージファイルをいちいち読み込む手間はかかるが(今回、読み込んだものも別途に保存しておこう)、この方法で、出張など出先でも、Raspberry Piにモニタやキーボードを繋がずにリモートで修復する、という段取りの実験なのである。 そして以下のように、62号機のイメージファイル「ssh_IP_62_fix.img」を作り、これを再度、SDカードに書き込み。起動させてみたところ、なんとVNCサーバに接続した後で、またまた61号機のRaspberry Pi上のLEDが「赤」だけとなった。 つまりこれは、SDカードのデータがdirtyになったのではなくて、この61号機のRaspberry Piのハード故障と判明して、新しいRaspberry Piに交換したところ、無事に3台のシステムが稼働した。 時間はかかったが、これで解決、さらにトラブル対応のいい練習となった(^_^)。

ここで2限になり「音楽情報科学」の講義を経て、午後は5限にアカペラの補習特訓希望の新入生が来るまで、お仕事タイムである(^_^)。 さっそく以下のように、やってみたかった実験を行った。 これまでと同じように見えるが、実はLANハブに挿さっていた、研究室LANのケーブルを抜いているのである。

研究室LANから分離されたという事は、SUACネットのDHCPサーバと接続されていない状態である。 もちろんこのために、Air2号機のIPアドレスも固定で「172.16.65.32」と設定した。 すると上のように、3台のRaspberry Piに対して、ホストのAir2号機からそれぞれVNCで接続できた。 ただし、SSHは無反応で駄目であった。 おそらくSSHはDHCPサーバとやりとりしているのだろうが、とりあえずVNCベースでは、文字通りのローカルネットワークをMac+Raspberry Pi3台で構築できたことになる。 これは少なくとも自分としては、今後に向けて、幸先良い実験結果なのである。

スタンドアロンLANの動作も確認でき、Slice_Pi基板も届いたので、いよいよ、やろうとしていた事に取りかかる環境が整った。 手元にあるRaspberry Pi本も、インブレス本は「Python解説」と「アドオンボード(拡張入出力)」のところだけ残っている。 CQ本は4章から11章まで、やはりハードのアクセスの部分を残して、他は既に捨てた。 ふーみん本(原稿)は98ページから最後(257ページ)までドサッと残っているが、ここがこの本の佳境、ハードを叩いて物理コンピューティング大会である。 ここでは、これら3冊のRaspberry Pi本のポイントと、あと何点か、ぜひやってみたい事が残っているので、備忘録として以下のようにまとめてみた。 これが今後1-2ヶ月、夏までのメインターゲットとなる。

さて、そこでインブレス本のPython解説である。 ここに サンプルブログラムがたくさんある、というので、これをコピペしては試してみよう。 既にMacではやっていたが、あらためてHello Worldプログラム(test.py)をMac上で作り、これをVNC越しにRaspberry Piのターミナルからrcpでゲットして走らせたのが、 以下である。

以下は、「raw_input」というコマンドで入力を受けて表示するだけであるが、このコマンドは入力待ちのところに不正コードを侵入させようとするのを防ぐものであるという。 Raspberry Piはじかにインターネットに繋がってしまうので、このセキュリティは重要だが、「Raspberry Piにハッキングする」というハッカーは少ないだろう。

以下の例は、以前にもやったフィボナッチ数列だが、親と赤ちゃんという名前で記述しているので、あまり「数学」という感じがしないのが好感を持てる。(^_^)

ここで時間となった。 遅々としているのは、同時に裏で学部関係の真面目なメイルが行き来しているからである。 学部の将来に関する重要な議論もまた、大切なのだ。 ここの 続きは、また明日にも進めてみよう。

2013年6月18日(火)

前期火曜日は1限と4-5限に講義があるものの、4-5限の「企画立案演習」は後半戦に入って、各グループがそれぞれ作業を進める段階なので、僕は冒頭のミーティングと最後の進捗確認、そして「チームランナー」のアニメーション撮影のための機材を撮影スタジオにセットすれば、最後の撤収に行くまでほとんど研究室で待機、という良好な期間である。 朝イチから「サウンドデザイン」で学生に紹介する、先輩のMax6アニメーション課題作品集を整理していたが、うーむ、メディア造形学科の学生は年々、優秀になっている事を再確認した。 受験倍率と偏差値が右肩上がりになっているのも納得がいく。

その1限を前にして、まず最初に行ったのは、Raspberry Piの「SSH設定済み」「VNCインストール済み」「固定IPアドレス(172.16.65.62)設定済み」のOSイメージファイルなどの、Raspberry PiのOSドライブとしては不調だった(ただしMacではすこぶる快調な)16GBのSDカード2枚へのバックアップである。 これで、何かのトラブルの際にはMacから新しいSDカードにこれを書き込めば、あとはIPアドレスだけ変更して使用できるので、あくまでRaspberry Piに外付けのモニタとマウスとキーボードは不要となる。(^_^)

そして ここの 続きである。上の例は、「5 lines: Functions」と「6 lines: Import, regular expressions」をそれぞれ「test.py」に書き出して、Raspberry Piからrcpでゲットして走らせた模様の、VNC経由のスクリーンショットである。 まだこの程度であればここのページにPythonソースを置く程のこともないだろう。

・・・と思ったら、なんと この ページには、Pythonサンプルが33個もあるのであった。 あと27個かぁ、こりゃかなりのボリュームである。 とりあえずここで1限の時間が近付いてきたので、マルチメディア室に移動、続きは2限からやっていこう。 Raspberry PiとVNCで繋いだまま放置していくが、90分のインターバルがあるとRaspberry Piはスリープする筈なので、そこも帰ってきたら、見てみよう。

・・・そして2限になったが、Raspberry Piはちゃんと生きていた(^_^;)。 どうやら、スクリーンセーバーもOFFにする設定にしていたようである。 こうなれば、もう この ページのサンプルをいちいちRaspberry Piに送って走らせて確認するだけである。 以下は「7 lines: Dictionaries, generator expressions」と「8 lines: Command line arguments, exception handling」をそれぞれ「test.py」に書き出して、Raspberry Piからrcpでゲットして走らせた模様の、VNC経由のスクリーンショットである。 プログラムの引き数として数値を渡すこの方法は、C言語のargcとargvと同じで、使えそうである。

次の「9 lines: Opening files」はファイルのアクセスのようであるが、「f:」というのは邪悪なDOS系パソコンのようだったのでパスした(^_^;)。 以下は「10 lines: Time, conditionals, from..import, for..else」を「test.py」に書き出して、Raspberry Piからrcpでゲットして走らせた模様の、VNC経由のスクリーンショットである。 ネットで調べて、Linuxでの日付時刻の設定「sudo date --set="2013/16/18 11:00"」というのもついでに覚えて、時間帯によって変化する応答も確認できた。(^_^)

以下は「11 lines: Triple-quoted strings, while loop」を「test.py」に書き出して、Raspberry Piからrcpでゲットして走らせた模様の、VNC経由のスクリーンショットである。 最初は「bottles_of_beer = 99」ということで膨大な行が表示されてスクロールしたので(^_^;)、nanoで「bottles_of_beer = 5」と変更している。

以下は「12 lines: Classes」と「13 lines: Unit testing with unittest」をそれぞれを「test.py」に書き出して、Raspberry Piからrcpでゲットして走らせた模様の、VNC経由のスクリーンショットである。 前者はdef定義のサンプル、後者はクイックソートの速度測定である。

次の14から17はパス、18の「8クイーン問題」もパス、20もパスした。 そして次の「21 lines: XML/HTML parsing (using Python 2.5 or third-party library)」はちょっと気になったのでやってみたが、以下のようにあまりパッとしなかった(^_^;)。 ・・・そしてここまで来て気付いたが、このPythonサンプルの最後は「33番」だったものの、全ての番号が揃っているわけではなくて、後は28番と33番のゲームだけ、ということで、これでオシマイとなった。

さて、そこでいよいよ これ である。 ザッと見たところでは、インプレス本で残っていた部分とほぼかぶっている模様だが、せっかくなので こっち を追いかけることにした。 このチュートリアルは「GPIO」について、という事である。 とりあえず確認の意味で「2 GPIO Basic's」をあらためて整理すると、以下のようになる。

まぁ、お約束の脅かしはこのへんにしておいて(^_^;)、いよいよライブラリのインストールである。 どうもapt-getではないようなので、以下に書かれている情報から、tar.gzをMacでダウンロードして・・・というのを試してみよう。
3 Installing RPi.GPIO

RPi.GPIO is a small python library that take some of the complexity out of driving 
the GPIO pins, once install a single LED can be lit with 3 lines of python. Installing 
the library is almost as simple, either at a text console or using LXTerminal enter 
the following

$ wget http://pypi.python.org/packages/source/R/RPi.GPIO/RPi.GPIO-0.1.0.tar.gz
$ tar zxf RPi.GPIO-0.1.0.tar.gz
$ cd RPi.GPIO-0.1.0
$ sudo python setup.py install
そこで、Macでダウンロードした「RPi.GPIO-0.1.0.tar.gz」をRaspberry Piの61号機にrcpして、「tar zxf RPi.GPIO-0.1.0.tar.gz」で解凍して、「cd RPi.GPIO-0.1.0」で移動して、「sudo python setup.py install」してみると、以下のような状態で止まった。 これはかつてふーみんに聞いた、SUACのプロキシサーバの情報を「export http_proxy="http://・・・」と入れても駄目だった(^_^;)。
pi@raspberrypi ~ $ ls
Desktop  ocr_pi.png  python_games  test.py  twilight.png
pi@raspberrypi ~ $ rcp nagasm@172.16.65.31:Desktop/RPi.GPIO-0.1.0.tar.gz .
Password:
RPi.GPIO-0.1.0.tar.gz                                                  100% 7931     7.8KB/s   00:00    
pi@raspberrypi ~ $ ls
Desktop  RPi.GPIO-0.1.0.tar.gz  ocr_pi.png  python_games  test.py  twilight.png
pi@raspberrypi ~ $ tar zxf RPi.GPIO-0.1.0.tar.gz
pi@raspberrypi ~ $ cd RPi.GPIO-0.1.0
pi@raspberrypi ~/RPi.GPIO-0.1.0 $ sudo python setup.py install
Downloading http://pypi.python.org/packages/source/d/distribute/distribute-0.6.21.tar.gz
^Z
[1]+  停止                  sudo python setup.py install
pi@raspberrypi ~/RPi.GPIO-0.1.0 $
こうなれば、久しぶりにモバイルWiFiルータの出番である。 直接にネットに繋いで、最新の「distribute-0.6.21.tar.gz」を取りに行かないといけないのだ。 さっそく61号機と62号機をシャットダウンして、 ここでやった方法 を再現して、以下のように63号機の固定IPをDHCPに変更してシャットダウンして、LANハブを研究室の学内LANからモバイルWiFiルータに繋ぎ変えた。

そして久しぶりに、以下のチャチいキーボードと見にくい液晶モニタの出番である(^_^;)。 何度もミスタイプを叱られながら、過去の失敗の経験から何度も「sudo apt-get update」をかましつつ、「GPIO」のゲットとインストールだけでなく、ふーみんに教わった「sudo apt-get install puredata」と「sudo apt-get install python-txosc」と「sudo apt-get install pd-osc」をエラー無しに完了させ、さらにThingmのBlibk(1)もついでにダウンロードしてみた。 経緯は こんな感じ である。

ここで重要なのは、ここまで63号機で進めてきて、まず一旦sudoでDHCPを固定IPに戻してシャットダウンして、研究室LANに戻してホストのMacとVNC出来たことを確認してまたシャットダウンして、そのSDカードのイメージデータを吸い上げた事である。 残りの61号機・62号機でまたいちいちWiFi経由で環境設定するのはナンなので、まずはこの段階を保持しておく、という事である。 ここまでで3限の時間が終わり、いったん「企画立案演習」に向かうこととなった。

そして15時を過ぎて、研究室に戻る時間ができた。 スタジオでのアニメーション撮影グループの作業もスタートし、研究室では別のグループが素材を発注するカタログをめくっている。 これ の「3 Installing RPi.GPIO」が終わったつもりなので、次はいよいよ「4 LED and Pushbutton Slice」である。 ここであらためてGPIOポートのコネクタを確認すると、以下のようになっている。 このうちI2CとSPIは使うつもりはないので、使えるのはGPIO0からGPIO7までの8ビットの汎用入出力と、UARTの2本だけである。

(この図は後で改訂されている事に注意)

そして、せっかく連結した3枚のRaspberry Piをバラすのもナンなので(^_^;)、また新しいRaspberry Piを出してきて63号機のSDカードを挿し、Slice基板を付けるほどの事もないので直接、コネクタに挿したピンヘッダ上に空中配線で8ビットのLEDを付けて、とりあえず+3.3Vから470Ωで点灯させてみたのが以下である。 まずは「sudo Python」でのコマンドであるが、ポートに出力設定すると既にlowレベル出力で点灯し、Falseをちゃんと出力して点灯できた。 まぁディジタル入力は未検証であるが、これで ここまで はオシマイである(^_^)。

Pythonのコマンドラインから点灯させるというのもナンなので、以下のようなPythonプログラムを作ってRaspberry Piに送って、「sudo python test.py」ということで実行させると、順にLEDが点灯した。 ただし何故か、3ビット目、13ピンのLEDが点灯しなかったのが謎だが(^_^;)、まぁこれでPythonプログラムでハードを叩く、という流れは確認できた。 謎の13ピンは置いておくとして(^_^;)、今日はまずまず進展した。 ここまでで、インプレスのRaspberry Pi本も全て終わったことになる。

import RPi.GPIO as GPIO
import time
GPIO.setup(11, GPIO.OUT)
GPIO.setup(12, GPIO.OUT)
GPIO.setup(13, GPIO.OUT)
GPIO.setup(15, GPIO.OUT)
GPIO.setup(16, GPIO.OUT)
GPIO.setup(18, GPIO.OUT)
GPIO.setup(22, GPIO.OUT)
GPIO.setup(7, GPIO.OUT)
GPIO.output(11,True)
GPIO.output(12,True)
GPIO.output(13,True)
GPIO.output(15,True)
GPIO.output(16,True)
GPIO.output(18,True)
GPIO.output(22,True)
GPIO.output(7,True)
while True:
    GPIO.output(11,False)
    GPIO.output(7,True)
    time.sleep(1)
    GPIO.output(12,False)
    GPIO.output(11,True)
    time.sleep(1)
    GPIO.output(13,False)
    GPIO.output(12,True)
    time.sleep(1)
    GPIO.output(15,False)
    GPIO.output(13,True)
    time.sleep(1)
    GPIO.output(16,False)
    GPIO.output(15,True)
    time.sleep(1)
    GPIO.output(18,False)
    GPIO.output(16,True)
    time.sleep(1)
    GPIO.output(22,False)
    GPIO.output(18,True)
    time.sleep(1)
    GPIO.output(7,False)
    GPIO.output(22,True)
    time.sleep(1)

YouTube

2013年6月19日(水)

水曜日、講義のない日である。 3限に及川さんのアポ(たぶん映像作品の音楽の相談)、その後に外出の予定があり、放課後にはアカペラなので、午前中が稼ぎ時である。 昨日、モバイルWiFiルータを使ってapt-getして、その1-dayの利用時間がまだあるので、ちょっと先走って、 Blink(1) の関係にトライしてみる事にした。 たぶんうまく行かないので、ここでまた、質問メイルをふーみんに送る、という計画である(^_^;)。

まず、Blink(1)の公式サイトは ここ である。 この右下あたりに「BLINK(1) TOOL : OSX - WINDOWS - LINUX_x86 - LINUX_x64 - RASPBERRY PI - GURUPLUG」というリンクがあり、Raspberry Pi版のBlink(1)ツールがあるのでダウンロードすると これ であり、解凍してRaspberry Piに送っても、実行させると以下のように「no blink(1) devices found」と表示されて、デバイスが認識されない。 おそらくこれは、defaultではRaspberry PiにUSB HIDの関係が全てはインストールされていないのだろう。

pi@raspberrypi ~/Desktop $ ./blink1-tool
Usage: 
  blink1-tool <cmd> [options]
where <cmd> is one of:
  --hidread                   Read a blink(1) USB HID GetFeature report 
  --hidwrite <listofbytes>    Write a blink(1) USB HID SetFeature report 
  --eeread <addr>             Read an EEPROM byte from blink(1)
  --eewrite <addr>,<val>      Write an EEPROM byte to blink(1) 
  --blink <numtimes>          Blink on/off 
  --random <numtimes>         Flash a number of random colors 
  --rgb <red>,<green>,<blue>  Fade to RGB value
  --savergb <r>,<g>,<b>,<pos> Write pattern RGB value at pos
  --readrgb <pos>             Read pattern RGB value at pos
  --servertickle <on/off>     Turn on/off servertickle (uses -t msec) 
  --on                        Turn blink(1) full-on white 
  --off                       Turn blink(1) off 
  --list                      List connected blink(1) devices 
  --version                   Display blink(1) firmware version 
and [options] are: 
  -g -nogamma                 Disable autogamma correction
  -d dNums --id all|deviceIds Use these blink(1) ids (from --list) 
//--serial <num>              Connect to blink(1) by its serial number 
  -m ms,   --miilis=millis    Set millisecs for color fading (default 300)
  -t ms,   --delay=millis     Set millisecs between events (default 500)
  --vid=vid --pid=pid         Specifcy alternate USB VID & PID
  -v, --verbose               verbose debugging msgs

Examples 
  blink1-tool -m 100 --rgb 255,0,255   # fade to #FF00FF in 0.1 seconds 
  blink1-tool -t 2000 --random 100     # every 2 seconds new random color

pi@raspberrypi ~/Desktop $ ./blink1-tool -m 100 --rgb 255,0,255
no blink(1) devices found

pi@raspberrypi ~/Desktop $
そして、たぶん これ をゲットした頃にどこかでゲットしてきたらしい(^_^;)、「blink1hid-demo.py」と「blink1-ctypes.py」の2本がずっとMacのデスクトップに置かれていたのだが、「blink1hid-demo.py」の内容は以下である。
#!/usr/bin/python
"""
blink1hid-demo.py -- Low-level HID access with Python
Thanks to Aaron Blondeau for this

As he says:
"I am working on controlling a Blink1 from an Android.
In order to understand how the set feature calls work without wearing out
the usb port on my Android I have created a python script that uses the pyusb library.
The code is as follows if you wish to share and save some time for other folks."

"""

import usb # https://github.com/walac/pyusb
          # + "brew install libusb" on osx
          # + libusb-win32 (inf method) on windows

#Find the Blink1
dev = usb.core.find(idVendor=0x27b8, idProduct=0x01ed)
assert dev is not None

#The Blink1 takes 8 bytes of input
# 1=report_id (0)
# 2=action (c = fade to rgb, n = set rgb now)
# 3=red
# 4=green
# 5=blue
# 6=th : time/cs high (T >>8) where time 'T' is a number of 10msec ticks
# 7=tl : time/cs low (T & 0xff)
# 8=step (0)

# once a buffer is set with these bytes, we need to do what blink1_write / hid_send_feature_report does
# https://github.com/todbot/blink1/blob/master/commandline/blink1-lib.c
# https://github.com/signal11/hidapi/blob/master/libusb/hid.c

#set color to red
bmRequestTypeOut = usb.util.build_request_type(usb.util.CTRL_OUT, usb.util.CTRL_TYPE_CLASS, usb.util.CTRL_RECIPIENT_INTERFACE)
action = 0x6E # ='n' (set rgb now)
red = 0xFF
green = 0x00
blue = 0x00
dev.ctrl_transfer(bmRequestTypeOut, 0x09, (3 << 8) | 0x01, 0, [0x00, action, red, green, blue, 0x00, 0x00, 0x00, 0x00])

#then fade to blue
action = 0x63 # ='c' (fade to rgb)
red = 0x00
green = 0x00
blue = 0xFF
T = 5000/10 #5 seconds worth of 10msec tics
th = (T & 0xff00) >> 8
tl = T & 0x00ff
dev.ctrl_transfer(bmRequestTypeOut, 0x09, (3 << 8) | 0x01, 0, [0x00, action, red, green, blue, th, tl, 0x00, 0x00])

#get version number
import time
import string
action = 0x76 # ='v' (version)
dev.ctrl_transfer(bmRequestTypeOut, 0x09, (3 << 8) | 0x01, 0, [0x00, action, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
time.sleep(.05)
bmRequestTypeIn = usb.util.build_request_type(usb.util.CTRL_IN, usb.util.CTRL_TYPE_CLASS, usb.util.CTRL_RECIPIENT_INTERFACE)
version_raw = dev.ctrl_transfer(bmRequestTypeIn, 0x01, (3 << 8) | 0x01, 0, 8)
version = ''.join(chr(i) for i in version_raw) # items in the array should correspond to ascii codes for something like "v 100"
version = filter(lambda x: x in string.printable, version)
print version #the c code must tack on an extra 0?
この冒頭に書かれている pyusb というのも気になる、 おそらく「PythonでUSB」というライブラリだろう。 そして「blink1-ctypes.py」の内容は以下である。
#/usr/bin/python
"""
blink1.py -- Python interface to libblink1 through the magic of ctypes

import blink1
dev = blink1.open()
blink1.setRGB(d, 255,0,0) # Red
...

Do not:
from blink1 import *
or you will polute your namespace with some very common names (i.e. File I/O)

Make sure the "blink1-lib" shared library / DLL is in your path
for more info see:
https://getsatisfaction.com/thingm/topics/more_comprehensive_python_support

Thanks to Stephen Youndt for this
"""

from ctypes import *
from ctypes.util import find_library
import inspect, os
import glob
# Find the library
localpath = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
libname = find_library("blink1")
if libname is None:
    libname = find_library("Blink1")
if libname is None:
    libname = find_library("blink1-lib")
if libname is None:
    libname = glob.glob(os.path.join(localpath, '[Bb]link1-lib.so'))[-1]
if libname is None:
    libname = glob.glob(os.path.join(localpath, 'blink1-lib.dll'))[-1]
if libname is None:
    libname = glob.glob(os.path.join(localpath, 'lib[Bb]link1*'))[-1]
# If we found the library, load it
assert libname is not None
libblink1 = CDLL(libname)
vid = libblink1.blink1_vid
vid.restype = c_int
pid = libblink1.blink1_pid
pid.restype = c_int
sortPaths = libblink1.blink1_sortPaths
sortSerials = libblink1.blink1_sortSerials
enumerate = libblink1.blink1_enumerate
enumerate.restype = c_int
enumerateByVidPid = libblink1.blink1_enumerateByVidPid
enumerateByVidPid.restype = c_int
enumerateByVidPid.argtypes = [c_int, c_int]
getCachedPath = libblink1.blink1_getCachedPath
getCachedPath.restype = c_char_p
getCachedPath.argtypes = [c_int]
getCachedSerial = libblink1.blink1_getCachedSerial
getCachedSerial.restype = c_wchar_p
getCachedSerial.argtypes = [c_int]
getCachedCount = libblink1.blink1_getCachedCount
getCachedCount.restype = c_int
open = libblink1.blink1_open
open.restype = c_void_p
openByPath = libblink1.blink1_openByPath
openByPath.restype = c_void_p
openByPath.argtypes = [c_char_p]
openBySerial = libblink1.blink1_openBySerial
openBySerial.restype = c_void_p
openBySerial.argtypes = [c_wchar_p]
openById = libblink1.blink1_openById
openById.restype = c_void_p
openById.argtypes = [c_int]
close = libblink1.blink1_close
close.argtypes = [c_void_p]
write = libblink1.blink1_write
write.restype = c_int
write.argtypes = [c_void_p, c_void_p, c_int]
read = libblink1.blink1_read
read.restype = c_int
read.argtypes = [c_void_p, c_void_p, c_int]
getSerialNumber = libblink1.blink1_getSerialNumber
getSerialNumber.restype = c_int
getSerialNumber.argtypes = [c_void_p, c_char_p]
getVersion = libblink1.blink1_getVersion
getVersion.restype = c_int
getVersion.argtypes = [c_void_p]
fadeToRGB = libblink1.blink1_fadeToRGB
fadeToRGB.restype = c_int
fadeToRGB.argtypes = [c_void_p, c_ushort, c_ubyte, c_ubyte, c_ubyte]
setRGB = libblink1.blink1_setRGB
setRGB.restype = c_int
setRGB.argtypes = [c_void_p, c_ubyte, c_ubyte, c_ubyte]
eeread = libblink1.blink1_eeread
eeread.restype = c_int
eeread.argtypes = [c_void_p, c_ushort, c_void_p]
eewrite = libblink1.blink1_eewrite
eewrite.restype = c_int
eewrite.argtypes = [c_void_p, c_ushort, c_void_p]
serialnumread = libblink1.blink1_serialnumread
serialnumread.restype = c_int
serialnumread.argtypes = [c_void_p, c_void_p]
serialnumwrite = libblink1.blink1_serialnumwrite
serialnumwrite.restype = c_int
serialnumwrite.argtypes = [c_void_p, c_void_p]
serverdown = libblink1.blink1_serverdown
serverdown.restype = c_int
serverdown.argtypes = [c_void_p, c_ubyte, c_ushort]
play = libblink1.blink1_play
play.restype = c_int
play.argtypes = [c_void_p, c_ubyte, c_ubyte]
writePatternLine = libblink1.blink1_writePatternLine
writePatternLine.restype = c_int
writePatternLine.argtypes = [c_void_p, c_ushort, c_ubyte, c_ubyte, c_ubyte, c_ubyte]
readPatternLine = libblink1.blink1_readPatternLine
readPatternLine.restype = c_int
readPatternLine.argtypes = [c_void_p, c_void_p, c_void_p, c_void_p, c_void_p, c_void_p]
error_msg = libblink1.blink1_error_msg
error_msg.restype = c_char_p
error_msg.argtypes = [c_int]
enableDegamma = libblink1.blink1_enableDegamma
disableDegamma = libblink1.blink1_disableDegamma
degamma = libblink1.blink1_degamma
degamma.restype = c_int
degamma.argtypes = [c_int]
sleep = libblink1.blink1_sleep
sleep.argtypes = [c_ushort]
この2本のPythonブログラムをRaspberry Piに送って実行すると、以下のようなエラーが出た。 これはまぁ、詳細不明である。(^_^;)

pi@raspberrypi ~ $ ls
Desktop         blink1-ctypes.py  blink1-tool-raspi.zip  ocr_pi.png    pyusb-1.0.0a3      test.py
RPi.GPIO-0.1.0  blink1-tool       blink1hid-demo.py      python_games  pyusb-1.0.0a3.zip  twilight.png

pi@raspberrypi ~ $ sudo python blink1hid-demo.py
Traceback (most recent call last):
  File "blink1hid-demo.py", line 42, in 
    dev.ctrl_transfer(bmRequestTypeOut, 0x09, (3 << 8) | 0x01, 0, [0x00, action, red, green, blue, 0x00, 0x00, 0x00, 0x00])
  File "/usr/local/lib/python2.7/dist-packages/usb/core.py", line 702, in ctrl_transfer
    self.__get_timeout(timeout)
  File "/usr/local/lib/python2.7/dist-packages/usb/backend/libusb1.py", line 596, in ctrl_transfer
    timeout))
  File "/usr/local/lib/python2.7/dist-packages/usb/backend/libusb1.py", line 403, in _check
    raise USBError(_str_error[ret], ret, _libusb_errno[ret])
usb.core.USBError: [Errno 5] Input/output error

pi@raspberrypi ~ $ sudo python blink1-ctypes.py
Traceback (most recent call last):
  File "blink1-ctypes.py", line 33, in 
    libname = glob.glob(os.path.join(localpath, '[Bb]link1-lib.so'))[-1]
IndexError: list index out of range

pi@raspberrypi ~ $ 
また、関連して検索すると このような ところも出て来た。 これも関係するかもしれない。 そして、 このページ には、「Installing PyUSB on GNU/Linux Systems」という、以下の説明があった。
$ sudo apt-get install python libusb

$ sudo python setup.py install
これは見慣れたやつである(^_^)。 そこで、またまたRaspberry Pi63号機を固定IPからDHCPにして、「sudo apt-get install python libusb」をしてみたが、「libusb」というライブラリは無いぞ、と叱られた(^_^;)。 ただし、 このページ に行って、「PyUSB is available for download in the SourceForge project page. 」とあったので このページ に行き、 これ をゲットできた。 この内容は以下のような構成であり、チュートリアルは これ である。 なんか、Pythonプログラムのオンパレードである。

ここにある「readme」に従って、「setup.python」を実行すると、以下のようにエラーが出なかったが、やはりそれでも「blink1hid-demo.py」を走らせようとするとエラーが出た。(^_^;)

チュートリアル を読む感じでは、やはり、ここはC言語でRaspberry Piを叩くという方向に行く必要がありそうである。 ここでフト、WiFiのサイトに行って、ここまでの利用の記録を見てみた。 やはり、パケット数は相当なもののようで、とうてい「従量制」では使い物にならない模様である。(^_^;)

さて、ふーみん本のGPIOの解説を読み進めていくと、いきなり、昨日のページに書いていた、Raspberry PiのGPIOポートのピンについて、新しい情報が続々と出て来た。 そういえば空きピンについて何も知らなかったが、ちゃんと電源とかGNDとか、あったのである。 そして、ふーみん本の情報から改訂した、Raspberry PiのGPIOポートのピン配置図が以下である。 今後はこちらを使っていこう。(^_^)

(この図は後で改訂されている事に注意)

ここから、ハンダごてとかブレッドボードとか電源とかLEDとかの話があって、一気に14ページほどすっとばした(^_^;)。 そしていよいよ、昨日LEDを付けたRaspberry Piに対して、まずはコマンドラインから叩く、という話となった。 GPIOはroot権限でないと叩けないので、ここではまず以下のように、「sudo」でなく、「sudo -i」として、rootになる事から始まる。

そして「echo "17" > ・・・」というところでハテと困った。 上のGPIOポートの17ピンは+3.3Vなのである(^_^;)。 そこでCQ本と照合した結果、なんとRaspberry PiのGPIOポートのピンと、ソフト側から叩く場合のGPIOポートの番号とは別物である、という驚愕の事実が発覚した。 つまりは、関係するところだけ挙げれば、以下のような対応なのだという。

ピン番号     信号名     ソフト上の呼び名
11GPIO 0GPIO17
12GPIO 1GPIO18
13GPIO 2GPIO21
15GPIO 3GPIO22
16GPIO 4GPIO23
18GPIO 5GPIO24
22GPIO 6GPIO25
7GPIO 7GPIO4
8TxGPIO14
10RxGPIO15
(この表は後で改訂されている事に注意)

これは、もの凄く、嫌である(^_^;)。 しかし仕方ないのでこれを何度も参照しよう。 すると、ここでCQ本で新たな発見があった。 なんと、サンプルとして、I2CとかSPIのピンを、スイッチ入力とかLED出力に使っているではないか。 そこで以下のように実験して、使わずに捨てるつもりだった計7ビットについても、ちゃんと出力ポートとして拡張できる事を確認した。 これは、「GPIO 0」から「GPIO 7」までの8ビットをデータバスとして使い、ラッチするためのアドレスやライトパルスに使えるので、たった8ビットという制約とは雲泥の違いとなる。 Pythonではアクセス出来ないとすれば使わないだけであり、これは相当に有益な発見なのだ(^_^)。 そこで、I2CとSPIの計7ビットについても追加して、「EXT 0」から「EXT 6」までのオリジナルの信号名を添えて、対応表を以下のように改訂した。

ピン番号     信号名     ソフト上の呼び名
11GPIO 0GPIO17
12GPIO 1GPIO18
13GPIO 2GPIO21
15GPIO 3GPIO22
16GPIO 4GPIO23
18GPIO 5GPIO24
22GPIO 6GPIO25
7GPIO 7GPIO4
8TxGPIO14
10RxGPIO15
3EXT 0GPIO0
5EXT 1GPIO1
19EXT 2GPIO10
21EXT 3GPIO9
23EXT 4GPIO11
24EXT 5GPIO8
26EXT 6GPIO7
(この表は後で改訂されている事に注意)

そしてrootになって、以下の手順でのポートの信号出力を確認した。 これは出力についてだが、とりあえずの拡張用途としては出力が多い(たくさんのプロセスが裏で動くUnixで入力のモニタをしたくない(^_^;))との想定である。 ちなみにCQ本の記述の中に、以下のどこかが抜けているバグも発見した。(^_^;)

そして、これらをいちいち手で打つのも嫌なので、Mac側のエディタで以下のような内容を作成して、テキストファイル化してrcpでRaspberry Piに送り、それをsudoでシェルスクリプトとして実行できる方法(「chmod -x shell.txt」とやると「shell.txt」がシェルスクリプトとして実行できる)をCQ本の記述から発見して、やってみた。 すると、SPIの5ビットは出力できたものの、I2Cの2ビットは沈黙を保ったままだった。 どうやら3ピンは内部プルアップしている事からも、入力専用のようである。

echo "0" > /sys/class/gpio/export
echo "1" > /sys/class/gpio/export
echo "10" > /sys/class/gpio/export
echo "9" > /sys/class/gpio/export
echo "11" > /sys/class/gpio/export
echo "8" > /sys/class/gpio/export
echo "7" > /sys/class/gpio/export
echo "out" > /sys/class/gpio/gpio0/direction
echo "out" > /sys/class/gpio/gpio1/direction
echo "out" > /sys/class/gpio/gpio10/direction
echo "out" > /sys/class/gpio/gpio9/direction
echo "out" > /sys/class/gpio/gpio11/direction
echo "out" > /sys/class/gpio/gpio8/direction
echo "out" > /sys/class/gpio/gpio7/direction
echo "1" > /sys/class/gpio/gpio0/value
echo "1" > /sys/class/gpio/gpio1/value
echo "1" > /sys/class/gpio/gpio10/value
echo "1" > /sys/class/gpio/gpio9/value
echo "1" > /sys/class/gpio/gpio11/value
echo "1" > /sys/class/gpio/gpio8/value
echo "1" > /sys/class/gpio/gpio7/value
sleep 1
echo "0" > /sys/class/gpio/gpio0/value
sleep 1
echo "1" > /sys/class/gpio/gpio0/value
echo "0" > /sys/class/gpio/gpio1/value
sleep 1
echo "1" > /sys/class/gpio/gpio1/value
echo "0" > /sys/class/gpio/gpio10/value
sleep 1
echo "1" > /sys/class/gpio/gpio10/value
echo "0" > /sys/class/gpio/gpio9/value
sleep 1
echo "1" > /sys/class/gpio/gpio9/value
echo "0" > /sys/class/gpio/gpio11/value
sleep 1
echo "1" > /sys/class/gpio/gpio11/value
echo "0" > /sys/class/gpio/gpio8/value
sleep 1
echo "1" > /sys/class/gpio/gpio8/value
echo "0" > /sys/class/gpio/gpio7/value
sleep 1
echo "1" > /sys/class/gpio/gpio7/value
echo "0" > /sys/class/gpio/unexport
echo "1" > /sys/class/gpio/unexport
echo "10" > /sys/class/gpio/unexport
echo "9" > /sys/class/gpio/unexport
echo "11" > /sys/class/gpio/unexport
echo "8" > /sys/class/gpio/unexport
echo "7" > /sys/class/gpio/unexport
まぁ、5ビットの拡張出力ビットがあれば、8ビット幅のラッチを16個まで増設できるので、総ビットは128ビットの出力ポート個別制御まで、簡単な外部回路の増設だけで出来るので、まずまず十分だろう。 ただしLinuxという重いシステムのRaspberry Piで、チラつかない十分なスピードで128個を個別にPWM制御できるかどうかは、実験してみないと判らない。

YouTube

ここで、汎用入出力の8ビットの他の7ビットの名前を以下のように変更して、当面はこの表で行くことにした。 I2Cの2ビットは入力の拡張用として、SPIの5ビットを出力の拡張に利用する作戦である。

ピン番号     信号名     ソフト上の呼び名
11GPIO 0GPIO17
12GPIO 1GPIO18
13GPIO 2GPIO21
15GPIO 3GPIO22
16GPIO 4GPIO23
18GPIO 5GPIO24
22GPIO 6GPIO25
7GPIO 7GPIO4
8TxGPIO14
10RxGPIO15
3Ex_In 0GPIO0
5Ex_In 1GPIO1
19Ex_Out 0GPIO10
21Ex_Out 1GPIO9
23Ex_Out 2GPIO11
24Ex_Out 3GPIO8
26Ex_Out 4GPIO7
(この表は後で改訂されている事に注意)

そして、午前中にふーみんに出していたメイルに、昼過ぎに以下のように返信があった。 さっそくBlink(1)と、せっかくなのでSlice基板もプレゼントとして発送手配した。 しかしやはり、このビン番号の対応は大混乱の模様である。(^_^;)

ちらっと読みましたが,現物を提供していただけるのであれば
手元でやってみた方が早そうです^^;

ちなみにピン番号の対応のごちゃごちゃさは,私の本の中でも
かなり強調して書いたつもりです^^;

・今後,RPiのRevisionが変わるとまた変わる可能性があると明言されている
・ボードの番号,ハード仕様の番号,OSレベルの番号,Wiringの番号,が,
  すべて規則性なくバラバラ

という素敵な状態ですが,magic numberを書かず,定数定義して
シンボリックに使いなさい,ということなのでしょう.

Systematicにやるなら,各言語ごとに BOARD_3V3_1,
OS_3V3_1,WIRING_3V3_1,などのようにマイヘッダを
一度定義してしまえば,あとはそれを使う,という感じでもやれるとは
思いますが,あ,リビジョンによっても違うからそれも定義名に入れないと
だめか・・・orz
ここで昼休みが終わり、アポのあった及川さんと、総合演習IIの映像作品の音楽について相談した。 今月中、という新たな目標が出来たが、来月末の完成が楽しみである(^_^)。 さて、ふーみん本の続きでは、「温度センサのデータをI2Cで取得する」というトピックである。 しかし僕はI2Cは使うつもりが無いので、ここはサクサクとスキップである。 ただし、defaultではライブラリもインストールされておらず、環境としても登録されていないI2Cをどうやって使うか、というのは、他のケースでも(もしかするとBlink(1)でも)関係するので、以下のようにメモとして整理しておいた。 ここからしばらくは、取得した温度データの16進数の話、Perlでのデータ変換、全体をシェルスクリプトでまとめる、というお話で軽くスルーできて、(校正原稿の)136ページまでジャンプした。 この次の「定期的な実行」というのは、Raspberry PiというよりもUnixの機能だが、チラッと見てみると「cron」を使っていて「分」単位の起動とあり、さらにWebサーバ(apache)を起動して外部からセンサデータを見る・・・という方向の話だったので、ここも今回はバスする事にした。

その次のトピックは「大気圧センサのデータを SPIで取得する」というもので、データの変換にはPythonを使っているが、なんせSPIのポートをハードウェア拡張に使用する、という方針のため(^_^;)、ここもパスとなった。 その次のトピックは(校正原稿の)162ページからで、「WebカメラをUSBに繋いでストリーミングと動体検知」というものである。 これは面白そうなので、明日以降にチェックしてみることにした。

2013年6月20日(木)

今週は月曜日から木曜日まで、朝6時半には研究室に出て来る早起きが続いている。 この日は1限に「しゃみーず」のアポ、昼休みに大学院教授会、3限にはゼミ(M1+3回生)の「電子回路講座」、4限に学生委員会、とコマ切れの予定が続く。 サッカーのコンフェデレーションズカップの初戦でブラジルにボロ負けした日本だが、朝7時からイタリア戦のネット中継ということで、サッカーをチラ見しながら1限「しゃみーず」を待つ、という事になったが、前半には本田のPK、香川のボレーをライヴで堪能できた(^_^)。

ハーフタイムに入った7:50あたりから15分間がお仕事タイムである(^_^;)。 まず、気付いてみれば これ をやったのに、続く これ がまだだったので、改めて眺めてみた。 すると、Raspberry Piでのビット出力の続きとして「while 1:」と「sleep(1)」で点滅の無限ループ、ビットのモニタ(入力)、入力に応じて出力、「sudo apt-get install xterm」でxtermを入れる話、とこれまで既になぞった内容ばかりであると判明した。 最後にあったPythonスクリプトのサンプルは以下であるが、これはもう終えていたものだった。

#!/usr/bin/env python
from time import sleep
# get the GPIO Library
import RPi.GPIO as GPIO
 
# setup some names references to the LED's and buttons
# red     = pin 18
# yellow  = pin 16
# green   = pin 15
# blue    = pin 13
 
# using an list to hold the led numbers
leds = [13, 15, 16, 18]
# the input buttons
up = 12
down = 11
 
# setup the pins as output and input as needed
# looping over the list 
for n in leds:
    GPIO.setup(n, GPIO.OUT)
 
GPIO.setup(up, GPIO.IN)
GPIO.setup(down, GPIO.IN)
# turn off all but the blue LED
GPIO.output(leds[3], True)
GPIO.output(leds[2], True)
GPIO.output(leds[1], True)
GPIO.output(leds[0], False)
 
#state trackers
level = 0
oldlevel = level
 
while 1:
    if GPIO.input(up):
        #up button pressed
        if level < 3:
            level += 1
    elif GPIO.input(down):
        #down button pressed
        if level > 0:
            level -= 1
    if oldlevel != level:
        # turn off last led and turn on next one
        GPIO.output(leds[oldlevel], True)
        GPIO.output(leds[level], False)
        # update state tracker
        oldlevel = level
        # sleep for a bit so button press is counted only once
        sleep(1)
上のサンプルでは、CQ本の例と違って、ちゃんとGPIOピンとして定義されたピンを使っている。 やはりPythonではこれだけが汎用入出力として使えるのだろう。 そしてサッカーの後半開始直後にイタリアに逆転されたところで諦めて(^_^;)、ふーみん本の残りについてパラパラと眺めてみると、なんとこれからのRaspberry Pi攻略について、おぼろげに「見えて」きたので、ここで改訂版の新たな「To Doメモ」として整理した。 サッカーは後半25分に岡崎のヘディングで同点となったが、厳然たる実力差があり、結局は力尽きた(^_^;)。 そして1限には「しゃみーず」が来て、システムとしての改造三味線はほぼ完成した(^_^)。 ここにFLASHでどんなゲームが繋がるか、楽しみである。 きちんと記録ビデオを作り、オープンキャンパスでも展示して高校生に体験してもらおう、という話になった。 2限になると、サッカーの合間に業者にメイル発注していた これこれこれこれこれこれ について、業者から「手配OK」のメイルが来た。 このスピード感は大事である。来週あたりには入手して実験に入れるだろう。

3限はゼミの「電子回路講座」でACアダプタの解説や時定数回路の実験などを行い、4限は学生委員会でつぶれた。 5限には、アカペラで夏休みに企画する事になった「合宿」の行き先について調べていたら終わってしまった(^_^;)。 まぁ、細切れの日はこんなもんである。 Raspberry Piに電源を入れる事もなかったが、状況の整理については進展したので、また明日以降にやっていこう。

2013年6月21日(金)

金曜日は2限にゼミの院生と3回生が集合するだけの日ということで、お仕事も稼ぎ時である。 研究室に出て来ると、ふーみんからの「re: Blink(1)」というメイルが3本、届いていた。 送っていたBlink(1)の現物が届き、ゆうべ、やってくれたようで、1本目のメイルから30分で2本目、さらに1時間で3本目、とサクサクと進んだようで、さすがである(^_^)。 今日はこれを順になぞって、Raspberry PiでBlink(1)を点灯させるとともに、Raspberry Piプログラミングのあれこれを勉強していこう。 まず最初のメイルは以下であった。
さきほどblink(1)が届きました.ありがとうございます.

で,手元に来てみて真面目に長嶋さんのログを読みましたが,
blink1-tool を動かすにはroot権限が必要,というだけのことのようです.

> 、実行させると以下のように「no blink(1) devices found」と表示されて、
> デバイスが認識されない
> ./blink1-tool -m 100 --rgb 255,0,255

ではなく,

sudo ./blink1-tool -m 100 --rgb 255,0,255

で普通にサクっと動きました.blink(1)を挿して,blink1-toolをダウンロードして
上記のようにしただけで動きます.特になにかをインストールしなければ
ならないということはありませんでした.

毎回sudoが面倒なら

sudo chown root blink1-tool
sudo chmod 4755 blink1-tool

としておけば,sudoなしで普通に動きます.

PythonやらCやらはこれからちょっとやってみます.
・・・あらら、そうだったのかぁ(^_^;)。 sudoでやっていなかったかなぁ。 さっそくやってみると、以下のようにアッサリとBlink(1)が点灯した。

YouTube

さっそく、上記のふーみんメイルに従って、「sudo chown root blink1-tool」でblink1-toolのオーナーをrootに、「sudo chmod 4755 blink1-tool」で実行権限を変更した。 これはしばらく使うかもしれないからである。 そして2限のゼミでは、M1のリュ君、3回生の土佐谷さんと森川さんの作品制作の進捗確認などを行った。 その後、以下の2本目のメイルに取りかかった。 「PythonでRaspberry Piに挿さったBlink(1)を制御」という事である。

pythonですが,

wget http://goo.gl/N9L4e
mv N9L4e pyusb-1.0.0-a1.tar.gz
tar xzvf pyusb-1.0.0-a1.tar.gz
cd pyusb-1.0.0-a1/
sudo python setup.py install
sudo python blink1-demo.py

で,blink1-demo.py は普通に動きました.
赤から5秒かけて青にフェード,という,コメント通りの動作です.

blink1-ctype.py の方は,Cのライブラリをpythonから
使えるようにするラッパーなので,まずCのライブラリを
ビルドする必要があると思います.
これはこれから試してみます.
上記の「wget」をそのままRaspberry Piで実行するには、「Raspberry Piを固定IPからDCHPに変更」「モバイルWiFiルータを起動して接続し1 day利用を契約」「Raspberry Piからwgetする」「後でRaspberry PiをDHCPから固定IPに戻す」という段取りが必要になる。 しかし、「wget http://goo.gl/N9L4e」でゲットするバイナリは、ホストのMacからアクセスすると、「pyusb-1.0.0-a1.tar.gz」というアーカイブ・バイナリファイルとしてダウンロードできたので、これをRaspberry Piにrcpすれば上記の2行まで終わる、と思われる。 そして、どこかファイル名に見覚えがあったので調べてみると、6月19日のところで、「pyusb-1.0.0a3.zip」という、より新しい?ものを既にゲットしてRaspberry Piに入っていることを発見した。

・・・ところが以下のように(詳細は これ )、「pyusb-1.0.0-a1」と「pyusb-1.0.0a3」の両方で、ふーみんの提示した処理を行ったところ、別にエラーは出ないものの、うまく行かなかった。 まず、ふーみんの言う「blink1-demo.py」が見当たらないのである(^_^;)。 そして、以前にゲットした「blink1hid-demo.py」では、前回と同じエラーが出た。 さて困った。

そこでふーみんに「blink1-demo.pyが見当たらない」とメイルすると、速攻で以下が届いた。 さすがの師匠である。(^_^)

Macに落としていたものをRPiに転送した、という
記述があったので、既にあるのかと思っていました。

RPi上でゲットするのであれば

git clone git://github.com/todbot/blink1.git

で、他のものも含めて全部取ってきます。

上のコマンドで blink1/ というディレクトリに一式落ちてきますが、
その中の python/ にあるはずです。
ということで、これは本来ならモバイルWiFiルータであるが、試しにMacから「http://github.com/todbot/blink1.git」をアクセスすると、 ここ にやってきて、「https://github.com/todbot/blink1.git」のメニューの横には「ZIP」というボタンがある。 たぶんこれでMacで落としても同じなのでは・・・という予想で、まずはこの30MBを落としにかかった。 「blink1/」の中身一式は必要ではなくて(ここにはlinuxもmacもwindowsもprocessingもpythonもrubyもある(^_^;))、その中の「python/」だけ拾えばいいのだろう。 そして、この「python/」の ここ から、さらにもう1本、「Python libraries」の「"bliPi.py" - DPontes wrapper of blink1-tool」というのがあったので、 ここ から、以下の「bliPy.py」もゲットした。
#!/usr/bin/env python
import time, sys, os, subprocess
path = '/home/diogo/i386/blink1/commandline/blink1-tool'
cmd_on = ' --on'
cmd_off = ' --off'
cmd_red = ' --red'
cmd_green = ' --green'
cmd_blue = ' --blue'
cmd_rgb = ' --rgb '
cmd_blink = ' --blink '
cmd_random = ' --random '
cmd_list = ' --list'
cmd_version = ' --version'
opt_fade = ' -m ' # written in milliseconds
opt_timing = ' -t ' # written in milliseconds

def emitCommand(command):
    output = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True)
    output = output.stdout.read()
    return output

class Blink1:
    def __init__(self):
        output = emitCommand(path + cmd_list)
        if (output[:2] == 'no'):
raise NameError('No blink1 Device Found!')
    def breath(self, interval, rgb = None):
        command = path + opt_fade + str(interval * 1000) + cmd_off
        emitCommand(command)
        time.sleep(interval)
        if(rgb != None):
            color = ','.join(str(x) for x in rgb)
            command = path + opt_fade + str(interval * 1000) + cmd_rgb + color
        else:
command = path + opt_fade + str(interval * 1000) + cmd_on
        emitCommand(command)
        time.sleep(interval)

if __name__ == '__main__':
    bl1 = Blink1()	
    time_interval = 1.75
    while 1:
        bl1.breath(time_interval)
するとここで、さらに以下のふーみん情報が届いた。 これはなかなか悩ましい。(^_^;)
どうも、色々やっているとblink1hid-demo.py が動くようになったりすることが
あるようで、必要なモジュールやデバイスの初期化、終了処理がおろそかな
ところがあるのかもしれない気がしています。

先にblink1-tool で--on とか--off とかしてやるとblink1hid-demo.py が
動くようになることがあるかと思えば、それでもだめな時もあって、
なにか特定の処理をきちんと入れないとだめなのかもしれません。
この意味では、Blink(1)はまだそれ自身にバグがあるカモというものだし、ツール等もまだまだ不完全なのかもしれない。 ・・・とメイルしたら、さらにふーみんから以下が届いた。 チャットに近いトラフィックである(^_^)。
python 関係は、今のところとりあえず動きます、という感じですね。

blink1/commandline/blink1-tool.c の

// get a list of all devices and their paths

あたりから下をみるとblink1-lib.so のAPIの使い方はわかるので、
それを使ってCで書いた方が幸せになれるかもしれません。

Blink(1)自体のバグというよりは、Pythonからデバイスを使いにいくときの
コーディングが枯れてないんじゃないかという印象です。
なるほど、これは決定的である。 「枯れていない」Pythonに付き合うつもりもなく、もともと、Pythonにあまり拘りたくないなぁ・・・と思っていたのであった。 ここは本命として、やはり、Cでやっていけばいいのであろう。

31.9MBの「blink1-master.zip」をダウンロードして解凍して「python/」ディレクトリを見てみると、やはり「blink1-demo.py」でなく「blink1hid-demo.py」しかなくて、エラーも同じだった(^_^;)。 そこでようやく、面倒な手順を再びセットして、「git clone git://github.com/todbot/blink1.git」でRaspberry Piから落とすことにした。 しかし結局、「git clone git://github.com/todbot/blink1.git」に対しては「gitなんていうプロトコルは対応していません」(^_^;)とかで断られて、仕方ないので「wget」の作戦に切り換えて、 このような 感じで、ふーみんメイルにあった

wget http://goo.gl/N9L4e
mv N9L4e pyusb-1.0.0-a1.tar.gz
tar xzvf pyusb-1.0.0-a1.tar.gz
cd pyusb-1.0.0-a1/
sudo python setup.py install
sudo python blink1-demo.py
の通りにやってみたものの、最後の1行はNot Foundで、以下の(1)のように捜しても見つからなかった。 そしてBlink(1)が挿さっている状態で「blink1hid-demo.py」を起動すると(2)のようなエラーが出て、Blink(1)を抜いて「blink1hid-demo.py」を起動すると(3)のようなエラーが出る、という状況は変わらなかった。

夕方には、注文していたちょっと面白いブツが届いたりして、今日の作業はここまでとなった。 朝に届いていたふーみんメイルの3本目、「Cのライブラリについて」という話題は、また明日以降となるが、この週末は作曲に没頭したいので、続きは来週になるかもしれない。

2013年6月22日(土)

昨日の夕方に届いたブツは こんなこと が出来る、というプツである。 いずれ何かに使うかもししれない。 そして週末になったが、土曜の午前中はおいちゃんの映像(Vコン)に音楽トラック制作作業、「試作1」の作曲で終わった。 午後には(ここを書いている研究室内)、「40虎」のメンバーのうち5人が集合して、「碧風祭・メディア企画の歴史」「中学星2」の上映会があって、ついつい映像に見入って進まなかった(^_^;)。 昨日のふーみんメイルの3本目は以下であるが、たしか「git」は駄目だったので(;_;)、「Raspberry PiをC言語で」という部分を進展させた後で、「Raspberry PiのBlink(1)をC言語で」に取りかかるのが適当だろう。
Cのライブラリも普通に使えるようです.

git clone git://github.com/todbot/blink1.git
cd blink1/
cd commandline
make
sudo cp blink1-lib.so /usr/lib
sudo cp blink1-lib.h /usr/include

で,ライブラリとヘッダファイルが使えるようになります.
ソースファイルのblink1-tool.c を見れば,
blink1-lib の使い方は大体わかると思います.

もっとも,これがなくても,/dev/hidraw0 などのデバイス名で
アクセスできるので,上記の blink1/commandline/  の
下にある,blink1raw のサンプルソースのようにすれば
Cからであれば直接プログラムすることも可能な気がします.

cd blink1raw
make
sudo ./blink1raw /dev/hidraw0 :0,255,0
sudo ./blink1raw /dev/hidraw0 =255,0,0,500

これで,緑にしてから5秒で赤にする,という動作をします.
blink1raw.c を見ればどういうプログラミングをすればいいかも
わかります.
ということで、CQ本のC言語の解説の部分から再開である。 この「Raspberry Pi日記」の最初、 5月15日のところ で、以下のようにあった「test.c」をRaspberry Piにrcpして、コンパイルして、無事に走ることを確認した。 まぁ、Raspberry PiはUnixなので、これは当然である。(^_^;)
#include <stdio.h>

main(){
	printf("\n\tHello, World \n\n");
}ls

CQのRaspberry Pi本の「おなじみC言語でI/O制御」の章には、いきなり1ページ2段組のソースコード(gpio_dev.c)があって、これを手打ちはナイよなあ・・・と調べてみると、ちゃんと 本のページ からのリンクに ダウンロードページ があった。 明日はおいちゃん作曲の日と決めているので、来週は、この gpio_dev.c を、手元のRaspberry Piのハードに合わせて改造するところからやってみよう。

2013年6月24日(月)

また新しい週の始まり、月曜日である。 昨日の午後は、おいちゃん作曲に集中して、合間に 学生の自主制作展 を見たりした。 朝6時に研究室に出て来ると、昨日レンダリングを走らせていたムービーが出来ていて、 こんなページ を作ったり、おいちゃんから「完成版OK」(^_^)のメイルが届いて、無事にあとは7月末のおいちゃん映像作品の完成を待つこととなった。

CQのRaspberry Pi本の gpio_dev.c であるが、実は ダウンロードページ からゲットしたサンプルは上のような内容(一部不要なものをカット)であり、「gpio_dev_c_test.c」よりもシンプルな「gpio_dev_c_test.c」というのがあった。 そしてさらに、「gpio_sh_c」というディレクトリに、以下のような「シェルコマンドをC言語で次々に送る」というものを発見した。

#include <stdio.h>

int main(int argc, char *argv[])
{
	int i;
	system("echo \"7\" > /sys/class/gpio/export");
	system("echo \"out\" > /sys/class/gpio/gpio7/direction");

	for (i=0; i<1000; i++) {
		system("echo \"1\" > /sys/class/gpio/gpio7/value");
		system("echo \"0\" > /sys/class/gpio/gpio7/value");
	}
	system("echo \"7\" > /sys/class/gpio/unexport");
}
これは、6月19日にやっていた「シェルコマンドを並べたシェルスクリプトを実行」というやつを、C言語プログラムとしてまとめたもので、遠い遠い昔、1996年に作品 "Asian Edge" ()の作曲において、背景音響パートをライヴ生成するために、「MIDI制御を受けて"サウンドファイルを再生する"というシェルコマンドを多数、どんどん起動していく(最大、同時に20本以上のサウンドを多重生成)というプログラム」を、シリコングラフィクスのIndyワークステーション上で制作したのを思い出した。 ここ にある、 これこれ である。

そこでまず、 Raspberry PiのGPIO解説ページ なども参考にして(ビン番号のバグも自己修正(^_^;))、 このプログラム をRaspberry Piにrcpで送り、コンパイルしてsudoで実行して、ほぼLEDが点灯することを確認した。 ただし「ほぼ」というところが謎で(^_^;)、「Ex_Out 0」から「Ex_Out 4」までの5ビットはOKだが、肝心の「GPIO 0」から「GPIO 7」までの8ビットのうち、「GPIO 2」と「GPIO 7」が無反応であった。 これはLEDの向きなのか断線なのか、それともポート設定に問題があるのか、以前に敢えて軽くスルーしていたが(^_^;)、きちんと確認する必要がありそうだ。 ここで2限の講義「音楽情報科学」の時間となったので、続きは午後からである。

そして午後になり、 この解説ページ を改めて眺めてみると、なんと上のように、ピン配置と信号名の対応表に、2つの異なったバージョンがある、という事実が判明した。 こんな事は、これまでどこにも書かれていなかった(^_^;)。 そしてまさに、ウンともスンとも言わなかった13ピンこそ、この信号名がRev1からRev2で変更されている場所だったのである。 また、以前の実験で「Ex_In 0」「Ex_In 1」と名付けた、pull-upされている2ピンのLEDが点灯しなかったのも、信号名が変更されていたためと思われた。 そこでさっそく、以前に書いていたピン信号対応表を、拡張出力も7ビットに戻して、以下のように新しいRev2に対応させてみた。

ピン番号     信号名     ソフト上の呼び名
11GPIO 0GPIO17
12GPIO 1GPIO18
13GPIO 2GPIO27
15GPIO 3GPIO22
16GPIO 4GPIO23
18GPIO 5GPIO24
22GPIO 6GPIO25
7GPIO 7GPIO4
8UART TxGPIO14
10UART RxGPIO15
3Ex_Out 0 / I2C SDAGPIO2
5Ex_Out 1 / I2C SDLGPIO3
19Ex_Out 2GPIO10
21Ex_Out 3GPIO9
23Ex_Out 4GPIO11
24Ex_Out 5GPIO8
26Ex_Out 6GPIO7

そして、「GPIO 7」が点灯しなかったのはdirectionの行が抜けていたというバグ(^_^;)も発見して、新たに作った C言語でシェルコマンドを実行するプログラム を転送して、以下のYouTubeのように見事に、汎用ポート8ビット、拡張ポート7ビットが全て快調に点灯すること、さらに「sleep 1」だけでなく「sleep 0.1」というように、sleep時間は実数で定義できることも確認した。 こうなれば、もうPythonでGPIOを制御する必要もなくなってくる(^_^)。

YouTube

動作を確認しているシェルコマンドを、単にC言語プログラムのシステムコールで並べただけなので、これが動くこと自体はそれほど嬉しい事もないのだが(^_^;)、GPIOコネクタの信号名の定義が新しいバージョンになっていた、という事実が判明して、これで不明確なところが無くなった、というのは大きな進歩である。 これで安心して、「C言語でGPIOデバイスドライバを叩く」という次のステップに進める。 CQのRaspberry Pi本の gpio_dev.c をベースに、今回実験している8+7=15ビットのLEDを表示するプログラムを作りたい。 こごてはまず、「Ex_Out 6」である「GPIO7」の点滅を目指そう。 プログラムの概形は以下であり、「init_gpio();」でexportとdirection設定、「gpio_close();」でunexportである。 ただし、「gpio_dev.c」よりシンプルな「gpio_dev_c_test.c」では、上記の「gpio_close();」が無く、「省略してもエラーにならない」などと書いてある(^_^;)。

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>

int fd_val;
char s[64];

int main()
{
    int i;

    init_gpio();

    for (i=0; i<10; i++) {
	write(fd_val,"1",2);
	sleep(0.5);
	write(fd_val,"0",2);
	sleep(0.5);
    }

    close(fd_val);

    gpio_close();
}
その「init_gpio();」は出力用であればおよそ以下のような感じで、fdというのはファイルディスクリプタの事だろう。 かつてUnixをお勉強した時に、何度も出て来たやつである。 サンプルにあるエラー処理を残すか省略するかは悩ましいが、当面は残しておく事にした。 expでもvalでも「2」を書くのに、どうしてdirだけ「4」なのか、どこにも書いてない。
void init_gpio()
{
    int fd_exp, fd_dir;

    fd_exp = open("/sys/class/gpio/export", O_WRONLY);
    if (fd_exp < 0) {
        printf("GPIO export open error\n");
        exit(1);
    }
    write(fd_exp, "7", 2);
    close(fd_exp);

    sprintf(s,"/sys/class/gpio/gpio%d/direction",7);
    fd_dir = open(s, O_RDWR);
    if (fd_dir < 0) {
        printf("GPIO %d direction open error\n",7);
        exit(1);
    }
    write(fd_dir, "out", 4);
    close(fd_dir);

    sprintf(s,"/sys/class/gpio/gpio%d/value",7);
    fd_val = open(s, O_RDWR);
    if (fd_val < 0) {
       	printf("GPIO %d value open error\n",7);
       	exit(1);
    }
}
対応する「gpio_close();」はおよそ以下のような感じである。 実験としては、これを呼ばなくてもまた実行できるかどうかを確認してみよう。
void gpio_close()
{
    int fd_exp;

    fd_exp = open("/sys/class/gpio/unexport", O_WRONLY);
    if (fd_exp < 0) {
        printf("GPIO unexport open error\n");
        exit(1);
    }
    write(fd_exp, "7", 2);
}
これを実行してみると、無事に「Ex_Out 6」である「GPIO7」が点滅したが、どうもその時間間隔が異常に長い。 今回はVNCでなくsshで実験しているが、sshのターミナル画面内のRaspberry Pi側でnanoを開けるので、わずかな変更はいちいち rcpしないでnanoで開いて修正してコンパイル→テストランが出来る。 いろいろ試して、以下のような事が判った。 要するに、fdで情報を取得してしまえば、Pythonドライバのようにリソースの管理をせずにGPIOポートがアクセス出来てしまうわけである。 ということで、エラー処理も省略して、さらに上記の「無くても実行できる」ものを全て省略(^_^;)すると、上のプログラムはなんと、以下のようになり、これでも何度でも実行できる事を確認した。
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>

int fd;
char s[64];

int main()
{
    int i;

    fd = open("/sys/class/gpio/export", O_WRONLY);
    write(fd, 7, 2);
    sprintf(s,"/sys/class/gpio/gpio%d/direction",7);
    fd = open(s, O_RDWR);
    write(fd, "out", 2);
    sprintf(s,"/sys/class/gpio/gpio%d/value",7);
    fd = open(s, O_RDWR);

    for (i=0; i<10; i++) {
	write(fd,"0",2);
	sleep(1);
	write(fd,"1",2);
	sleep(1);
    }
}
ということで、とりあえず全15ビットのLEDを8ビットと7ビットのグループごとに交互に全点滅させる、という以下のCプログラムが完成して、無事に動いた。 やはり、GPIOのビン配置と、新しい信号名の対応がキチンとした事が本日最大の収穫だろう。
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>

int main()
{
	int fd[15];
	int assign[15] = {17,18,27,22,23,24,25,4,2,3,10,9,11,8,7};
	char s[64];
	int i, j, ff;

	for (i=0; i<15; i++) {
		ff = open("/sys/class/gpio/export", O_WRONLY);
		write(ff, assign[i], 2);
		sprintf(s,"/sys/class/gpio/gpio%d/direction", assign[i]);
		ff = open(s, O_RDWR);
		write(ff, "out", 2);
		sprintf(s,"/sys/class/gpio/gpio%d/value", assign[i]);
		fd[i] = open(s, O_RDWR);
	}

	for (j=0; j<4; j++) {
		for (i=0; i<8; i++) {
			write(fd[i],"0",2);
		}
		for (i=8; i<15; i++) {
			write(fd[i],"1",2);
		}
		sleep(1);
		for (i=0; i<8; i++) {
 			write(fd[i],"1",2);
		}
		for (i=8; i<15; i++) {
			write(fd[i],"0",2);
		}
		sleep(1);
	}
}
YouTube

合わせて、上のようにGPIOポートのビン信号マップも最新に改訂した。 これでちょうど5限となり、アカペラの新入生・補習特訓の時間となった。 まずまず進んだというところである。(^_^)

2013年6月25日(火)

このところ早起きが快調に続いている。 昨日も今日も、朝06:15にはもう研究室で仕事開始である。 1限に「サウンドデザイン」が、4-5限に「企画立案演習」があるが、Raspberry Pi関係でもやりたい事がどんどん浮かんできて、来月に迫ったSketchingを前に、いよいよ佳境なのである(^_^)。 まずは朝イチで、以下のように、Raspberry PiのGPIOコネクタ上に空中配線していた15個のLEDの根元を、接触とか断線とかしないように、硬化型の充填接着剤で固めた。

その裏で同時に、実験してきた63号機のRaspberry PiのSDカードイメージの現状バージョンを「Blink_IP_63.img」として吸い出し、61号機と62号機のSDカードに書き出して、いったん単体のIPアドレス「63」となったものをVNCからそれぞれの固定IPに変更する、という作業も進めた。 これに先立って、どうやら「枯れていない」というPythonでのBlink(1)制御はとりあえず見捨てて(^_^;)、コマンドラインからのツール「blink1-tool」でも、モニタ出力をnullにリダイレクトすれば十分なことを、以下のように確認した。

Last login: Tue Jun 25 06:15:12 on console

nagasm-Mac-mini:~ nagasm$ ls
Desktop		Downloads	Movies		Pictures	Sites
Documents	Library		Music		Public

nagasm-Mac-mini:~ nagasm$ cd Desktop

nagasm-Mac-mini:Desktop nagasm$ ssh pi@172.16.65.63
pi@172.16.65.63'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: Sat Jun 22 05:30:39 2013

pi@raspberrypi ~ $ ls
Desktop         blink1-ctypes.py   mess2.txt       pyusb-1.0.0a3
RPi.GPIO-0.1.0  blink1-tool        ocr_pi.png      test.c
a.out           blink1.git         python_games    test.py
bliPy.py        blink1hid-demo.py  pyusb-1.0.0-a1  twilight.png

pi@raspberrypi ~ $ rm mess2.txt

pi@raspberrypi ~ $ ls
Desktop         bliPy.py          blink1.git         python_games    test.c
RPi.GPIO-0.1.0  blink1-ctypes.py  blink1hid-demo.py  pyusb-1.0.0-a1  test.py
a.out           blink1-tool       ocr_pi.png         pyusb-1.0.0a3   twilight.png

pi@raspberrypi ~ $ ls *.py
bliPy.py  blink1-ctypes.py  blink1hid-demo.py  test.py

pi@raspberrypi ~ $ rm *.py

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

pi@raspberrypi ~ $ sudo ./blink1-tool -t 2000 --random 100
random 100 times: 
0: 0/1 : f4,4b,4b 
1: 0/1 : 10,e3,9a 
2: 0/1 : f5,af,be 
3: 0/1 : 36,1e,7c 
4: 0/1 : c5,dd,a5 
5: 0/1 : f3,e5,19 
6: 0/1 : a3,f5,bb 
7: 0/1 : d2,45,24 
^Z
[1]+  停止                  sudo ./blink1-tool -t 2000 --random 100

pi@raspberrypi ~ $ sudo ./blink1-tool -t 2000 --random 10 > null

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

pi@raspberrypi ~ $ sudo halt

Broadcast message from root@raspberrypi (pts/0) (Sat Jun 22 05:37:37 2013):
The system is going down for system halt NOW!
pi@raspberrypi ~ $ Connection to 172.16.65.63 closed by remote host.
Connection to 172.16.65.63 closed.

nagasm-Mac-mini:Desktop nagasm$ 
そして、GPIOポートの実験のために63号機のSDカードを挿して実験してきたRaspberry Piから、以下のように、また3枚タンデムのRaspberry Piシステムに戻した。 この状態で、しばらくはプログラミングを行うことになる。

そして、ここで3枚のRaspberry Piに、「起動時にBlink(1)を自動ランダム点灯させるプログラムを仕込む」という作業にとりかかった。 まずは以下のような、「1000msecごとにランダムな色でBlink(1)を100回発光させる」というblink1-toolのコマンドを無限ループで呼び出す、という超カンタンなCブログラム「test.c」を作った。

#include <stdio.h>

int main()
{
	while (1) {
    		system("./blink1-tool -t 1000 --random 100 > null");
     	}
}
これをRaspberry Piに転送して、「cc test.c -o blink_autorun」としてカレントディレクトリに置いた。 この「./blink_autorun」が動くことを確認して、次に6月3日に行った手順に従って、以下のように続けた。 ちなみに、せっかくなので61号機では1000msecだった変化間隔を、62号機では800msecに、63号機では700msecにしてみた(^_^;)。 これでそれぞれを再起動すると、以下のように見事に、3台のRaspberry Piがカラフルな自動発光をするようになった。 なお、このプロセスを止めるには「ps ax」してPIDを捜して、例えばPIDが2201であれば「kill -9 2201」で止まる。 これを改めて起動したい場合には「./blink_autorun」とするとコマンドラインに戻らないので、「./blink_autorun &」とすれば、バックグラウントで起動するのでコマンドラインに戻る。

YouTube

ここまで出来たところで、昨日はちゃんと実行できていた最後のCブログラムを転送・コンパイル・実行させてみると、何故か15個のLEDが全て沈黙した(^_^;)。 朝イチの接着剤が問題か・・・と思ったが、昨日の C言語でシェルコマンドを実行するプログラム は、ちゃんと転送・コンパイル・実行がOK(LEDは全てきちんと点滅)、ということで、ハードの問題ではなさそうである。 今日はC言語であれこれLED点灯に芸をさせよう・・・と思っていたのに、出鼻をくじかれた(^_^;)。

またroot権限かと、sudoを入れたり、「sudo chown root ./a.out」「sudo chmod 4755 ./a.out」などとやっても駄目である。 昨日、実行できたRaspberry Piに戻したり、61号機・62号機・63号機のハードとSDカードを交換しても現象は同じで、シェルプログラムはOK、シェルコマンドを送るC言語プログラムもOK、ただしC言語でGPIOデバイスドライバを叩くもの(昨日はOKでちゃんと走って点滅)だけが、全て駄目だった。 午後の合間にあれこれ悩んだが解決せず、悩んでいるうちにこの日が終わった。(^_^;)

2013年6月26日(水)

さて水曜日である。 講義の無いこの日は、放課後のアカペラまでがっつり進める日である。 昨日の帰り際に、「昨日走ったソフトが何故か今日走らない」という情けないメイルをふーみんに出していたが、今朝読んだ返信は以下であり、まさにもっともであった(^_^;)。 ちゃんとふーみんはピン番号の新旧についても記述していたが、僕が読み落としていた。 今日はまず、ヘンな省略をしないところから再開し、紹介された情報をチェックしていこう。
http://netlog.jpn.org/r271-635/2013/01/raspberry_pi_gpio_test.html
あたりをみて,
http://www.open.com.au/mikem/bcm2835/
のライブラリを使った方が楽ではないかと・・・.

# *_close()を省略しても大丈夫,という記述はちょっと信じられません.

ちなみにRevisionによるピンアサインの差異は私の本でも
見分け方とともに説明してあります.
CQのRaspberry Pi本の第5章の「デバイスドライバを使う」のところに書かれていた「省略可能」という記述を並べると以下のようになる。 OSが別途に管理してくれているので、まぁ、動くよ、という事なのだが、ここは基本に戻って、しっかりと形式を整えることにしよう。 ・・・ということで、Raspberry Piを立ち上げ、VNCだとコピペが出来ないのでsshでRaspberry Piに入り、まずは昨日の現象を確認した。 リセット直後のRaspberry Piで再現確認しておく事は意味があるだろう。 まず、再掲になるが、拡張7ビットに対してコマンドラインから表示を指令したシェルスクリプトの test1.c をrcpすると、以下のようにこれは自動的に実行可能と表示され、sudoで実行してLEDが順に点灯した。
Last login: Wed Jun 26 06:43:50 on console
nagasm-Mac-mini:~ nagasm$ ssh pi@172.16.65.61
pi@172.16.65.61'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: Sat Jun 22 13:45:45 2013

pi@raspberrypi ~ $ ls
Desktop         blink1-tool    null        python_games    pyusb-1.0.0a3  twilight.png
RPi.GPIO-0.1.0  blink_autorun  ocr_pi.png  pyusb-1.0.0-a1  testrun

pi@raspberrypi ~ $ rcp nagasm@172.16.65.31:Desktop/test1.c .
Password:
test1.c                                                        100% 1700     1.7KB/s   00:00    

pi@raspberrypi ~ $ ls -l
合計 1068
drwxr-xr-x 2 pi   pi    4096  6月 18 23:18 Desktop
drwxr-xr-x 7 pi   pi    4096  6月 18 04:31 RPi.GPIO-0.1.0
-rwsr-xr-x 1 root pi 1020394 11月 28  2012 blink1-tool
-rwxr-xr-x 1 pi   pi    5170  6月 22 06:19 blink_autorun
-rw-r--r-- 1 pi   pi       0  6月 22 14:10 null
-rw-r--r-- 1 pi   pi    5781  2月  3 05:07 ocr_pi.png
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
-rwxr-xr-x 1 pi   pi    1700  6月 22 14:11 test1.c
-rwxr-xr-x 1 pi   pi    6289  6月 22 12:02 testrun
-rw-r--r-- 1 pi   pi   20123  5月 31 11:45 twilight.png

pi@raspberrypi ~ $ sudo ./test1.c
pi@raspberrypi ~ $ 
次に、汎用8ビットと拡張7ビットに対して、C言語のシステムコールとしてシェルコマンドを並べた test2.c をrcpすると、以下のようにこれは実行できないCソースコードと表示され、ccでコンパイルして、sudoで「a.out」を実行すると、全てのLEDが順に点灯した。 ここまでは昨日も一昨日も問題なかったところである。
pi@raspberrypi ~ $ rcp nagasm@172.16.65.31:Desktop/test2.c .
Password:
test2.c                                                        100% 5427     5.3KB/s   00:00    

pi@raspberrypi ~ $ ls -l
合計 1076
drwxr-xr-x 2 pi   pi    4096  6月 18 23:18 Desktop
drwxr-xr-x 7 pi   pi    4096  6月 18 04:31 RPi.GPIO-0.1.0
-rwsr-xr-x 1 root pi 1020394 11月 28  2012 blink1-tool
-rwxr-xr-x 1 pi   pi    5170  6月 22 06:19 blink_autorun
-rw-r--r-- 1 pi   pi       0  6月 22 14:14 null
-rw-r--r-- 1 pi   pi    5781  2月  3 05:07 ocr_pi.png
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
-rwxr-xr-x 1 pi   pi    1700  6月 22 14:11 test1.c
-rw-r--r-- 1 pi   pi    5427  6月 22 14:15 test2.c
-rwxr-xr-x 1 pi   pi    6289  6月 22 12:02 testrun
-rw-r--r-- 1 pi   pi   20123  5月 31 11:45 twilight.png

pi@raspberrypi ~ $ cc test2.c
pi@raspberrypi ~ $ sudo ./a.out
pi@raspberrypi ~ $ 
次に、ふーみんの指摘を真摯に受け止めて(^_^;)、exportに帯するunexportも、openに対するcloseも、ちゃんと全て完備させ(当たり前の)、さらにエラー処理もサンプルの通りに戻したCプログラムとして修正した test3.c をrcpしてccでコンパイルしてsudoで「a.out」を実行すると、以下のように「GPIO 7 direction open error」と出た。 なるほど、printfを入れたエラー処理は入れておくものだった(^_^;)。
pi@raspberrypi ~ $ rcp nagasm@172.16.65.31:Desktop/test3.c .
Password:
test3.c                                                        100%  824     0.8KB/s   00:00    

pi@raspberrypi ~ $ ls -l
合計 1092
drwxr-xr-x 2 pi   pi    4096  6月 18 23:18 Desktop
drwxr-xr-x 7 pi   pi    4096  6月 18 04:31 RPi.GPIO-0.1.0
-rwxr-xr-x 1 pi   pi    9355  6月 22 14:15 a.out
-rwsr-xr-x 1 root pi 1020394 11月 28  2012 blink1-tool
-rwxr-xr-x 1 pi   pi    5170  6月 22 06:19 blink_autorun
-rw-r--r-- 1 pi   pi       0  6月 22 14:25 null
-rw-r--r-- 1 pi   pi    5781  2月  3 05:07 ocr_pi.png
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
-rwxr-xr-x 1 pi   pi    1700  6月 22 14:11 test1.c
-rw-r--r-- 1 pi   pi    5427  6月 22 14:15 test2.c
-rw-r--r-- 1 pi   pi     824  6月 22 14:26 test3.c
-rwxr-xr-x 1 pi   pi    6289  6月 22 12:02 testrun
-rw-r--r-- 1 pi   pi   20123  5月 31 11:45 twilight.png

pi@raspberrypi ~ $ cc test3.c
pi@raspberrypi ~ $ sudo ./a.out
GPIO 7 direction open error

pi@raspberrypi ~ $ 
エラーが出ればそこを攻める、というのがデバッグの常道である。 そして今ではインターネットという強力な助っ人がいる。 エラーの「GPIO direction open error」というタームでyahoo.comから検索すると、 こんなページ が出て来て、そこには以下のようなプログラム(ただしunexportしていない(^_^;))があった。
public void sync () {
    int fd = -1;
    fd = open("/sys/class/gpio/export", O_WRONLY);
    if (fd < 0) {
        GLib.stdout.printf("Error sync export\n");
        return;
    }
    write(fd, "31", 3);
    close(fd);
    fd = open("/sys/class/gpio/gpio31/direction", O_WRONLY);
    if (fd < 0) {
        GLib.stdout.printf("Error sync direction\n");
        return;
    }
    write(fd, "out", 4);
    close(fd);
    fd = open("/sys/class/gpio/gpio31/value", O_WRONLY);
    if (fd < 0) {
        GLib.stdout.printf("Error sync value\n");
        return;
    }
    write(fd, "0", 2);
    write(fd, "1", 2);
    Thread.usleep (1000); /* 1ms */
    write(fd, "0", 2);
    close(fd);
}
違いとしては、ポートのopenに、CQ本では「O_WRONLY」と「O_RDWR」とが混在しているが、こちらでは全て「O_WRONLY」というのがまず、目についた。 そこで試しに、 test3.c の中の「O_RDWR」を全て「O_RDWR」に置換しただけの test4.c をrcpしてccでコンパイルしてsudoで「a.out」を実行すると、ちゃんと拡張ポートの1ビット(GPIO7)が点滅した。 これはつまり、解決ということかな(^_^)。 そこで、一昨日の最後に走っていたが昨日は駄目だった、15ビットのLEDを7ビットと8ビットと交互にまとめて点滅させる、というC言語プログラムを、ここまでの修正を反映させて以下のような「test5.c」として、rcpしてccでコンパイルしてsudoで「a.out」を実行すると、ちゃんと走った。
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>

int main()
{
	int fd[15];
	int assign[15] = {17,18,27,22,23,24,25,4,2,3,10,9,11,8,7};
	char s[64];
	int i, j, ff;

	for (i=0; i<15; i++) {
		ff = open("/sys/class/gpio/export", O_WRONLY);
		if (ff < 0) {
			printf("GPIO export open error\n");
			exit(1);
		}
		write(ff, assign[i], 2);
		close(ff);
	}

	for (i=0; i<15; i++) {
		sprintf(s,"/sys/class/gpio/gpio%d/direction", assign[i]);
		ff = open(s, O_WRONLY);
		if (ff < 0) {
			printf("GPIO %d direction open error\n",assign[i]);
			exit(1);
		}
		write(ff, "out", 2);
		close(ff);
	}

	for (i=0; i<15; i++) {
		sprintf(s,"/sys/class/gpio/gpio%d/value", assign[i]);
		fd[i] = open(s, O_WRONLY);
		if (ff < 0) {
			printf("GPIO %d value open error\n",assign[i]);
			exit(1);
		}
	}

	for (j=0; j<7; j++) {
		for (i=0; i<8; i++) {
			write(fd[i],"0",2);
		}
		for (i=8; i<15; i++) {
			write(fd[i],"1",2);
		}
		sleep(1);
		for (i=0; i<8; i++) {
 			write(fd[i],"1",2);
		}
		for (i=8; i<15; i++) {
			write(fd[i],"0",2);
		}
		sleep(1);
	}

	for (i=0; i<15; i++) {
		close(fd[i]);
	}

	for (i=0; i<15; i++) {
		ff = open("/sys/class/gpio/unexport", O_WRONLY);
		if (ff < 0) {
			printf("GPIO unexport open error\n");
			exit(1);
		}
		write(ff, assign[i], 2);
		close(ff);
	}
}
・・・ところが、ここからまた、昨日の悪夢の再現のような現象が起きた(^_^;)。 現象としては、 test1.c のスクリプト実行と、 test2.c のコンパイル結果の実行は、必ず問題なくできるが、 C言語で叩く、というタイプについては、またまた「GPIO 7 direction open error」とか「GPIO 17 direction open error」、つまりdirectionの設定のいちばん最初のところでエラーとなる現象が起きるが、まれに test1.c のスクリプト実行、あるいは test2.c のコンパイル結果の実行によって、状態が正常に戻るのか、問題なく実行できることも「まれに」ある、という状況だった。 「まれに」というのが、100%よりもずっと厄介なのである。(^_^;)

ここでさすがにふーみんメイルのように、 このページ と、実質的にはその元ネタである このページ に行くしかない、と観念して(^_^;)、調べてみることにした。

 # download the latest version of the library, say bcm2835-1.xx.tar.gz, then:
tar zxvf bcm2835-1.xx.tar.gz
cd bcm2835-1.xx
./configure
make
sudo make check
sudo make install
上のように簡単に書かれていた このページ の手順に従い、最新の「bcm2835-1.25.tar.gz」をダウンロードして、Raspberry Piにrcpして、tarから実行すると、実際には以下のように表示された。
pi@raspberrypi ~ $ rcp nagasm@172.16.65.31:Desktop/bcm2835-1.25.tar.gz .
Password:
bcm2835-1.25.tar.gz                                            100%  225KB 225.5KB/s   00:00    

pi@raspberrypi ~ $ tar zxvf bcm2835-1.25.tar.gz
bcm2835-1.25/
bcm2835-1.25/config.sub
bcm2835-1.25/ChangeLog
bcm2835-1.25/doc/
bcm2835-1.25/doc/Makefile.am
bcm2835-1.25/doc/Makefile.in
bcm2835-1.25/doc/Doxyfile.in
bcm2835-1.25/aclocal.m4
bcm2835-1.25/README
bcm2835-1.25/src/
bcm2835-1.25/src/bcm2835.h
bcm2835-1.25/src/Makefile.am
bcm2835-1.25/src/bcm2835.c
bcm2835-1.25/src/test.c
bcm2835-1.25/src/Makefile.in
bcm2835-1.25/INSTALL
bcm2835-1.25/depcomp
bcm2835-1.25/config.h.in
bcm2835-1.25/config.guess
bcm2835-1.25/configure.in
bcm2835-1.25/NEWS
bcm2835-1.25/COPYING
bcm2835-1.25/Makefile.am
bcm2835-1.25/ltmain.sh
bcm2835-1.25/configure
bcm2835-1.25/missing
bcm2835-1.25/install-sh
bcm2835-1.25/Makefile.in
bcm2835-1.25/AUTHORS
bcm2835-1.25/examples/
bcm2835-1.25/examples/blink/
bcm2835-1.25/examples/blink/blink.c
bcm2835-1.25/examples/input/
bcm2835-1.25/examples/input/input.c
bcm2835-1.25/examples/spin/
bcm2835-1.25/examples/spin/spin.c
bcm2835-1.25/examples/event/
bcm2835-1.25/examples/event/event.c
bcm2835-1.25/examples/spi/
bcm2835-1.25/examples/spi/spi.c

pi@raspberrypi ~ $ cd bcm2835-1.25

pi@raspberrypi ~/bcm2835-1.25 $ ./configure
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /bin/mkdir -p
checking for gawk... no
checking for mawk... mawk
checking whether make sets $(MAKE)... yes
checking for style of include used by make... GNU
checking for gcc... gcc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables... 
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for gcc option to accept ISO C89... none needed
checking dependency style of gcc... gcc3
checking for clock_gettime in -lrt... yes
checking for doxygen... no
configure: WARNING: Doxygen not found - continuing without Doxygen support
checking for ranlib... ranlib
checking for gcc... (cached) gcc
checking whether we are using the GNU C compiler... (cached) yes
checking whether gcc accepts -g... (cached) yes
checking for gcc option to accept ISO C89... (cached) none needed
checking dependency style of gcc... (cached) gcc3
checking that generated files are newer than configure... done
configure: creating ./config.status
config.status: creating Makefile
config.status: creating src/Makefile
config.status: creating doc/Makefile
config.status: creating config.h
config.status: executing depfiles commands
pi@raspberrypi ~/bcm2835-1.25 $ make
make  all-recursive
make[1]: ディレクトリ `/home/pi/bcm2835-1.25' に入ります
Making all in src
make[2]: ディレクトリ `/home/pi/bcm2835-1.25/src' に入ります
gcc -DHAVE_CONFIG_H -I. -I..     -g -O2 -MT bcm2835.o -MD -MP -MF .deps/bcm2835.Tpo -c -o bcm2835.o bcm2835.c
mv -f .deps/bcm2835.Tpo .deps/bcm2835.Po
rm -f libbcm2835.a
ar cru libbcm2835.a bcm2835.o 
ranlib libbcm2835.a
make[2]: ディレクトリ `/home/pi/bcm2835-1.25/src' から出ます
Making all in doc
make[2]: ディレクトリ `/home/pi/bcm2835-1.25/doc' に入ります
make[2]: `all' に対して行うべき事はありません.
make[2]: ディレクトリ `/home/pi/bcm2835-1.25/doc' から出ます
make[2]: ディレクトリ `/home/pi/bcm2835-1.25' に入ります
make[2]: ディレクトリ `/home/pi/bcm2835-1.25' から出ます
make[1]: ディレクトリ `/home/pi/bcm2835-1.25' から出ます

pi@raspberrypi ~/bcm2835-1.25 $ sudo make check
Making check in src
make[1]: ディレクトリ `/home/pi/bcm2835-1.25/src' に入ります
make  test
make[2]: ディレクトリ `/home/pi/bcm2835-1.25/src' に入ります
gcc -DHAVE_CONFIG_H -I. -I..     -g -O2 -MT test.o -MD -MP -MF .deps/test.Tpo -c -o test.o test.c
mv -f .deps/test.Tpo .deps/test.Po
gcc  -g -O2   -o test test.o ./libbcm2835.a -lrt 
make[2]: ディレクトリ `/home/pi/bcm2835-1.25/src' から出ます
make  check-TESTS
make[2]: ディレクトリ `/home/pi/bcm2835-1.25/src' に入ります
PASS: test
=============
1 test passed
=============
make[2]: ディレクトリ `/home/pi/bcm2835-1.25/src' から出ます
make[1]: ディレクトリ `/home/pi/bcm2835-1.25/src' から出ます
Making check in doc
make[1]: ディレクトリ `/home/pi/bcm2835-1.25/doc' に入ります
make[1]: `check' に対して行うべき事はありません.
make[1]: ディレクトリ `/home/pi/bcm2835-1.25/doc' から出ます
make[1]: ディレクトリ `/home/pi/bcm2835-1.25' に入ります
make[1]: ディレクトリ `/home/pi/bcm2835-1.25' から出ます

pi@raspberrypi ~/bcm2835-1.25 $ sudo make install
Making install in src
make[1]: ディレクトリ `/home/pi/bcm2835-1.25/src' に入ります
make[2]: ディレクトリ `/home/pi/bcm2835-1.25/src' に入ります
 /bin/mkdir -p '/usr/local/lib'
 /usr/bin/install -c -m 644  libbcm2835.a '/usr/local/lib'
 ( cd '/usr/local/lib' && ranlib libbcm2835.a )
 /bin/mkdir -p '/usr/local/include'
 /usr/bin/install -c -m 644 bcm2835.h '/usr/local/include'
make[2]: ディレクトリ `/home/pi/bcm2835-1.25/src' から出ます
make[1]: ディレクトリ `/home/pi/bcm2835-1.25/src' から出ます
Making install in doc
make[1]: ディレクトリ `/home/pi/bcm2835-1.25/doc' に入ります
make[2]: ディレクトリ `/home/pi/bcm2835-1.25/doc' に入ります
make[2]: `install-exec-am' に対して行うべき事はありません.
make[2]: `install-data-am' に対して行うべき事はありません.
make[2]: ディレクトリ `/home/pi/bcm2835-1.25/doc' から出ます
make[1]: ディレクトリ `/home/pi/bcm2835-1.25/doc' から出ます
make[1]: ディレクトリ `/home/pi/bcm2835-1.25' に入ります
make[2]: ディレクトリ `/home/pi/bcm2835-1.25' に入ります
make[2]: `install-exec-am' に対して行うべき事はありません.
make[2]: `install-data-am' に対して行うべき事はありません.
make[2]: ディレクトリ `/home/pi/bcm2835-1.25' から出ます
make[1]: ディレクトリ `/home/pi/bcm2835-1.25' から出ます

pi@raspberrypi ~/bcm2835-1.25 $ 
そして、実質的には このページ でなく、 このページ の方に全て必要な情報がある、と判明したので、とりあえず Examples のページの先頭にあった、以下の「blink.c」をRaspberry Piにrcpして、このプログラムの冒頭にあった「gcc -o blink blink.c -l bcm2835」というオプションでコンパイルして、「sudo ./blink」してみると、ちゃんと先頭のLEDが0.5秒間隔で点滅した(^_^)。
// Example program for bcm2835 library
// Blinks a pin on an off every 0.5 secs
//
// After installing bcm2835, you can build this with something like:
// gcc -o blink blink.c -l bcm2835
// sudo ./blink

#include <bcm2835.h>

// Blinks on RPi Plug P1 pin 11 (which is GPIO pin 17)
#define PIN RPI_GPIO_P1_11

int main(int argc, char **argv)
{
	if (!bcm2835_init())
		return 1;

	// Set the pin to be an output
	bcm2835_gpio_fsel(PIN, BCM2835_GPIO_FSEL_OUTP);

	while (1)
	{
		// Turn it on
		bcm2835_gpio_write(PIN, HIGH);
		// wait a bit
		bcm2835_delay(500);
		// turn it off
		bcm2835_gpio_write(PIN, LOW);
		// wait a bit
		bcm2835_delay(500);
	}
	bcm2835_close();
	return 0;
}
これはかなり簡単である(^_^)。 このページ には、他にも「event.c」「input.c」「spi.c」「spin.c」など、シンプルな低水準プログラムが並んでいる。 ヘッダファイル も参考になる。 まさにここ数日、こういう低水準のライブラリが欲しかったのである(^_^)。 ここで昼休みになったところに、以下のふーみんメイルが届き、ちょうど欲しかった資料として BCM2835のマニュアルRaspberry Pi(バージョン1)の回路図Raspberry Pi(バージョン2)の回路図 とがゲットできた。 欲しかったデータを以心伝心で送ってくれるあたり、さすがの師匠である。(^_^)
ちょっと今すぐには長嶋さんのwebを詳細にチェックしている
時間がないのですが、Rev.1 とRev.2のschematic、bcm2835の
データシートを送ります。

elinux.orgによれば、ブート時にAlt0にいくのはUARTのTX/RXのみ、
のはずですが、SPIやI2Cのモジュールを有効にしていれば
単純なGPIOではなくそれぞれのfunctionに切り替わっている
可能性もあります。

長嶋さんの作業手順をちゃんと読んでいないので蓋然性の指摘に
留まりますが、ライブラリを呼び出しているようなプログラムが動いた
ときにbasic functionに切り替わっていてたまたま動いた、とかも
あるかもしれません。この辺は調べてみないとわかりませんが・・・
初期状態に何かありそうだ、というのは予想がついていたが、これに限るものでもなさそうである。 現象としては、出来たり駄目だったり、ときに戻ったり(^_^;)、という怪しいものだった。 まぁ、このCQ本のライブラリは、ちょっと放置プレイである。 PythonのBlink(1)に続いて、そういうのが増えてきた。 昼食をはさんで再開する前に、以下のように、新しいライブラリの解凍からインストールまで、作業していたRaspberry Pi61号機だけでなく、さらにターミナルを開いて62号機・63号機にも、コマンドのコピペで済むので作業しておいた。 まだ62号機・63号機のGPIOには何も繋がっていないが、これからそれぞれに別のブツを繋いでいく計画である。(^_^)

さて、こうなると、新しく知った低水準のGPIOアクセスを、C言語のプログラムとしてきちんと整理する、というのが第一目標である。 まずは既に動いている「blink.c」を、15ビット個別制御バージョンまで拡張である。 「blink.c」の冒頭にあるピン定義の「#define PIN RPI_GPIO_P1_11」であるが、これは ヘッダファイル の中に、Raspberry Piバージョン2の定義として「RPI_GPIO_P1_11 = 17」というのがあったので、11ピンを指定するにはGPIOポート番号の「17」を使えばいい、と判った。 つまり、先に作ったCプログラムの定数配列「int assign[15] = {17,18,27,22,23,24,25,4,2,3,10,9,11,8,7};」が使えるのでは、と期待できる。 そして、数分ほどで、以下のようなプログラムを作って、Raspberry Piにrcpして、「gcc -o blink blink.c -l bcm2835」でコンパイルして、「sudo ./blink」すると、何度でも確実に、15ビットのLEDが全て点滅する、と確認できた。(^_^)

#include <bcm2835.h>

int main()
{
	int i, j;
	int assign[15] = {17,18,27,22,23,24,25,4,2,3,10,9,11,8,7};

	if (!bcm2835_init())
		return 1;

	for (i=0; i<15; i++) {
		bcm2835_gpio_fsel(assign[i], BCM2835_GPIO_FSEL_OUTP);
	}

	for (j=0; j<7; j++) {
		for (i=0; i<8; i++) {
			bcm2835_gpio_write(assign[i], HIGH);
		}
		for (i=8; i<15; i++) {
			bcm2835_gpio_write(assign[i], LOW);
		}
		bcm2835_delay(500);
		for (i=0; i<8; i++) {
 			bcm2835_gpio_write(assign[i], LOW);
		}
		for (i=8; i<15; i++) {
			bcm2835_gpio_write(assign[i], HIGH);
		}
		bcm2835_delay(500);
	}

	bcm2835_close();
	return 0;
}
こうなればC言語の世界なので、いちいちネットで「ビット排他的論理和」「ビット論理積」「ビットシフト」などの記号を調べて思い出したものの、以下のように無限ループでLEDを点灯させる改訂版の「blink.c」はすぐに出来た。 8ビットのGPIO汎用ポートを1msecのインターバルごとに2進数で0から255までカウントアップして、ゼロに戻るとGPIO拡張ポートが1だけカウントアップし、これが7ビットの拡張ポートで0から127まで回ると一周である。

YouTube
#include <bcm2835.h>

int assign[15] = {17,18,27,22,23,24,25,4,2,3,10,9,11,8,7};

void GPIO_out(int data, int mode){
	int i;
	mode = mode & 1;	// mode=0 : active high / mode=1 : active low
	data = data & 255;
	for (i=0; i<8; i++) {
		bcm2835_gpio_write( assign[i], ( ( (data >>i) & 1) ^ mode ) );
	}
}

void EXT_out(int data, int mode){
	int i;
	mode = mode & 1;	// mode=0 : active high / mode=1 : active low
	data = data & 127;
	for (i=0; i<7; i++) {
		bcm2835_gpio_write( assign[i+8], ( ( (data >>i) & 1) ^ mode ) );
	}
}

int main()
{
	int i, j;

	if (!bcm2835_init())
		return 1;
	for (i=0; i<15; i++)
		bcm2835_gpio_fsel(assign[i], BCM2835_GPIO_FSEL_OUTP);

	EXT_out(0, 1);
	GPIO_out(0, 1);

	while(1){
		for (j=0; j<128; j++) {
			EXT_out(j, 1);
			for (i=0; i<256; i++) {
				GPIO_out(i, 1);
				bcm2835_delay(1); // 1msec interval
			}
		}
	}

	EXT_out(0, 1);
	GPIO_out(0, 1);

	bcm2835_close();
}
これを61号機では、「LED_blink」とリネームして、「sudo nano .profile」からの追加によって、Blink(1)の自動点滅と合わせて起動時に呼ばれるようにセットすることにした。 また、Raspberry PiをHALTせずにリブートするのは「sudo reboot」というのも知った。 なかなか進んだところで、今日はここまでである。(^_^)

2013年6月28日(金)

もう週末である。 朝、研究室に出て来ると、卒業生の山村さんから こんな素敵なプレゼント が届いていた(^_^)。 山村さんの作品「おはなしパネル」は今年もオープンキャンパスで展示するので、さっそく明日、展示プロジェクトの「40虎」メンバーに紹介しよう。 関連して、「SUACインスタレーション」 のページに加筆改訂して、オープンキャンパスでの展示記録と「動態保存作品」を付記した。 今日は、ゼミのミーティングに加えて、企画立案演習でコマ撮りアニメに初挑戦(^_^;)という無謀なグループを応援するアポも入っている。

ゼミまでの時間にちょっと実験、というのを思いついたのでやってみた。 水曜日に作った「LED_blink」(起動時に自動実行)のソースは これ であるが、要するに7+8=15ビットのLEDをバイナリ2進数としてインクリメントして表示している。 ただしメインルーブでは以下のように、インクリメントするたびに「bcm2835_delay(1); // 1msec interval」を入れている。

	while(1){
		for (j=0; j<128; j++) {
			EXT_out(j, 1);
			for (i=0; i<256; i++) {
				GPIO_out(i, 1);
				bcm2835_delay(1); // 1msec interval
			}
		}
	}
この状態だと、下位8ビットが一周するのに256msec、その上の7ビットのLSBは512msecの点灯と消灯を繰り返すのでおよそ1秒の間隔で点滅し、7ビットのMSBは64秒、つまりおよそ1分間というインターバルで点滅している。 この、「1msecのウェイト」を取ったらどうなるのか、というのはまず、実験してみる意義がある。 Raspberry Piの、このGPIOアクセス手法での最高速度を見てみたい、という事である。

そこで これ の「bcm2835_delay(1); // 1msec interval」を消した「blibk.c」 を作って、 Raspberry Piにrcpして、「gcc blink.c -l bcm2835」でコンパイルして、「sudo ./a.out」すると、15ビットの最上位ビットのLEDだけがわずかに高速点滅しているように見えて、他のビットは全て点灯しているだけ、つまり「相当に高速」(^_^;)と判明した。

これでは判らないので、次に以下のように、「15ビットのうち下位8ビットを最高速度でインクリメント表示する」というループを1000回ループするごとに、15ビットのうち上位7ビットを1だけインクリメントする、というように変更した。1000分の1のスローモーションという事である。 すると7ビットのうち「ビット3」をストップウォッチで測ったら、およそ2.7秒程度の点灯/消灯時間だった。

#include <bcm2835.h>

int assign[15] = {17,18,27,22,23,24,25,4,2,3,10,9,11,8,7};

void GPIO_out(int data, int mode){
	int i;
	mode = mode & 1;	// mode=0 : active high / mode=1 : active low
	data = data & 255;
	for (i=0; i<8; i++) {
		bcm2835_gpio_write( assign[i], ( ( (data >>i) & 1) ^ mode ) );
	}
}

void EXT_out(int data, int mode){
	int i;
	mode = mode & 1;	// mode=0 : active high / mode=1 : active low
	data = data & 127;
	for (i=0; i<7; i++) {
		bcm2835_gpio_write( assign[i+8], ( ( (data >>i) & 1) ^ mode ) );
	}
}

int main()
{
	int i, j, k;

	if (!bcm2835_init())
		return 1;
	for (i=0; i<15; i++)
		bcm2835_gpio_fsel(assign[i], BCM2835_GPIO_FSEL_OUTP);

	EXT_out(0, 1);
	GPIO_out(0, 1);

	while(1){
		for (j=0; j<128; j++) {
			EXT_out(j, 1);
			for (k=0; k<1000; k++) {
				for (i=0; i<256; i++) {
					GPIO_out(i, 1);
				}
			}
		}
	}

	EXT_out(0, 1);
	GPIO_out(0, 1);

	bcm2835_close();
}

YouTube

つまり、7ビットのLSBの点灯時間は2700÷8=337.5ミリ秒となり、この時間内に「GPIO_out(i, 1);」を256000回、実行しているので、計算上は「GPIO_out(i, 1);」を1回実行する処理時間は1.3μ秒、という概算となる。 ただし「GPIO_out()」の中身としては、引き数の8ビットバイナリデータに対して、「bcm2835_gpio_write( assign[i], ( ( (data >>i) & 1) ^ mode ) );」ということで、ビットごとにビットシフト・論理積・排他的論理和の演算を行って8回、GPIOに書き出しアクセスしている。 つまり、「bcm2835_gpio_write()」のGPIOアクセス時間は0.1μ秒のオーダという事である。 これは、なかなかに、速い(^_^)。

ここで2限にゼミがあり、3人の進捗確認や関連映像などで勉強した。 そして午後には、「企画立案演習」でコマ撮りアニメに初挑戦というグループを支援して、久しぶりにFinalCutProで静止画を連結して映像化し、サウンドトラックを仕込む、というデモを行った。 サンプルとして、デスメタル版の「トトロ」→「ポニョ」というメドレーのシュールな映像が出来たのだが、YouTubeに叱られるので残念ながらここには上げられない(^_^;)。 そしてこの4限を挟んで、以下のように研究室LANからLANハブを切り離して、2台となったMacBookAirそれぞれをホストとして、3台のRaspberry Piがネットワーク接続され、MacからsshとかVNCでそれぞれのRaspberry Piに接続できる確認を行った。

このネットワーク接続確認作業そのものは順調に出来たのだが、ここで驚くべき現象に遭遇した。 これまで世界各地/国内各地を一緒に出張してきた、MacBookAirの1号機の方が、突然に画面が真っ暗になったのである。 ただし「落ちた」のではなくて、よく見るとバックライトを最低にした時のように、画面内にうっすらと何か出ている時もあり、また完全に真っ暗になる時もある。 PRAMリセットでも戻らず、リセットとかスリープとかの反応から、落ちるのではなくてバックライトがゼロになる現象のように見えた。 USBに接続しているLANアダプタのLEDは点灯しているので、パソコン本体は生きているのだ。 サブモニタに出すVGAアダプタを差し込むとスパッと画面が出た(バックライト正常復帰)現象もヒントになりそうである。

これは過去のG4PowerBookの時代の経験からすれば、ディスプレイの開け閉めのヒンジ部分で、ディスプレイと本体内のマザーボードを連結しているリボンケーブルの断線とか接触不良、という嫌なパターンである。 しかしタイミングとして、棚ぼたでMacBookAirの2号機がやってきたそのタイミングでこれが起きるというのは、まるで2号さんが来て本妻が拗ねるという古典的シナリオみたいである(^_^;)。

さっそくネットで「MacBookAir + 画面 + 真っ暗」で゛検索してみると、出るわ出るわ、 これ とか これ とか これ とか これ とか これ とか これ とか これ とか これ とか、ゾロゾロ出て来た。 これはもう、MacBookAirの定番トラブルのようである。 そして、 ここ にあった情報から、「システム環境設定>ディスプレイ>ディスプレイの輝度の自動調整」のチェックを外したところ、無事に復帰した。 これは別にOFFでも困らないので、十分である。 以下のように両方のMacBookAirのOFFを確認して、やれやれである。

2013年6月29日(土)

もう週末である。 明日の日曜日の午前には、SUACのプロキシサーバ交換工事が予告されていて仕事に出て来れない(;_;)が、その分、この土曜日の予定は満載である。 バスで大学に出て来て、午後には「40虎」のミーティングがあり、今日は予約しておいた講堂とギャラリーの探検である。 その後、某秘蔵映像の上映会(勉強会)があり、晩にはお楽しみの予定もある(のでクルマでなくバスで来た(^_^;))。 そのバス車内でRaspberry Piで次に実験してみたい事も浮かんできていて、午前中ガシガシ進めよう、と思っていたところにSOS連絡があり、急遽タクシーを大学に呼んで、自宅に行ってスグまた大学に戻ってきたりして時間を取られたが、 可愛いこの子 のためだ、仕方ない。(^_^;)

さてRaspberry Piでの実験であるが、C言語で゛GPIOポートをアクセスするプログラムが出来る、と判ったので、その部分ではPythonのGPIOアクセスはパスする事にしたが、せっかく高機能なRaspberry Piなので、あれこれ他の仕事も同時に(時分割多重化)行うためには、やはり標準推奨環境であるPythonは必要だ、という当たり前の事に気付いたのがスタートラインである。 すっかり油断して、もうぼちぼちPythonは忘却の彼方に行きかけていたが、ふーみん本の残された勉強項目などは、ほとんどPythonベースのアプローチなのだった。 そこでまず、残っていた「blink.c」をベースに、以下のような「test.c」を作って、まずは「コマンドラインとのインターフェース」という、C言語の標準入出力「stdio.h」の復習から始めることにした。「stdlib.h」と「fcntl.h」は、ファイルアクセス等で必要だ、とエラーが出たら追加することにした。

#include <stdio.h>
#include <bcm2835.h>

int assign[15] = {17,18,27,22,23,24,25,4,2,3,10,9,11,8,7};

void GPIO_out(int data, int mode){
	int i;
	mode = mode & 1;	// mode=0 : active high / mode=1 : active low
	data = data & 255;
	for (i=0; i<8; i++) {
		bcm2835_gpio_write( assign[i], ( ( (data >>i) & 1) ^ mode ) );
	}
}

void EXT_out(int data, int mode){
	int i;
	mode = mode & 1;	// mode=0 : active high / mode=1 : active low
	data = data & 127;
	for (i=0; i<7; i++) {
		bcm2835_gpio_write( assign[i+8], ( ( (data >>i) & 1) ^ mode ) );
	}
}

int main(int argc, char *argv[])
{
	int i, j, k;

	if (!bcm2835_init()){
		printf("GPIO is not found.\n");
		return 1;
	}
	for (i=0; i<15; i++)
		bcm2835_gpio_fsel(assign[i], BCM2835_GPIO_FSEL_OUTP);
	EXT_out(0, 1);
	GPIO_out(0, 1);

	printf("argc = %d\n", argc);
	printf("argv[0] = %s\n", argv[0]);
	for(j=1; j<argc; j++){
		printf("argv[%d] = %s\n", j, argv[j]);
	}

/*
	while(1){
		for (j=0; j<128; j++) {
			EXT_out(j, 1);
			for (k=0; k<1000; k++) {
				for (i=0; i<256; i++) {
					GPIO_out(i, 1);
				}
			}
		}
	}
*/
	EXT_out(0, 1);
	GPIO_out(0, 1);
	bcm2835_close();
}
この結果は以下のようになり、無事にargcとargvの意味と、コマンドラインからオプション付きで呼び出すやり方を思い出した。 こんなC言語の基本中の基本は、初めて触れたのはもう25年以上の昔で、たぶん10年以上、まったく書いていなかった(^_^;)。

ここでしばし実験して、以下のような GPIO_out.c を完成させた。 これはオプションとして3つの数値が必要で、第1パラメータはGPIOポートの8ビット(port=0)かEXTポートの7ビット(port=1)かを指定、第2パラメータはそのデータ(GPIOなら0-255、EXTなら0-127)、第3パラメータは正論理にら0で負論理なら1である。

#include <stdio.h>
#include <bcm2835.h>

int assign[15] = {17,18,27,22,23,24,25,4,2,3,10,9,11,8,7};

void GPIO_out(int data, int mode){
	int i;
	mode = mode & 1;	// mode=0 : active high / mode=1 : active low
	data = data & 255;
	for (i=0; i<8; i++) {
		bcm2835_gpio_write( assign[i], ( ( (data >>i) & 1) ^ mode ) );
	}
}

void EXT_out(int data, int mode){
	int i;
	mode = mode & 1;	// mode=0 : active high / mode=1 : active low
	data = data & 127;
	for (i=0; i<7; i++) {
		bcm2835_gpio_write( assign[i+8], ( ( (data >>i) & 1) ^ mode ) );
	}
}

int main(int argc, char *argv[])
{
	int i, p[3];

	if(argc != 4){
		printf("Format Error (;_;)\n");
		printf("usage : GPIO_out para1 para2 para3\n");
		printf("\tpara1 = port : 0=GPIO[0-7], 1=EXT[0-6]\n");
		printf("\tpara2 = data : 0-255(GPIO), 0-127(EXT)\n");
		printf("\tpara3 = mode : 0=active_HI, 1=active_LOW\n");
		return 1;
	}
	for(i=0; i<3; i++){
		p[i] = atoi(argv[i+1]);
	}
//	printf("port=%d, data=%d, mode=%d\n", p[0], p[1], p[2]);

	if (!bcm2835_init()){
		printf("GPIO is not found.\n");
		return 1;
	}
	for (i=0; i<15; i++){
		bcm2835_gpio_fsel(assign[i], BCM2835_GPIO_FSEL_OUTP);
	}

	switch(p[0]){
		case 0:
			GPIO_out(p[1], p[2]);
			break;
		case 1:
			EXT_out(p[1], p[2]);
			break;
	}
	bcm2835_close();
}
作った後で数値の範囲外はチェックしていない・・・と思ったら、switch分でportの指定がチェックされていて、さらにサブルーチンではデータとモードのチェックをしていたので、そこそこしっかりしたものなった(^_^;)。 これで、コマンドラインからパラメータを添えて全15ビットの出力ポートを、個々のビットごとの操作でなく、8ビットまたは7ビットの数値として出力指定できる事になった。 これは汎用ツールとして使えるという想定なので、これも「sudo chown root GPIO_out」でオーナーをrootにして、「sudo chmod 4755 GPIO_out」で実行権限を変更した。

このようなGPIO汎用出力ツールが出来たところで、いよいよまずはC言語からのシステムコールとして、この「GPIO_out」に対して、今週やってきた「8ビットのGPIOポートを2進数で0から255までカウントアップして、ゼロに戻るとEXTポートが1だけカウントアップし、これが7ビットのEXTポートで0から127まで回ると一周」というのを試してみることにした。 全て1本のCプログラムの内部で回すのと、いちいちシステムコールを経由するのとで、スピードを比較するというのが第一の目的であり、これをC言語でなくPythonプログラムから同様に実現して実験・比較する、というのが第二の目的(これは忘れかけてきたPython思い出しのリハビリを兼ねる)である。

#include <stdio.h>

int main()
{
	int i, j;
	char s[64];

	for(j=0; j<128; j++){
		sprintf(s,"./GPIO_out %d %d %d", 1, j, 1);
		system(s);
		for(i=0; i<256; i++){
			sprintf(s,"./GPIO_out %d %d %d", 0, i, 1);
			system(s);
		}
	}
}

YouTube

そしてしばしあれこれ試して、上のようなシンプルなプログラムが完成した。 これはgccでなく普通に「cc test.c」としてコンパイルして「./a.out」で実行できるが、やはり、いちいちシステムコールをするという事で、処理スビードは目に見えて遅くなった。 上の動画のように、EXTポートの7ビットのうち「ビット0」をストップウォッチで測ったら、およそ2.9秒程度の点灯/消灯時間だった。 つまり、7ビットのLSBの点灯時間が2900ミリ秒とすると、この時間内に8ビットのGPIOポートを256回、アクセスしているので、計算上は内側のGPIO_out()をシステムコール経由で1回アクセスする処理時間は約11ミリ秒、という概算となる。

2013年7月1日(月)

いよいよ7月、Shetching2013と前期終了の月に突入である。 主催者のMike Kuniavskyからは、"Hi! We're very excited here as we get the last pieces of Sketching in Hardware 2013 ready. Alex and Leah are staring down restaurants and drawing up contracts, while I put together the schedule." などというメイルも届いた。 おいちゃんからは、先週完成した映像作品のサウンドトラックについて改訂依頼が来て、改訂されたVコン映像も届いた。 これは今週中には改訂の作曲をするつもりで、Raspberry Piのpriorityを一つ下げることにした。

先週土曜日は、午後に「40虎」の新入生と、 こんなツアー に行ったりしたが、その後、学生が某秘蔵映像の上映会に集中している傍らで、あれこれ検索して、遂に以下のようなPythonプログラムで、C言語とほぼ同様のシステムコールが出来ることを突き止めていた。

#!/usr/bin/env python
import os
s = './GPIO_out' + ' ' + str(0) + ' ' + str(0) + ' ' + str(1)
os.system(s)
そこでこの日は、2限の「音楽情報科学」と5限のアカペラ補習特訓の合間に、おいちゃん作曲をメインに据えるとして、とりあえず上のPythonプログラムをC言語版と同じようにループで回して速度を測るところまで、やってみる事にした。 考えてみればきちんとPythonプログラムをゼロから書いたことは無かったので、 Python公式ページリファレンス を参照しつつ、という作業である。 for文のループは「in range(x1, x2)」で、初期値x1からX2未満まで繰り返すこと、最後にコロンが無いとエラーになる事(^_^;)などを経て、15分ほどで無事に以下のようなPython版のルーブ実験プログラムが完成した。
#!/usr/bin/env python
import os
for j in range(0, 128):
    s = './GPIO_out' + ' ' + str(1) + ' ' + str(j) + ' ' + str(1)
    os.system(s)
    for i in range(0, 256):
        s = './GPIO_out' + ' ' + str(0) + ' ' + str(i) + ' ' + str(1)
        os.system(s)

YouTube

上の動画のように、EXTポートの7ビットのうち「ビット0」をストップウォッチで測ったら、およそ3.1秒程度の点灯/消灯時間だった。 つまり、7ビットのLSBの点灯時間が3100ミリ秒とすると、この時間内に8ビットのGPIOポートを256回、アクセスしているので、計算上は内側のGPIO_out()をシステムコール経由で1回アクセスする処理時間は約12ミリ秒、という概算となる。 これはまぁ、C言語版とほぼ同様であり、つまりはLinuxのシステムコールにかかるオーバーヘッドが処理時間の大部分を占めている、上位階層の高級言語レベルでの差はほとんど無い、ということになる。 PythonでもC言語並みに処理してくれる、というのは、今後に向けての朗報である。 ここまで゛朝の8時前にアッサリと出来てしまったので、作曲は午後にまとめて行うことにして、2限までの時間、もう少し調べてみることにした。 遠い昔に、SGIのIndyワークステーション(Unix)でも実験していた事だが、完全に忘却した20年ぶりに挑戦して、どれだけ出来るか、成長しているのかいないのかが問われそうだ(^_^;)。

Raspberry Piは高機能なので、いくつもの仕事を時分割多重処理させたい。 LEDを連続点灯するジョブはシステムコールの末尾に「&」を添えればバックグラウンド処理で実行できるが、そのバックグラウンド処理に対して、他のプロセスからリアルタイムにパラメータを引き渡したいのである。 具体的には、Raspberry PiのGPIOポートに、Propeller/Arduino/AKI-H8/Gainerをホストとして開発した「SUAC board」を接続して、その先にある64個のLEDを、単純なON/OFFでなく、個別にPWM制御してみたい。 そのためには、Raspberry PiのPWM制御プログラムに対して、外部から、64個の個々のLEDのPWM値(0-255)を与える必要があるが、いちいち上記のようなシステムコールを経由したのでは、呼び出しのたびに不整脈となる可能性があるので、あくまでRaspberry Piプログラムは「実行しっ放し」にしたいのである。

ふーみん本などではこのような場合、Unixでは定番であるが、この64個のLEDのPWM値を保持するサーバをまず走らせて、PWM点灯プログラムはそのサーバの値を参照しては点灯処理するクライアントとして走らせ、さらにリアルタイムにパラメータを変更するためにサーバにアクセスする別のクライアントも走らせる、という事になる。 これはUnix屋さんにとってはもっとも正当的で、定番の実現手法であり、勉強としてもこの定番をやるべきなのかもしれない。 しかし、過去の経験ではライブComputer Musicの領域では、この手法は「遅い」という先入観があるので(^_^;)、まずは「シェアードメモリ(共有メモリ)」の手法に挑戦してみたい、という事なのである。

さっそく「linux shared memory」調べてみると、 Wikipedia では、 "Both the RedHat and Debian based distributions include it by default. Support for this type of RAM disk is completely optional within the kernel configuration file."とあり、RAM diskという形でdefaultでサポートされている模様である。 そして「ramdisk linux」で検索すると、 これ とか これ とか これ とか これ とか これ とか これ とか、たくさん出て来た。 また、「ramdisk Raspberry Pi」で検索しても、 こんなページ が出て来た。 ただし、 このページ では、何故あなたはRamDiskを使おうとするのか、考え直してみては(^_^;)、という記載もあった。 確かにRaspberry Piでは、HDDでなくSDカード(フラッシュメモリ)を使っているので、よほど多量の読み書きを連続しない限りは、普通にディスクアクセスしたものとしても同じなのかもしれないし、RAMディスクだと、いちいちRaspberry Piが起動するたびに設定する必要がある。

しかしまぁ、使う時に一度だけ「おまじない」で起動する、というのは別に構わない(なんなら起動時の自動実行プログラムとしてRAMdiskの生成処理を加えるのもアリ)という事で、とりあえずやってみる事にした。 kernel.org の記述は難しそうだったので、とりあえずもっともアッサリと記述している ここ に従ってみることにした。 必要なのはたった3行である。 まずは以下のように「/media」の下にディレクトリを作る。 ここでは「/media/ramdisk」とした。

sudo mkdir -p /media/<FOLDERNAME>
次はマウントである。 以下のサンプルは2GBだったが、小さくていいので1KBとしてみた(^_^;)。
sudo mount -t tmpfs -o size=1K tmpfs /media/<FOLDERNAME>
以下はアンマウントである。 これは最後に行うだけである。
sudo umount /media/<FOLDERNAME>
これだけとシンプル過ぎるのが逆に心配だが(^_^;)、とりあえずsshしているターミナルからやってみると、以下のようにちゃんと出来た。 root権限(sudo)で作ったRAMディスクのディレクトリだが、一般ユーザのpiでアクセス出来ることも判った。 そして以下のように、新たに作ってマウントしたRAMディスクには、1つだけしかファイルが置けないらしい事が判明した。 ファイル管理のディレクトリ構造を持たないシンプルなものでも、必要に応じてどんどん「/media」の下にディレクトリを作ればいいので、これは問題ない。(^_^)
pi@raspberrypi ~ $ ls -l /media
合計 0

pi@raspberrypi ~ $ sudo mkdir -p /media/ramdisk
pi@raspberrypi ~ $ ls -l /media
合計 4
drwxr-xr-x 2 root root 4096  6月 23 08:07 ramdisk

pi@raspberrypi ~ $ sudo mount -t tmpfs -o size=1K tmpfs /media/ramdisk
pi@raspberrypi ~ $ ls -l /media
合計 0
drwxrwxrwt 2 root root 40  6月 23 08:07 ramdisk

pi@raspberrypi ~ $ sudo rmdir /media/ramdisk
rmdir: `/media/ramdisk' を削除できません: デバイスもしくはリソースがビジー状態です

pi@raspberrypi ~ $ sudo umount /media/ramdisk
pi@raspberrypi ~ $ sudo rmdir /media/ramdisk
pi@raspberrypi ~ $ ls -l /media
合計 0

pi@raspberrypi ~ $ sudo mkdir -p /media/ramdisk
pi@raspberrypi ~ $ ls -l /media
合計 4
drwxr-xr-x 2 root root 4096  6月 23 08:09 ramdisk

pi@raspberrypi ~ $ sudo mount -t tmpfs -o size=1K tmpfs /media/ramdisk
pi@raspberrypi ~ $ ls -l /media
合計 0
drwxrwxrwt 2 root root 40  6月 23 08:09 ramdisk

pi@raspberrypi ~ $ cp test.py /media/ramdisk/
pi@raspberrypi ~ $ ls -l /media/ramdisk
合計 4
-rwxr-xr-x 1 pi pi 258  6月 23 08:14 test.py

pi@raspberrypi ~ $ cp test.c /media/ramdisk/
cp: `/media/ramdisk/test.c' を書き込んでいます: デバイスに空き領域がありません
cp: `/media/ramdisk/test.c' の拡張に失敗しました: デバイスに空き領域がありません
pi@raspberrypi ~ $ sudo cp test.c /media/ramdisk/
cp: `/media/ramdisk/test.c' を書き込んでいます: デバイスに空き領域がありません
cp: `/media/ramdisk/test.c' の拡張に失敗しました: デバイスに空き領域がありません

pi@raspberrypi ~ $ rm /media/ramdisk/test.py
pi@raspberrypi ~ $ ls -l /media/ramdisk
合計 0
-rwxr-xr-x 1 pi pi 0  6月 23 08:17 test.c

pi@raspberrypi ~ $ cp test.py /media/ramdisk/
pi@raspberrypi ~ $ ls -l /media/ramdisk
合計 4
-rwxr-xr-x 1 pi pi   0  6月 23 08:17 test.c
-rwxr-xr-x 1 pi pi 258  6月 23 08:18 test.py

pi@raspberrypi ~ $ cp test.c /media/ramdisk/
cp: `/media/ramdisk/test.c' を書き込んでいます: デバイスに空き領域がありません
cp: `/media/ramdisk/test.c' の拡張に失敗しました: デバイスに空き領域がありません

pi@raspberrypi ~ $ rm /media/ramdisk/test.py
pi@raspberrypi ~ $ ls -l /media/ramdisk
合計 0
-rwxr-xr-x 1 pi pi 0  6月 23 08:19 test.c

pi@raspberrypi ~ $ rm /media/ramdisk/test.c
pi@raspberrypi ~ $ ls -l /media/ramdisk
合計 0

pi@raspberrypi ~ $ cp test.c /media/ramdisk/
pi@raspberrypi ~ $ ls -l /media/ramdisk
合計 4
-rwxr-xr-x 1 pi pi 232  6月 23 08:19 test.c

pi@raspberrypi ~ $ cp test.py /media/ramdisk/
cp: `/media/ramdisk/test.py' を書き込んでいます: デバイスに空き領域がありません
cp: `/media/ramdisk/test.py' の拡張に失敗しました: デバイスに空き領域がありません

pi@raspberrypi ~ $ ls -l /media/ramdisk
合計 4
-rwxr-xr-x 1 pi pi 232  6月 23 08:19 test.c
-rwxr-xr-x 1 pi pi   0  6月 23 08:19 test.py

pi@raspberrypi ~ $ 
上にあるように、RAMディスク領域には1つしかファイルが置けず、新たに加えようとすると、システム的には加わったようでサイズが0の幽霊状態になっている。 また、cでもpyでも、書き込み権限が無いというのはもしかすると困るので、ここで、このRAMディスク領域(/media/ramdisk)に対して、パラメータファイルの読み書きを実験することにした。 データはバイナリでなくても、CでもPythonでも文字列と数値の相互変換は簡単なので、ここではテキストエディタで読み書きできるように、16進数のテキストファイルとする事にした。
pi@raspberrypi ~ $ rcp nagasm@172.16.65.31:Desktop/test.c .
Password:
test.c                                                           100%  325     0.3KB/s   00:00    

pi@raspberrypi ~ $ cat test.c
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	FILE *fp;	
	if ((fp = fopen("/media/ramdisk/test.txt", "w")) == NULL) {
		printf("file open error!!\n");
		exit(EXIT_FAILURE);
	}
	fputs("0123abc", fp);
	fclose(fp);	
	return 0;
}
pi@raspberrypi ~ $ cc test.c
pi@raspberrypi ~ $ ./a.out
pi@raspberrypi ~ $ ls -l /media/ramdisk
合計 4
-rw-r--r-- 1 pi pi 7  6月 23 08:49 test.txt

pi@raspberrypi ~ $ cat /media/ramdisk/test.txt
0123abc

pi@raspberrypi ~ $ rcp nagasm@172.16.65.31:Desktop/test.c .
Password:
test.c                                                           100%  341     0.3KB/s   00:00    

pi@raspberrypi ~ $ cat test.c
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	FILE *fp;	
	if ((fp = fopen("/media/ramdisk/test.txt", "w")) == NULL) {
		printf("file open error!!\n");
		exit(EXIT_FAILURE);
	}
	fputs("0123abc9876543210ABCDEF", fp);
	fclose(fp);	
	return 0;
}

pi@raspberrypi ~ $ cc test.c
pi@raspberrypi ~ $ ./a.out
pi@raspberrypi ~ $ ls -l /media/ramdisk
合計 4
-rw-r--r-- 1 pi pi 23  6月 23 08:53 test.txt

pi@raspberrypi ~ $ cat /media/ramdisk/test.txt
0123abc9876543210ABCDEF

pi@raspberrypi ~ $ 
そして上のように、root権限でなく一般ユーザのpiでも、RAMディスクに上書きによって新しいファイルを書き出すことが出来る、と確認できたところで1限が終わり、2限の講義が終わって、午後にはもう少しだけ、これを進めることにした。 ファイルの書き出しと読み込みとを2つのプロセスで同時に実行した場合のセマフォが必要かどうか、というのが最後のチェックポイントだからである。 ネットからC言語のファイルアクセスの記録などを20年ぶりに発掘して(^_^;)、以下のように無事に、RAMディスクに64データを16進数の128文字として書き出すプログラム「test1.c」と、それを読み出して表示するプログラム「test2.c」が完成した。
pi@raspberrypi ~ $ rcp nagasm@172.16.65.31:Desktop/test1.c .
Password:
test1.c                                                          100%  381     0.4KB/s   00:00    

pi@raspberrypi ~ $ cat test1.c
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	FILE *fp;
	char ss[3], s[130];
	int i;
	for(i=0; i<64; i++){
		sprintf(ss, "%02x", i*4+3);
		s[2*i] = ss[0];
		s[2*i+1] = ss[1];
	}
	s[128] = '\n';	
	s[129] = 0;	
	if ((fp = fopen("/media/ramdisk/test.txt", "w")) == NULL) {
		printf("file open error!!\n");
		exit(EXIT_FAILURE);
	}
	fputs(s, fp);
	fclose(fp);
	return 0;
}

pi@raspberrypi ~ $ ./test1
pi@raspberrypi ~ $ ls -l /media/ramdisk
合計 4
-rw-r--r-- 1 pi pi 129  6月 23 12:28 test.txt

pi@raspberrypi ~ $ cat /media/ramdisk/test.txt
03070b0f13171b1f23272b2f33373b3f43474b4f53575b5f63676b6f73777b
7f83878b8f93979b9fa3a7abafb3b7bbbfc3c7cbcfd3d7dbdfe3e7ebeff3f7fbff

pi@raspberrypi ~ $ rcp nagasm@172.16.65.31:Desktop/test2.c .
Password:
test2.c                                                          100%  324     0.3KB/s   00:00    

pi@raspberrypi ~ $ cat test2.c
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	FILE *fp;
	char s[130];
	int i;
	if ((fp = fopen("/media/ramdisk/test.txt", "rb")) == NULL) {
		printf("file open error!!\n");
		exit(EXIT_FAILURE);
	}
	for(i=0; i<129; i++){
		s[i] = fgetc(fp);
	}
	s[129] = 0;	

	printf("result :\n%s",s);
	fclose(fp);
	return 0;
}

pi@raspberrypi ~ $ cc test2.c -o test2
pi@raspberrypi ~ $ ./test2
result :
03070b0f13171b1f23272b2f33373b3f43474b4f53575b5f63676b6f73777b
7f83878b8f93979b9fa3a7abafb3b7bbbfc3c7cbcfd3d7dbdfe3e7ebeff3f7fbff

pi@raspberrypi ~ $ 
そこでいよいよ実験である。 作戦としては、RAMディスクのデータファイルを読み出しアクセスするプロセスを無限ループで回して、そこにもう1つのプロセスから同じRAMディスクのデータファイルを書き換えに行ったその瞬間にカチ合うとエラーが出るか、どのくらいの頻度で起きうるか、を調べたいのである。 予備的に調べてみると、システム表示の「printf()」は、相当に中身のデータが溜まるまでバッファリングしていて、刻々のモニタに使えないと判明した。 そこで、「RAMディスクのデータファイルを読み出しアクセスするプロセス」として、以下のように「test2.c」を改訂して、これをRaspberry Piにrcpして、「gcc test2.c -l bcm2835 -o test2」でコンパイルして、「sudo ./test2 &」でバックグラウンド実行させた。 128バイトのファイル読み込みをopenして、読み込みして、closeして、というループを100回。回したところでようやく、GPIO+EXTの15ビットのLSBをインクリメントする、過酷な事実上の無限ループ(GPIO+EXTの15ビットが全てカウントアップするループを10回、回すので1時間以上かかる)である。
#include <stdio.h>
#include <stdlib.h>
#include <bcm2835.>

int assign[15] = {17,18,27,22,23,24,25,4,2,3,10,9,11,8,7};
FILE *fp;
char s[130];

void GPIO_out(int data, int mode){
	int i;
	mode = mode & 1;	// mode=0 : active high / mode=1 : active low
	data = data & 255;
	for (i=0; i<8; i++) {
		bcm2835_gpio_write( assign[i], ( ( (data >>i) & 1) ^ mode ) );
	}
}

void EXT_out(int data, int mode){
	int i;
	mode = mode & 1;	// mode=0 : active high / mode=1 : active low
	data = data & 127;
	for (i=0; i<7; i++) {
		bcm2835_gpio_write( assign[i+8], ( ( (data >>i) & 1) ^ mode ) );
	}
}

void data_file_read(){
	int i;
	if ((fp = fopen("/media/ramdisk/test.txt", "rb")) == NULL) {
		printf("\nread error!!\n");
	}
	else{
		for(i=0; i<129; i++){
			s[i] = fgetc(fp);
		}
		s[129] = 0;	
		fclose(fp);
	}
}

int main(void)
{
	int i, j, l, k=0;
	if (!bcm2835_init()){
		printf("GPIO is not found.\n");
		return 1;
	}
	for (i=0; i<15; i++){
		bcm2835_gpio_fsel(assign[i], BCM2835_GPIO_FSEL_OUTP);
	}
	EXT_out(0, 1);
	GPIO_out(0, 1);
	for (l=0; l<10; l++) {
		for (j=0; j<128; j++) {
			EXT_out(j, 1);
			for (i=0; i<256; i++) {
				GPIO_out(i, 1);
				for (k=0; k<100; k++) {
					data_file_read();
				}
			}
		}
	}
	GPIO_out(0, 1);
	EXT_out(0, 1);
	bcm2835_close();
	return 0;
}
これを走らせている間に、「RAMディスクのデータファイルを上書きで書き換えに行く」という、先に作った「./test1」を、ターミナルからのコピペでマニュアル連打したが、まったく「printf("\nread error!!\n");」が表示されなかった。 ただしこのメッセージも256バイトあたりのバッファに積まれている可能性があるので、いじわるテストとして、「test1.c」を以下のように改訂して実験してみた。
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	FILE *fp;
	char ss[3], s[130];
	int i, j;
	for(i=0; i<64; i++){
		sprintf(ss, "%02x", i*4+3);
		s[2*i] = ss[0];
		s[2*i+1] = ss[1];
	}
	s[128] = '\n';	
	s[129] = 0;
	for(j=0; j<50000; j++){
		if ((fp = fopen("/media/ramdisk/test.txt", "w")) == NULL) {
			printf("file open error!!\n");
			exit(EXIT_FAILURE);
		}
		fputs(s, fp);
		fclose(fp);
	}
	printf("50000 times write access done.\n");
	return 0;
}
すると、「sudo ./test2 &」で過酷な無限ループが回っているところに、「RAMディスクのデータファイルを上書きで50000回、書き換えに行く」という「./test1」を実行すると、10数秒で「50000 times write access done.」が返ってくるが、何度やっても書き込みopenも読み出しopenもエラーが表示されることは無かった。 もしかすると、Linuxが内部的に待たせているのかもしれないが、とりあえず、このRAMディスクを経由して2つのプロセス間のパラメータ通信を出来そうだ、と確認できた。 ここで4限も半ばとなり、明日の企画立案演習の準備もあるので、今日はここまでである。 なかなかに進んだし、何よりC言語のリハビリとなった。(^_^)

2013年7月3日(水)

さて水曜日である。 昨日の火曜日は1限と4-5限の合間に、改訂依頼が来たおいちゃん作曲に取りかかって没頭した。 そして今日は、1限に藤本さんのアポを受けてArduinoシステムの相談に乗り、その後2限に再びおいちゃん作曲に没頭して、遂に改訂版の映像作品サウンドトラックが完成した(^_^)。 これであとは映像作品の完成を待つばかりである。

夕方にはいったん帰宅してハムスターの餌やりしてから大学に戻ってアカペラなので、それまで午後の3時間ほどが今日のRaspberry Piタイムである。 Sketching2013に持参するために、Raspberry Piで多数LEDのPWM制御の実験用に、まずは秋月電子の青色LEDを発注した。 そしてその後で気付いたのだが、もともと今年のSketching2013での発表のメインは、Raspberry PiではなくてSUAC boardとPropellerであった事を思い出して(^_^;)、以下のようにシリコンバレーに持参するものを並べてみた。 去年のICMCに持参して実験を終えていた「Propeller+XBee」とか、PropellerでコントロールするSUAC boardなど、すっかり忘却していたところを思い出して(動作確認を兼ねて)、ここにPropellerの代わりにRaspberry Piを繋いで、64ビットLEDのPWM実験とか8チャンネルA/Dの取り込み実験とかをしておく必要があったのだ。 思い出ししているだけで半日以上かかる、という確信がある(^_^;)。

そしてまず、さすがのエボルタ電池も半年経てば抜けている(^_^;)、という事実を確認しつつ、 Propeller日記part2・第3話 の「2012年9月17日(月/祝)」のところまで行っていた、 「XBee_013.maxpat」と「XBee_007.spin」 との動作を確認できた。 OLED内のPropellerのプログラムはとりあえずEEPROMに書かれていて、制御側のMaxプログラムにさらに改良の余地がある、という状態だったようなので、以下の改良点(課題)を再掲しておくことにした。 これはもしかすると、Sketching2013に行っている合間に、進展することになるかもしれない。

残った「中心座標」「半径」「カラー」の指定の4種類、8コマンドの部分でストップした。
Propellerの方のプログラムはコマンドごとに変数バッファを持っているので、 そのまま
延長して拡張していけばいいのだが、Maxの方(jit.lcd)のモニタ画面の描画に関しては、
これまで作ったサブパッチでは、全て共通にカラー指定を行っていた事に気付いた。
これはMaxでは基本のイベント主義、つまり「最後に指定された情報が有効」というもの
だが、 ここまでに定義したコマンドと、新たに円ごとにカラー指定をした場合、Propeller
の方ではちゃんと受けられるのに対して、 Maxの方(jit.lcd)のモニタ画面では、カラー
情報が上書きされるので、そのままではいかない(^_^;)。
Max側で、カラーを記憶しておくメカニズムを、いくつものサブパッチに対して横断的に
新設する必要がある。
これに続いて、久しぶりにSUACboardを取り出してきて、 Propeller日記part3・第2話 の「2013年1月11日(金)」のところにあった、64個のLEDの個別制御PWMの 「SUACboard_test009.maxpat」と「board_010.spin」 との動作と、 Propeller日記part3・第3話 の「2013年2月12日(火)」のところにあった、外付けA/Dコンバータのデータ取得の 「SUACboard_test011.maxpat」と「board_011.spin」 との動作を確認した。 ちょうどふーみん本にあった「Raspberry PiでMIDI」のところから、ローランドの新しいMIDIインターフェース「UM-ONE mk2」も買ったので、このドライバもローランドのダウンロードページから落としてインストールした。 以下が、新しい「UM-ONE mk2」の姿である。

SUACboardにRaspberry Piを繋ぐ時には、Propellerボードを取り去らないといけないので、とりあえず、PropellerのEEPROMには、MIDIを受けてLEDを個別PWM制御する、という「board_010.spin」を書き込んでおいた。 Sketchingに行く際には、Propellerのメインプログラムから呼ばれるライブラリ等も必要なので、「Propeller」ディレクトリごと全部、持参する必要がある。 忘れないように、ここで2台のMacBookAirにも、「UM-ONE mk2」のドライバをインストールしておいた。 これで、Propellerを載せたSUACboardについて、合わせてハードウェアの動作チェックまで出来たことになり、これをRaspberry Piに接続する、という準備までが完了した。 ちょうど16時となり、今日はここまでである。

2013年7月4日(木)

この日は3限にゼミの「電子回路講座」があるだけの日ということで、午前中に慎重にケーブルをハンダ付けして(SUACboardのGainerのピンが「P板.com」のミスで左右反対というバグを思い出すのに苦労(^_^;))、以下のようにRaspberry Piの62号機のGPIOコネクタから、SUACboardのGainerソケットに立てたピンとを接続した。

SUACboardの入出力バスラインはPropellerに直結できる3.3Vレベルと設計していて、5V系のデバイスはCMOSのスレショルド(2.5V)以上として3.3Vレベルに対応し、5V系の入力デバイスのバスラインからはビットごとに1kΩの抵抗を入れている。 これにより、3.3V系であるRaspberry Piとは、そのまま接続できることになる。 目標はもちろん、Raspberry Piでも64個のLEDの個別PWM制御であるが、まずはラッチにデジタルデータを書き込んでの表示からである。 Raspberry PiとSUACboardの接続の仕様は以下である。

そこで、Raspberry Pi61号機の15個のLED出力とはポートのアサインが違うので、無駄な流用をせず、SUACboard接続用に、C言語プログラムを書き換えることにした。 データバスのラッチデータは「GPIO_out(int data, int mode)」で共通であるが、拡張ビットの「EXT_out(int data, int mode)」は下位2ビットとMSBは不要であり、同じ関数名では混乱するので、関数名を「Select_out(int data)」と変えて、ラッチバスの「下げ」「上げ」までをセットとした。 Raspberry Piのビン番号の対応を定義した「assign[15]」を二重にするとかえって混乱するので、これは共通とした。

以下がその最初のプログラム「test.c」である。 ここでは、1列目と5列目の8ビットをインクリメントしてバイナリ表示し(ほとんど肉眼では点灯しっ放し)、それが1巡するたびに2列目と6列目の8ビットをインクリメントしてバイナリ表示し、それが1巡するたびに3列目と7列目の8ビットをインクリメントしてバイナリ表示し、それが1巡するたびに4列目と8列目の8ビットをインクリメントしてバイナリ表示する。

#include <stdio.h>
#include <bcm2835.>

int assign[15] = {17,18,27,22,23,24,25,4,2,3,10,9,11,8,7};

void GPIO_out(int data, int mode){
	int i;
	mode = mode & 1;	// mode=0 : active high / mode=1 : active low
	data = data & 255;
	for (i=0; i<8; i++) {
		bcm2835_gpio_write( assign[i], ( ( (data >>i) & 1) ^ mode ) );
	}
}

void Select_out(int data){
	int i;
	data = data & 7;
	for (i=0; i<3; i++) {
		bcm2835_gpio_write( assign[i+10], ( (data >>i) & 1)  );
	}
	bcm2835_gpio_write( assign[13], 0 );
	bcm2835_gpio_write( assign[13], 1 );
}

int main(void)
{
	int i, j, k, l;
	if (!bcm2835_init()){
		printf("GPIO is not found.\n");
		return 1;
	}
	for (i=0; i<15; i++){
		bcm2835_gpio_fsel(assign[i], BCM2835_GPIO_FSEL_OUTP);
	}
	for (l=0; l<8; l++) {
		GPIO_out(0, 0);
		Select_out(i);
	}
	for (l=0; l<256; l++) {
		GPIO_out(l, 0);
		Select_out(3);
		Select_out(7);
		for (k=0; k<256; k++) {
			GPIO_out(k, 0);
			Select_out(2);
			Select_out(6);
			for (j=0; j<256; j++) {
				GPIO_out(j, 0);
				Select_out(1);
				Select_out(5);
				for (i=0; i<256; i++) {
					GPIO_out(i, 0);
					Select_out(0);
					Select_out(4);
				}
			}
		}
	}
	bcm2835_close();
	return 0;
}

YouTube

ここで3限となり、ゼミの「Gainer講座」「Arduino講座」をした後に、上のYouTubeを上げたりして、一つの区切りとなった。 そしてRaspberry PiのPWMでなく、さらに修正希望のメイルが届いたおいちゃん作曲に没頭して(しかし結局は納得できず(^_^;))、この日は終わりとなった。 ハンダ付けとプログラミングと作曲、という、脳の異なった領域をそれぞれ駆使した一日であった。

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が届いていることをを確認した。 いろいろあったが、収穫と進展もあったということで、今日はおしまいである。

2013年7月6日(土)

週末になった。 今日は午後に「40虎」の勉強会(作品映像鑑賞会)ということで、学生が映像を見ている裏でRaspberry Piを進めることにした。 昼前から研究室で準備にとりかかったのは、昨日の最後に、OSCメッセージを受け取って処理するという部分にまで行けなかったので、その突破口を探ることだった。 昨日の流れは、整理してみると以下のようなものだった。
  1. Raspberry Piに既にOSCそのものはインストール済を確認
  2. 「Simple OSC」をゲットした
  3. Simple OSCの実行には「pyOSC」が必要ということで「pyOSC」をゲットした
  4. 「Simple OSC」内の「simpleOSC.py」を見ると、ちょっと違うっぽい??
  5. 「Simple OSC」は学生など初学者向けだという説明
  6. 「pyOSC_examples」内の「basic_send.py」と「basic_receive.py」を見ると、欲しかったもの
  7. 「Simple OSC」のインストールを待って、まず「pyOSC」をインストール
  8. 「basic_send.py」を実行させてMaxへのOSCメッセージ送信を確認
  9. 「basic_receive.py」実行させるとMaxからのOSCメッセージ到達は確認できたもののメッセージ自体の取得ハンドラが無い
  10. 「pyOSC」内の「OSC.py」は長大でえらく大変そう(^_^;)
つまり、混乱を避けて「Simple OSC」はダウンロードしたもののインストールせず、上位互換フルセット?の「pyOSC」に挑戦して行き詰まっていた、という事である。 ここまで整理して気付いたのが、「Simple OSC」内には「simpleOSC.py」とともに、以下の「app_example.py」があるのを見落としていたことである。 そして以下の「app_example.py」を眺めてみると、ちょうど長大な「OSC.py」で困っていたものがスッキリとしているように思えてきた。 そうか、学生など初学者向けでちょうど良かったのである。(^_^;)
#!/usr/bin/env python

from simpleOSC import initOSCClient, initOSCServer, setOSCHandler, sendOSCMsg, closeOSC, \
     createOSCBundle, sendOSCBundle, startOSCServer
    
def myTest():
    import time # in this example we will have a small delay in the while loop
    
    initOSCClient() # takes args : ip, port
    initOSCServer() # takes args : ip, port, mode --> 0 for basic server, 1 for threading server, 2 for forking server
    
    # bind addresses to functions 
    setOSCHandler('/check', checkcheckcheck)

    startOSCServer() # and now set it into action

    print 'ready to receive and send osc messages ...'

    try:
        while 1:
            sendOSCMsg("/test", [444]) # !! it sends by default to localhost ip "127.0.0.1" and port 9000 

            # create and send a bundle
            bundle = createOSCBundle("/test/bndlprt1")
            bundle.append(666) # 1st message appent to bundle
            bundle.append("the number of the beast") # 2nd message appent to bundle
            sendOSCBundle(bundle) # !! it sends by default to localhost ip "127.0.0.1" and port 9000 

            time.sleep(0.5) # you don't need this, but otherwise we're sending as fast as possible.

    except KeyboardInterrupt:
       print "closing all OSC connections... and exit"
       closeOSC() # finally close the connection before exiting or program.

def checkcheckcheck(addr, tags, data, source):
    print "CHECK CHECK CHECK..."
    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

if __name__ == '__main__': myTest()
そこで、昨日の手順と同様に、以下のように SimpleOSC_0.3.2.zip を「SimpleOSC.zip」とリネームしてRaspberry Pi62号機にrcpしてunzipしてインストールしてみた。 これで、指定されたように「pyOSCをインストールした後に」「SimpleOSCインストールした」という事になった。
Last login: Sat Jul  6 08:52:29 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 21:27:04 2013

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

pi@raspberrypi ~ $ rcp nagasm@172.16.65.31:Desktop/SimpleOSC.zip .
Password:
SimpleOSC.zip                                                            100% 7355     7.2KB/s   00:00    

pi@raspberrypi ~ $ unzip SimpleOSC.zip
Archive:  SimpleOSC.zip
   creating: SimpleOSC/
  inflating: SimpleOSC/app_example.py  
   creating: __MACOSX/
   creating: __MACOSX/SimpleOSC/
  inflating: __MACOSX/SimpleOSC/._app_example.py  
  inflating: SimpleOSC/readme.txt    
  inflating: SimpleOSC/setup.py      
  inflating: __MACOSX/SimpleOSC/._setup.py  
  inflating: SimpleOSC/simpleOSC.py  
  inflating: __MACOSX/SimpleOSC/._simpleOSC.py  
  inflating: __MACOSX/._SimpleOSC    

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

pi@raspberrypi ~/SimpleOSC $ ls -l
合計 20
-rw-rw-r-- 1 pi pi 3097 12月 13  2012 app_example.py
-rw-rw-r-- 1 pi pi 4153 12月 13  2012 readme.txt
-rw-rw-r-- 1 pi pi  588 11月 29  2012 setup.py
-rw-rw-r-- 1 pi pi 3292 12月 13  2012 simpleOSC.py

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

pi@raspberrypi ~/SimpleOSC $ 
ここで改めて「Simple OSC」内の「simpleOSC.py」を見ると、「basic_send.py」や「basic_receive.py」に比べてdefineばかり・・・と思ったが、関係ないことは無くて、まずはここから実験すべき、と思えた。 昨日の夕方にあれこれ悩んで調べたことで、少しは視界が開けてきたようで、無駄でもなかったのかもしれない。 そこで「simpleOSC.py」を「test.py」とリネームして、少しずつ関係なさそうなところを削ってテストランさせる、という実験を進めている中で、遂に「判って」しまった(^o^)。 「simpleOSC.py」をリネームした「test.py」では、何も起きないのである。 そして、見落としていた「app_example.py」をリネームした「test.py」で実験していて、突然に気付いたのである。 「app_example.py」の冒頭の
from simpleOSC import initOSCClient, initOSCServer, setOSCHandler, sendOSCMsg, closeOSC, \
     createOSCBundle, sendOSCBundle, startOSCServer
の意味が判って、そこからイモヅル式に全ての繋がりが判ったのである。 defaultのpathとかの詳しいところは不明だが、上の「from simpleOSC import」というのは、インストールされた「simpleOSC.py」に並んでいる定義をインクルード(import)する、という事だったのだ。 そして参照されている「simpleOSC.py」の冒頭では
from OSC import OSCServer, ThreadingOSCServer, ForkingOSCServer, OSCClient, OSCMessage, OSCBundle
とあった。これが、先にインストールしておくべき、という事だったpyOSCの中にあった「OSC.py」なのである。 そして参照されている「OSC.py」の冒頭では
import math, re, socket, select, string, struct, sys, threading, time, types
とあった。 おそらくこれは、apt-getでインストール完了していたOSCのパッケージで定義されているのだろう。 これにより、結局のところ、defaultでpathとかの折り合いは済んでいる模様で(^_^;)、「app_example.py」をリネームした以下の「test.py」は、ちゃんと定期的にMaxにOSCメッセージを送り、またMaxからのOSCメッセージに反応するだけでなくその中身のデータを表示する、という事まで確認できた。
#!/usr/bin/env python

from simpleOSC import initOSCClient, initOSCServer, setOSCHandler, sendOSCMsg, closeOSC, \
     createOSCBundle, sendOSCBundle, startOSCServer
import time, threading

def check1(addr, tags, data, source):
    print "%s" % data

def check2(addr, tags, data, source):
    print "data = %s" % data

initOSCClient(ip='172.16.65.31', port=7001)
initOSCServer(ip='172.16.65.62', port=7003, mode=0)   
setOSCHandler('/check1', check1)
setOSCHandler('/check2', check2)
startOSCServer()
sendOSCMsg("/test", [444])
bundle = createOSCBundle("/test/bndlprt1")
bundle.append("Hello, World !")
sendOSCBundle(bundle)
try:
    while 1:
        time.sleep(3) 
except KeyboardInterrupt:
    print "closing all OSC connections... and exit"
    closeOSC()

地味なようでこれは大いなる進展なので、ここで忘れないうちに、ターミナルを2画面さらに開いて、Raspberry Pi61号機と63号機にも「SimpleOSC.zip」をrcpしてunzipしてインストールした。 また、いろいろ実験したRaspberry Pi62号機のカレントディレクトリには「SimpleOSC.pyc」「USC.pyc」というファイルが出来ていたが、調べてみるとこれはコンパイルされたPythonプログラムということで、消しても「test.py」は問題なく動いたので消しておいた。 これで道具立てがぼちぼち揃ってきたが、40虎の映像作品名作鑑賞会をついつい観入ってMaxプログラミングが滞ってしまったので(^_^;)、今日はここまでとなった。

2013年7月7日(日)

日曜日の朝から「サンフランシスコ空港でアシアナ機が着陸失敗」というニュースが届いたが、11日後にそのサンフランシスコ空港に僕のフライト(ANA)も着陸する。 Sketching2013の主宰者Mike Kuniavskyからは、全体の詳細なスケジュールとともに直前連絡のメイルも届いた。 午後にアカペラ合宿の行先検討会議で学生2人が研究室に来る予定があるが、午前中はRaspberry Piタイムだ、と思っていたところ、某将棋杯の解説が山崎7段ということで、七夕のキャスティング「矢内・山崎漫才」をチェックしないと・・・と予定が大幅に変わった(^_^;)。

それでも、朝から頑張って、将棋ネット中継の開始までに、かつて 未踏プロジェクト で作ったMaxパッチに、昨日ぶつかっていた壁を突破するネタがあったのを思い出して、無事に以下のようなパッチを作った。 これで、OSC経由でRaspberry Piに64個のLEDのPWMデータを64個の16進データ(00-FF)をまとめた文字列として与えられる。 そして、午前中は「矢内・山崎漫才」を囃し立てる某掲示板のスレを2本同時に眺めつつ、最後はグタグダとなった将棋とともに過ごした。

午後にはアカペラの代表2人と、この夏休みの「合宿」の行き先を検討して浜北森林公園と決定した。 そして作業再開、まず午前に完成させた、「幅64ポイント、値0-255」のテーブルデータをOSCでポート7003に送る、という UDP_test_01.maxpat を呼び出し、さらに昨日作った、このOSCメッセージを受けて表示する、という Pythonプログラム を呼び出し、Raspberry Pi62号機で以下のようにOSC受信を確認した。

そして次に、「2013年7月1日(月)」にやったRAMディスクの実験のところをなぞって、以下のような手順でRaspberry PiにRAMdiskを設定した。 これはスクリプト化してRaspberry Piの起動時に呼び出すようにする事も、あれこれうまく行ったら検討しよう。

ここで、手元に実験途中で残してきたC言語プログラムを確認すると、 RAMdiskに50000回、test.txtを書き込むプログラムRAMdisk内のtest.txtを15ビットのGPIOポートのLEDに表示するRaspberry Pi61号機で動くプログラムSUACboardの64個のLEDを個別ON/OFF表示するRaspberry Pi62号機で動くプログラム、 の3本である。 コンパイルは3本目の場合であれば「gcc test3.c -l bcm2835」で、実行は「sudo ./a.out」であった。

そこでまず、 RAMdiskにtest.txtを書き込むプログラム、 を改訂した以下の「test4.c」を作ってRaspberry Piにrcpして「cc」でコンパイルして実行を確認し、RAMdiskにMaxで実験した適当な64バイトの16進数データの並んだファイルを置いた。(一部、表示のため改行を挿入)

Last login: Sun Jul  7 07:40:52 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: Mon Jun 24 02:18:54 2013

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

pi@raspberrypi ~ $ rcp nagasm@172.16.65.31:Desktop/test4.c .
Password:
test4.c                                                   100%  451     0.4KB/s   00:00    

pi@raspberrypi ~ $ cat test4.c
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	FILE *fp;
	char ss[3];
	char s[130] = "1E3246586A6A868B919494938B857E62605C5D5E60626A6E758C93949597989A9
				C9D9E9D9B9A99928A84817A787773757B7DADC0CACBCDD6DEE4E5EAECF0F4F5";
	s[128] = '\n';	
	s[129] = 0;
	if ((fp = fopen("/media/ramdisk/test.txt", "w")) == NULL) {
		printf("file open error!!\n");
		exit(EXIT_FAILURE);
	}
	fputs(s, fp);
	fclose(fp);
	printf("Write access done.\n");
	return 0;
}

pi@raspberrypi ~ $ cc test4.c
pi@raspberrypi ~ $ ./a.out
Write access done.

pi@raspberrypi ~ $ ls -l /media/ramdisk
合計 4
-rw-r--r-- 1 pi pi 129  6月 24 04:06 test.txt

pi@raspberrypi ~ $ cat /media/ramdisk/test.txt
1E3246586A6A868B919494938B857E62605C5D5E60626A6E758C93949597989A9
C9D9E9D9B9A99928A84817A787773757B7DADC0CACBCDD6DEE4E5EAECF0F4F5

pi@raspberrypi ~ $ 
これでRAMdiskにサンプルとなるテキストファイルが出来たので、次に、 RAMdisk内のtest.txtの呼び出しSUACboardの64個のLEDを点灯 の2本を合体させて改編し、「RAMdisk内のtest.txtを読み出し、64個の16進数(00-FF)データが128以上なら該当のLEDが点灯、128未満なら消灯する」という、以下のプログラム「test5.c」を作った。 これをコンパイルした結果の「a.out」を「disp_go」とリネームして、「sudo ./disp_go &」としてバックグラウンドで無限ループで走らせておくと、常にRAMdiskにあるtest.txtを読み出し、64個の16進数(00-FF)データに対応してLEDをONまたはOFFさせている。 その状態で、Maxのパッチから色々なPWMデータの16進文字列を作って「test4.c」に入れて実験することで、この動作を確認した。
#include <stdio.h>
#include <stdlib.h>
#include <bcm2835.>

int assign[15] = {17,18,27,22,23,24,25,4,2,3,10,9,11,8,7};
FILE *fp;
char s[130];

void GPIO_out(int data, int mode){
	int i;
	mode = mode & 1;	// mode=0 : active high / mode=1 : active low
	data = data & 255;
	for (i=0; i<8; i++) {
		bcm2835_gpio_write( assign[i], ( ( (data >>i) & 1) ^ mode ) );
	}
}

void Select_out(int data){
	int i;
	data = data & 7;
	for (i=0; i<3; i++) {
		bcm2835_gpio_write( assign[i+10], ( (data >>i) & 1)  );
	}
	bcm2835_gpio_write( assign[13], 0 );
	bcm2835_gpio_write( assign[13], 1 );
}

void data_file_read(){
	int i;
	if ((fp = fopen("/media/ramdisk/test.txt", "rb")) == NULL) {
		printf("\nread error!!\n");
	}
	else{
		for(i=0; i<129; i++){
			s[i] = fgetc(fp);
		}
		s[129] = 0;	
		fclose(fp);
	}
}

int asc_2_int(char s){
	char c;
	if ('0' <= s && s <= '9')
		return(s - '0');
	else if ('a' <= (c = tolower(s)) && c <= 'f')
		return(c - 'a' + 10);
}

int main(void)
{
	int i, j, get_data, bit_data, put_data;

	if (!bcm2835_init()){
		printf("GPIO is not found.\n");
		return 1;
	}
	for (i=0; i<15; i++){
		bcm2835_gpio_fsel(assign[i], BCM2835_GPIO_FSEL_OUTP);
	}
	while(1){
		data_file_read();
		for (j=0; j<8; j++) {
			put_data = 0;
			for (i=0; i<8; i++) {
				get_data =16*asc_2_int(s[2*(8*j+i)]);
				get_data = get_data + asc_2_int(s[2*(8*j+i)+1]);
				if(get_data<128)
					bit_data = 0;
				else
					bit_data = 1;
				put_data = put_data + (bit_data<
これでようやく、最後の接続の部分に到達した。 つまり、「Maxから送られる64個の16進数(00-FF)データに対応したPWM値をパックしたOSCメッセージ」を受信して、これを刻々とRAMdiskにある「test.txt」に書き出す、というPythonプログラム★を作れば、最終的には以下の流れとなるわけである。 これが動いたら、ビット単位の点滅だった「disp_go」を、いよいよPWM制御の時分割プログラムに成長させる、という事になる。 一気にやらずに外堀から埋めてきたので、「PythonでOSC受信」までは既に出来ていて、いよいよ最後に残ったのは「Pythonでファイル書き出し」という一点だけになった。

そこで「Python file write」などと検索して、コレかな・・・というサンプルを発見し、以下のように改訂した「test.py」を作って、Raspberry Piにrcpして「sudo python ./test.py」で走らせてみると、MaxからのOSCメッセージは受け取るものの、バックグラウンドで走っている「disp_go」は、全LEDを点灯させた(^_^;)。 調べてみると、以下の「test.py」は、どうもopenの際に既存の「test.txt」を消して、そして新しいファイルの書き出しに成功していないらしく、「cat /media/ramdisk/test.txt」の結果が空白、つまりファイルが消えていた(^_^;)。 Maxのパッチで、異なるPWMデータの16進文字列を作って「test4.c」に入れて実験した「go_1」や「go_2」を走らせると、ちゃんとRAMdisk内のtest.txtが復活して、バックグラウンドの「disp_go」はこれを点灯させるのである。

#!/usr/bin/env python

from simpleOSC import initOSCClient, initOSCServer, setOSCHandler, sendOSCMsg, closeOSC, \
     createOSCBundle, sendOSCBundle, startOSCServer
import time, threading, OSC

def pi_62_test(addr, tags, data, source):
    print "%s" % data
    f = open('/media/ramdisk/test.txt', 'w')
    f.write( data )
    f.close()

initOSCServer(ip='172.16.65.62', port=7003, mode=0)   
setOSCHandler('/pi_62', pi_62_test)
startOSCServer()

send_address = '172.16.65.31', 7001
c = OSC.OSCClient()
c.connect( send_address )
msg = OSC.OSCMessage()
msg.setAddress("/pi_62")
msg.append( "Raspberry Pi 62 OK (^_^)")
c.send(msg)

try:
    while 1:
        time.sleep(3) 
except KeyboardInterrupt:
    print "closing all OSC connections... and exit"
    closeOSC()
明らかに、この「test.py」の、それも追加した「f=・・・」の部分のバグということである。 しかしここで時間切れとなり、また明日以降に継続となった。 かなりいいセンまで進んでいるので、なんとか頑張って、Sketchingまでに作り上げたいところである。

2013年7月8日(月)

週末にはCGクリエイター検定試験が待つ、新しい週である。 2限の「音楽情報科学」では各グループのプロジェクトが遅々として進展しているが、 僕は この国際会議で発表、さらにその後、リンツの アルスエレクトロニカ に行っているので、参加できないものの、水面下の企画でお手伝いした 音楽情報科学研究会第100回研究会 も紹介しておいた。 興味のある学生が参加するかもしれない。 そして午後となり、5限のアカペラ補習特訓までの3-4限が、今日のRaspberry Piタイムである。 準備がたくさんになってきたが、とりあえず以下のように昨日の続きを再現した。

そして、「test.py」をあれこれ変えつつエラーの様子を眺めて試行錯誤しているうちに、何度も出て来た「OSCServer: TypeError on request from 172.16.65.31:63830: expected a character buffer object」というエラーメッセージは、イベントハンドラの起点から「from 172.16.65.31」とあるものの、どうもOSCで受けたメッセージの方に問題があるのではなくて、「f.write( data )」の方らしい・・・と見えてきた。 そしてあれこれ検索して出て来た このページこのページ の解説から、遂に勘所を掴んだ。 どうもPythonでは、「s = "%s" % data」ということでdataのポインタで指定された文字列をsに格納するが、先頭に「['」が、そして末尾に「']」が付いてしまうようである。 しかしこれは、そのままRAMdisk内のtest.txtにこの形式で書き込みしても、読み出す側で先頭の2バイトを読み飛ばし、最後に付いている2バイトを無視すればいいだけである。

・・・ということで、遂に、以下の「test6.c」と「test.py」の組み合わせで、無事にMaxからOSC経由で64個のLEDのPWM値(00-FF)を受け取って、バックグラウンドでRAMdisk内のtest.txtを経由して、まずはPWM値が128以上で点灯、127以下で消灯、という動作を実現できた。

test6.c

#include <stdio.h>
#include <stdlib.h>
#include <bcm2835.>

int assign[15] = {17,18,27,22,23,24,25,4,2,3,10,9,11,8,7};
FILE *fp;
char s[134];

void GPIO_out(int data, int mode){
	int i;
	mode = mode & 1;	// mode=0 : active high / mode=1 : active low
	data = data & 255;
	for (i=0; i<8; i++) {
		bcm2835_gpio_write( assign[i], ( ( (data >>i) & 1) ^ mode ) );
	}
}

void Select_out(int data){
	int i;
	data = data & 7;
	for (i=0; i<3; i++) {
		bcm2835_gpio_write( assign[i+10], ( (data >>i) & 1)  );
	}
	bcm2835_gpio_write( assign[13], 0 );
	bcm2835_gpio_write( assign[13], 1 );
}

void data_file_read(){
	int i;
	if ((fp = fopen("/media/ramdisk/test.txt", "rb")) == NULL) {
		printf("\nread error!!\n");
	}
	else{
		for(i=0; i<132; i++){
			s[i] = fgetc(fp);
		}
		s[132] = 0;	
		fclose(fp);
	}
}

int asc_2_int(char s){
	char c;
	if ('0' <= s && s <= '9')
		return(s - '0');
	else if ('a' <= (c = tolower(s)) && c <= 'f')
		return(c - 'a' + 10);
}

int main(void)
{
	int i, j, get_data, bit_data, put_data;

	if (!bcm2835_init()){
		printf("GPIO is not found.\n");
		return 1;
	}
	for (i=0; i<15; i++){
		bcm2835_gpio_fsel(assign[i], BCM2835_GPIO_FSEL_OUTP);
	}
	while(1){
		data_file_read();
		for (j=0; j<8; j++) {
			put_data = 0;
			for (i=0; i<8; i++) {
				get_data =16*asc_2_int(s[2*(8*j+i)+2]);
				get_data = get_data + asc_2_int(s[2*(8*j+i)+3]);
				if(get_data<128)
					bit_data = 0;
				else
					bit_data = 1;
				put_data = put_data + (bit_data<

test.py

#!/usr/bin/env python

from simpleOSC import initOSCClient, initOSCServer, setOSCHandler, sendOSCMsg, closeOSC, \
     createOSCBundle, sendOSCBundle, startOSCServer
import time, threading, OSC

def pi_62_test(addr, tags, data, source):
    f = open('/media/ramdisk/test.txt', 'w')
    s = "%s" % data
    print s
    f.write(s)
    f.close()

initOSCServer(ip='172.16.65.62', port=7003, mode=0)   
setOSCHandler('/pi_62', pi_62_test)
startOSCServer()

send_address = '172.16.65.31', 7001
c = OSC.OSCClient()
c.connect( send_address )
msg = OSC.OSCMessage()
msg.setAddress("/pi_62")
msg.append( "Raspberry Pi 62 OK (^_^)")
c.send(msg)

try:
    while 1:
        time.sleep(3) 
except KeyboardInterrupt:
    print "closing all OSC connections... and exit"
    closeOSC()

YouTube

これでいよいよ、Raspberry PiでSUACboardのPWM制御、という楽しい楽しいCプログラミングの準備が整った。 RAMdisk内のtest.txtをシェアード・メモリとして使うために、2つのプログラムが同時に走る必要があり、これまでは「&」を付けて片方をバックグラウンドで走らせてきたが、以下のようにターミナルを2画面として、それぞれRaspberry Piにsshればいい、という当たり前のことに気付いた(^_^;)。

そしてここから1時間ほどで、なんとか4限のうちに、Raspberry PiでPWM、というものが出来てしまった(^_^)。 以下の「test.c」がそれで、とりあえず無限ループの中で、5000回に1回だけ、RAMdisk内のtest.txtを読み出しに行って64個のPWMデータを更新している。 64個のPWMデータは、256回のループごとに、各LEDのPWM値とループ値とを比較して、点灯と消灯を指定しているだけであるが、さすが高速のRaspberry Piとはいえ、Unixのバックグラウンド処理があれこれ多いためか、わずかにチラつきがある。

test7.c

#include <stdio.h>
#include <stdlib.h>
#include <bcm2835.>

int assign[15] = {17,18,27,22,23,24,25,4,2,3,10,9,11,8,7};
FILE *fp;
char s[134];
int PWM_data[64];

void GPIO_out(int data, int mode){
	int i;
	mode = mode & 1;	// mode=0 : active high / mode=1 : active low
	data = data & 255;
	for (i=0; i<8; i++) {
		bcm2835_gpio_write( assign[i], ( ( (data >>i) & 1) ^ mode ) );
	}
}

void Select_out(int data){
	int i;
	data = data & 7;
	for (i=0; i<3; i++) {
		bcm2835_gpio_write( assign[i+10], ( (data >>i) & 1)  );
	}
	bcm2835_gpio_write( assign[13], 0 );
	bcm2835_gpio_write( assign[13], 1 );
}

int asc_2_int(char s){
	char c;
	if ('0' <= s && s <= '9'){
		return(s - '0');
	}
	else if ('a' <= (c = tolower(s)) && c <= 'f'){
		return(c - 'a' + 10);
	}
}

void data_file_read(){
	int i, j;
	if ((fp = fopen("/media/ramdisk/test.txt", "rb")) == NULL) {
		for(i=0; i<64; i++){
			PWM_data[i] = 0;
		}
	}
	else{
		for(i=0; i<132; i++){
			s[i] = fgetc(fp);
		}
		s[132] = 0;	
		fclose(fp);
		for (j=0; j<8; j++) {
			for (i=0; i<8; i++) {
				PWM_data[8*j+i]  = 16*asc_2_int(s[2*(8*j+i)+2])  + asc_2_int(s[2*(8*j+i)+3]);
			}
		}
	}
}

int main(void)
{
	int i, j, bit_data, put_data;
	int interval=0;
	int current_value=0;

	if (!bcm2835_init()){
		printf("GPIO is not found.\n");
		return 1;
	}
	for (i=0; i<15; i++){
		bcm2835_gpio_fsel(assign[i], BCM2835_GPIO_FSEL_OUTP);
	}
	while(1){
		if(++interval > 5000){
			interval = 0;
			data_file_read();
		}
		if (++current_value > 255){
			current_value = 0;
		}
		for (j=0; j<8; j++) {
			put_data = 0;
			for (i=0; i<8; i++) {
				if(PWM_data[8*j+i] < current_value){
					bit_data = 0;
				}
				else{
					bit_data = 1;
				}
				put_data = put_data + (bit_data<

改訂(表示省略)版 test.py

#!/usr/bin/env python

from simpleOSC import initOSCClient, initOSCServer, setOSCHandler, sendOSCMsg, closeOSC, \
     createOSCBundle, sendOSCBundle, startOSCServer
import time, threading, OSC

def pi_62_test(addr, tags, data, source):
    f = open('/media/ramdisk/test.txt', 'w')
    s = "%s" % data
    f.write(s)
    f.close()

initOSCServer(ip='172.16.65.62', port=7003, mode=0)   
setOSCHandler('/pi_62', pi_62_test)
startOSCServer()

send_address = '172.16.65.31', 7001
c = OSC.OSCClient()
c.connect( send_address )
msg = OSC.OSCMessage()
msg.setAddress("/pi_62")
msg.append( "Raspberry Pi 62 OK (^_^)")
c.send(msg)

try:
    while 1:
        time.sleep(3) 
except KeyboardInterrupt:
    print "closing all OSC connections... and exit"
    closeOSC()
こうなると、今度は64個のPWM値を送るMaxパッチの方が、いちいちテーブルにマウスで描画してスペースキーで送る、という実験仕様がカッタルくて面白くなくなってきた。 個別のPWM制御、とわかるYouTube動画は、現状の「UDP_test_01.maxpat」はうまく伝わらない。 明日にでも余裕があれば、ここらを改訂して、念願の「Raspberry PiでPWM」を完成させたいところである。

2013年7月9日(火)

梅雨前線が消滅して全国的に梅雨明けし、いきなり真夏日続きで熱中症続出のニュースが飛び交う火曜日である。 1限の「サウンドデザイン」では最終課題に2回生がいろいろと食い付いてきていて、前期末の最終課題合評が楽しみになってきた。 4-5限の「企画立案演習」はクラス内プレゼンであり、来週の3クラス合同・成果発表会に向けて、こちらも大詰めである。

その合間の2-3限で、昨日の続きに取りかかった。 まず、「test7.c」をコマンドラインの数値オプションを与えられるようにして、RAMdisk内のtest.txtを読み出しに行く頻度を変更できるようにした上で、以下のように改訂したMaxパッチで、LED[0]の値を0から255までインクリメントしつつ、64個のPWMデータを全て周期的にOSCメッセージとして連続的に送るようにした。

このMaxパッチで実験したところ、連続送出のインターバルを20msecまで下げても(毎秒50メッセージ)動作したが、19msecにすると表示の変化がストップしたので、最小値を20msecとした。 その上で、LED[0]がじわじわと明るくなりつつ、さらに画面内の「Random」ボタンを連打でクリックして、Raspberry Pi側の対応を調べた。 そして、LEDのチラつきがもっとも少ない「良好なゾーン」として、以下の改訂「test7.c」のRAMdisk内のtest.txtを読み出しに行く頻度パラメータについて、オプション無しの場合の初期値を200、と設定した。 これより小さくても(2とかでも)大きくても(10000とかでも)動作するが、大きく違えてみると反応速度とかチラつきが気になってくる。

改訂版 test7.c (RAMdiskアクセス頻度 設定可能)

#include <stdio.h>
#include <stdlib.h>
#include <bcm2835.>

int assign[15] = {17,18,27,22,23,24,25,4,2,3,10,9,11,8,7};
FILE *fp;
char s[134];
int PWM_data[64];

void GPIO_out(int data, int mode){
	int i;
	mode = mode & 1;	// mode=0 : active high / mode=1 : active low
	data = data & 255;
	for (i=0; i<8; i++) {
		bcm2835_gpio_write( assign[i], ( ( (data >>i) & 1) ^ mode ) );
	}
}

void Select_out(int data){
	int i;
	data = data & 7;
	for (i=0; i<3; i++) {
		bcm2835_gpio_write( assign[i+10], ( (data >>i) & 1)  );
	}
	bcm2835_gpio_write( assign[13], 0 );
	bcm2835_gpio_write( assign[13], 1 );
}

int asc_2_int(char s){
	char c;
	if ('0' <= s && s <= '9'){
		return(s - '0');
	}
	else if ('a' <= (c = tolower(s)) && c <= 'f'){
		return(c - 'a' + 10);
	}
}

void data_file_read(){
	int i, j;
	if ((fp = fopen("/media/ramdisk/test.txt", "rb")) == NULL) {
		for(i=0; i<64; i++){
			PWM_data[i] = 0;
		}
	}
	else{
		for(i=0; i<132; i++){
			s[i] = fgetc(fp);
		}
		s[132] = 0;	
		fclose(fp);
		for (j=0; j<8; j++) {
			for (i=0; i<8; i++) {
				PWM_data[8*j+i]  = 16*asc_2_int(s[2*(8*j+i)+2])  + asc_2_int(s[2*(8*j+i)+3]);
			}
		}
	}
}

int main(int argc, char *argv[])
{
	int i, j, bit_data, put_data;
	int interval = 0;
	int current_value = 0;
	int RADdisk_access = 200;

	if (!bcm2835_init()){
		printf("GPIO is not found.\n");
		return 1;
	}
	if(argc > 1){
		RADdisk_access = atoi(argv[1]);
	}
	for (i=0; i<15; i++){
		bcm2835_gpio_fsel(assign[i], BCM2835_GPIO_FSEL_OUTP);
	}
	while(1){
		if(++interval > RADdisk_access){
			interval = 0;
			data_file_read();
		}
		if (++current_value > 255){
			current_value = 0;
		}
		for (j=0; j<8; j++) {
			put_data = 0;
			for (i=0; i<8; i++) {
				if(PWM_data[8*j+i] < current_value){
					bit_data = 0;
				}
				else{
					bit_data = 1;
				}
				put_data = put_data + (bit_data<
これで、Raspberry Pi+SUACboardのシステム側のツールは完成、と見て、「test7.c」を「gcc test7.c -l bcm2835」でコンパイルした「a.out」を「disp_go」にリネームし、「test.py」を「OSC_2_RAMdisk.py」とリネームした。 RAMディスクの設定から含めると、この環境での起動準備は、以下のようになった。 ターミナルを1画面にしたいのであれば、以下のように、起動の最後に「&」を付けてバックグラウンド実行、とすればよい。 ここまでをRaspberry Piの起動時処理として登録してもいいのだが、それほどの手間でもないので、Raspberry Pi62号機にだけ「disp_go」と「OSC_2_RAMdisk.py」を置いておき、必要に応じて呼び出すこととした。 そして3限のうちに、改訂したMaxパッチ UDP_test_03.maxpat によって、以下のように色々なモードで、多数のLEDを同時にPWM制御する、というデモを実現することが出来た。 あまりにRaspberry Piが高速なためか、GPIOポートの出力電圧が一部の74HCロジックのスレショルドレベルと拮抗して正しく伝わらずにチラつくところもあるが、これはLinuxのバックグラウンド処理の不定期な間欠処理とも関係しているようで、まぁ「気にしない」というところである(^_^;)。

YouTube

ここまで来たところで、ちょうどこのHTMLのファイルサイズが「part1」と同じ238KBとなった。 ちょうど区切りなので、ここまでを「part2」として、日ごとのHTMLにも分割することにした。 Sketching2013でのお披露目に向けて、まだまだ続くところは「part3」としよう。(^_^)


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

「日記」シリーズ の記録