ArduinoとRaspberryPiでECメーターを作ってみた


概要

 本記事は、ArduinoとRaspberryPiおよびコンセントの端子を用いてEC計を作製した記録です。Arduinoをアナログデータ(EC値)の取得に用い、RaspberryPiでcsvファイルへの連続的なデータとしての保存とインターネット接続によるIoT化を図りました。コンセントとArduinoを使用したEC計はMichael Ratcliffe氏が作製したもの*1をベースにしています。
 作製した結果、概ね通常の(amazonで数千円くらいの)肥料濃度計測に使用する分には問題のないレベルの測定装置を作成することができました。また、cronを活用することでcsvファイルへの連続的なEC値の記録を可能とします。

0. そもそもEC計とは

 この記事をご覧になられているということは、EC値を図りたいという良い意味で変態の方であるので笑、今さら説明は不要かと思いますが、私の勉強のため記しておきます。不要な方は読み飛ばしてください。
 詳細な式展開を伴う正確な理論は、教科書に譲るとして、今の私たちの目標は養液の濃度(特に養液栽培等農業分野における養液の濃度)を図ることです。
 この時、ある一つの物理のことわりに着目してみます。それは、「水の電気伝導率はイオン性の不純物が多いほど上昇する」*2ということです。肥料が水に溶けるとき、これらはイオンという形で溶けます。例えば、食塩(NaCl)が水に溶けたという場合、NaイオンとClイオンに分かれます(イオン結合が解ける)。また、電気伝導率とはその物体がどれくらい電気を通すのかということです。抵抗(電気の通しにくさ)の逆と考えるとわかりやすいかもしれません。
 つまり、水の電気伝導率を測れば、それがその水に溶けているイオンの量を表すと考えられます。ここまでくるとEC計がしていることは、案外にも単純だと考えることができます。水に2本の電極(プローブ)を突っ込んで微弱な電気を流して、ある一方からもう一方への電気の流れ具合を測っているということです。

 上図は、測定の様子。プローブは家庭用コンセントの端子を流用しています。これは、標準化された大きさの電極を用いることで、較正をおこないやすくする一助になっています。なお、パンダはDAISOで購入したお気に入りの加湿器です。
 銀色に光って見える細長いセンサが温度センサです。水溶液中のイオンの移動速度は一般的に温度が上がるほど活性化されるので、温度による補償が必ずレベルで必要になります(イメージ的に水を温めたら、水分子が激しく動いて蒸気になるのと同様、イオンも暴れるのだろう)。計測メーカのHP等を見ていても、EC値の横には温度の記載が必須と記されているので、液温とEC値はセットであると考えて差し支えないと思います。

1. 開発環境

 ラズパイとArduinoそれぞれにわけて記します。特筆して特殊な環境は用いていません。家庭用コンセントも参考文献1では欧米使用ですが、今回は日本仕様のもので構いません。今回の工作では、アナログ入力ができないがIoT化やデータ保存に向くRaspberryPiをアナログ入力できるArduinoによって補完しようとする設計思想です。

1.1 RaspberryPi

ver:RaspberryPi3b+
OS:Rasbian
Python:Python3.7.3

1.2 Arduino

Arduino UNO(今回はサードパーティ製の互換品KUMAN UNOを使用。やっぱ、安い。。。筆者は、500円代で購入した。性能に本家と遜色はない)
言語:Arduino言語(ArduinoIDEのスケッチにてプログラミングを作成)

1.3 その他製作に使用したもの

◆測定部端子:日本で流通している家庭用コンセントの端子を使用した(差し込む2本の金属板に穴があいてるもの)×1
◆温度センサー:DS18B20センサー(センサ部が防水の金属筐体に収まっていて、液体温度を測定できる仕様のもの)×1
◆抵抗:1kΩ×1(温度計に抵抗を噛ます場合は2個)

