【完全自作】ECメーターを自作しよう。回路とプログラムを詳しく解説。でECメーターの回路は一応完成しました。回路が少し複雑なので基板化しているところです。
ちょっと時間があるのでその間になんでもできるラズパイを使ってリモートでECメーターの値を取れるようにしたいと思います。IoT化というやつですかね(๑•̀ㅂ•́)و✧
実はこれまでに【ラズパイでIoT】 温度モニター実装方法を詳細解説。水耕栽培に活用します。という内容で手元にある温度計をブラウザから見れるようにしたのですが同じことをしたいと思います。
- ブラウザでECと温度のデータをグラフを見れるようにします。
インタラクティブにできるJavaScriptでグラフを描画します。 - グラフの元データはPHP経由でMySQLに登録されているデータを使います。JSでやっちゃうと元データのクエリ方法が見らてしまいます。
- ExeファイルがCrontabのスケジュールで動いてMySQLにECと温度を登録します。
C++からMySQLを使う方法もご紹介しますよー!
最終的にはこんなものができます。EC値と温度が表示されています。⚠️EC値の補正はしていません
まずはMySQLにデータが登録できないとしょうがないので、その方法を簡単にご紹介します。
そのあとJS -> PHP -> MySQLでどうやってデータを取得できるかをご紹介しますね(〃’ω’)
C++のコードにMySQLへのアクセスを追加します。
【完全自作】ECメーターを自作しよう。回路とプログラムを詳しく解説。でEC値は取得できるようになりました。
せっかく取得したデータは保存しておかないともったいないですね。
ということでMySQLのデータベースにデータを保存して活用できるようにします。
MySQLをC++で使うための事前準備。
MySQLをラズパイにインストールしないことには何もできません。。
ということでC++でMySQLを使うために必要なライブラリをインストールしましょう。
1 2 3 4 5 |
まずはapt-getを使ってクライアントマネージャーをインストールします。 sudo apt-get install mysql-client 次に開発用のライブラリをインストールします。 sudo apt-get install libmysqlclient-dev |
次にC++のコードにMySQLへのアクセス部分を追加します。
C++でMySQLにデータを登録できるクラスを追加
ライブラリがインストールできたので、あとは実際に呼び出してあげましょう。
新しいクラスMySqlInterfaceがSQLへのアクセスを担当します。まずは簡単にご説明。
- メイン関数に新しくMySqlInterfaceクラスのmySqlを作ります
MySQLのユーザーネーム、パスワード、使用するデータベース、使用するテーブルの名前で初期化します。 - ECの値や温度をRegisterメソッドで登録します。
ConnectメソッドでMySQLへのソケットができたらデータを登録します。
そんなMySqlInterfaceを使って拡張したメイン関数がこちら。たいしたことはしていないです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include "ecMeasure.hpp" #include "thermistor.hpp" #include "MySqlInterface.hpp" int main(int argc, char *argv[]) { // current time time_t now = time(NULL); struct tm *pnow = localtime(&now); char strDateNow[16]; char strTimeNow[16]; sprintf(strDateNow, "%04d-%02d-%02d", pnow->tm_year+1900, pnow->tm_mon+1, pnow->tm_mday); sprintf(strTimeNow, "%02d:%02d:%02d", pnow->tm_hour, pnow->tm_min, pnow->tm_sec); // SQL setting MySqlInterface mySql("username", "password", "mysql_database", "mysql_table"); // initialization of classes and operation EcMeasure ec = EcMeasure(); Thermistor thermistor = Thermistor(); // initial setup before SPI communication start ec.SPISetUp(); ec.SetRefResistance(1000.0); // skip SPI setup because it has already done by EcMeasure class. thermistor.SetRefResistance(10000.0); float ecResisance = ec.GetEC(); float temperature = thermistor.GetTemperature(); printf("Resistance and temperature : %s %s %4.2f %4.2f\n", strDateNow, strTimeNow, ecResisance, temperature); if(!mySql.Connect()) { // Registration if connection is established. char toSendSql[256]; sprintf(toSendSql, "\'%s\',\'%s\',\'%4.2f\',\'%4.2f\'", strDateNow, strTimeNow, ecResisance, temperature); mySql.Register(toSendSql); // Close connection mySql.Close(); } return 0; } |
新しく作ったMySqlInterfaceクラスを簡単に解説します。
メイン関数からの呼び出しは簡単でしたね。簡単にMySqlInterfaceのメソッドを説明します。
#include mysql/mysql.hというライブラリを使います。
- Connectメソッドのmysql_real_connect関数が実際にMySQLとの接続を確率します。
- Registerメソッドのmysql_queryで第二引数のメッセージが送られます。
INSERT INTO %s VALUES(%s)の部分です。
ライブラリのフォーマットにあうように文字列を整形しているだけになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
#ifndef _MYSQL_H_INCLUDED_ #define _MYSQL_H_INCLUDED_ #include <mysql/mysql.h> #define SQL_SERV "localhost" class MySqlInterface { public: MySqlInterface(const char *theUserName, const char *userPassword, const char *theSqlDB, const char *theSqlTbl); virtual ~MySqlInterface(); int Connect(); int Close(void); int Register(char *buf); private: MYSQL *con; MYSQL_RES *resp; MYSQL_ROW row; const char *userName; const char *userPassword; const char *sqlDB; const char *sqlTbl; }; #endif |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "MySqlInterface.hpp" MySqlInterface::MySqlInterface(const char *theUserName, const char *userPassword, const char *theSqlDB, const char *theSqlTbl) : userName(theUserName), userPassword(userPassword), sqlDB(theSqlDB), sqlTbl(theSqlTbl) { con = mysql_init(NULL); } int MySqlInterface::Connect(void) { if(!mysql_real_connect(con, SQL_SERV, userName, userPassword, sqlDB, 0, NULL, 0)) { printf("SQL initialization is failed\n"); return -1; } return 0; } int MySqlInterface::Close() { mysql_close(con); return 0; } /* This function expects to recieve the buffer which is sent from originator. table name is specified in constructor so that only registration message should be sent. */ int MySqlInterface::Register(char *buf) { char strSqlRegister[256]; sprintf(strSqlRegister, "INSERT INTO %s VALUES(%s)", sqlTbl, buf); printf("%s\n", strSqlRegister); if(mysql_query(con, strSqlRegister)) { printf("failed to register\n"); return -1; } return 0; } MySqlInterface::~MySqlInterface() { } |
これでEC値とその時の温度が取得できるようになりました。
次はこのプログラムが定期的に実行されるようにします。
crontabでプログラムを定期的に実行
ECの値の取得のためにプログラムをずっと稼働しているのはあまりよくなさそうです。
ということでプログラム自体はデータをワンショットでとる仕様にして、定期的に実行する方法をとりたいと思います。
crontabでやってみたいと思います( ಠωಠ)
Crontab(CRON TABle)はcronタスクを持つファイルで、このファイルに記載することでcronタスクは実行されます。
cronはunixやLinuxなどでcron daemonを使いバックグラウンドで一定間隔で自動的に実行されることを許されているタスクです。
crontabを使ったスケジュールを設定しましょう。
それではcrontabを使ってスケジュールを設定しましょう。
cronタスク(job / schedule)には設定、日付、時間を設定できます。
こちらがよく使うコマンドです。これらを使いながら設定しましょう。
- crontab -e
crontabファイルを編集します。 - crontab -l
cronタスクを確認できます。 - crontab -r
cronのファイルを削除します。
crontabので定期実行の設定を追加
crontab -e でcrontabファイルを編集します。
crontabファイル5つの6つフィールドがあります。分、時間、月の日、月、曜日、最後に実行したいコマンドです。
* * * * * command to be executed
– – – – –
| | | | |
| | | | +—– day of week (0 – 6) (Sunday=0)
| | | +——- month (1 – 12)
| | +——— day of month (1 – 31)
| +———– hour (0 – 23)
+————- min (0 – 59)
*をワイルドカードとして使うことができます。最小の単位はminなので1分ごとのスケジュール実行ができます。
よく使いそうな5分ごとの実行は */5 * * * * と書きます。
サンプルやフォーマットの説明はこちらにありました。
crontabの解説
今回は毎時間ごとに実行したいのでminのフィールドに00します。
MeasureECというプログラムを作製して、/usr/local/binに置いた場合の記載方法はこんな感じです。
00 * * * * /usr/local/bin/MeasureEC
ここではcrontabでの設定方法を簡単にご紹介しました。
結構使えますよね。スケジュール管理はこのプログラムにお任せでいいのかなと思います。
次は登録されたMySQLのデータをclient(User)側から取得してみましょう。
Web applicationを動かすための環境を構築
ラズパイでweb applicationを動かすためにはウェブサーバーソフトを入れる必要があります。
世界でもっとも使われているウェブサーバーソフトのApacheのインストールとPHPの設定をまとめます。
まずはインストール作業
インストール作業がたくさん続きます。
Apache2とPHPをインストールします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
まずはデータベースを最新のものにしましょう。 $ sudo apt-get update それではApache2をインストールします。 $ sudo apt-get install apache2 次にPHPをインストールします。 $ sudo apt-get install php7.0 PHPからMySqlにアクセスするために追加のモジュールをインストールします。 今ラズパイにインストールしているのはPHP7なのでphp7.0-mysqlをインストールします。 $ sudo apt-get install php7.0-mysql 設定を反映させるためにapacheを再起動しましょう。 <pre class="lang:c++ decode:true "> $ sudo /etc/init.d/apache2 restart Apacheをシステム起動時に起動するようにしておきましょう。 $ sudo update-rc.d apache2 defaults |
ApacheとPHPがちゃんとインストールされたかを確認します。
Apacheがうまくインストールされたかどうかの確認は簡単です。
http://Your_RaspberryPi_IP-Address/ へご自身のスマホやPCからアクセスしてみてください。Your_RaspberryPi_IP-AddressはラズパイのIPアドレスです。
うまくアクセスできれば下記画面が表示されます。
PHPがインストールされたかも確認しましょう。
PHPが正しくインストールされたかはコマンドラインから確認できます。
php7.0-mysqlがインストールされたかも確認しましょう。
下記のコードをinfo.phpとして保存して/var/www/htmlに保存して
http://Your_RaspberryPi_IP-Address/info.php とアクセスしてみてください。
1 2 3 4 5 6 7 8 9 10 |
<html> <head> <title>PHP info</title> </head> <body> <?php phpinfo(); ?> </body> </html> |
表示されたPDOの項目をご覧ください。ちゃんとインストールされてないとPDO driverの項目にno valueと表示されます。
これで必要なインストールや設定は終わりました。次回からweb applicationを書いていきます。
ブラウザからEC値と温度を取得します。
残すはweb applicationの実装のみです。サンプルのプログラムec-measure-web_sampleはこちらです。
こちらをラズパイの/var/www/html/の配下においてください。ブラウザからhttp://Your_PI_IP_Addr/EC-Status.php とアクセスすればデータが見えるようになります。
MySQLのユーザー、パスワード、テーブル等はDbManager.phpに設定してください。
ここでは内容を簡単に説明します。
PHPからMySQLにアクセスしてデータを取得しよう
- DAY/WEEK/MONTHオプションがあります。
1日の変化が見たかったり、月の変化が見たかったりしますよね。呼び出し元のコードからPOSTデータを取得して把握します。 - DbManager.phpのGetDbメソッドでPHPからMySQLへのソケットを開けます。
- DAY/WEEK/MONTHでqueryのoptionが変わります。
WEEK/MONTHでは平均のデータを使うようにします。 - 取得したデータは呼び出し元にJSON formatで返します。
呼び出し元ではグラフの作成にGoogle chartsを使うことを想定しています。Google ChartsはJSONフォーマットのデータに対応しているのでこの形にしました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
<?php require_once 'DbManager.php'; require_once 'Encode.php'; // Retrieve from $_GET $range = (isset($_GET['range'])) ? $_GET['range'] : 'Day'; $element_1 = 'ecval'; $element_2 = 'temperature'; // Day unit will handle data as 'Time' unit from table $unit = ($range == 'Day') ? 'time' : 'date'; // initialization $arryCol = array(); $arryRow = array(); // Prepare for data format. switch ($range){ case 'Month': case 'Week': // Date range for query. Week/Month $dayRange = ($range == 'Week') ? 7 : 28; $qryDate = strtotime("-{$dayRange} day"); // Variables to shorten recoring date $substrStart = 5; $substrLen = 5; // Column definition for GoogleCharts Table array_push($arryCol, MakeColElement('Date', 'string')); break; case 'Day': $days = date("Y-m-d", strtotime("-1 day")); // Variables to shorten recoring date $substrStart = 0; $substrLen = 5; // Column definition for GoogleCharts Table array_push($arryCol, MakeColElement('Time', 'string')); break; default: break; } // Set element name array_push($arryCol, MakeColElement($element_1, 'number')); array_push($arryCol, MakeColElement($element_2, 'number')); // Open socket and query try { $db = GetDb(); switch ($range){ case 'Month': case 'Week': for ($day=0; $day <$dayRange ; $day++) { // prepare query string. min/ave/max will be targeted. $days = date("Y-m-d", $qryDate); $qry = "select $unit, ave($element_1), ave($element_2) from ec_measure where Date like '$days'"; $stt = $db->prepare($qry); // call $stt->execute(); $row = $stt->fetch(PDO::FETCH_ASSOC); $rsltToJson = array(substr($row[$unit],$substrStart,$substrLen), round($row['avg('.$element_1.')'],2), round($row['avg('.$element_2.')'],2)); array_push($arryRow, MakeRowElements($rsltToJson)); // + 1 day for next day. $qryDate = strtotime("+1 day", $qryDate); } break; case 'Day': $qry = "select $unit, $element_1, $element_2 from ec_measure where Date >= '$days'"; $stt = $db->prepare($qry); $stt->execute(); // loop till fetch all aquired values and store. while($row = $stt->fetch(PDO::FETCH_ASSOC)) { $rsltToJson = array(substr($row[$unit],$substrStart,$substrLen), round($row[$element_1],2), round($row[$element_2],2)); array_push($arryRow, MakeRowElements($rsltToJson)); } break; } // delete $db = NULL; } catch (PDOEXception $e) { die("connection error:{$e->getMessage()}"); } // return created Json format. echo ReturnGoogleChartsTable($arryCol, $arryRow); function MakeColElement ($label, $type) { return '{"id":"","label":"'.$label.'"'.',"pattern":"","type":"'.$type.'"'.'}'; } function MakeRowElements ($arry) { $ret = '{"c":['; for ($i=0; $i < count($arry); $i++) { $element = '{"v":"'.$arry[$i].'","f":null}'; $ret .= ($i != (count($arry)-1)) ? $element.',' : $element ; } $ret = $ret.']}'; return $ret; } function ReturnGoogleChartsTable ($arryCol, $arryRow) { $ret = '{ "cols": [' .ConnectArry($arryCol) .'],' .'"rows": [' .ConnectArry($arryRow) .'] }'; return $ret; } function ConnectArry($arry) { $ret = ''; for ($i=0; $i < count($arry); $i++) { $ret .= ($i != (count($arry)-1)) ? $arry[$i].',' : $arry[$i] ; } return $ret; } ?> |
JavaScriptからPHPを呼んでグラフを描画しよう
ここまでくればあとはデータを描画するだけです。Google Chartsを使ってインタラクティブなグラフを描画しましょう。
- Google ChartsのLineChartを使っています。
- データはjQueryのajax()関数からPHPを呼んでJSONフォーマットのリスポンスを受け取ります。
- PHPを呼ぶときにはDAY/WEEK/MONTHをPOSTデータとして渡します。
JavaScript内でjQueryのajax()関数を使いURLにアクセスしてそのリスポンスを受け取っています。
URLに先ほどのPHPのコードを指定してPHP内で動かしたMySQLのクエリ結果をリスポンスの形で受け取ることができます。
ちなみにレスポンスはJSONフォーマットに整形しているのでその値をデータとして使うだけです。
1 2 3 4 5 6 |
var urlWithGetData = "CreateJsonFromMySql.php?range=" + showspan; var jsonData = $.ajax({ url: urlWithGetData, dataType: "json", async: false }).responseText; |
全体のプログラムです。EC値と温度があるので、Y軸を2軸にしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
<html> <head> <!--Load the AJAX API--> <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script> <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script> <script type="text/javascript"> // Load the Visualization API and the piechart package. google.charts.load('current', {'packages':['corechart']}); // Set a callback to run when the Google Visualization API is loaded. google.charts.setOnLoadCallback(drawChart); function drawChart(showspan = 'Day') { var urlWithGetData = "CreateJsonFromMySql.php?range=" + showspan; var jsonData = $.ajax({ url: urlWithGetData, dataType: "json", async: false }).responseText; // Create our data table out of JSON data loaded from server. var data = new google.visualization.DataTable(jsonData); // Instantiate and draw our chart, passing in some options. var chart = new google.visualization.LineChart(document.getElementById('chart_div')); var options = { title: 'EC measurement report', curveType: 'function', height: 500, series: { 0: {targetAxisIndex: 0}, 1: {targetAxisIndex: 1} }, vAxes: { // Adds titles to each axis. 0: {title: 'EC resistance[ohm]'}, 1: {title: 'Tempa[Degree Celsius]'} }, hAxis: {title: 'Time', showTextEvery: 1, slantedText: true, slantedTextAngle: 90}, //vAxis: {title: 'Temperature[degC]'}, legend: { position: 'bottom' } }; chart.draw(data, options); } </script> </head> <body> <!--Div that will hold the Line chart--> <form> <select name="showspan" onchange="drawChart(this.value)"> <option value="Day" selected="">Day</option> <option value="Week">Week</option> <option value="Month">Month</option> </select> </form> <div id="chart_div"></div> </body> </html> |
まとめ
自作のECメーターをWebでインタラクティブに見れるようにしました。やることは多いですが、一度作っちゃえば応用が効くのかなという印象です。
記事を読んでいただいてありがとうございます。この記事がいいなと思ったら下記のSNSボタンのクリックをお願いします。励みになります😁
初めまして
ググってたらたどり着いた者です
自分は現在イチゴ トマト キュウリ レタスなどを自家栽培してるんですがキット的な物が高く自作しようと思っていたらここにたどり着いたのですが電子学的な物が初めてな為コメントしました
現在はLEDを時間タイマーで設定しているのですが1個1,000円する為ポンプなどを別途制御すると更に加算されるのでWEBかアプリで一括制御したくコメントしたのですがPPM (EC)、水温、湿度、LED、ポンプの5種類を同時に制御できればいいと思ったのですが知恵をお借りできたらと思いコメントしました。同時に5個制御しようと思うとかなり難しくなるのでしょうか?
setuさん
こんにちは。
ご質問内容ですと同時に3種のパラメータ(EC値、水温、湿度)を取得し、2種のパラメータ(LED、ポンプ)を制御するものと理解しました。
今私も画策している最中ですが可能です。
今ご紹介できることはすべて電子工作の知識が必要です。
具体的な作業量が参考になるとおもいます(๑•̀ㅂ•́)و✧
https://jitaku-yasai.com/home-made/iot-checktemperature/
https://jitaku-yasai.com/home-made/thermistor/
https://jitaku-yasai.com/home-made/ec-meter-selfmade/
ありがとうございます
なんとなくですがまずはラズベリーパイ購入してから順番ということっぽいですね。。。
先がかなり長く感じます;;
そうですね、ラズパイがあるといろいろできます。
はじめまして 水耕栽培の設備について調べていましたらこちらにサイトをお見受けしました。私はポンプ他機械の設備工事を扱う会社を営んでおる者ですが、深井戸ポンプの設備を利用しての水耕栽培設備に大変興味を持っております。まだまだ夢の話ですがポンプヤブロワなどの自社が持つノウハウを利用して水耕栽培設備を整えたシェアファーム的な事がしたいなと思っております。管理人様の持たれる知識やノウハウに驚愕しながら興味深く勉強させて頂いております。まずは自分でも管理人様ご紹介の自作キットをそのまま真似て作ってみようと計画しております。管理人様にお伺いしたいのですが、実際に農業事業化されている規模での水耕栽培に関する御関わりやアドバイザー的な活動等をされておられるのでしょうか?もしそのような事がおありでしたら改めてご連絡等とらせていただきたいと思いコメントをさせて頂きました。突然の不躾をお許しください。
はじめまして、ご参考になりましたら幸いです。
ご質問頂きました内容ですが、素人レベルの趣味の範囲でやっているので恐れ多いです。。
tanapさんの活動がうまくいくことを願っております。