ここ何回かにわたって、pythonでFlask-BootstrapやAjaxのお話をしてきた。
- Flaskでpythonのウェブアプリケーションを作る
- Flask-Bootstrapを使ってみる
- Flask-BootstrapでjQuery
- Flask-BootstrapでAJAX(jQuery)
なぜ急にFlaskでAjaxだったのか。実は今回のこれがやりたかった。 yoctoで作ったOSをより組み込み機器っぽくするために、Webインターフェースによる設定画面やハードウェア制御をできるようにしたかったのだ。
そのためyoctoでパッケージが提供されていて、使い方も比較的シンプルなものを探したところ、Flask-Bootstrapが見つかった。
作成するもの
こんな感じで、ブラウザからラズベリーパイに接続したLEDを点灯/消灯できるようにする。
サーバとPCはEthernetで接続し、データの送受信はAjaxで行なう。 Ajaxを使うのは、LED制御時にいちいち画面を遷移させたくないため。
ディレクトリ構成
ディレクトリ構成はFlaskの時と同じ。
. ├── led.py └── templates └── led.html
JSONデータ
Ajaxでやり取りするのはJSON形式のデータで次のような構造を持っている。
例としてここでは例としてled
の値に13、status
の値に0を指定している。
{ "led": 13, "status": 0 }
キー | 値 | 概要 |
---|---|---|
led | 13 | LEDのGPIO番号(BCM) |
status | 0 | 0=消灯/1=点灯 |
led.html(htmlファイル)
led.htmlの内容を次に示す。
分割して掲載する。
先頭〜contentブロック
{% extends "bootstrap/base.html" %} {% block title %}LED{% endblock title %} {% block content %} <div class="container"> <div class="page-header"> {% for led in leds %} <p> <label for="led{{led}}">led{{led}} = off</label> <button id="led{{led}}" value="1">on</button> </p> {% endfor %} </div> </div> {% endblock content %}
ラベルとボタンの組み合わせを表示している。 ボタンの表示及びvalueは「押された時にどの状態にするか」を保持している。
leds
はLEDの番号が入ったリストをFlaskから渡される。
scriptsブロック
led.htmlの残りの部分を次に示す。
{{ super() }}
は忘れずに。
setStatus()関数と、各ボタンのclick()関数に分けることができる。
{% block scripts %} {{ super() }} <script> function setStatus(label, button, data) { if (data.status == 0) { label.text('led'+data.led+' = off') button.val(1+''); button.text('on'); } else { label.text('led'+data.led+' = on') button.val(0+''); button.text('off'); } } {% for led in leds %} $(function() { $('#led{{led}}').click(function() { let status = parseInt($('#led{{led}}').val()); let jsonData = JSON.stringify({"led":{{led}}, "status":status}); console.log(jsonData); $.ajax({ url: '{{ url_for('led_change') }}', data: jsonData, contentType: 'application/json;charset=UTF-8', type: 'POST' }).done(function(data){ let label = $('label[for=led'+data.led+']'); let button = $('#led'+data.led); setStatus(label, button, data); }).fail(function(){ console.log('fail'); }); }) }); {% endfor %} </script> {% endblock scripts %}
setStatus()はボタンの状態を変更する関数でajaxのdone()から呼び出される。
ボタンに設定するvalue
は文字列型である必要があるため1+''
や0+''
で変換している。
dataはajaxでサーバから返されたJSON形式の文字列。 内容はサーバに送信したものと同じになる。
各ボタンのclick()関数はボタンをクリックされた時に、対応するボタンのclick()関数が呼び出される。
これらの関数はleds
のリストに入っている分だけ生成される。
処理内容は、JSON形式の文字列を生成し、ajaxでサーバに送信。 帰ってきた内容をdone()で処理する。
done()では、ボタンとラベルを検索し、setStatus()関数を呼び出している。
led.py(pythonスクリプト)
# coding: utf-8 from flask import Flask, render_template, request, jsonify from flask_bootstrap import Bootstrap #import RPi.GPIO as GPIO # for Flask-Bootstrap app = Flask(__name__) bootstrap = Bootstrap(app) # GPIO pins for LED leds = [13, 14, 15] @app.route('/') def root(): return render_template('led.html', leds=leds) @app.route('/ledChange', methods=['POST']) def led_change(): id = request.json['led'] status = request.json['status'] GPIO.output(id, status) return jsonify({'led': id, 'status': status}) if __name__ == '__main__': GPIO.setmode(GPIO.BCM) GPIO.setup(leds, GPIO.OUT) app.run(host='0.0.0.0') GPIO.cleanup()
サイトのルートにアクセスされると、led.htmlの内容を返す。
ブラウザでボタンをクリックされると「/ledChange」にアクセスされ、led_change()
関数が呼び出される。
led_change()ではGPIO.output()により、GPIOの状態を設定している。
GPIO.cleanup()はおそらく到達することは無いとおもう。
yocto環境
今回はrockoブランチを使用している。
local.conf
このスクリプトが動作する環境をyoctoで作るにはlocal.conf
を次のようにする。
MACHINE = "raspberrypi3" DL_DIR = "${TOPDIR}/../downloads" IMAGE_INSTALL_append = " \ rpi-gpio \ python-flask \ python-flask-bootstrap \ "
bblayers.conf
必要なレイヤを追加するには次のレイヤを追加する。
- meta-oe
- meta-python
- meta-raspberrypi