2. 装置概要

 装置の概要は下記の図に示す通りです。fritzingで描けば良いのですが、暫定版をとりあえず上げないと永遠下書きで眠りそうなので、雑な写真でご容赦を。

 他の作業も並行して行っているためかなり、ごちゃごちゃ目ですが、基本はラズパイ、Arduino、温度計電極で構成されています。また、温度計にも念のため抵抗を介していますがなくても正確な値を得ることができそうです。
 また、GPIOピンの場所はコードから読み取るか、参考文献1にある図を参照ください。

3. EC値を計測するコードをArduinoに書き込む

3.1 必要なライブラリをインストール

 ArduinoIDEのダウンロードや各種セットアップは、他のよりわかりやすい記事をご覧ください。ここでは割愛します。
 必要なライブラリは下記のとおりです。
◆one wire(温度計の通信がone wireのため)
https://www.arduinolibraries.info/libraries/one-wire
◆DallasTemperature
https://www.arduino.cc/reference/en/libraries/dallastemperature/
いずれのURLも2021/2/9時点で有効
上記のURL先からzipファイルをダウンロードします。基本的には(いじっていなければ)、使用されているPCのarduino librariesのファイルに保存します(windowsの場合PC>ドキュメント>Arduino>libraries)。

3.2 ArduinoIDEにコードを書く

 ライブラリがダウンロードできれば、下記のコードをArduino IDEにスケッチします。

/*
 このプログラミングコードはGNU General Public Licenseに則り、Michael Ratcliffe氏が作製したもの(参考文献1)を基に改変・加筆しました。 
*/
//必要なライブラリをincludeする
#include <OneWire.h>
#include <DallasTemperature.h>

//変数の定義
int R1= 1000;//電極に使用するプルダウン抵抗の値。手元に1000Ωの抵抗がない場合は適宜変更。ただし、300Ω以上にしないといけない。
int Ra=25; //Arduino側にある抵抗の値。これを変える必要はない。
int ECPin= A0;
int ECGround=A1;
int ECPower =A4;

float PPMconversion=0.7;//PPM値を算出するのに必要定数
float TemperatureCoef = 0.019; //今回のコードでは温度較正を線形の関数を用いて行う。その際に使用する1次関数の傾きの値。液肥を測る分には変更する必要はないと思われる。
float K=2.8;//電極の大きさに依存する値。もし、日本の家庭用コンセント以外の電極(例えば自分で切り出したアルミ板等)を使用する場合は、計算しなおして下さい。詳しい算出方法は他の記事参照。


//温度センサの通信設定
const int TempProbePossitive =8;  //温度計の電源線の指定
const int TempProbeNegative=9;    //温度計のGND線の指定

//各種ライブラリの設定 
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

//初期化
float Temperature=10;
float EC=0;
float EC25 =0;
int ppm =0;
float raw= 0;
float Vin= 5;
float Vdrop= 0;
float Rc= 0;
float buffer=0;

