読者です 読者をやめる 読者になる 読者になる

Take’s diary

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

Intel Edison で ArduinoとPythonのプロセス間通信 その2(動画配信)

 前回は1つのEdison の中で動く Arduinoネイティブプログラムと、Pythonプログラムのプロセス間通信で、MLX90621センサーから4X16のデータを取り出しました。
 今回はそのデータを利用してEdisonから画像配信してみます。最終的にはこの写真のようになりました。(Henry基盤に接続したWeb Cameraとセンサーを使って、Edisonで合成して配信!!)
     f:id:TAKEsan:20160111133222p:plain
             右端の赤い点がダメにした測点

今回は2種類のEdison用Eagletボードを使いました

 Arduino側では前回出力した温度データではなく、温度データをRGBカラーに変換する下準備まで実行させます。これは画像処理をPythonで行うため、時間ロスを少しでも少なくするためです。
 温度データを色データにする方法は、測定した最大温度と最小温度の差を255等分。それから各点の温度を0〜255に変換して、Python側でRGBに振り分けてます。
 Python側ではSimpleCVでカメラ画像と温度データを合成したmjpeg動画をWeb配信させます。4X16 すなわち64個の四角形を画像と合成して配信すればどうなるか(どれだけ遅くなるか!!)が見ものです。
でーーーー、実行してみると、画像の大きさはともかく、思いの外早いので驚きです。下の動画を見て下さい。中央のSafariに赤青の濃淡でセンサーの読み取り結果が表示されています。どうですか?実験してみたくなったでしょ。
       
 今回はWebCameraの接続が簡単で、私の環境で3.3V I2cが使えるようにしたHenryボード上で最終的に動かします。このボードは前にも書きましたがArduinoのコードが転送できないので、コンパイルまでをスイッチサイエンスEagletで行い、できた実行形式ファイルをHenryに乗せたEdisonにコピーして使います。今回の場合、センサーがArduinoコードでしか動かないためEdison Arduinoのi2c接続先であるi2c-6の使えるボードであることが条件です。
 最初はブレッドボードでテストしていましたが、接触不良になる場合が多いので、前回の回路を基板にしてみました。Henry基盤のUSBにWeb Cameraを接続しています。
f:id:TAKEsan:20160111130945j:plain
センサーはこんな感じ
f:id:TAKEsan:20160111134338j:plain

では、コード紹介

こちらはPython部分

 相変わらずこんなめんどくさそうな処理をしているのに簡単に書けます。(Arduinoで間違って256分割してしまいRGB変換時エラーになってしまうので、255以上にならないように3を引いてます。)カメラ画像と合わせるためにセンサーの表示は左右反転しています。今後直すとすれば強制終了させた時にondo.elfをkillする処理でしょうか。

from SimpleCV import *
import os
import time

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

c=Camera(0, { "width":320, "height":240 })
print "Start!!"
print "width:320, height:240"
js = JpegStreamer("TakeEE.local:8090")
while(True):
  toArduino.write("C")
  toArduino.flush()
  data=fromArduino.readline()
  odata=map(int,data.split(","))   #odata Color list 64
  img=c.getImage()
  for y in range(4):
      for x in range(16):  
          col1=odata[x+16*y]-3
          if not(col1 >=0 and col1 <= 255):
                 col1=255
          img.dl().rectangle((300-x*20,80+y*20),(20,20),color=(col1,0,255-col1),filled=True,alpha=220)
  img.save(js)

こちらはArduinoコード

 前回から多少変更しました。今後のためにPythonから"C"が送られてきた時は、カラーコード用の数値。そして"A"が送られてきた時は温度を送るようにしました。出来上がったネイティブコードsketch.elfをondo.elfとして、Edison の /sketch 以下にコピーしました。

#include <Arduino.h>
#include <Wire.h>
#include "MLX906211.h"

MLX90621 sensor; // create an instance of the Sensor classaaaa

FILE *to_python;
FILE *from_python;
char buffer[32];
char buf1[500];
String str1;
char inChar;
char s[64];

void setup(){
    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(1);
    }
    else if(buffer[0]=='C'){ //カラー表示切り替え最低から最高の間256段階
        xread(2);
    }
}

void xread(int xx){
    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
            if (xx==2){
                float min=sensor.getMinTemp();
                float max=sensor.getMaxTemp();
                int sss=(256/(max-min))*( sensor.getTemperature(y+x*4)-min);
                sprintf(s,"%i",sss);
            }
            else if(xx==1){
                float ttt=sensor.getTemperature(y+x*4);
                sprintf(s,"%2.2f",ttt);
            }
            str1=String(str1+s+',');
        }
    }
    str1=str1.substring(0, str1.length()-1); //最後の,を取る 
    str1=String(str1+'\n');
    str1.toCharArray(buf1, str1.length()+1);
    fputs(buf1, to_python); 
    fflush(to_python);
}

最後に

 たったこれだけのことを実験するだけなのに、買ったばかりの3dプリンタもほったらかしにして、かなりの時間を消費しました。ただ、すごく面白かったのも事実です。
 結構メモリを使う処理だと思いますが、問題なく動いて、スピードもそこそこだったことに結構満足しています。SimpleCV内部でnumpyやscipyを使っているのでかなりの解像度で補間できそうな気がしますが、ダメ元で今後トライしてみます。(ただし狂ってしまったセンサーを買い直すお金が貯まってから!!)

補足

 i2cセンサーに関するArduino資産は膨大な数です。いちいちEdison用にコーディングし直さなくとも、スピードを損なわずにそのまま動くということは、ある意味とんでもないことだと思いません?