2015/10/20

Mathematica for RPi2 でGPIOのSPIデータプロット&Webで確認

RaspbianにはMathematicaが入っている。今回はこのMathematicaからSPIデータを取得してプロットしてみようという試み。

組み込みAPIでは1か0だけ
Mathematica/Wolfram言語にGPIOへアクセスする方法が書かれている。 →GPIO
標準APIでは1と0しか扱えないようだ。

SPI/I2C/PWMのためには?
MathLinkというプラグイン的なものを使うことで出来そうだ。
Wolframドキュメントセンター上では、MathLinkは WSTP に置き換えられているが、自分のRasbianでは、、mathlinkのまま。最新を取ってくるとまた違うかも。


MathLink開発環境
/opt/Wolfram/WolframEngine/10.0/SystemFiles/Links/MathLink/DeveloperKit/Linux-ARM
に、必要なライブラリやサンプルプログラムが入っている。情報はこれだけで十分。

WiringPi
下回りのライブラリとして、GPIO制御の定番らしいWiringPiを使う。
$ sudo apt-get install wiringpi
で入れてもいいと思うけど、最新がいいらしいので
$ git clone git://git.drogon.net/wiringPi
$ cd wiringPi
$ sudo ./build 
で入れる。

やってみたこと
お試しで買った工作キットに、mcp3002(ADC)とフォトトランジスタ、温度センサーが入っていたので、SPIにmcp3002をつけ、mcp3002にフォトトランジスタと温度センサーをつけて、部屋の明るさと温度をプロットしてみる。そして、それをWebで見れるようにする。という感じ。


SPIプログラム(mcp3002.c)
#include 
#include 
#include 

#define PINBASE 64
#define SPI_CH  0
#define TO_VOLT (3.3f/1024)

extern float spi(int ch)
{
  return TO_VOLT * analogRead(PINBASE + ch);
}

int main(int argc, char* argv[])
{
  mcp3002Setup(PINBASE,SPI_CH);
  return MLMain(argc, argv);
}
SPIデータを取得するMathLinkプログラムはこんな感じ。WiringPiのmcp3002 API使うと非常に簡素だ。MathLink対応はmathlink.hをインクルードして、mainでMLMainを呼ぶようにするだけで良い。

MathLinkライブラリ化(mcp3002.tm)
float spi P((int));

:Begin:
:Function:       spi
:Pattern:        Spi[ch_Integer]
:Arguments:      { ch }
:ArgumentTypes:  { Integer }
:ReturnType:     Float
:End:

:Evaluate: Spi::usage = "Spi[ch] gives the SPI value."
単にプログラムをコンパイルするだけではダメで、上のようなテンプレートファイルを作成して、 mprep ツールで tmからCに変換したものをセットでビルドする。

uuidライブラリがなくて、リンク時にこけたので
$ sudo apt-get install uuid-dev
で入れた。

Makefile
CADDSDIR = /opt/Wolfram/WolframEngine/10.0/SystemFiles/Links/MathLink/DeveloperKit/Linux-ARM/CompilerAdditions
INCDIR = ${CADDSDIR}
LIBDIR = ${CADDSDIR}

MPREP = ${CADDSDIR}/mprep
CXX = /usr/bin/c++
RM = rm

EXTRA_CFLAGS= -O2

PROGRAM = mcp3002
OBJS = $(PROGRAM).o $(PROGRAM)tm.o

all : $(PROGRAM)

$(PROGRAM) : $(OBJS)
        ${CXX} ${EXTRA_CFLAGS} -I${INCDIR} $(OBJS) -L${LIBDIR} -lwiringPi -lML32i4 -lm -lpthread -lrt -lstdc++ -ldl -luuid -o $@

.c.o :
        ${CXX} -c ${EXTRA_CFLAGS} -I${INCDIR} $<

$(PROGRAM)tm.c : $(PROGRAM).tm
        ${MPREP} $? -o $@

clean :
        @ ${RM} -rf *.o *tm.c $(PROGRAM)
makeビルドすると、mcp3002という実行プログラムが出来上がる。
これをMathematicaでInstallで読み込むと、Spi[0]とSpi[1]でCのspi(int ch)を呼べるようになる。

Mathematicaスクリプト(spi.m)
(* ::Package:: *)

(* Load 'mcp3002' MathLink Module *)
Install["mcp3002"]


(* Function *)
lux := {DateList[], Spi[0] * (100/(7500*0.000033))}
deg := {DateList[], (Spi[1]-0.5)*100}

(* Data List *)
luxData = {}
degData = {}
interval = 5 * 60;


(* Plot *)
(* Lux *)
luxNow = Dynamic[Last[AppendTo[luxData,lux];
             If[Length[luxData]>144,luxData=Drop[luxData,1]];
             luxData],
        UpdateInterval->interval, TrackedSymbols->{} ]
