Take’s diary

Macとマイコンに関すること--ワクワクの製作日記

Intel Edison で ArduinoとPythonのプロセス間通信

 今回は、Edison の中で Arduino実行部分とPython部分のデータ交換(プロセス間通信)を簡単に実行する方法について、試行錯誤してみました。

 

まず、なぜプロセス間通信が必要になったかについて。

 以前、楢の木技研のサーモセンサを取り上げました。

takesan.hatenablog.com

が、フレームレイトとか、対象物の放射率などを指定できるのですが、これらは何なのか、不明瞭だったことに端を発します。たまたま、Micropython Pyboardの関連商品でOpenMVというボードがあるのですが、この中のサーモセンサーアダプタがまさにそのセンサ。

            f:id:TAKEsan:20160108150418j:plain

 MLX90620というセンサであることが判明。早速調べてみると、部品の中に単体でi2cでインターフェイス回路を持っていることと、日本でもマウザーで手に入ることがわかりました。メーカーの資料を手に入れると、詳しい内容がわかります。ただし現在はMLX90621となり若干性能が上がっています。

       f:id:TAKEsan:20160108150411p:plain

 1万円弱で手に入りました。(基本アメリカへの発注で住所などもローマ字なのですが、日本代理店があるらしく短期間で手に入りました)

 部品が届く前に下調べすると、Arduino関連のソフトは2〜3ある模様。センサーが届いてから、Arduino Unoで試してみましたが、まともに動きませんでした。さらに探してやっと見つけたのがこれです。

MLX90621_Arduino_Camera/readTemperatures at master · longjos/MLX90621_Arduino_Camera · GitHub

 結果をAdafruitのディスプレイに表示させるプログラムですが、この表示部分を取れば、簡単にコンソール表示できます。接続はこんな感じ

         f:id:TAKEsan:20160108150410j:plain

 センサー電源が2.6Vなので、Arduinoの3.3Vにダイオードをかませて2.9V程度まで落とし、i2cのプルアップのため4.7KΩの抵抗を入れるそうです。

 Arduino標準ライブラリであるWireで動くので、Edisonでも動くのでは?と思い、早々スイッチサイエンスEgletを使って、Edison用のArduinoIDEでコンパイルしてEdisonに転送してみるとすんなり動きました。センサーの接続方法も上記方法通りとしました。

         f:id:TAKEsan:20160108153454j:plain

 

f:id:TAKEsan:20160108152315p:plain

                                    Edisonでコンソール出力している様子

 そこで次の段階。これを利用してEclipseロスコンパイラのC++で組み直し、SWIGPythonのネイティブライブラリを作ろうと思いました。(このEclipseとっても便利ですがMacだと頻繁にハングしてしまいます)

f:id:TAKEsan:20160108154102p:plain

                                    Edisonのクロスコンパイル環境

 ところがどうしてもまともに動きません。散々調べた挙句i2cdetectで確認してみると、このセンサーは2つのi2cアドレスを持っているのですが、片方のアドレスがEdisonでは認識しないことが判明。

f:id:TAKEsan:20160108150415p:plain

                                    0x50は認識するが0x60が認識しない!!

 同じEdisonで、なぜArduinネイティブの方だけがこのアドレスを認識するかが不思議なところです。これはMraaでは解決できないことが分かりましたが、それから先は私の力では無理。

 そこで今回のタイトルにたどり着きます。つまりEdisonの中で動いているArduinoPythonで組んだプログラムのプロセス間通信です。

 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個もラムディスクが設定されてました。

github.com

 これを使えばファイル読み書きが、eMMCを使うことなくハイスピードで実行できるはず。

 タイミングをどうするのか疑問でしたが、mkfifoコマンドでファイルを作れば解決するみたいで早々試してみました。

communities.intel.com

 データ受け取りのタイミングを色々試しましたが、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配信してみます。

takesan.hatenablog.com

 

  最新のEdison用Yoctoは容量が大きくなりUSB接続時に現れるEdison記憶媒体容量より大きくEdisonにコピーできないため、reboot ota ができないようです。純正か、スパークファンのボードで intel純正mac用のインストーラを使えば簡単にインストールできます。