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
- Getting started with Raspberry Pi GPIO and Python
- Raspberry Pi GPIO Inputs in Python
- Uploading firmware from a Pi
- review of the board
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を見慣れているので(まだ全くプログラミングしていないが(^_^;))、最後にあった以下のプログラムはとても良く読める。 この程度のプログラムではまだ何も出来ないのだが、入口としてはこれでほぼ完備、ということのようだ。
この週末は、明日はいよいよ「40虎」(オープンキャンパスに向けた新入生プロジェクト)の勉強会「SUAC探検」などがあり、晩にもお楽しみの予定があるので進めないが、来週から、ぼちぼちRaspberry Piに戻ることにしよう。#!/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)2013年6月17日(月)
新しい週が始まった。5月はゴールデンウイークがあり、7月にも海の日があるが、6月は祝日も無く梅雨の時期で、学生は課題などに追われてあまり好きな季節ではないかもしれないが、コンスタントに仕事を進められるこの時期は嫌いではない。 今年はNIMEをスキップしていて静かな6月である。 Sketching2013の主催者であるMike Kuniavskyからは、今年の参加者に以下のようなメイルが届いた。 このメイルの末尾には、Googleやゼロックスに次いで、インテルもSketching2013のスポンサーに加わったとの情報もあった。 いよいよあと1ヶ月、参加者は皆んな、世界先端のスリリングな仕事を持って集まってくるので、こちらも負けずにあれこれ用意して臨みたい。さて、先週新しくゲットしたMacBookAir(これは今後、「Air2号機」とでも呼ぶことにしよう)の環境設定もあらかた出来たので、インストールしたVNCやターミナルの環境設定を確認するところからRaspberry Piを再開することにした。 まずは以下のように、新しく仕入れた小型LANハブとLANリールと改造ACアダプタでコンパクトに組み上げた。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.
そして、Raspberry Piとコンタクトするのが初めてのAir2号機からターミナル経由で通信してみると、以下のようにそれぞれ「初めてのSSH」でも何でもなく開通した。 つまり、以前にあった「MacとのSSH問題」はやはり、Raspberry Piの方でMacとSSHをしていない場合に発生する模様である。 「MacとSSHで繋がった」というような履歴情報がRaspberry Piにいったん書かれてしまえば、初めてのMacからのSSHで、「-X (X11)」という謎のおまじないは不要であるらしい。
・・・とここまで順調だったが、ここでトラブルが発生した。 以下のように、3台のRaspberry Piのうち、IPアドレス「172.16.65.61」のマシン(これも今後のために「61号機」とか呼ぶことにしよう)の基板上のLEDが他の2台と異なり、どうやら静かに死んでいる模様である(^_^;)。 新しいAir2号機からVNCのアドレス登録とともにVNC接続すると、62号機と63号機はちゃんと応答したものの、61号機はVNCの接続待ちのままストップした。 最初はSSHで接続できたが、この状態だとSSHでも繋がらなくなった。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$
これはつまり、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台のシステムが稼働した。 時間はかかったが、これで解決、さらにトラブル対応のいい練習となった(^_^)。
- 62号機のSDカードを読み込んでイメージファイルを作る
- これを61号機のdirtyなSDカードに上書き(書き換え)で書き込む
- このシステムで61号機だけを起動する
- この状態では61号機のIPアドレスは「172.16.65.62」(62号機の固定IP)なので、「172.16.65.61」に書き換える
- その後、61号機と62号機と63号機の同時起動を確認する
ここで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でゲットして走らせたのが、 以下である。
- Mac上で実験したPythonを、VNC経由でRaspberry Pi上で実験する
- Getting started with Raspberry Pi GPIO and Pythonを勉強する
- Raspberry Pi GPIO Inputs in Pythonを勉強する
- Raspberry Piのインブレス本の残ったところをクリアする
- Raspberry PiのCQ本の残ったところをクリアする
- Raspberry Piのふーみん本の残ったところをクリアする
- Raspberry Piと他システムとでXBee通信する
- Raspberry Piと他システムとでMIDI通信する
- Macに入れてみたPureDataをRaspberry Piにインストールして稼働させる (※ふーみん情報)
- OSCもRaspberry Piにインストールして稼働させる (※ふーみん情報)
- SketchingまでにRaspberry PiでBlink(1)を稼働させて持参する
- Raspberry PiにSUAC boardを接続して拡張する
以下は、「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でダウンロードして・・・というのを試してみよう。
- Raspberry PiのGPIOはヘッダで出ていて、2ピンのUART、2ピンのI2C、6ピンのSPIを含むI/Oピンからなる
- 全てのピンは入出力に使えて、I2C(pull-upのみ)以外は内部的にpull-upとpull-downの抵抗も付いている
- Raspberry Piの入出力ロジック電圧は+3.3Vなので、XBeeやPropellerは直接繋げるが、ArduinoやGainerやAKI-H8のような+5V系の回路をそのまま繋ぐと壊れる
- Raspberry Piの出力ピンの電流ドライブ能力は2mA-16mAなので、1ピンでもこれ以上流すと壊れる
- 電源の3.3Vラインから50mA以上を使ったり、USB電源端子の1A供給からRaspberry Piが使う700mAを差し引いた300mA以上を使ったりすると壊れる
そこで、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://・・・」と入れても駄目だった(^_^;)。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こうなれば、久しぶりにモバイルWiFiルータの出番である。 直接にネットに繋いで、最新の「distribute-0.6.21.tar.gz」を取りに行かないといけないのだ。 さっそく61号機と62号機をシャットダウンして、 ここでやった方法 を再現して、以下のように63号機の固定IPをDHCPに変更してシャットダウンして、LANハブを研究室の学内LANからモバイルWiFiルータに繋ぎ変えた。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 $
そして久しぶりに、以下のチャチいキーボードと見にくい液晶モニタの出番である(^_^;)。 何度もミスタイプを叱られながら、過去の失敗の経験から何度も「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の関係が全てはインストールされていないのだろう。
そして、たぶん これ をゲットした頃にどこかでゲットしてきたらしい(^_^;)、「blink1hid-demo.py」と「blink1-ctypes.py」の2本がずっとMacのデスクトップに置かれていたのだが、「blink1hid-demo.py」の内容は以下である。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 $この冒頭に書かれている pyusb というのも気になる、 おそらく「PythonでUSB」というライブラリだろう。 そして「blink1-ctypes.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?この2本のPythonブログラムをRaspberry Piに送って実行すると、以下のようなエラーが出た。 これはまぁ、詳細不明である。(^_^;)#/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]
また、関連して検索すると このような ところも出て来た。 これも関係するかもしれない。 そして、 このページ には、「Installing PyUSB on GNU/Linux Systems」という、以下の説明があった。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, indev.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 ~ $ これは見慣れたやつである(^_^)。 そこで、またまたRaspberry Pi63号機を固定IPからDHCPにして、「sudo apt-get install python libusb」をしてみたが、「libusb」というライブラリは無いぞ、と叱られた(^_^;)。 ただし、 このページ に行って、「PyUSB is available for download in the SourceForge project page. 」とあったので このページ に行き、 これ をゲットできた。 この内容は以下のような構成であり、チュートリアルは これ である。 なんか、Pythonプログラムのオンパレードである。$ sudo apt-get install python libusb $ sudo python setup.py install
ここにある「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ポートの番号とは別物である、という驚愕の事実が発覚した。 つまりは、関係するところだけ挙げれば、以下のような対応なのだという。
(この表は後で改訂されている事に注意)
ピン番号 信号名 ソフト上の呼び名 11 GPIO 0 GPIO17 12 GPIO 1 GPIO18 13 GPIO 2 GPIO21 15 GPIO 3 GPIO22 16 GPIO 4 GPIO23 18 GPIO 5 GPIO24 22 GPIO 6 GPIO25 7 GPIO 7 GPIO4 8 Tx GPIO14 10 Rx GPIO15 これは、もの凄く、嫌である(^_^;)。 しかし仕方ないのでこれを何度も参照しよう。 すると、ここでCQ本で新たな発見があった。 なんと、サンプルとして、I2CとかSPIのピンを、スイッチ入力とかLED出力に使っているではないか。 そこで以下のように実験して、使わずに捨てるつもりだった計7ビットについても、ちゃんと出力ポートとして拡張できる事を確認した。 これは、「GPIO 0」から「GPIO 7」までの8ビットをデータバスとして使い、ラッチするためのアドレスやライトパルスに使えるので、たった8ビットという制約とは雲泥の違いとなる。 Pythonではアクセス出来ないとすれば使わないだけであり、これは相当に有益な発見なのだ(^_^)。 そこで、I2CとSPIの計7ビットについても追加して、「EXT 0」から「EXT 6」までのオリジナルの信号名を添えて、対応表を以下のように改訂した。
(この表は後で改訂されている事に注意)
ピン番号 信号名 ソフト上の呼び名 11 GPIO 0 GPIO17 12 GPIO 1 GPIO18 13 GPIO 2 GPIO21 15 GPIO 3 GPIO22 16 GPIO 4 GPIO23 18 GPIO 5 GPIO24 22 GPIO 6 GPIO25 7 GPIO 7 GPIO4 8 Tx GPIO14 10 Rx GPIO15 3 EXT 0 GPIO0 5 EXT 1 GPIO1 19 EXT 2 GPIO10 21 EXT 3 GPIO9 23 EXT 4 GPIO11 24 EXT 5 GPIO8 26 EXT 6 GPIO7 そしてrootになって、以下の手順でのポートの信号出力を確認した。 これは出力についてだが、とりあえずの拡張用途としては出力が多い(たくさんのプロセスが裏で動くUnixで入力のモニタをしたくない(^_^;))との想定である。 ちなみにCQ本の記述の中に、以下のどこかが抜けているバグも発見した。(^_^;)
- 「sudo -i」としてrootになる
- 「echo "7" > /sys/class/gpio/export」としてポートを使用開始
- 「echo "out" > /sys/class/gpio/gpio7/direction」としてポートの入出力方向を出力に設定
- 「echo "1" > /sys/class/gpio/gpio7/value」でポートにHIGH出力
- 「echo "0" > /sys/class/gpio/gpio7/value」でポートにLOW出力
- 「echo "7" > /sys/class/gpio/unexport」としてポート使用を終了
- rootから抜けるのは「exit」
そして、これらをいちいち手で打つのも嫌なので、Mac側のエディタで以下のような内容を作成して、テキストファイル化してrcpでRaspberry Piに送り、それをsudoでシェルスクリプトとして実行できる方法(「chmod -x shell.txt」とやると「shell.txt」がシェルスクリプトとして実行できる)をCQ本の記述から発見して、やってみた。 すると、SPIの5ビットは出力できたものの、I2Cの2ビットは沈黙を保ったままだった。 どうやら3ピンは内部プルアップしている事からも、入力専用のようである。
まぁ、5ビットの拡張出力ビットがあれば、8ビット幅のラッチを16個まで増設できるので、総ビットは128ビットの出力ポート個別制御まで、簡単な外部回路の増設だけで出来るので、まずまず十分だろう。 ただしLinuxという重いシステムのRaspberry Piで、チラつかない十分なスピードで128個を個別にPWM制御できるかどうかは、実験してみないと判らない。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
YouTube ここで、汎用入出力の8ビットの他の7ビットの名前を以下のように変更して、当面はこの表で行くことにした。 I2Cの2ビットは入力の拡張用として、SPIの5ビットを出力の拡張に利用する作戦である。
(この表は後で改訂されている事に注意)
ピン番号 信号名 ソフト上の呼び名 11 GPIO 0 GPIO17 12 GPIO 1 GPIO18 13 GPIO 2 GPIO21 15 GPIO 3 GPIO22 16 GPIO 4 GPIO23 18 GPIO 5 GPIO24 22 GPIO 6 GPIO25 7 GPIO 7 GPIO4 8 Tx GPIO14 10 Rx GPIO15 3 Ex_In 0 GPIO0 5 Ex_In 1 GPIO1 19 Ex_Out 0 GPIO10 21 Ex_Out 1 GPIO9 23 Ex_Out 2 GPIO11 24 Ex_Out 3 GPIO8 26 Ex_Out 4 GPIO7 そして、午前中にふーみんに出していたメイルに、昼過ぎに以下のように返信があった。 さっそくBlink(1)と、せっかくなのでSlice基板もプレゼントとして発送手配した。 しかしやはり、このビン番号の対応は大混乱の模様である。(^_^;)
ここで昼休みが終わり、アポのあった及川さんと、総合演習IIの映像作品の音楽について相談した。 今月中、という新たな目標が出来たが、来月末の完成が楽しみである(^_^)。 さて、ふーみん本の続きでは、「温度センサのデータをI2Cで取得する」というトピックである。 しかし僕はI2Cは使うつもりが無いので、ここはサクサクとスキップである。 ただし、defaultではライブラリもインストールされておらず、環境としても登録されていないI2Cをどうやって使うか、というのは、他のケースでも(もしかするとBlink(1)でも)関係するので、以下のようにメモとして整理しておいた。ちらっと読みましたが,現物を提供していただけるのであれば 手元でやってみた方が早そうです^^; ちなみにピン番号の対応のごちゃごちゃさは,私の本の中でも かなり強調して書いたつもりです^^; ・今後,RPiのRevisionが変わるとまた変わる可能性があると明言されている ・ボードの番号,ハード仕様の番号,OSレベルの番号,Wiringの番号,が, すべて規則性なくバラバラ という素敵な状態ですが,magic numberを書かず,定数定義して シンボリックに使いなさい,ということなのでしょう. Systematicにやるなら,各言語ごとに BOARD_3V3_1, OS_3V3_1,WIRING_3V3_1,などのようにマイヘッダを 一度定義してしまえば,あとはそれを使う,という感じでもやれるとは 思いますが,あ,リビジョンによっても違うからそれも定義名に入れないと だめか・・・orzここからしばらくは、取得した温度データの16進数の話、Perlでのデータ変換、全体をシェルスクリプトでまとめる、というお話で軽くスルーできて、(校正原稿の)136ページまでジャンプした。 この次の「定期的な実行」というのは、Raspberry PiというよりもUnixの機能だが、チラッと見てみると「cron」を使っていて「分」単位の起動とあり、さらにWebサーバ(apache)を起動して外部からセンサデータを見る・・・という方向の話だったので、ここも今回はバスする事にした。
- モジュールを有効にする。「sudo nano /etc/modules」で「/etc/modules」を開き、「i2c-dev」を追加する
- ブラックリスト(使わないモジュールの登録ファイル)から外す。「sudo nano /etc/modprobe.d/raspi-blacklist.conf」で、「blacklist i2c-bcm2708」などの行の先頭に「#」を付ける
- ブラックリストを解除したモジュールを「sudo modprobe i2c-bcm2708」としてカーネルに読み込ませる(確認は「Ismod」)
- 「sudo apt-get install i2c-tools」でI2C関係のツールをインストールする
- 正しくインストールされていれば「sudo i2cdetect -y 1」とやるとデバイスを表示する
- 温度データの取得は「sudo i2cget -y 0x48 0x00 w」など
その次のトピックは「大気圧センサのデータを 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スクリプトのサンプルは以下であるが、これはもう終えていたものだった。
上のサンプルでは、CQ本の例と違って、ちゃんとGPIOピンとして定義されたピンを使っている。 やはりPythonではこれだけが汎用入出力として使えるのだろう。 そしてサッカーの後半開始直後にイタリアに逆転されたところで諦めて(^_^;)、ふーみん本の残りについてパラパラと眺めてみると、なんとこれからのRaspberry Pi攻略について、おぼろげに「見えて」きたので、ここで改訂版の新たな「To Doメモ」として整理した。 サッカーは後半25分に岡崎のヘディングで同点となったが、厳然たる実力差があり、結局は力尽きた(^_^;)。#!/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)そして1限には「しゃみーず」が来て、システムとしての改造三味線はほぼ完成した(^_^)。 ここにFLASHでどんなゲームが繋がるか、楽しみである。 きちんと記録ビデオを作り、オープンキャンパスでも展示して高校生に体験してもらおう、という話になった。 2限になると、サッカーの合間に業者にメイル発注していた これ と これ と これ と これ と これ と これ について、業者から「手配OK」のメイルが来た。 このスピード感は大事である。来週あたりには入手して実験に入れるだろう。
- ふーみん本の「WebカメラをUSBに繋いでストリーミングと動体検知」は、後でチェックすることにする
- ふーみん本の「ピコボード+スクラッチ」は、とりあえずパス
- ふーみん本の「センサ情報をTCP/IPで送るサーバと、node.jsを使ってこれを読み出すクライアント」は、やってみる
- ふーみん本の「USBサウンドインターフェースとMIDIインターフェース」は、まず業者に発注して、届いたら実験する
- ふーみん本の「GIMP」と「LibreOffice」は、とりあえずパス
- ふーみん本の「PythonでI2C」「多重化版 TCP Pythonサーバ」は、とりあえずパス
- ふーみん本にも「PureData」があった(^_^)ので、ここはきちんと実験する
- ふーみん本の「ADコンバータ」は、I2C経由で4ポートのアナログ入力と面白そうなので、まず業者に発注して、届いたら実験する
- ふーみん本の「pygame」「リモートデスクトップ」「シリアルコンソール」は、とりあえずパス
- CQ本の「C言語でデバイスドライバ経由で叩く」を実験する
- CQ本の「C言語でレジスタを直接に叩く」を実験する
- CQ本の「Ruby」は、とりあえずパス
- CQ本の「C言語でデバイスドライバを自作する」を実験する
- CQ本の「タイマ割り込み」を実験する
- CQ本の「シリアルコンソール」「I2C」は、とりあえずパス
- CQ本の「動画中継ラジコンカー」はふーみん本の「WebカメラをUSBに繋いでストリーミングと動体検知」と同じなので、後でチェックすることにする
- Raspberry Piと他システムとでXBee通信する
- Raspberry Piと他システムとでMIDI通信する
- OSCもRaspberry Piにインストールして稼働させる
- SketchingまでにRaspberry PiでBlink(1)を稼働させて持参する
- Raspberry PiにSUAC boardを接続して拡張する
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プログラミングのあれこれを勉強していこう。 まず最初のメイルは以下であった。・・・あらら、そうだったのかぁ(^_^;)。 sudoでやっていなかったかなぁ。 さっそくやってみると、以下のようにアッサリとBlink(1)が点灯した。さきほど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 chown root blink1-tool」でblink1-toolのオーナーをrootに、「sudo chmod 4755 blink1-tool」で実行権限を変更した。 これはしばらく使うかもしれないからである。 そして2限のゼミでは、M1のリュ君、3回生の土佐谷さんと森川さんの作品制作の進捗確認などを行った。 その後、以下の2本目のメイルに取りかかった。 「PythonでRaspberry Piに挿さったBlink(1)を制御」という事である。
上記の「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に入っていることを発見した。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のライブラリを ビルドする必要があると思います. これはこれから試してみます.・・・ところが以下のように(詳細は これ )、「pyusb-1.0.0-a1」と「pyusb-1.0.0a3」の両方で、ふーみんの提示した処理を行ったところ、別にエラーは出ないものの、うまく行かなかった。 まず、ふーみんの言う「blink1-demo.py」が見当たらないのである(^_^;)。 そして、以前にゲットした「blink1hid-demo.py」では、前回と同じエラーが出た。 さて困った。
そこでふーみんに「blink1-demo.pyが見当たらない」とメイルすると、速攻で以下が届いた。 さすがの師匠である。(^_^)
ということで、これは本来ならモバイル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」もゲットした。Macに落としていたものをRPiに転送した、という 記述があったので、既にあるのかと思っていました。 RPi上でゲットするのであれば git clone git://github.com/todbot/blink1.git で、他のものも含めて全部取ってきます。 上のコマンドで blink1/ というディレクトリに一式落ちてきますが、 その中の python/ にあるはずです。するとここで、さらに以下のふーみん情報が届いた。 これはなかなか悩ましい。(^_^;)#!/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)この意味では、Blink(1)はまだそれ自身にバグがあるカモというものだし、ツール等もまだまだ不完全なのかもしれない。 ・・・とメイルしたら、さらにふーみんから以下が届いた。 チャットに近いトラフィックである(^_^)。どうも、色々やっているとblink1hid-demo.py が動くようになったりすることが あるようで、必要なモジュールやデバイスの初期化、終了処理がおろそかな ところがあるのかもしれない気がしています。 先にblink1-tool で--on とか--off とかしてやるとblink1hid-demo.py が 動くようになることがあるかと思えば、それでもだめな時もあって、 なにか特定の処理をきちんと入れないとだめなのかもしれません。なるほど、これは決定的である。 「枯れていない」Pythonに付き合うつもりもなく、もともと、Pythonにあまり拘りたくないなぁ・・・と思っていたのであった。 ここは本命として、やはり、Cでやっていけばいいのであろう。python 関係は、今のところとりあえず動きます、という感じですね。 blink1/commandline/blink1-tool.c の // get a list of all devices and their paths あたりから下をみるとblink1-lib.so のAPIの使い方はわかるので、 それを使ってCで書いた方が幸せになれるかもしれません。 Blink(1)自体のバグというよりは、Pythonからデバイスを使いにいくときの コーディングが枯れてないんじゃないかという印象です。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」の作戦に切り換えて、 このような 感じで、ふーみんメイルにあった
の通りにやってみたものの、最後の1行はNot Foundで、以下の(1)のように捜しても見つからなかった。 そしてBlink(1)が挿さっている状態で「blink1hid-demo.py」を起動すると(2)のようなエラーが出て、Blink(1)を抜いて「blink1hid-demo.py」を起動すると(3)のようなエラーが出る、という状況は変わらなかった。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
夕方には、注文していたちょっと面白いブツが届いたりして、今日の作業はここまでとなった。 朝に届いていたふーみんメイルの3本目、「Cのライブラリについて」という話題は、また明日以降となるが、この週末は作曲に没頭したいので、続きは来週になるかもしれない。
2013年6月22日(土)
昨日の夕方に届いたブツは こんなこと が出来る、というプツである。 いずれ何かに使うかもししれない。 そして週末になったが、土曜の午前中はおいちゃんの映像(Vコン)に音楽トラック制作作業、「試作1」の作曲で終わった。 午後には(ここを書いている研究室内)、「40虎」のメンバーのうち5人が集合して、「碧風祭・メディア企画の歴史」「中学星2」の上映会があって、ついつい映像に見入って進まなかった(^_^;)。 昨日のふーみんメイルの3本目は以下であるが、たしか「git」は駄目だったので(;_;)、「Raspberry PiをC言語で」という部分を進展させた後で、「Raspberry PiのBlink(1)をC言語で」に取りかかるのが適当だろう。ということで、CQ本のC言語の解説の部分から再開である。 この「Raspberry Pi日記」の最初、 5月15日のところ で、以下のようにあった「test.c」をRaspberry Piにrcpして、コンパイルして、無事に走ることを確認した。 まぁ、Raspberry PiはUnixなので、これは当然である。(^_^;)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 を見ればどういうプログラミングをすればいいかも わかります.#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言語で次々に送る」というものを発見した。
これは、6月19日にやっていた「シェルコマンドを並べたシェルスクリプトを実行」というやつを、C言語プログラムとしてまとめたもので、遠い遠い昔、1996年に作品 "Asian Edge" (★★★★)の作曲において、背景音響パートをライヴ生成するために、「MIDI制御を受けて"サウンドファイルを再生する"というシェルコマンドを多数、どんどん起動していく(最大、同時に20本以上のサウンドを多重生成)というプログラム」を、シリコングラフィクスのIndyワークステーション上で制作したのを思い出した。 ここ にある、 これ と これ である。#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"); }そこでまず、 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に対応させてみた。
ピン番号 信号名 ソフト上の呼び名 11 GPIO 0 GPIO17 12 GPIO 1 GPIO18 13 GPIO 2 GPIO27 15 GPIO 3 GPIO22 16 GPIO 4 GPIO23 18 GPIO 5 GPIO24 22 GPIO 6 GPIO25 7 GPIO 7 GPIO4 8 UART Tx GPIO14 10 UART Rx GPIO15 3 Ex_Out 0 / I2C SDA GPIO2 5 Ex_Out 1 / I2C SDL GPIO3 19 Ex_Out 2 GPIO10 21 Ex_Out 3 GPIO9 23 Ex_Out 4 GPIO11 24 Ex_Out 5 GPIO8 26 Ex_Out 6 GPIO7 そして、「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();」が無く、「省略してもエラーにならない」などと書いてある(^_^;)。
その「init_gpio();」は出力用であればおよそ以下のような感じで、fdというのはファイルディスクリプタの事だろう。 かつてUnixをお勉強した時に、何度も出て来たやつである。 サンプルにあるエラー処理を残すか省略するかは悩ましいが、当面は残しておく事にした。 expでもvalでも「2」を書くのに、どうしてdirだけ「4」なのか、どこにも書いてない。#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(); }対応する「gpio_close();」はおよそ以下のような感じである。 実験としては、これを呼ばなくてもまた実行できるかどうかを確認してみよう。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); } }これを実行してみると、無事に「Ex_Out 6」である「GPIO7」が点滅したが、どうもその時間間隔が異常に長い。 今回はVNCでなくsshで実験しているが、sshのターミナル画面内のRaspberry Pi側でnanoを開けるので、わずかな変更はいちいち rcpしないでnanoで開いて修正してコンパイル→テストランが出来る。 いろいろ試して、以下のような事が判った。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); }要するに、fdで情報を取得してしまえば、Pythonドライバのようにリソースの管理をせずにGPIOポートがアクセス出来てしまうわけである。 ということで、エラー処理も省略して、さらに上記の「無くても実行できる」ものを全て省略(^_^;)すると、上のプログラムはなんと、以下のようになり、これでも何度でも実行できる事を確認した。
- 「sleep(1);」だとおよそ1秒で、これが最速
- 「sleep(0.5);」だと、およそ2秒になる
- 「sleep(0.1);」でも、およそ2秒になる
- 「sleep(0.01);」でも、およそ2秒になる
- 「sleep(0);」だとゼロで何も待たない
- 最後に「gpio_close();」を呼ばなくても、新たに実行させるとちゃんと動く(^_^;)
- main()の中の最後の「close(fd_val);」を消しても、新たに実行させるとちゃんと動く(^_^;)
- dirで書き込む値を「4」でなく他と同じ「2」にしても走った
- それぞれのfdのclose(fd_???);」を消しても、新たに実行させるとちゃんと動く
- exportの信号名(番号)は、「"7"」でなく数字の「7」でも動く
- valueに書き込む信号レベルは「"0"」や「"1"」でないと駄目で、「0」や「1」では動かない
ということで、とりあえず全15ビットのLEDを8ビットと7ビットのグループごとに交互に全点滅させる、という以下のCプログラムが完成して、無事に動いた。 やはり、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); } }#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にリダイレクトすれば十分なことを、以下のように確認した。
そして、GPIOポートの実験のために63号機のSDカードを挿して実験してきたRaspberry Piから、以下のように、また3枚タンデムのRaspberry Piシステムに戻した。 この状態で、しばらくはプログラミングを行うことになる。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$
そして、ここで3枚のRaspberry Piに、「起動時にBlink(1)を自動ランダム点灯させるプログラムを仕込む」という作業にとりかかった。 まずは以下のような、「1000msecごとにランダムな色でBlink(1)を100回発光させる」というblink1-toolのコマンドを無限ループで呼び出す、という超カンタンなCブログラム「test.c」を作った。
これをRaspberry Piに転送して、「cc test.c -o blink_autorun」としてカレントディレクトリに置いた。 この「./blink_autorun」が動くことを確認して、次に6月3日に行った手順に従って、以下のように続けた。#include <stdio.h> int main() { while (1) { system("./blink1-tool -t 1000 --random 100 > null"); } }ちなみに、せっかくなので61号機では1000msecだった変化間隔を、62号機では800msecに、63号機では700msecにしてみた(^_^;)。 これでそれぞれを再起動すると、以下のように見事に、3台のRaspberry Piがカラフルな自動発光をするようになった。 なお、このプロセスを止めるには「ps ax」してPIDを捜して、例えばPIDが2201であれば「kill -9 2201」で止まる。 これを改めて起動したい場合には「./blink_autorun」とするとコマンドラインに戻らないので、「./blink_autorun &」とすれば、バックグラウントで起動するのでコマンドラインに戻る。
- (Macのターミナルからsshでログインしたまま)
- 「nano /.profile」として設定ファイルを開く
- いちばん下にある以下の3行の部分
if [ -z "$DISPLAY" ] && [ $(tty) == /dev/tty1 ]; then
tightvncserver
fi- に1行加えて以下のようにする
if [ -z "$DISPLAY" ] && [ $(tty) == /dev/tty1 ]; then
tightvncserver
./blink_autorun
fi- [ctrl+O]で書き出す
- [ctrl+X]で終了
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日(水)
さて水曜日である。 講義の無いこの日は、放課後のアカペラまでがっつり進める日である。 昨日の帰り際に、「昨日走ったソフトが何故か今日走らない」という情けないメイルをふーみんに出していたが、今朝読んだ返信は以下であり、まさにもっともであった(^_^;)。 ちゃんとふーみんはピン番号の新旧についても記述していたが、僕が読み落としていた。 今日はまず、ヘンな省略をしないところから再開し、紹介された情報をチェックしていこう。CQのRaspberry Pi本の第5章の「デバイスドライバを使う」のところに書かれていた「省略可能」という記述を並べると以下のようになる。 OSが別途に管理してくれているので、まぁ、動くよ、という事なのだが、ここは基本に戻って、しっかりと形式を整えることにしよう。http://netlog.jpn.org/r271-635/2013/01/raspberry_pi_gpio_test.html あたりをみて, http://www.open.com.au/mikem/bcm2835/ のライブラリを使った方が楽ではないかと・・・. # *_close()を省略しても大丈夫,という記述はちょっと信じられません. ちなみにRevisionによるピンアサインの差異は私の本でも 見分け方とともに説明してあります.・・・ということで、Raspberry Piを立ち上げ、VNCだとコピペが出来ないのでsshでRaspberry Piに入り、まずは昨日の現象を確認した。 リセット直後のRaspberry Piで再現確認しておく事は意味があるだろう。 まず、再掲になるが、拡張7ビットに対してコマンドラインから表示を指令したシェルスクリプトの test1.c をrcpすると、以下のようにこれは自動的に実行可能と表示され、sudoで実行してLEDが順に点灯した。
- C言語からのアクセスでunexportは省略してもエラーにならない
- アクセスが終わったらfdを使ってcloseするのが約束
- ただしOSもopenしたファイルを管理しているのでプログラム終了時に自動closeする
- なのでcloseを忘れても実害は無い
次に、汎用8ビットと拡張7ビットに対して、C言語のシステムコールとしてシェルコマンドを並べた test2.c をrcpすると、以下のようにこれは実行できないCソースコードと表示され、ccでコンパイルして、sudoで「a.out」を実行すると、全ての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 ~ $次に、ふーみんの指摘を真摯に受け止めて(^_^;)、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/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 ~ $エラーが出ればそこを攻める、というのがデバッグの常道である。 そして今ではインターネットという強力な助っ人がいる。 エラーの「GPIO direction open error」というタームでyahoo.comから検索すると、 こんなページ が出て来て、そこには以下のようなプログラム(ただしunexportしていない(^_^;))があった。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 ~ $違いとしては、ポートの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」を実行すると、ちゃんと走った。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); }・・・ところが、ここからまた、昨日の悪夢の再現のような現象が起きた(^_^;)。 現象としては、 test1.c のスクリプト実行と、 test2.c のコンパイル結果の実行は、必ず問題なくできるが、 C言語で叩く、というタイプについては、またまた「GPIO 7 direction open error」とか「GPIO 17 direction open error」、つまりdirectionの設定のいちばん最初のところでエラーとなる現象が起きるが、まれに test1.c のスクリプト実行、あるいは test2.c のコンパイル結果の実行によって、状態が正常に戻るのか、問題なく実行できることも「まれに」ある、という状況だった。 「まれに」というのが、100%よりもずっと厄介なのである。(^_^;)#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); } }ここでさすがにふーみんメイルのように、 このページ と、実質的にはその元ネタである このページ に行くしかない、と観念して(^_^;)、調べてみることにした。
上のように簡単に書かれていた このページ の手順に従い、最新の「bcm2835-1.25.tar.gz」をダウンロードして、Raspberry Piにrcpして、tarから実行すると、実際には以下のように表示された。# 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そして、実質的には このページ でなく、 このページ の方に全て必要な情報がある、と判明したので、とりあえず Examples のページの先頭にあった、以下の「blink.c」をRaspberry Piにrcpして、このプログラムの冒頭にあった「gcc -o blink blink.c -l bcm2835」というオプションでコンパイルして、「sudo ./blink」してみると、ちゃんと先頭のLEDが0.5秒間隔で点滅した(^_^)。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 $これはかなり簡単である(^_^)。 このページ には、他にも「event.c」「input.c」「spi.c」「spin.c」など、シンプルな低水準プログラムが並んでいる。 ヘッダファイル も参考になる。 まさにここ数日、こういう低水準のライブラリが欲しかったのである(^_^)。 ここで昼休みになったところに、以下のふーみんメイルが届き、ちょうど欲しかった資料として BCM2835のマニュアル と Raspberry Pi(バージョン1)の回路図 と Raspberry Pi(バージョン2)の回路図 とがゲットできた。 欲しかったデータを以心伝心で送ってくれるあたり、さすがの師匠である。(^_^)// 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; }初期状態に何かありそうだ、というのは予想がついていたが、これに限るものでもなさそうである。 現象としては、出来たり駄目だったり、ときに戻ったり(^_^;)、という怪しいものだった。 まぁ、このCQ本のライブラリは、ちょっと放置プレイである。 PythonのBlink(1)に続いて、そういうのが増えてきた。 昼食をはさんで再開する前に、以下のように、新しいライブラリの解凍からインストールまで、作業していたRaspberry Pi61号機だけでなく、さらにターミナルを開いて62号機・63号機にも、コマンドのコピペで済むので作業しておいた。 まだ62号機・63号機のGPIOには何も繋がっていないが、これからそれぞれに別のブツを繋いでいく計画である。(^_^)ちょっと今すぐには長嶋さんのwebを詳細にチェックしている 時間がないのですが、Rev.1 とRev.2のschematic、bcm2835の データシートを送ります。 elinux.orgによれば、ブート時にAlt0にいくのはUARTのTX/RXのみ、 のはずですが、SPIやI2Cのモジュールを有効にしていれば 単純なGPIOではなくそれぞれのfunctionに切り替わっている 可能性もあります。 長嶋さんの作業手順をちゃんと読んでいないので蓋然性の指摘に 留まりますが、ライブラリを呼び出しているようなプログラムが動いた ときにbasic functionに切り替わっていてたまたま動いた、とかも あるかもしれません。この辺は調べてみないとわかりませんが・・・
さて、こうなると、新しく知った低水準の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が全て点滅する、と確認できた。(^_^)
こうなればC言語の世界なので、いちいちネットで「ビット排他的論理和」「ビット論理積」「ビットシフト」などの記号を調べて思い出したものの、以下のように無限ループでLEDを点灯させる改訂版の「blink.c」はすぐに出来た。 8ビットのGPIO汎用ポートを1msecのインターバルごとに2進数で0から255までカウントアップして、ゼロに戻るとGPIO拡張ポートが1だけカウントアップし、これが7ビットの拡張ポートで0から127まで回ると一周である。#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; }
YouTube これを61号機では、「LED_blink」とリネームして、「sudo nano .profile」からの追加によって、Blink(1)の自動点滅と合わせて起動時に呼ばれるようにセットすることにした。 また、Raspberry PiをHALTせずにリブートするのは「sudo reboot」というのも知った。 なかなか進んだところで、今日はここまでである。(^_^)#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(); }2013年6月28日(金)
もう週末である。 朝、研究室に出て来ると、卒業生の山村さんから こんな素敵なプレゼント が届いていた(^_^)。 山村さんの作品「おはなしパネル」は今年もオープンキャンパスで展示するので、さっそく明日、展示プロジェクトの「40虎」メンバーに紹介しよう。 関連して、「SUACインスタレーション」 ★ ★ ★ のページに加筆改訂して、オープンキャンパスでの展示記録と「動態保存作品」を付記した。 今日は、ゼミのミーティングに加えて、企画立案演習でコマ撮りアニメに初挑戦(^_^;)という無謀なグループを応援するアポも入っている。ゼミまでの時間にちょっと実験、というのを思いついたのでやってみた。 水曜日に作った「LED_blink」(起動時に自動実行)のソースは これ であるが、要するに7+8=15ビットのLEDをバイナリ2進数としてインクリメントして表示している。 ただしメインルーブでは以下のように、インクリメントするたびに「bcm2835_delay(1); // 1msec interval」を入れている。
この状態だと、下位8ビットが一周するのに256msec、その上の7ビットのLSBは512msecの点灯と消灯を繰り返すのでおよそ1秒の間隔で点滅し、7ビットのMSBは64秒、つまりおよそ1分間というインターバルで点滅している。 この、「1msecのウェイト」を取ったらどうなるのか、というのはまず、実験してみる意義がある。 Raspberry Piの、このGPIOアクセス手法での最高速度を見てみたい、という事である。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 } } }そこで これ の「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」は、ファイルアクセス等で必要だ、とエラーが出たら追加することにした。
この結果は以下のようになり、無事にargcとargvの意味と、コマンドラインからオプション付きで呼び出すやり方を思い出した。 こんなC言語の基本中の基本は、初めて触れたのはもう25年以上の昔で、たぶん10年以上、まったく書いていなかった(^_^;)。#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(); }
ここでしばし実験して、以下のような GPIO_out.c を完成させた。 これはオプションとして3つの数値が必要で、第1パラメータはGPIOポートの8ビット(port=0)かEXTポートの7ビット(port=1)かを指定、第2パラメータはそのデータ(GPIOなら0-255、EXTなら0-127)、第3パラメータは正論理にら0で負論理なら1である。
作った後で数値の範囲外はチェックしていない・・・と思ったら、switch分でportの指定がチェックされていて、さらにサブルーチンではデータとモードのチェックをしていたので、そこそこしっかりしたものなった(^_^;)。 これで、コマンドラインからパラメータを添えて全15ビットの出力ポートを、個々のビットごとの操作でなく、8ビットまたは7ビットの数値として出力指定できる事になった。 これは汎用ツールとして使えるという想定なので、これも「sudo chown root GPIO_out」でオーナーをrootにして、「sudo chmod 4755 GPIO_out」で実行権限を変更した。#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(); }このような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言語とほぼ同様のシステムコールが出来ることを突き止めていた。
そこでこの日は、2限の「音楽情報科学」と5限のアカペラ補習特訓の合間に、おいちゃん作曲をメインに据えるとして、とりあえず上のPythonプログラムをC言語版と同じようにループで回して速度を測るところまで、やってみる事にした。 考えてみればきちんとPythonプログラムをゼロから書いたことは無かったので、 Python公式ページ の リファレンス を参照しつつ、という作業である。 for文のループは「in range(x1, x2)」で、初期値x1からX2未満まで繰り返すこと、最後にコロンが無いとエラーになる事(^_^;)などを経て、15分ほどで無事に以下のようなPython版のルーブ実験プログラムが完成した。#!/usr/bin/env python import os s = './GPIO_out' + ' ' + str(0) + ' ' + str(0) + ' ' + str(1) os.system(s)#!/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」とした。
次はマウントである。 以下のサンプルは2GBだったが、小さくていいので1KBとしてみた(^_^;)。sudo mkdir -p /media/<FOLDERNAME>以下はアンマウントである。 これは最後に行うだけである。sudo mount -t tmpfs -o size=1K tmpfs /media/<FOLDERNAME>これだけとシンプル過ぎるのが逆に心配だが(^_^;)、とりあえずsshしているターミナルからやってみると、以下のようにちゃんと出来た。 root権限(sudo)で作ったRAMディスクのディレクトリだが、一般ユーザのpiでアクセス出来ることも判った。 そして以下のように、新たに作ってマウントしたRAMディスクには、1つだけしかファイルが置けないらしい事が判明した。 ファイル管理のディレクトリ構造を持たないシンプルなものでも、必要に応じてどんどん「/media」の下にディレクトリを作ればいいので、これは問題ない。(^_^)sudo umount /media/<FOLDERNAME>上にあるように、RAMディスク領域には1つしかファイルが置けず、新たに加えようとすると、システム的には加わったようでサイズが0の幽霊状態になっている。 また、cでもpyでも、書き込み権限が無いというのはもしかすると困るので、ここで、このRAMディスク領域(/media/ramdisk)に対して、パラメータファイルの読み書きを実験することにした。 データはバイナリでなくても、CでもPythonでも文字列と数値の相互変換は簡単なので、ここではテキストエディタで読み書きできるように、16進数のテキストファイルとする事にした。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 ~ $そして上のように、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/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 ~ $そこでいよいよ実験である。 作戦としては、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時間以上かかる)である。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ディスクのデータファイルを上書きで書き換えに行く」という、先に作った「./test1」を、ターミナルからのコピペでマニュアル連打したが、まったく「printf("\nread error!!\n");」が表示されなかった。 ただしこのメッセージも256バイトあたりのバッファに積まれている可能性があるので、いじわるテストとして、「test1.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 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; }すると、「sudo ./test2 &」で過酷な無限ループが回っているところに、「RAMディスクのデータファイルを上書きで50000回、書き換えに行く」という「./test1」を実行すると、10数秒で「50000 times write access done.」が返ってくるが、何度やっても書き込みopenも読み出しopenもエラーが表示されることは無かった。 もしかすると、Linuxが内部的に待たせているのかもしれないが、とりあえず、このRAMディスクを経由して2つのプロセス間のパラメータ通信を出来そうだ、と確認できた。 ここで4限も半ばとなり、明日の企画立案演習の準備もあるので、今日はここまでである。 なかなかに進んだし、何より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; }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に行っている合間に、進展することになるかもしれない。
これに続いて、久しぶりに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」の姿である。残った「中心座標」「半径」「カラー」の指定の4種類、8コマンドの部分でストップした。 Propellerの方のプログラムはコマンドごとに変数バッファを持っているので、 そのまま 延長して拡張していけばいいのだが、Maxの方(jit.lcd)のモニタ画面の描画に関しては、 これまで作ったサブパッチでは、全て共通にカラー指定を行っていた事に気付いた。 これはMaxでは基本のイベント主義、つまり「最後に指定された情報が有効」というもの だが、 ここまでに定義したコマンドと、新たに円ごとにカラー指定をした場合、Propeller の方ではちゃんと受けられるのに対して、 Maxの方(jit.lcd)のモニタ画面では、カラー 情報が上書きされるので、そのままではいかない(^_^;)。 Max側で、カラーを記憶しておくメカニズムを、いくつものサブパッチに対して横断的に 新設する必要がある。
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]」を二重にするとかえって混乱するので、これは共通とした。
- Raspberry Piの汎用出力ポート(GPIO0-GPIO7)の8ビットを245でバッファして8個の574に入力
- Raspberry PiのEXT2(LSB)からEXT4(MSB)の3ビットで、8個の574をセレクトするアドレス(0-7)を形成
- Raspberry PiのEXT5のビットが、セレクトされた574にデータをラッチするラッチパルス(立ち上がりエッジ)
以下がその最初のプログラム「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については、以前にふーみんメイルで、
と紹介されていた。 単純で十分、いよいよ久しぶりのOSCである。 OSCは GDS (global delayed session) Music の研究でやっていて、ここで開発したシステムはヤマハの受託研究として作り、僕が筆頭発明者として米国特許登録されているのだ。 そしてなんと、「2013年6月18日(火)」の日記のところで、以下のように書いていた。PythonのOSCは, sudo apt-get install python-txosc で入ります.単純なやつは http://opensoundcontrol.org/implementation/python-simple-osc にもあります. Pd用のものは sudo apt-get install pd-osc です.「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用にラップしたもので、以下のように使え、プラットフォームを問わないという。これは簡単そうである。 そこでリンクから SimpleOSC module 0.3.2 の記述を読むと、この「SimpleOSC」を走らせるためには、まず pyOSC に行って、「pyOSC」をインストールせよ、という事である。 そして、学生など初学者のためのAPIとしてSimpleOSCをデザインしたので、エキスパートはpyOSCそのものを使え、という。 そのために pyOSCのサンプルプログラム も用意しているのだという。 そこでとりあえず、このサンプルに加えて、zip形式で SimpleOSC_0.3.2.zip をダウンロードして、 ここ から pyOSC-0.3.5b-5294.zip とをダウンロードしてみると、その中身は以下のようなものであった。import osc osc.init() osc.sendMsg( '/test', 999)
初学者向けAPIという「SimpleOSC」の「simpleOSC.py」Pythonソースを見ると、以下であり、定義が並んでいるだけだった。 これはまさにAPIで、今回、使いたいものではなくて、Pythonベースで何かソフトウェアを作る場合にOSCと橋渡しするもののようである。
これに対して、「pyOSC_examples」にたった二つだけ入っているサンプルは名前そのままの「basic_send.py」と「basic_receive.py」であり、「basic_send.py」の中身は以下で、まさに、これが欲しかったものである。(^_^)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「basic_receive.py」の中身は以下で、まさに、これが欲しかったものである(^_^)。 これで判明したのは、とりあえず「SimpleOSC」は不要のようなので、この2本のサンプルを使うとして、まずは「pyOSC」をインストールすればいい、という事である。 同時に二つをインストールして良くないことが起きた事は少なくない(^_^;)ので、この作戦で行こう。""" 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"・・・ということで、「pyOSC」のフォルダにある「REAMDE.txt」を開くと、なるほどこれは このように 長大であった(^_^;)。 ただし欲しいのは、最後の以下の部分の情報である。""" 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"ここでまず、以下のようにMax6でUDPで7001と7002の2つのポートで、OSCメッセージを出すものと受けて表示するものを用意した。 これにより、もしRaspberry Piから何かが届けば、こちらで確認できる。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
そしてここでフト思ったのが、この「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」で中身ごと消去できるので問題ない。
これで準備は終わりである。 とりあえずREADME.txtの最後に以下のようにあったので、「pyOSC」のディレクトリにいる状態で「./OSC.py」とやってみた。 すると、出るわ出るわ、膨大に こんなメッセージ が出た。 とりあえず、OSCサーバも走って、特にエラーが無いことだけは判った。(^_^;)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 $しかしこれではあまりに情報が膨大なので、ホームディレクトリに戻って、サンブルの「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」の歌詞であろうか。 ちょっと起動してからの遅れが気になるが、まずは開通である(^_^)。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#!/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」 の冒頭に以下のように書かれていたので、これを調べてみることにした。
- OSCでメッセージを送る場合には以下の書式となる。「send_address」に記述するのは、受け手(Maxなど)のIPアドレスと、受け手がモニタしているポート番号
send_address = '172.16.65.31', 7001
c = OSC.OSCClient()
c.connect( send_address )- このメッセージを受ける受け手のMax側の「udpreceive」にはIPアドレスの記述は不要で、ポート番号だけ指定する
- OSCでメッセージを受ける場合には以下の書式となる。「send_address」に記述するのは、送り手(Maxなど)のIPアドレスと、受け手がモニタしているポート番号
receive_address = '172.16.65.62', 7003
s = OSC.OSCServer(receive_address)
s.addDefaultHandlers()- このメッセージを送る送り手のMax側の「udpsend」には、受け手であるRaspberry PiのIPアドレスとポート番号を指定する
- このメッセージ受信のためには、上記のハンドラ記述に続いて、以下でスレッドを起動して無限ループで待つ
st = threading.Thread( target = s.serve_forever )
st.start()- メッセージ受信のプロセス終了にはキーボード割り込み例外のハンドラで以下で正常終了するのが望ましい
s.close()
st.join()そしてここから延々と、イベントハンドラって何だっけ・・・という、遠い記憶をなぞる謎解きの旅となった。 色々と試しているうちに18時となり、ハムスターの餌やりのために帰宅する時間となって、この作業は週末に持ち越しとなった。 とりあえず、OSCはRaspberry Pi62号機で動作が確認できたので、以下のように61号機と63号機のためにさらにシェルウインドウを開いて、62号機で作業したコマンドのコピペで無事に3台ともOSCのインストール、そして実際に「test1.py」を走らせてMax6にOSCが届いていることをを確認した。 いろいろあったが、収穫と進展もあったということで、今日はおしまいである。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)
2013年7月6日(土)
週末になった。 今日は午後に「40虎」の勉強会(作品映像鑑賞会)ということで、学生が映像を見ている裏でRaspberry Piを進めることにした。 昼前から研究室で準備にとりかかったのは、昨日の最後に、OSCメッセージを受け取って処理するという部分にまで行けなかったので、その突破口を探ることだった。 昨日の流れは、整理してみると以下のようなものだった。つまり、混乱を避けて「Simple OSC」はダウンロードしたもののインストールせず、上位互換フルセット?の「pyOSC」に挑戦して行き詰まっていた、という事である。 ここまで整理して気付いたのが、「Simple OSC」内には「simpleOSC.py」とともに、以下の「app_example.py」があるのを見落としていたことである。 そして以下の「app_example.py」を眺めてみると、ちょうど長大な「OSC.py」で困っていたものがスッキリとしているように思えてきた。 そうか、学生など初学者向けでちょうど良かったのである。(^_^;)
- Raspberry Piに既にOSCそのものはインストール済を確認
- 「Simple OSC」をゲットした
- Simple OSCの実行には「pyOSC」が必要ということで「pyOSC」をゲットした
- 「Simple OSC」内の「simpleOSC.py」を見ると、ちょっと違うっぽい??
- 「Simple OSC」は学生など初学者向けだという説明
- 「pyOSC_examples」内の「basic_send.py」と「basic_receive.py」を見ると、欲しかったもの
- 「Simple OSC」のインストールを待って、まず「pyOSC」をインストール
- 「basic_send.py」を実行させてMaxへのOSCメッセージ送信を確認
- 「basic_receive.py」実行させるとMaxからのOSCメッセージ到達は確認できたもののメッセージ自体の取得ハンドラが無い
- 「pyOSC」内の「OSC.py」は長大でえらく大変そう(^_^;)
そこで、昨日の手順と同様に、以下のように SimpleOSC_0.3.2.zip を「SimpleOSC.zip」とリネームしてRaspberry Pi62号機にrcpしてunzipしてインストールしてみた。 これで、指定されたように「pyOSCをインストールした後に」「SimpleOSCインストールした」という事になった。#!/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()ここで改めて「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」の冒頭の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 $の意味が判って、そこからイモヅル式に全ての繋がりが判ったのである。 defaultのpathとかの詳しいところは不明だが、上の「from simpleOSC import」というのは、インストールされた「simpleOSC.py」に並んでいる定義をインクルード(import)する、という事だったのだ。 そして参照されている「simpleOSC.py」の冒頭ではfrom simpleOSC import initOSCClient, initOSCServer, setOSCHandler, sendOSCMsg, closeOSC, \ createOSCBundle, sendOSCBundle, startOSCServerとあった。これが、先にインストールしておくべき、という事だったpyOSCの中にあった「OSC.py」なのである。 そして参照されている「OSC.py」の冒頭ではfrom OSC import OSCServer, ThreadingOSCServer, ForkingOSCServer, OSCClient, OSCMessage, OSCBundleとあった。 おそらくこれは、apt-getでインストール完了していたOSCのパッケージで定義されているのだろう。 これにより、結局のところ、defaultでpathとかの折り合いは済んでいる模様で(^_^;)、「app_example.py」をリネームした以下の「test.py」は、ちゃんと定期的にMaxにOSCメッセージを送り、またMaxからのOSCメッセージに反応するだけでなくその中身のデータを表示する、という事まで確認できた。import math, re, socket, select, string, struct, sys, threading, time, types#!/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」であった。
- sudo mkdir -p /media/ramdisk
- sudo mount -t tmpfs -o size=1K tmpfs /media/ramdisk
- 上の2行で、RAMdiskの作られるディレクトリの作成とマウントは完了
- 下の1行で、終了時のアンマウントができる
- sudo umount /media/ramdisk
- このRAMdisk領域へのファイル書き込み例は以下
- fp = fopen("/media/ramdisk/test.txt", "w");
- fputs(s, fp);
- このRAMdisk領域からのファイル読み込み例は以下
- fp = fopen("/media/ramdisk/test.txt", "rb");
- s[i] = fgetc(fp);
そこでまず、 RAMdiskにtest.txtを書き込むプログラム、 を改訂した以下の「test4.c」を作ってRaspberry Piにrcpして「cc」でコンパイルして実行を確認し、RAMdiskにMaxで実験した適当な64バイトの16進数データの並んだファイルを置いた。(一部、表示のため改行を挿入)
これで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」に入れて実験することで、この動作を確認した。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 ~ $これでようやく、最後の接続の部分に到達した。 つまり、「Maxから送られる64個の16進数(00-FF)データに対応したPWM値をパックしたOSCメッセージ」を受信して、これを刻々とRAMdiskにある「test.txt」に書き出す、というPythonプログラム★を作れば、最終的には以下の流れとなるわけである。#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<これが動いたら、ビット単位の点滅だった「disp_go」を、いよいよPWM制御の時分割プログラムに成長させる、という事になる。 一気にやらずに外堀から埋めてきたので、「PythonでOSC受信」までは既に出来ていて、いよいよ最後に残ったのは「Pythonでファイル書き出し」という一点だけになった。
- (最初に)RAMdiskを設定、マウントしておく
- MaxからOSCメッセージとして64個の16進数(00-FF)データに対応したPWM値を送信
- これをPythonプログラム★がOSC受信してRAMdisk内のtest.txtに書き出す
- これをバックグラウンドで走る「disp_go」がビットごとに表示(PWM値が127以上だと点灯)
そこで「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」はこれを点灯させるのである。
明らかに、この「test.py」の、それも追加した「f=・・・」の部分のバグということである。 しかしここで時間切れとなり、また明日以降に継続となった。 かなりいいセンまで進んでいるので、なんとか頑張って、Sketchingまでに作り上げたいところである。#!/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()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
こうなると、今度は64個のPWM値を送るMaxパッチの方が、いちいちテーブルにマウスで描画してスペースキーで送る、という実験仕様がカッタルくて面白くなくなってきた。 個別のPWM制御、とわかるYouTube動画は、現状の「UDP_test_01.maxpat」はうまく伝わらない。 明日にでも余裕があれば、ここらを改訂して、念願の「Raspberry PiでPWM」を完成させたいところである。#!/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()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アクセス頻度 設定可能)
これで、Raspberry Pi+SUACboardのシステム側のツールは完成、と見て、「test7.c」を「gcc test7.c -l bcm2835」でコンパイルした「a.out」を「disp_go」にリネームし、「test.py」を「OSC_2_RAMdisk.py」とリネームした。 RAMディスクの設定から含めると、この環境での起動準備は、以下のようになった。#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<ターミナルを1画面にしたいのであれば、以下のように、起動の最後に「&」を付けてバックグラウンド実行、とすればよい。 ここまでをRaspberry Piの起動時処理として登録してもいいのだが、それほどの手間でもないので、Raspberry Pi62号機にだけ「disp_go」と「OSC_2_RAMdisk.py」を置いておき、必要に応じて呼び出すこととした。
- ターミナルを開き、sshログインする
- sudo mkdir -p /media/ramdisk
- sudo mount -t tmpfs -o size=1K tmpfs /media/ramdisk
- sudo ./OSC_2_RAMdisk.py (OSC→RAMdiskのツールの起動)
- ここでMaxから初期値のPWMデータをOSC経由で送る
- ターミナルをもう1画面、開き、sshログインする
- sudo ./disp_go (RAMdisk→Raspberry PiのPWM点灯表示ツールの起動)
そして3限のうちに、改訂したMaxパッチ UDP_test_03.maxpat によって、以下のように色々なモードで、多数のLEDを同時にPWM制御する、というデモを実現することが出来た。 あまりにRaspberry Piが高速なためか、GPIOポートの出力電圧が一部の74HCロジックのスレショルドレベルと拮抗して正しく伝わらずにチラつくところもあるが、これはLinuxのバックグラウンド処理の不定期な間欠処理とも関係しているようで、まぁ「気にしない」というところである(^_^;)。
- ターミナルを開き、sshログインする
- sudo mkdir -p /media/ramdisk
- sudo mount -t tmpfs -o size=1K tmpfs /media/ramdisk
- sudo ./OSC_2_RAMdisk.py &
- ここでMaxから初期値のPWMデータをOSC経由で送る
- sudo ./disp_go &
YouTube
ここまで来たところで、ちょうどこのHTMLのファイルサイズが「part1」と同じ238KBとなった。 ちょうど区切りなので、ここまでを「part2」として、日ごとのHTMLにも分割することにした。 Sketching2013でのお披露目に向けて、まだまだ続くところは「part3」としよう。(^_^)
「Raspberry Pi日記」トップに戻る
「日記」シリーズ の記録