組み込み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つけてビルド&インストール
することで全ユーザが使えるところにインストールされるはずです。
自分はそうしています。