luxPlot = Dynamic[ DateListPlot[luxData, Joined->True, PlotLabel->"Lux"], SynchronousUpdating->False]

(* Tempeture *)
degNow = Dynamic[Last[AppendTo[degData,deg];
             If[Length[degData]>144,degData=Drop[degData,1]];
             degData],
        UpdateInterval->interval, TrackedSymbols->{} ]
degPlot = Dynamic[ DateListPlot[degData, Joined->True, PlotLabel->"Temp"], SynchronousUpdating->False]

(* Export *)
Dynamic[
        Export["lux_now.gif",luxNow];
        Export["lux_plot.gif",luxPlot];
        Export["temp_now.gif",degNow];
        Export["temp_plot.gif",degPlot];
        Share[],
        SynchronousUpdating->False,
        UpdateInterval->interval]
Install["mcp3002"]でMathLinkライブラリを読み込み。これを実行すると実際にmcp3002プログラムがプロセスとして起動する。プロセス間通信でやりとりしているようだ。
その後、明るさ(lux)と温度(deg)関数を定義。Spi[0]とSpi[1]で取得したデータを変換している。

Dynamic[]を使って、5分毎にSPIデータをサンプリングして、Dateリスト型の変数へ追加していく、 さらにそれをプロットする。Mathematicaノートブック上でプロット結果が見れるようになる。

最後が、それぞれの出力結果をGIFイメージへExportする処理。このイメージをWebServerで見れるようにすれば、外のブラウザで結果を見ることができるようになる。

実行
vncサーバーでも立てて、繋いで、プログラムとスクリプトがあるディレクトリから
$ mathematica spi2.m
で起動して、右上の「Run Package」を実行すると、プロットなどのDynamic処理が5分ごとに更新される。

これはこれで問題がある、後述の「考察」が解決策。


軽量Webサーバー立てる
$ sudo apt-get install lighttpd
$ sudo lightly-enable mod userdir
$ sudo /etc/init.d/lighttpd force-reload
で、~/public_htmlにindex.html書いて、http://(pi2)/~pi/ で見れるようになる。
~/public_htmlにmathematicを実行しているカレントフォルダのリンクを貼ってgifへアクセスできるように。
あとは、適当にindex.htmlを用意。

ブラウザからアクセスしてみた結果がこちら
こちらも5分毎に更新される。

2015/10/16

snappy on kvm on lxc

RaspberryPi2でUbuntu Core Snappyは普通に動いた。が、アプリ作らないと面白く無さそうだし、ラズパイはまだまだRaspbianで遊びたいので、Snappy環境を別に作ろうと思った。
SnappyのKVMイメージが提供されているので、それで出来そうだ。

KVM on LXC

すでにLXCコンテナで環境構築しているので、その上にKVM仮想環境を動かせばいいかと。
作って動かしてみることにした。
ホストでLXCと平行してKVMも動かすのも可能だろうけど、その方が複雑な気がする。

KVMを動かすためのコンテナ作成
$ sudo lxc-clone master -n kvm
分かりにくいかもしれんけど、'kvm'という名前のlxcコンテナにした。
新規作成は面倒なので、いつもmasterコンテナをクローンする方法で作る。

kvmコンテナ設定
色々試行錯誤した結果、kvmをlxc内で動かすには以下のデバイスが必要。

/dev/net/tun
ブリッジネットワーク構築時に/dev/net/tunにアクセスするらしく、mknodで作ったのではダメで、ホストの/dev/netをマウントする必要があった。

/dev/kvm
は、kvmが稼働するときにアクセスするものだそうで、これはmknodで作ればよいらしい。

それを作ってアクセスできるように、コンテナのconfファイルに以下を追記
# device
lxc.cgroup.devices.allow = c 10:232 rwm
lxc.cgroup.devices.allow = c 10:200 rwm
lxc.mount.entry=/dev/net dev/net none bind,optional,create=dir
lxc.hook.autodev = /var/lib/lxc/kvm/mount-hook.sh
 /var/lib/lxc/kvm/mount-hook.shの中身
#!/bin/sh
mknod -m 600 ${LXC_ROOTFS_MOUNT}/dev/kvm c 10 232
コンテナ起動
$ sudo lxc-start -n kvm
で、/dev/kvm、/dev/net/tunがちゃんとできていることを確認する。

あとは、sshなりconsoleなりからコンテナにログインしての作業

kvmインストール

$ sudo apt-get install kvm virt-manager libvirt-bin bridge-utils
$ sudo apt-get install lxde tightvncserver fonts-ipafont
$ mkdir .vnc
$ ln -s /etc/X11/Xsession .vnc/xstartup

