今回は、Edison の中で Arduino実行部分とPython部分のデータ交換(プロセス間通信)を簡単に実行する方法について、試行錯誤してみました。
まず、なぜプロセス間通信が必要になったかについて。
以前、楢の木技研のサーモセンサを取り上げました。
が、フレームレイトとか、対象物の放射率などを指定できるのですが、これらは何なのか、不明瞭だったことに端を発します。たまたま、Micropython Pyboardの関連商品でOpenMVというボードがあるのですが、この中のサーモセンサーアダプタがまさにそのセンサ。
MLX90620というセンサであることが判明。早速調べてみると、部品の中に単体でi2cでインターフェイス回路を持っていることと、日本でもマウザーで手に入ることがわかりました。メーカーの資料を手に入れると、詳しい内容がわかります。ただし現在はMLX90621となり若干性能が上がっています。
1万円弱で手に入りました。(基本アメリカへの発注で住所などもローマ字なのですが、日本代理店があるらしく短期間で手に入りました)
部品が届く前に下調べすると、Arduino関連のソフトは2〜3ある模様。センサーが届いてから、Arduino Unoで試してみましたが、まともに動きませんでした。さらに探してやっと見つけたのがこれです。
MLX90621_Arduino_Camera/readTemperatures at master · longjos/MLX90621_Arduino_Camera · GitHub
結果をAdafruitのディスプレイに表示させるプログラムですが、この表示部分を取れば、簡単にコンソール表示できます。接続はこんな感じ
センサー電源が2.6Vなので、Arduinoの3.3Vにダイオードをかませて2.9V程度まで落とし、i2cのプルアップのため4.7KΩの抵抗を入れるそうです。
Arduino標準ライブラリであるWireで動くので、Edisonでも動くのでは?と思い、早々スイッチサイエンスEgletを使って、Edison用のArduinoIDEでコンパイルしてEdisonに転送してみるとすんなり動きました。センサーの接続方法も上記方法通りとしました。
Edisonでコンソール出力している様子
そこで次の段階。これを利用してEclipseクロスコンパイラのC++で組み直し、SWIGでPythonのネイティブライブラリを作ろうと思いました。(このEclipseとっても便利ですがMacだと頻繁にハングしてしまいます)
Edisonのクロスコンパイル環境
ところがどうしてもまともに動きません。散々調べた挙句i2cdetectで確認してみると、このセンサーは2つのi2cアドレスを持っているのですが、片方のアドレスがEdisonでは認識しないことが判明。
0x50は認識するが0x60が認識しない!!
同じEdisonで、なぜArduinネイティブの方だけがこのアドレスを認識するかが不思議なところです。これはMraaでは解決できないことが分かりましたが、それから先は私の力では無理。
そこで今回のタイトルにたどり着きます。つまりEdisonの中で動いているArduinoとPythonで組んだプログラムのプロセス間通信です。
IntelではEdison発売初期にプロセス間通信を取り上げていますが、あまりにもややこしすぎ。例題はC言語なので、さらにPythonで実行するには少しハードルが高すぎます。
IoT - Efficient communication between Arduino* and Linux native processes | Intel® Developer Zone
次に考えられるのは、双方でファイルのやり取りをする方法。これだと今回のようなデータを流し続けるようなセンサの使い方では、寿命のあるeMMCを介することになるので心情的に許せません。
How to make interprocess communication between ... | Intel Communities
そこで私なりに考えたのは、「シリアルのTXとRXをショートさせてArduinoで送出したデータを同じシリアルポートを開いてPythonでモニターできないか?」です。これができればシリアルポートが1つふさがるだけで超簡単にデータ交換ができるはずです。が、うまくいきません。
Pythonコマンドモードでは受信データは取りこぼしながらも拾うのですが、プログラムファイルで実行すると、うんともすんとも反応しません。したがって 全然ダメ!!
ここまで来るのに1週間。知識がないということは時間の浪費なんだと、今更ながら思い知らされました。
ここで発想を変えました。そう.......RamDiskを使うんです。色々調べると、Edisonには最初から16個もラムディスクが設定されてました。
これを使えばファイル読み書きが、eMMCを使うことなくハイスピードで実行できるはず。
タイミングをどうするのか疑問でしたが、mkfifoコマンドでファイルを作れば解決するみたいで早々試してみました。
データ受け取りのタイミングを色々試しましたが、Python主導で確実にArduinoソフトのデータを読めるようです(ファイルに書き込まれた信号'A'を受け取るまでArduino側が待っているので)。このプログラムを以下に載せます。センサのライブラリは先ほどのものを使います。(MLX90621cpp,MLX90621.hを使う)
以下Arduinoプログラム
特に問題ないと思います。
-------------------------------------------------------------------------------------
#include <Arduino.h>
#include <Wire.h>
#include "MLX906211.h"
MLX90621 sensor;
FILE *to_python;
FILE *from_python;
char buffer[32];
char buf1[500];
String str1;
char s[16];
void setup(){
Serial.begin(115200);
from_python = fopen("/mnt/ram0/pipe1", "r");
to_python = fopen("/mnt/ram0/pipe2", "w");
if (from_python==NULL)
Serial.println("Error: PIPE 1");
if (to_python==NULL)
Serial.println("Error: PIPE 2");
sensor.initialise (4);
}
void loop(){
fgets(buffer, 2, from_python);
if(buffer[0]=='A'){
xread();
}
}
void xread(){
str1="";
sensor.measure(true); //get new readings from the sensor
for(int y=0;y<4;y++){ //go through all the rows
str1=str1+String(char(65+y))+',';
for(int x=0;x<16;x++){ //go through all the columns
float sss= sensor.getTemperature(y+x*4);
sprintf(s,"%3.2f",sss);
str1=String(str1+s+',');
}
str1=String(str1+'\n');
}
str1.toCharArray(buf1, str1.length()+1);
fputs(buf1, to_python);
fflush(to_python);
}
-------------------------------------------------------------------------------------
以下Pythonプログラム
上記ArduinoスケッチはMacでコンパイル&転送するとEdisonの /sketch
の中に sketch.elf という名称で実行ファイルができます。今回は、これをondo.elfという名称に変更してPythonで実行させてます。また、/mnt にはあらかじめ ram0というディレクトリを作成しておきます。
プログラム前半はRamDiskをマウント。さらにこの中にmkfifoでpipe1、pipe2ファイルを作成してから、Arduinoプログラムをバックグランドで起動させています。
データを流す合図"A"を送ってからsleepを入れていますが、さらに待ち時間をを長くしてもArduino側が確実に追従しているので一応成功。ただし、このPythonプログラムを何回も停止実行させるとバックグランドでondo.elfがいっぱい実行されるので注意!!。
-------------------------------------------------------------------------------------
import time
import os
os.system("mke2fs /dev/ram0")
os.system("mount -t ext2 /dev/ram0 /mnt/ram0")
os.system("rm /mnt/ram0/*")
os.system("mkfifo /mnt/ram0/pipe1")
os.system("mkfifo /mnt/ram0/pipe2")
os.system("/sketch/./ondo.elf /dev/ttyMFD0 &")
a=0
toArduino=open("/mnt/ram0/pipe1","w")
fromArduino=open("/mnt/ram0/pipe2","r")
print toArduino
print fromArduino
while(True):
toArduino.write("A")
toArduino.flush()
a=a+1
print "Reading....", a
time.sleep(0.1)
data=fromArduino.readline()
print data
data=fromArduino.readline()
print data
data=fromArduino.readline()
print data
data=fromArduino.readline()
print data
-------------------------------------------------------------------------------------
実行している様子。結構めんどくさいことをしてる割には、かなり早いでしょ。
今回あまりにもテストをしすぎて(ArduinoやMbedやEdisonに頻繁に差し替えたり、抵抗を抜いたり差したり)センサー中の測点を1個ダメにしてしまいました。ちょうど20度くらいの差なので、ライブラリで調整することにしました。
あーあ......。
次回は、これをSimplecvを利用して画像と合体させてjpeg配信してみます。
最新のEdison用Yoctoは容量が大きくなりUSB接続時に現れるEdison記憶媒体容量より大きくEdisonにコピーできないため、reboot ota ができないようです。純正か、スパークファンのボードで intel純正mac用のインストーラを使えば簡単にインストールできます。