組み込み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)
#includeSPIデータを取得するMathLinkプログラムはこんな感じ。WiringPiのmcp3002 API使うと非常に簡素だ。MathLink対応はmathlink.hをインクルードして、mainでMLMainを呼ぶようにするだけで良い。#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); }
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分毎に更新される。
考察(メモリ不足)
コンソールから起動すると"can't open display"と言われたため、VNCなり何なりでX11ディスプレイが必要なのだと考えた。実際それで動いた。が、数時間稼働させると、ほぼハング状態に陥った(死んではいないが操作不能状態)。それは、Share[]を処理のサイクルに加える事で改善した。また数時間稼働させると、今度は
No more memory available.となって、停止してしまった。
Mathematica kernel has shut down.
Try quitting other applications and then retry.
ディスプレイ(VNC)無しで実行できないか?
と考えて、最初はxvfb経由でバッチ的に動かしたらどうだ?とか試した
$ xvfb-run -s "-screen 0 640x480x8" mathematica spi.m
スクリプトは読まれるが、ノートブックに読み込まれるだけで評価されない。
ディスプレイ(VNC)無しで実行できる!
Creating and Post-Processing Mathematica Graphics が参考になる。
MathematicaのNotebookインターフェース通さずに、直接MathematicaKernelを起動すればいい。
math.sh(Kernel起動シェルスクリプト)
#!/bin/sh export LD_LIBRARY_PATH=/opt/Wolfram/WolframEngine/10.0/SystemFiles/Libraries/Linux-ARM MathKernel=/opt/Wolfram/WolframEngine/10.0/SystemFiles/Kernel/Binaries/Linux-ARM/WolframKernel ${MathKernel} -noprompt -script $1こんなのを用意して
$ math.sh spi.m
で起動すると直接Kernelでのバッチ起動となって、-scriptで指定したスクリプトファイルが実行される。
Dynamic[]はスルーされる
直接実行は出来たけど、Dynamic[]は無視されてしまい、結果が得られないため、Dynamic[]を使わない処理フローに変更
spi2.m
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; While[True, (* Lux Plot *) luxNow = Last[AppendTo[luxData,lux]; If[Length[luxData]>144,luxData=Drop[luxData,1]]; luxData]; luxPlot = DateListPlot[luxData, Joined->True, PlotLabel->"Lux"]; (* Tempeture Plot *) degNow = Last[AppendTo[degData,deg]; If[Length[degData]>144,degData=Drop[degData,1]]; degData]; degPlot = DateListPlot[degData, Joined->True, PlotLabel->"Temp"]; (* Export *) Export["lux_now.gif",luxNow]; Export["lux_plot.gif",luxPlot]; Export["temp_now.gif",degNow]; Export["temp_plot.gif",degPlot]; Share[]; Pause[interval] ]DynamicをやめてWhileブロック内に全部入れた。サンプリングタイミングはPause[]で。
$ math.sh spi2.m &
しばらくすると、gifイメージが作られ始めた。
VNCサーバー、Mathematica FrontEnd(Notebook)を介さないため、メモリ消費の少ない状態をキープ出来ている。CPUにも余裕がある。lighttpdもストレスが無い。
いやー、長い道のりだった・・・Mathematica使うのなんて10数年ぶりだし。だけど面白かった~
こんにちは(・・)
返信削除すみません、教えてほしいことがあります。
wiringPiを使ってプログラムを作成されていますが、実行するときはsudoを付けないでも大丈夫でしょうか?
なにか、工夫されているのでしたら、よろしければ教えていただければと思っております。
それでは、失礼します。
デフォルトのpiユーザで実行していますか?
返信削除また、普通に
$ gpio -v
$ gpio readall
とやった時にどうなりますか?
だいぶ前のことなので記憶が曖昧ですが、
http://wiringpi.com/download-and-install/
にも書かれていますが、wiringPiをビルドする時にsudoつけてビルド&インストール
することで全ユーザが使えるところにインストールされるはずです。
自分はそうしています。