isapanda が 2021年03月01日02時04分13秒 に編集
コメント無し
記事種類の変更
製作品
本文の変更
概要 --- このシステムは、うっかり植物のみずやりを忘れてしまう私をサポートしてくれる、 自動水やり+植物の状態をブラウザから確認できるIoTシステムです。 ラベンダーを育てているので、名を「ラベンダー見守りシステム」略して **LMS** としています。 (もちろん、ラベンダー以外の植物でも対応できます) 最近は素人でも頑張ればいろいろ作れると思い、勉強のためにIoTシステムをハードからサーバサイドまで作ってみました。  この装置でできること ---- 拡張の余地はありますが、現時点でできることは以下の通り 1. 鉢の水分を測定し、設定値以下の場合は植物に水を与える 2. 鉢の水分量と周辺温度のロギング (1日1つのcsvファイルで記録) 3. ブラウザからの各種情報の確認、機器操作と設定変更 ・水分量と周辺温度のグラフによる確認 ・強制水やり ・自動水やり機能のON /OFF ・水分枯渇判定の閾値変更 ・測定タイミングの間隔変更 ・ポンプモーターの駆動時間変更  大まかな構成 --- 詳細の説明をする前に、ざっくりとしたシステムの説明をします。 LMSでは、RaspberryPiが定期的にセンサ情報を取得し、その情報をグラフ化して、Node.jsで設けたサイトに表示します。 ソケット通信を採用しているので、ページをリロードしなくても、勝手にグラフは更新されていく仕様です。  ### ハードウェアに関して ハードウェアはRaspberryPiに拡張基板を設けて実現しています。 拡張基板にはセンサの値を取得するための回路や、ポンプモーターを駆動するロー再度出力回路が実装されています。RaspberryPiの電源がなくても時間を保持できるように、リアルタイムクロック&ボタン電池も実装されています。 水分センサの情報取得やポンプモーターの駆動にわざわざPICマイコンを介しているのは、RaspberryPi無しでも拡張基板だけで水やりをできるようにするためです。(プログラムの書き換えは必要ですが。。。)  ### ソフトウェアに関して ソフトはnodjsを中心に構成されています。 ・Index.jsというjavascriptのファイルが定期的にpythonのファイルを実行し、PICからセンサ情報を取得したり、htmlのグラフを更新したりしています。グラフはchart.jsというライブラリを使っています。 ・ユーザーの設定した内容や、気温や水分量の情報はcsvファイルに書き出して保存しています。 ・PICマイコンはI2Cのスレーブとして動作し、RaspberryPiからの命令に応じてセンサ情報を渡したりポンプモーターを駆動させたりします。  詳細の話 --- ### 土壌水分センサの使用方法 このシステムでは、とにもかくにも土壌水分センサがないと始まりません。 土壌水分センサは秋月電子などで購入できるものを使用しています。 センサを刺した土の中に含まれる水分量に応じてベース電流が変化して、出力電圧も変化する仕組みです。 このセンサは電気を流すことで水分量を測定するという性質上、常にセンサをONしていると電気分解によって、あっという間に電極がさびてしまいます。 **使うときは測定するときだけ電源をONするように制御しなくてはなりません。**  駆動回路&読み取り回路は下図のとおりです。 PICマイコン(U3)が指令を受けるか、一定時間が経過したしたらRA5をHiにしてQ2とQ3を駆動します。 すると、CN4にセンサを接続されたセンサの電源がONになります。 センサは5V電源で動きますが、U3のPICマイコンは3.3Vで駆動させているため、R8とR9で分圧した電圧が入力されます。あとはよくあるAD変換で得た値をRaspberryPiにI2Cで受け渡せば完了です。  ### モーターの駆動方法 これといって何の変哲もないのですが、モーターよりも後段にPch MOSFETを設けています。 モーターの電源は5Vでも3.3Vでも使えるように0Ωによるジャンパ回路を設けてます。 Q48をRaspberryPiが操作して、モーターをON/OFFします。Q48はRN4902という2回路入りのデジトラを使ってますが、トランジスタはスイッチ用途でしか使ってないので、他のトランジスタでも問題ありません。**なんとなく選んだだけです。** C4とC3はモーターが急にONすると電圧降下を起こしてRaspberryPiがリセットするのではないかと思って、突入電流防止のためにFETをゆっくりON するために設けました。 ところが、こいつらがなくても電圧変動は起きないですし、むしろC4とC3があることでFETをOFFできなくなってポンプモーターがしばらく動き続けるという不具合が発生するので、C3とC4は未実装がいいと思います。 D4はお約束の還流ダイオードですね。  モーターはAmazonで買えるこいつを使ってます。水にそのまま浸けてもちゃんと動いてます。 ポンプに口にはホームセンターで買った適当なチューブを付けます。  雰囲気を掴むために、PICマイコンのソースコードの抜粋をはります。 ```arduino:PICマイコンのプログラム抜粋~指令による動作切り替え~ while(1){ //Raspberry Piから受け取った"command"によって動作を変える switch (command) { // ポンプモーターの駆動 (駆動時間はmotor * __delay_ms(500)). case 0x01: PORTAbits.RA4 = ON; //モーターON for(i=0; i<mortor_time;i++) __delay_ms(500); PORTAbits.RA4 = OFF; //モーターOFF command = 0x00; break; //土壌水分量の取得 case 0x02: PORTAbits.RA5 = ON; //センサON __delay_ms(100); ADCON0bits.GO =ON; __delay_ms(200); PORTAbits.RA5 = OFF; //センサOFF moist = ADRESH; command =0x00; break; //デフォルト(何もしない) case 0x00: PORTAbits.RA5 = OFF; break; } ```
### 回路図 上記のメインの動作ができるような回路図は下図です。  温度センサやRTCなどはI2Cで接続しています。 今回の工作で特に用いていませんが、LED(D3)を駆動する回路や、抵抗変化型のセンサを加えられるようにU4やCN2を設けています。 多少冗長性をもった設計をすると、あとで思いついた機能を付け足せるのでよいと思います。
### RaspberryPiのプログラム プログラムの全容は、ここに記載するには量が多すぎるので、githubのリンクを張っておきます。 https://github.com/isapanda/LMS.git 中にあるinstal.shがうまくRaspberryPi上で走れば、必要なディレクトリや必要なデータが用意され、とりあえず動く状態にはなるはずです。 (日付.csvがみつからないってエラーが出たら、とりあえずその日付のcsvを作ってあげれば動きます。バグです。) LMSは、先に説明した通り、Node.jsを中心にして動いています。 Node.jsが何なのかは私が説明するよりも、詳しく丁寧かつ正確な説明がいくらでもあると思うので、ここでは割愛します。 LMSでは、index.jsというスクリプトがいろいろなファイルを読んだり実行したりして、システムを動作させてます。 まず、setting.csvにある各種設定値を読み込みます。 ```arduino:index.jsの抜粋(設定値の取得) function getCSV(){ result=fs.readFileSync('/home/LMS/setting.csv','utf8').split(','); console.log(result[0]); interval=result[0]; // センサの読み取り周期 threshold=result[1]; //水分不足と判定する閾値 drivetime=result[2]; //モーターの連続ON時間 pompMode=result[3]; //自動水やり機能の有効無効設定 } ``` 次に、setIntervalコマンドによってintervalの設定時間に応じてlms.pyというpythonスクリプトが実行されます。 lms.pyでは先ほどのPICマイコンから水分量を入手したり、RTCから時間を入手したり、温度センサよ読み取ったりするスクリプトです。 lms.pyによって入手されたデータは、socket.ioというライブラリを用いたソケット通信によりクライアント側へ渡されます。また、データはcsvにも記録されます。 ```arduino:index.jsの抜粋(lms.pyの定期実行とデータの送信) ggg = setInterval(function(){ //この辺の処理でデータがhtmlに送られる exec("sudo python /home/LMS/lms.py",function(error,stdout,stderr){ io.sockets.emit('chart',{str:"--"+stdout}); console.log("stdout:",{Data:"--"+stdout}); console.log("stderr:",+stderr); var box = stdout.split(','); //水分量が閾値以下かつ自動水やり機能がONの時はモーターを駆動 if (threshold > box[3] && pompMode == "ON") exec("sudo python /home/LMS/mortor.py"); }); },interval); //intervalで設定された時間で実行される ``` ```arduino:lms.pyの抜粋 if __name__ == '__main__': try: d = datetime.datetime.today() dt = d.strftime("%Y%m%d") tm = d.strftime("%H%M%S") rtcdey = str(year) + str(month) + dey rtctime = str(hour) + str(min) + str(sec) disp = str(rtcdey) + "," + str(hour) + str(min) + str(sec) + "," + str(temp) + "," + str (adc) + ",\n" fname = rtcdey + '_lmsdata.csv' f = open("/home/LMS/log.csv","r") reader = csv.reader(f) rData = [] for row in reader: rData.append(row) i = row f.close() i = map(str,row) i= ",".join(i) path = "/home/LMS/" pname = path + fname if i != pname: f = open("/home/LMS/log.csv","a") csvWriter = csv.writer(f) dayData = [] dayData.append(pname) csvWriter.writerow(dayData) f.close() f = open(pname,'a') csvWriter = csv.writer(f) listData = [] listData.append(rtcdey) listData.append(rtctime) listData.append(temp) listData.append(adc) csvWriter.writerow(listData) f.close() print disp ``` グラフを確認するためのhtmlはこちら。見た目はcssで整えます chart.jsという便利ライブラリを使うことで手軽におしゃれなグラフを作ることができます。 ```arduino:index5.htmlの抜粋 (このへんでグラフを描いたり更新している) function MainViewModel() { function TmpChart(csvdata){ var tmpLabels = [], tmpData1 = [], tmpData2 = [], tmpData3 = []; var tlabels = [], ti2cdata = [], ti2cdata2 = []; //index.jsから受信したデータを順番に格納 for (var row in csvdata) { tmpLabels.push(csvdata[row][0]) tmpData1.push(csvdata[row][1]) tmpData2.push(csvdata[row][2]) tmpData3.push(csvdata[row][3]) } var l = tmpLabels.length; if ( l > 11 ) l=11; for (var i =0; i < l-1 ; i++) { tmpLabels.push(tmpLabels[tmpLabels.length-l+i]) tlabels.push(tmpData1[tmpData1.length-l+i]) ti2cdata.push(tmpData2[tmpData2.length-l+i]) ti2cdata2.push(tmpData3[tmpData3.length-l+i]) } var labelnum =tlabels.length; var ctx = document.getElementById('chart').getContext('2d'); var myChart = new Chart(ctx, { type: 'line', options:{ animation:false, responsive:false, scales :{ yAxes:[{ticks:{beginAtZero:true, min:0, max:100}}]}}, data :{ labels: tlabels, datasets: [ {label: 'Temperture', data: ti2cdata, backgroundColor: "rgba(250,160,0,0.6)", tension: 0, min:0}, {label: 'Moisture', data: ti2cdata2, backgroundColor: "rgba(54,164,235,0.2)", tension: 0} ]}}); this.update = function(val, val2, val3){ ti2cdata.push(val); if(ti2cdata.length>labelnum){ ti2cdata.shift(); } ti2cdata2.push(val2); if(ti2cdata2.length>labelnum){ ti2cdata2.shift(); } tlabels.push(val3); if(tlabels.length>labelnum){ tlabels.shift(); } myChart.update(); //グラフの更新 } } ``` モノづくり --- ### 基板づくり 難しい話はこのへんまでにして、あとはモノ作りです。 回路図をもとに作りますが、ユニバーサル基板で作ってもOKです。 ちょっと頑張りたい人はアートワークを作ってみましょう。私はフリーソフトのKiCadを使用しました。 アートワークが出来上がったら基板メーカーに発注です。格安メーカーに頼めば送料込みで3千円程度で注文できます。 KiCADの使い方はトラ技や個人ブログの方々の記事を参考にしましょう。 出来上がった写真を掲載します。 ケースに収まるように設計したのでコンパクトにまとまってます。ただし、ふたは締まりません!!笑  ### 組み立て LMSが水をくみ上げるためのタンクを用意します。 100均の適当なポットとかでOKです。チューブと電源を通すための穴をあけたら、モーターを中に沈めます。 穴から水が漏れないように、エアコンホースなどの隙間を埋めるパテ(粘土?)で隙間をふさぎます。  各アイテムが1つの台にまとまっていると、取り回しが良いので、ベーク板にその上にRaspberryPiやタンクを設置します。ベーク板そのままだと味気ないので、100均の芝をベーク板に貼りました。 ホースはそのままだとプラプラしてしまうので、名札などを机に立てておく道具を使って固定しています。 電源はACアダプタからの給電でも良いと思いますが、モバイルバッテリーを使用してもOKです。 **タンクの水はホース出口より高いところにあると、モーターを止めてもホース出口と同じ水位になるまで水が出続けるので注意が必要です!!**  動作確認 --- あれこれセッティングが終わったら、いざ動作確認です。 RaspberryPiの電源をONにして、しばらくするとNodejsによって設けたサーバーにブラウザからアクセスできるようになります。 @[youtube](http://www.youtube.com/watch?v=mqxww4MeRQM) 動画では、最初1秒に1回グラフを更新しています。 水分量の変化に伴い、水色のグラフが変化しています。 設定画面ではLMSのグラフ更新間隔などをプルダウンメニューから変更できます。 グラフ更新間隔を30minなど長く設定していてもHome画面の"Reload"ボタンを押せばグラフを更新してくれます。 ちなみに、各種アイコンはパワポの図作成で頑張って作りました。 おわりに --- 記事が長すぎると読む気が失せると思い、割愛しながら書いたつもりが、それでも結構長くなってしまいました。 自動水やりシステム"LMS"の作成を通じて、高度な技術や知識が無くとも、出来合いの技術を組み合わせれば、なんとなくIoT機器が作れることがわかりました。 専門家ではないので、間違っているところは多々あるかと思いますが、雰囲気だけでも掴んでいただければ幸いです。