まとめて書いちゃいましたが、lxde以降はVNCデスクトップ構築のためです。kvmのvirt-managerや本題のSnappyアプリがディスプレイを必要とする場面があるため。

この時点で、libvirtdやlxcと同じようにdnsmasqが動いていればOK。
ifconfigしたときには、virbr0ブリッジインターフェースが出来ているはず。最初、これが作られなくて悩んだ。
自分はデフォルトのままでいいので、kvmネットワークは 192.168.122.0 である。lxcが10.0.3.0なので、10.0.2.0とかにした方が綺麗かもしれないけど。

UbuntuCore Snappy 取得と起動

https://developer.ubuntu.com/en/snappy/start/
をそのまんま実行しても動くには動くが・・

$ kvm -m 512 -redir :8090::80 -redir :8022::22 ubuntu-15.04-snappy-amd64-generic.img -curses
で起動はできけど、kvmのネットワークと繋がらない。kvmコマンドがよくわからないし面倒なので、libvirtへインストール方法で。

Snappyゲスト作成

https://help.ubuntu.com/community/KVM/CreateGuests
は参考にはなるが具体的なところがよくわからなくて結構悩んだ。

img => qcow2 変換 (必須じゃないけど、qcow2の方がサイズを小さくできるんで)

$ qemu-img convert -f raw -O qcow2 ubuntu-15.04-snappy-amd64-generic.img ubuntu-15.04-snappy-amd64-generic.qcow2

インストール

$ virt-install --name snappy --import --memory 512 --disk ubuntu-15.04-snappy-amd64-generic.qcow2

インストール後、勝手にコンソールが出てきて起動しちゃう。しかもキー入力が効かない。抜ける・・
でも、これでインストールは完了だ。

コンソール
$ virsh console snappy

Snappyは最初からsshd動いているんで、sshで入っても同じ。
ifconfigすると、ちゃんとKVMのゾーンのIPアドレスになっている。
外へpingしても、ちゃんと名前解決もできているし、外へも出られているようだ。

consoleから抜けるには、"Ctrl+5"とか"Ctrl+]"で抜けられる。

VNCから使ってみる

Snappyインスタンス内では、webdm/snappydが稼働しているので、見てみたい。
けれど、kvmコンテナ以外からのアクセスは出来ない。
そこで、KVMコンテナで、tightvncserverを動かして、そこに外からVNC接続して
そこからブラウザで
http://snappy-ip-address:4200
にアクセスすると、snappyアプリのダウンロードマネージャを操作できる。

仮想の仮想になっているんで、NATを2重にやれば外からのアクセスもできるはず。


左上に出ているのがSnappyゲストで疎いているwebdm画面で、SnappyStoreを覗いているところ。その横がSnappyのコンソール。左下がKVMのvirt-manager、で右下には愛用のEmacs上でbashを使っている状態。

Snappyを動かす環境として完全な状態か?といえば、そうじゃないと思う。
その都度、kvmやlxcの設定をやることになるんだろうなと思っている。それがまた楽し。

2015/10/06

RaspberryPiのモニタ出力をVNC経由で表示

前記事で、RaspberryPi2(ModelB)にRasbianをセットアップして、普通にRasbianデスクトップ(LXDE)をVNCで表示するところまでやりましたが、モニタ接続しないで描画デモを見るには?と調べてたら dispmanx_vnc に行き着きましたので、書いておきます。

何分RaspberryPiデビューが遅かったもんで、今こんなことをやっています。

目的は
・モニタレスで DEMO PROGRAMS にあるような描画デモを一通り動かしてみたい。
・遠隔地からVNC経由でRaspberriPiのディスプレイ出力を得たい。

最終的にはこんなイメージになります。
(スナップショットを載せないポリシーなのだけど、こればっかりは無いと辛いんで)


ということをできるようにするまでの流れ

dispmanx_vncをビルドするのに必要なライブラリのインストール
$ sudo apt-get install libvncserver-dev

dispmanx_vncをクローンしてビルド
$ git clone https://github.com/hanzelpeter/dispmanx_vnc.git
$ cd dispmanx_vnc
$ sh ./makeit

起動
$ sudo ./dispman_vncserver -r

すると、5900ポートでVNCサーバーが1つ立ちます。
後はどこからでもVNCでRaspberryPiのコンソール画面が転送されてきます。

本題はここまで

自分はRaspberryPi(Raspbian)自体のデスクトップに表示してみたかったので、RasberianにVncViewerインストールして、VNC内でVNCしてみました。

VncViewerインストール(vnc4viewerでもいいと思う)
$ sudo apt-get install tightvncviewer

自分の5900ポートをvncする。RaspberriPiのコンソールディスプレイそのものが現れる。
$ xtightvncviewer localhost:5900