//起動時に一回行うセットアップ過程
void setup()
{
  Serial.begin(9600);
  pinMode(TempProbeNegative , OUTPUT ); 
  digitalWrite(TempProbeNegative , LOW );
  pinMode(TempProbePossitive , OUTPUT );
  digitalWrite(TempProbePossitive , HIGH );
  pinMode(ECPin,INPUT);
  pinMode(ECPower,OUTPUT);
  pinMode(ECGround,OUTPUT);
  digitalWrite(ECGround,LOW);

  delay(100);// センサの設定のための待機時間
  sensors.begin();
  delay(100);

//くり返し処理の過程
void loop()
{
GetEC();          
PrintReadings();  
delay(5000); 
}

//メインループからの呼び出し
void GetEC(){
sensors.requestTemperatures();// Send the command to get temperatures
Temperature=sensors.getTempCByIndex(0); //Stores Value in Variable
//液体の抵抗の推定
digitalWrite(ECPower,HIGH);
raw= analogRead(ECPin);
raw= analogRead(ECPin);// This is not a mistake, First reading will be low beause if charged a capacitor
digitalWrite(ECPower,LOW);
//ECに変換
Vdrop= (Vin*raw)/1024.0;
Rc=(Vdrop*R1)/(Vin-Vdrop);
Rc=Rc-Ra; 
EC = 1000/(Rc*K);
//温度較正
EC25  =  EC/ (1+ TemperatureCoef*(Temperature-25.0));
ppm=(EC25)*(PPMconversion*1000);
;}

//外部への書き出し
void PrintReadings(){
Serial.print(EC25);
Serial.print(",");
Serial.print(ppm);
Serial.print(",");
Serial.println(Temperature);
};

Arduino側のコードは以上になります。詳しいことは参考文献1を参照してください。私より仕組み的なとこは100倍詳しく解説されています。
参考文献1のコードからの主な変更点は下記の2点です。
(1)日本で流通しているコンセントの端子の形状にあわせて、K値を変更しています*3。また、任意の電極を使用する場合も算出可能です。
(2)外部への書き出しをCSVファイルに保存しやすい形で出すため、カンマ区切りでEC値、ppm値、温度の三つで一行となるように変更しました。
 このコードをコンパイルすると、EC値、ppm値、温度の3つの値が1行にシリアルモニタに出力されると思います。筆者の場合、水道水を使用した場合EC値、ppm値、温度の3つの値はそれぞれEC:0.14,ppm:98,温度:11.13になりました。水道水には塩素が若干溶けているので、0にはならないはずです。また、この値はお住いの自治体の水道局やもちろん水温によっても変動します。
 温度が-127℃と表示される場合は(そして宇宙かどこかにいない場合は)、Arduinoについている再起動ボタンを押すとまともな値が返ってきます。特に、ArduinoからのUSBの抜き差しを行うとかなりの確率で-127℃固定化現象が起きますので、リブートをお忘れなく。

4. RaspberryPi側のコード

4.1 ArduinoとRaspberryPiの接続を確認する

 次に、Arduinoで取得したEC値等の各種の値をRaspberryPiに投げて保存します。今回は、通信にシリアル通信を使用しました。いろいろ通信方法はありますが、これが一番簡単っぽいのです。
 まず、装置概要図にあるようにArduinoとラズパイをUSBケーブルで接続します。ラズパイ側に挿すUSBポートはどこでも構いません。USBポートに挿したら、ラズパイがArduinoを認識しているかを確認するため、ターミナルに下記を実行します。

lsusb

そーすると、

Bus 001 Device 008: ID 058f:6387 Alcor Micro Corp. Flash Drive
Bus 001 Device 004: ID 046d:0825 Logitech, Inc. Webcam C270
Bus 001 Device 009: ID 2341:0043 Arduino SA Uno R3 (CDC ACM)  ←コイツがいたらOK!
Bus 001 Device 005: ID 093a:2521 Pixart Imaging, Inc. Optical Mouse
Bus 001 Device 007: ID 0424:7800 Standard Microsystems Corp. 
Bus 001 Device 003: ID 0424:2514 Standard Microsystems Corp. USB 2.0 Hub
Bus 001 Device 002: ID 0424:2514 Standard Microsystems Corp. USB 2.0 Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

と返ってくると思います。筆者はラズパイにカメラやらメモリやらいろいろ接続していますが、Arduinoの文字があればOK。

4.2 シリアル通信でArduino側の出力を受け取る

 接続が確認されれば、あとはラズパイ側がArduinoからのデータ(EC値,ppm,温度)を受け取れるようにします。筆者の場合は、Thonny Python IDEに記載しました。もちろん、ここはお好みのIDEで構いません。

#必要なライブラリをインポートします。pyserialをインストールされていない方はpyserialをpip等を用いてインストールしてください。
import serial
import datetime
import os

#保存するファイル名の定義。ここでは、csvファイル形式で保存することにしました。
fileName = '/home/pi/ECmeter/EC_value.csv'

#シリアルポートを開ける。おまじない的なもの笑
ser = serial.Serial("/dev/ttyACM0",9600)

#もしファイルがなかったら作るプロセス
if not os.path.exists(fileName):
    thData = open(fileName,'w')
    thData.write('date_time,EC_value\n')
    thData.close()

#Arduinoからデータを受け取って、ファイルに保存するプロセス
while True:
    Arduino_data = ser.readline().strip().decode('utf-8')
    Arduino_data = [Arduino_data] #listにします
    now = str(datetime.datetime.now())[0:16] #時間は秒まで記載
    date_data = [now]
    data = date_data + Arduino_data 
    thData = open(fileName, 'a')
    thData.write(','.join(map(str,data))+'\n')
    thData.close()
    break

ser.close()

 ファイルに保存するプロセスが初学者丸出しのかなりモッサリしたコードになっています。かっこよくできる人はぜひ変えて使用してください。
 最後の部分のコードの補足を記します。

Arduino_data = ser.readline().strip().decode('utf-8')

 この部分はArduino_dataという変数に(Arduinoからくるdataなのでこう名付けました)ser.readline()でarduinoのシリアルモニタに表示されるところの1行分を拾ってきます*4。また、strip()とdecode()を入れることで、csvに保存したときに文字化ける('bや\r\nなどの後々の解析の時に邪魔になるものがついてくる)のを防ぐために必要です*5。

5. csvファイルに書き込まれたことを確認

 下記の画像のようになればOKです。写真はLibreOffice(Rasbianについているやつ)で開いています。なお、9行目が前項のdecode関数を入れなかった場合のある種の失敗例です。

6. おわりに

ここまで完成すれば、あとはcronで定期実行処理を記載nanoエディタ等で記述すれば、任意の時間ごとに養液のEC値等を記録していってくれます。毎朝測る必要も記録する必要もないので、ありがたや~。
ラズパイをネットワークに接続できる環境にある場合(圃場の多くはそうではないかもしれないですが)、WinSCPを使ってお手持ちのPCにファイルを移動させたり、LINEとの連携なんかもするとさらに楽しいかもしれません。

参考文献

下記のURLを大いに参考にさせていただきました。ありがとうございます。Michael Ratcliffe氏を筆頭とする有益な情報をネットに公開してくださる皆さまのオープンソース精神に感謝します。
私もそういった文化の一助になれれば幸甚です。
また、本記事は電子工作初心者の学生が作製したものであり、記事内容に一切保証はありません。また、ここはこうしたほうが良いよとか、ここはこっちが適切な表現等、建設的ご指摘がありましたらお手数ですがぜひよろしくお願い致します。ご一読ありがとうございました~!!

*1:arduinoでECメーター作製のバイブル
https://hackaday.io/project/7008-fly-wars-a-hackers-solution-to-world-hunger/log/24646-three-dollar-ec-ppm-meter-arduino  
最終閲覧日2021/2/9
*2:電気伝導率についてhttp://www.kenq.net/dic/133.html#:~:text=%E9%9B%BB%E6%B0%97%E4%BC%9D%E5%B0%8E%E7%8E%87%EF%BC%88%E5%B0%8E%E9%9B%BB%E7%8E%87,%E3%81%A8%E3%81%84%E3%81%86%E5%8D%98%E4%BD%8D%E3%81%A7%E8%A1%A8%E3%81%97%E3%81%BE%E3%81%99%E3%80%82
最終閲覧日2021/2/9
*3:K値の参照先。液肥調整まで自動化したい方はこの記事がおすすめ!
https://nadegata.info/ec-automation-how-to-make/
最終閲覧日2021/2/9
*4:シリアル通信のイロハ的記事
https://qiita.com/kosystem/items/0023cfee941fdf099087
最終閲覧日2021/2/9
*5:decode関数等その他について
https://ameblo.jp/hitochan007/entry-12103254519.html
最終閲覧日2021/2/9