Webiopiをディスプレイに表示させてボタン操作をするのと、そのWebiopiの表示の中にGrafanaで作った水位や気温・湿度などを表示させる設定をしていきます。
Webiopiは母艦のラズパイに入れて16連のリレーをつなげて水槽のポンプや充電器などのスイッチをオン・オフするボタンに使います。今回ディスプレイを設置したサブ機のラズパイはIPアドレスで母艦のラズパイに接続してWebIOPiの画面をキオスクモードで表示する。
WebIOPi インストール・設定
WebIOPiを使うとブラウザからラズパイを操作できるというのでやってみました。
どうやら開発は終了しているようで・・・残念です。
サイト
本家のサイト
インストールで参考にしたページ

インストール・設定
$ wget https://sourceforge.net/projects/webiopi/files/WebIOPi-0.7.1.tar.gz $ tar xvzf WebIOPi-0.7.1.tar.gz $ cd WebIOPi-0.7.1 #パッチも当てておく $ wget https://raw.githubusercontent.com/doublebind/raspi/master/webiopi-pi2bplus.patch $ patch -p1 -i webiopi-pi2bplus.patch
準備できたらセットアップ実行
$ sudo ./setup.sh
自動起動設定
systemctlで自動起動できるように設定するためwebiopi.serviceをダウンロードする。
$ cd /etc/systemd/system/ $ sudo wget https://raw.githubusercontent.com/doublebind/raspi/master/webiopi.service
webiopi.serviceファイル確認
$ sudo cat /etc/systemd/system/webiopi.service #webiopi.serviceは以下のようになっていた [Unit] Description = WebIOPi After = syslog.target network.target [Service] Type = simple WorkingDirectory = /usr/share/webiopi/htdocs ExecStart = /usr/bin/python3 -m webiopi -l /var/log/webiopi -c /etc/webiopi/config [Install] WantedBy = multi-user.target
起動・終了・再起動
$ sudo systemctl start webiopi #スタート $ sudo systemctl stop webiopi #ストップ $ sudo systemctl restart webiopi #リスタート $ sudo systemctl enable webiopi #ラズパイ起動時に起動 $ sudo systemctl status webiopi #ステータス
起動確認
$ sudo systemctl status webiopi ● webiopi.service - WebIOPi Loaded: loaded (/etc/systemd/system/webiopi.service; disabled; vendor preset: Active: active (running) since Fri 2019-03-29 15:58:52 JST; 4s ago Main PID: 15116 (python3) CGroup: /system.slice/webiopi.service └─15116 /usr/bin/python3 -m webiopi -l /var/log/webiopi -c /etc/webio 3月 29 15:58:52 raspberrypi systemd[1]: Started WebIOPi. 3月 29 15:58:54 raspberrypi python3[15116]: 2019-03-29 15:58:54 - WebIOPi - INFO 3月 29 15:58:54 raspberrypi python3[15116]: 2019-03-29 15:58:54 - WebIOPi - INFO 3月 29 15:58:54 raspberrypi python3[15116]: 2019-03-29 15:58:54 - WebIOPi - INFO 3月 29 15:58:54 raspberrypi python3[15116]: 2019-03-29 15:58:54 - WebIOPi - INFO 3月 29 15:58:54 raspberrypi python3[15116]: 2019-03-29 15:58:54 - WebIOPi - INFO 3月 29 15:58:54 raspberrypi python3[15116]: 2019-03-29 15:58:54 - WebIOPi - INFO 3月 29 15:58:54 raspberrypi python3[15116]: 2019-03-29 15:58:54 - WebIOPi - INFO
デバッグモードで起動
これで起動すると一応WebページにはアクセスできるがGPIOの状態がうまく表示されずリレーなどのオン・オフ操作もできなかった。これだとカスタマイズのページは404エラーになって表示されない。
$ sudo webiopi -d 2019-04-14 17:24:13 - WebIOPi - DEBUG - Mapping GPIO.digitalCount to REST GET /GPIO/count 2019-04-14 17:24:13 - WebIOPi - DEBUG - Mapping GPIO.digitalRead to REST GET /GPIO/%(channel)d/value 2019-04-14 17:24:13 - WebIOPi - DEBUG - Mapping GPIO.digitalWrite to REST POST /GPIO/%(channel)d/value/%(value)d 2019-04-14 17:24:13 - WebIOPi - DEBUG - Mapping GPIO.getFunctionString to REST GET /GPIO/%(channel)d/function 2019-04-14 17:24:13 - WebIOPi - DEBUG - Mapping GPIO.getPulse to REST GET /GPIO/%(channel)d/pulse 2019-04-14 17:24:13 - WebIOPi - DEBUG - Mapping GPIO.outputSequence to REST POST /GPIO/%(channel)d/sequence/%(args)s 2019-04-14 17:24:13 - WebIOPi - DEBUG - Mapping GPIO.portRead to REST GET /GPIO/*/integer 2019-04-14 17:24:13 - WebIOPi - DEBUG - Mapping GPIO.portWrite to REST POST /GPIO/*/integer/%(value)d 2019-04-14 17:24:13 - WebIOPi - DEBUG - Mapping GPIO.pulse to REST POST /GPIO/%(channel)d/pulse/ 2019-04-14 17:24:13 - WebIOPi - DEBUG - Mapping GPIO.pulseAngle to REST POST /GPIO/%(channel)d/pulseAngle/%(value)f 2019-04-14 17:24:13 - WebIOPi - DEBUG - Mapping GPIO.pulseRatio to REST POST /GPIO/%(channel)d/pulseRatio/%(value)f 2019-04-14 17:24:13 - WebIOPi - DEBUG - Mapping GPIO.setFunctionString to REST POST /GPIO/%(channel)d/function/%(value)s 2019-04-14 17:24:13 - WebIOPi - DEBUG - Mapping GPIO.wildcard to REST GET /GPIO/* 2019-04-14 17:24:13 - WebIOPi - INFO - GPIO - Native mapped to REST API /GPIO 2019-04-14 17:24:13 - WebIOPi - WARNING - Access unprotected 2019-04-14 17:24:13 - WebIOPi - INFO - HTTP Server binded on http://192.168.31.53:8000/ 2019-04-14 17:24:13 - WebIOPi - INFO - CoAP Server binded on coap://192.168.31.53:5683/ 2019-04-14 17:24:13 - WebIOPi - INFO - CoAP Server binded on coap://224.0.1.123:5683/ (MULTICAST) 2019-04-14 17:24:13 - HTTP - DEBUG - "GET /* HTTP/1.1" - 200 OK (Client: ::ffff:192.168.31.118 <Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:68.0) Gecko/20100101 Firefox/68.0>) 2019-04-14 17:24:13 - HTTP - DEBUG - "GET /* HTTP/1.1" - 200 OK (Client: ::ffff:192.168.31.118 <Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:68.0) Gecko/20100101 Firefox/68.0>) 2019-04-14 17:24:13 - HTTP - DEBUG - "GET /* HTTP/1.1" - 200 OK (Client: ::ffff:192.168.31.118 <Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:68.0) Gecko/20100101 Firefox/68.0>) 2019-04-14 17:24:13 - HTTP - DEBUG - "GET /* HTTP/1.1" - 200 OK (Client: ::ffff:192.168.31.118 <Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:68.0) Gecko/20100101 Firefox/68.0>) 2019-04-14 17:24:40 - WebIOPi - DEBUG - Closing device GPIO - GPIO 2019-04-14 17:24:40 - WebIOPi - INFO - CoAP Server stopped
[Ctrl]+[c]で終了してもコマンドプロンプトが表示されなかった。(Puttyで操作した時)
configをカスタマイズしてページを作った時は以下のようにしてデバッグモードでテストすることができた。
$ sudo webiopi -c /etc/webiopi/config -d
Webiopiページにブラウザでアクセス
WindowsのWebブラウザからアクセスしてみる
WindowsのFirefoxでラズパイのWebiopiのページにアクセスしてみます。
以下のようなBasic認証画面が表示されるのでwebiopi・raspberryを入力
- ID:webiopi
- Pass:raspberry
ラズパイのGPIOの状況が表示されるページなどGPIOの状況を分かりやすい。
このままだと、どのPINを何に使っているのか分からないので、専用ページを作っていきます。
Webiopi リレースイッチ用の専用ページを作る
HTML・Python・CSSでボタン操作できるページを作っていきます。
元のファイルをコピー
ディレクトリwebiopiとbackを作って元のファイルをコピーしておきます。
$ mkdir -p /home/pi/script/webiopi/back $ cp -r /usr/share/webiopi/htdocs/* /home/pi/script/webiopi/back
設定
$ sudo vim /etc/webiopi/config ・・・省略 [SCRIPTS] #myscript = /home/pi/webiopi/examples/scripts/macros/script.py myscript = /home/pi/script/webiopi/script.py#追加 [HTTP] #doc-root = /home/pi/webiopi/examples/scripts/macrosこの下に追加 doc-root = /home/pi/script/webiopi#追加
設定を適用させるために再起動、しかし再起動に時間がかかります。
$ sudo systemctl restart webiopi
Grafanaの温度・湿度・水位など表示させる
iframeでWebiopiのページの中にGrafanaのグラフを表示させます。
こんな感じ↓に作ってsrcにGrafanaで取得したURLを入れます。
<div class="grafana" style="height:300px;"> <iframe src="GrafanaのURL" width="auto" height="100" frameborder="0" style="border:0" allowfullscreen></iframe> </div> <div class="grafana" style="height:1260px;"> <iframe src="GrafanaのURL" width="auto" height="400" frameborder="0" style="border:0" allowfullscreen></iframe> </div>
Grafanaでダッシュボードを作って表示させたいグラフを入れていきます。
右上のほうにあるShareアイコンを押してLinkでURLを取得。Webiopiのページは背景が白なのでThemeをLightを選びました。
以下のようにCurrent Time RangeやTemplate Variablesもオフにしてみました。
Grafana ログインしないで表示させる
キオスクモードで表示させたいので、Grafanaも再起動のたびにキーボードを使ってログインするのも面倒なのでログインしないでもGrafanaのグラフが表示されるようにする。
$ sudo vim /etc/grafana/grafana.ini [auth] oauth_auto_login = true [auth.anonymous] #この項目を探して以下のようにTrueにする。 enabled = true $ sudo systemctl stop grafana-server #設定反映させるためにストップしてからスタート $ sudo systemctl start grafana-server
これでログインしないでも表示されるようになりました。
Grafana V6.2から表示されなくなった
V6.2からX-Frame-Options:denyになったので<iframe>の中にGrafanaのリンクを入れても表示されなくなった。

対応方法は以下のようにallow_embedding = trueを追加する。
$ sudo vim /etc/grafana/grafana.ini #[security]を探して下に以下をコメントアウト・編集 cookie_samesite = none allow_embedding = true $ sudo systemctl stop grafana-server #設定反映 $ sudo systemctl start grafana-server
これでアクセスを拒否されずに表示するようになった。
Webiopi ページの中にGrafana入れる
WebiopiのHTMLの中にこんな感じでiframeで入れてみた。
<div class="grafana"> <iframe src="http://192.168.31.53:3000/d/g_Ju8BZRk/panel?orgId=1&theme=light" width="auto" height="1200" frameborder="0" style="border:0" allowfullscreen>></iframe> </div>
以下は、メインのラズパイ3B+のWebiopiで、ディスプレイを表示させるサブのラズパイ3B+にchromiumでWebiopiのページをキオスクモードで表示させています。(ちょっとややこしくなってきた)
WebiopiのHTML/py/cssの全文も載せておきます。
ディスプレイ表示用のWebiopi HTML
HTMLはこんな感じ↓でWebiopiでボタンを作って16連リレーをラズパイにつなげてオン・オフしてます。ついでにGrafanaのグラフを表示させるページもiframeで挿入します。
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Hydroponic culture Control</title> <meta name="viewport" content="width=device-width, initial=1.0" /> <script type="text/javascript" src="/webiopi.js"></script> <link href="https://use.fontawesome.com/releases/v5.6.1/css/all.css" rel="stylesheet"> <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> <script type="text/javascript"> webiopi().ready(function() { var button12 = webiopi().createGPIOButton(12, "左1ポンプ"); var button25 = webiopi().createGPIOButton(25, "左2ポンプ"); var button5 = webiopi().createGPIOButton(5, "右1ポンプ"); var button24 = webiopi().createGPIOButton(24, "右2ポンプ"); var button13 = webiopi().createGPIOButton(13, "弱USBポンプ"); var button21 = webiopi().createGPIOButton(21, "水位計測"); var button19 = webiopi().createGPIOButton(19, "エア"); var button26 = webiopi().createGPIOButton(26, "オゾン"); var button16 = webiopi().createGPIOButton(16, "LEDライト"); var button18 = webiopi().createGPIOButton(18, "ファン"); var button20 = webiopi().createGPIOButton(20, "ペルチェポンプ"); var button17 = webiopi().createGPIOButton(17, "ペルチェ"); $("#water1").append(button25,button5); $("#water2").append(button12,button24); $("#pump1").append(button21); $("#pump2").append(button13); $("#air1").append(button19); $("#air2").append(button26); $("#light1").append(button16); $("#light2").append(button18); $("#peltier1").append(button20); $("#peltier2").append(button17); var button6 = webiopi().createGPIOButton(6, "未使用USB 6"); var button22 = webiopi().createGPIOButton(22, "充電器"); var button23 = webiopi().createGPIOButton(23, "未使用 23"); var button27 = webiopi().createGPIOButton(27, "掃除機"); //$("#other2").append(button23); $("#other1").append(button27,button22); $("#other2").append(button23,button6); webiopi().refreshGPIO(true); w().refreshGPIO(true); }); </script> <link rel="stylesheet" type="text/css" href="style.css"> </head> <body> <div align="center"> <h2 class="icon-air">空気</h2> <ul class="air"> <li id="air1"></li> <li id="air2"></li> </ul> <h2 class="icon-water">水 ポンプ</h2> <ul class="water"> <li id="water1"></li> <li id="water2"></li> <li id="water3"></li> <li id="water4"></li> </ul> <h2 class="icon-pump">ポンプ サブ</h2> <ul class="pump"> <li id="pump1"></li> <li id="pump2"></li> </ul> <h2 class="icon-light">光・風</h2> <ul class="light"> <li id="light1"></li> <li id="light2"></li> </ul> <h2 class="icon-peltier">冷却</h2> <ul class="peltier"> <li id="peltier1"></li> <li id="peltier2"></li> </ul> <h2 class="icon-pump">Other</h2> <ul class="other"> <li id="other1"></li> <li id="other2"></li> </ul> <div class="grafana" style="height:300px;"> <iframe src="http://192.168.31.53:3000/d/lp-WN-Rgz/waterlevel?orgId=1&refresh=2s&theme=light&kiosk" width="auto" height="100" frameborder="0" style="border:0" allowfullscreen></iframe> </div> <div class="grafana" style="height:1260px;"> <iframe src="http://192.168.31.53:3000/d/g_Ju8BZRk/panel?orgId=1&refresh=1m&theme=light&kiosk" width="auto" height="400" frameborder="0" style="border:0" allowfullscreen></iframe> </div> </div> </body> </html>
script.py
script.pyは最低限の設定
GPIO = webiopi.GPIO BUTTON20 = 20 BUTTON16 = 16 BUTTON19 = 19 BUTTON26 = 26 BUTTON12 = 12 BUTTON6 = 6 BUTTON13 = 13 BUTTON21 = 21 BUTTON17 = 17 BUTTON18 = 18 BUTTON22 = 22 BUTTON23 = 23 BUTTON27 = 27 BUTTON5 = 5 BUTTON24 = 24 BUTTON25 = 25 def setup(): # ラズパイ再起動 OUT LED以外は全部オンになる。 GPIO.setFunction(BUTTON20, GPIO.OUT) GPIO.setFunction(BUTTON19, GPIO.OUT) GPIO.setFunction(BUTTON26, GPIO.OUT) GPIO.setFunction(BUTTON12, GPIO.OUT) GPIO.setFunction(BUTTON6, GPIO.OUT) GPIO.setFunction(BUTTON16, GPIO.OUT) GPIO.setFunction(BUTTON13, GPIO.OUT) GPIO.setFunction(BUTTON21, GPIO.OUT) GPIO.setFunction(BUTTON17, GPIO.OUT) GPIO.setFunction(BUTTON18, GPIO.OUT) GPIO.setFunction(BUTTON22, GPIO.OUT) GPIO.setFunction(BUTTON23, GPIO.OUT) GPIO.setFunction(BUTTON27, GPIO.OUT) GPIO.setFunction(BUTTON5, GPIO.OUT) GPIO.setFunction(BUTTON24, GPIO.OUT) GPIO.setFunction(BUTTON25, GPIO.OUT) GPIO.digitalWrite(BUTTON19, GPIO.HIGH) GPIO.digitalWrite(BUTTON20, GPIO.HIGH) GPIO.digitalWrite(BUTTON26, GPIO.HIGH) GPIO.digitalWrite(BUTTON12, GPIO.HIGH) GPIO.digitalWrite(BUTTON6, GPIO.HIGH) GPIO.digitalWrite(BUTTON16, GPIO.HIGH) GPIO.digitalWrite(BUTTON13, GPIO.HIGH) GPIO.digitalWrite(BUTTON21, GPIO.HIGH) GPIO.digitalWrite(BUTTON17, GPIO.HIGH) GPIO.digitalWrite(BUTTON18, GPIO.HIGH) GPIO.digitalWrite(BUTTON22, GPIO.HIGH) GPIO.digitalWrite(BUTTON23, GPIO.HIGH) GPIO.digitalWrite(BUTTON27, GPIO.HIGH) GPIO.digitalWrite(BUTTON5, GPIO.HIGH) GPIO.digitalWrite(BUTTON24, GPIO.HIGH) GPIO.digitalWrite(BUTTON25, GPIO.HIGH) def destroy(): GPIO.digitalWrite(BUTTON19, GPIO.HIGH) GPIO.digitalWrite(BUTTON20, GPIO.HIGH) GPIO.digitalWrite(BUTTON26, GPIO.HIGH) GPIO.digitalWrite(BUTTON12, GPIO.HIGH) GPIO.digitalWrite(BUTTON6, GPIO.HIGH) GPIO.digitalWrite(BUTTON13, GPIO.HIGH) GPIO.digitalWrite(BUTTON21, GPIO.HIGH) GPIO.digitalWrite(BUTTON17, GPIO.HIGH) GPIO.digitalWrite(BUTTON18, GPIO.HIGH) GPIO.digitalWrite(BUTTON22, GPIO.HIGH) GPIO.digitalWrite(BUTTON23, GPIO.HIGH) GPIO.digitalWrite(BUTTON27, GPIO.HIGH) GPIO.digitalWrite(BUTTON5, GPIO.HIGH) GPIO.digitalWrite(BUTTON24, GPIO.HIGH) GPIO.digitalWrite(BUTTON25, GPIO.HIGH)
style.css
CSSも載せておきます。
/* Grafana */ .grafana{ position:relative; width:100%; marigin:0; } .grafana iframe{ position:absolute; top:0; left:0; width:100%; height:100%; } body{ margin:0; } .water, .air, .light, .wind, .other, .peltier, .pump{ display: table; background: #FFF; margin: 5px 5px 5px -40px; } [id^="water"],[id^="air"],[id^="light"],[id^="wind"],[id^="peltier"],[id^="other"],[id^="pump"] { display: table-cell; text-align: center; vertical-align: middle; } h2 { position:relative; margin: 5px; line-height: 250%; text-decoration: none; width: 350px; font-size: 16px; /*vertical-align: bottom;*/ padding: 2px 4px; background: -moz-linear-gradient( top, #787878 0%, #a8a8a8); background: -webkit-gradient( linear, left top, left bottom, from(#787878), to(#a8a8a8)); -moz-border-radius: 3px; -webkit-border-radius: 3px; border-radius: 3px; border: 1px solid #c2c2c2; -moz-box-shadow: 0px 2px 0px rgb(213, 213, 213), inset 0px 2px 0px rgba(255,255,255,1); -webkit-box-shadow: 0px 2px 0px rgb(213, 213, 213), inset 0px 2px 0px rgba(255,255,255,1); box-shadow: 0px 2px 0px rgb(213, 213, 213), inset 0px 2px 0px rgba(255,255,255,1); text-shadow: 0px 2px 2px rgba(199,199,199,1), 0px -2px 5px rgba(255,255,255,1); background: #f5f5f5 ; } button { display: block !important; margin: 10px; width: 150px; font-size: 14pt; height:40px; line-height:20px; padding: 5px 10px; color: #FFF; text-decoration: none; /*text-align: center;*/ /*background-color: Silver !important;*/ /*border-bottom: solid 6px #ec6c00;*/ border-bottom: initial; box-shadow: 0 3px 0 #dadada; text-shadow: 0 1px 1px rgba(0, 0, 0, .4); border-radius: 3px; border:initial; } /*button:hover{ background-color: #f9c500; border-bottom: solid 6px #f39800; }*/ button:active { background-color: #f9c500 !important; box-shadow: 0 3px 0 #f3d703 inset; margin-top: 6px; -webkit-transform: translateY(6px); transform: translateY(6px); border:initial; } .HIGH{ background-color: Silver; } .LOW{ background-color:#501b0a; text-shadow: 0 1px 1px #1e1a1a; box-shadow: 0 3px 0 #1e1a1a inset; } ul.air button.LOW{ background-color: #3576ad; box-shadow: 0 3px 0 #1f4980 inset; } ul.water button.LOW{ background-color: #46c5f8; box-shadow: 0 3px 0 #80bcd4 inset; } ul.light button.LOW{ background-color: #f6d416; box-shadow: 0 3px 0 #edc157 inset; } ul.peltier button.LOW{ background-color: #16cbdb; box-shadow: 0 3px 0 #aee5ea inset; } ul.pump button.LOW{ background-color: #e82727; box-shadow: 0 3px 0 #9f1111 inset; } button:before,[class^="icon-"]:before{ position: absolute; font-family: "Font Awesome 5 Free"; border-radius: 4px; top: 0; left: 0; bottom: 0; /*text-align:center;*/ width:30px; /*vertical-align: middle;*/ /*background: #0c0601e0;*/ /*padding: 10px 0 0 5px;*/ } .icon-light:before{ content: '\f0eb'; } .icon-air:before{ content: '\f72e'; } .icon-water:before{ content: '\f773'; } .icon-pump:before{ content: '\f0c3'; } .icon-peltier:before{ content: '\f769'; } @media screen and (max-width:639px){ body{ padding: 5px; } }
メインで使っているラズパイと16連リレーを接続した様子など記事にしてないので後ほど詳しく説明していきますね。
コメント