デモをビルド
$ cd /opt/vc/src/hello_pi
$ ./rebuild.sh

どこかの端末からデモを1つ実行してみる。
$ cd hello_triangle
$ ./hello_triangle.bin


するとイメージのような状態になりました。15fps程度でキューブがくるくる回ります。

dispmanxって何だ?って調べてたら Raspberry Pi VideoCore APIs に概要説明がありました。
自分の理解としては、raspbianにプリインストールされている/opt/vc 自体がそれってこと。
dispmanxもvcの一部で、OpenGLESデモもビデオデバイス制御にvcライブラリを使っている。

dispmanx経由でディスプレイに描画されたイメージを直接キャプチャしてVNCストリームするツールがdispmanx_vncってことだね。

RaspberryPi用のXBMCやQtもこのVCライブラリを使っているらしいので、それらもVNC転送可能だということですね。

Raspberry Pi2 ModelB にlxcコンテナからraspbianインストール

気になっていたRaspberry Pi 2 Model B を購入しました。
https://www.raspberrypi.org
以前は教育目的じゃないと入手が困難だったけど今はアマゾンから普通に買えるのね。
同時にcocopar™ の3.5インチタッチパネル ディスプレイと定番の電子工作キットも購入。
色々遊んでみようと思う。

まずは、RaspbianをMicroSDに書き込まねばならない。
Ubuntuマシンとして使っているShuttleDS57UにSDCardソケットがあるんで、そこ使って書き込もうと思う。

RaspberryPi関係のことをやるためのlxcコンテナを作成して、そこから書き込むことにしよう。

マスターにしているコンテナをクローン
$ sudo lxc-clone master -n raspberry
$ ssh raspberry

自分の環境の場合、SDRAMカードデバイスは/dev/sdaになっている。一番最初のブロックデバイスなのだね。(メインのSSDストレージは/dev/sdb)
標準ではコンテナからはブロックデバイスへのアクセスはできないようになっている。

lxcコンテナから/dev/sdaへ書き込みできるようにする必要がある
http://forum.proxmox.com/threads/23256-LXC-Cannot-assign-a-block-device-to-container
を参考に

/var/lib/lxc/raspberry/config に以下の設定を追記
# block device
lxc.aa_profile = lxc-container-default-with-mounting
lxc.cgroup.devices.allow = b 8:* rwm
lxc.hook.autodev = /var/lib/lxc/raspberry/mount-hook.sh
mount-hook.sh は以下のような感じ
#!/bin/sh
mknod -m 666 ${LXC_ROOTFS_MOUNT}/dev/sda b 8 0
mknod -m 666 ${LXC_ROOTFS_MOUNT}/dev/sda1 b 8 1
”b”はブロックの意味かな。"8"とか"8 0"とかは、ホストで ls -l /dev/sda* とした時のデバイスのメジャーID、マイナーIDで、コンテナでも同じになるように。

起動時にフックが働いて、/dev/sdaと/dev/sda1が作成され、使えるようになる。

コンテナconfファイルの詳細は
https://linuxcontainers.org/ja/lxc/manpages/man5/lxc.container.conf.5.html

raspbianインストール
wgetでDownload ZIP からraspbian_jessie.zipをダウンロード。

unzipをインストールして、zipファイルを解凍
$ sudo apt-get install unzip
$ unzip raspbian_jessie.zip


日付-raspbian-jessie.imgファイルが出来上がる。

書き込み
$ sudo dd bs=4M if=/home/ubuntu/2015-09-24-raspbian-jessie.img of=/dev/sda

RaspberryPiにLANと焼いたMicroSDを差し込んで電源オン

IPアドレス調べるのがちょっと面倒
ホストから、
$ nmap 192.168.1.0/24
で、見慣れない奴を見つける。きっとそれがRaspberryPiだ。
早速 sshして、固定アドレスに変更。

設定をちょっとやっておこう

$ sudo raspi-config
Expand Filesystemを選ぶと、SDRAM全体が使えるようになる
Boot OptionでB1 Console
Internationalian OptionでTimezoneをAsia/Tokyo
Advanced OptionでB8 Serial Disable
ざっとこれくらい。

まずはVNCでもやって、様子を見てみる

RaspberryPiにVNCサーバー入れて起動
$ sudo apt-get install tightvncserver
$ tightvncserver
VNC接続パスワード設定。readOnly?はnと答える

raspberryPiのIP:5901へVNC接続するとデスクトップが出てくる。LXDEなのだね。

プログラムの教育用だけあって、開発環境はすでに色々入っているようですね。
特に、/opt/にはライブラリやらデモプログラムやらがどっさり。
これから、いじってみよう。