Raspberry Pi 備忘録 / Mbedもあるよ!

Raspberry Pi であれこれやった事の記録

Python で WEB Server > 諦めて apache

Python で簡単にWEBサーバーを立てられるので、試す。

Lチカを目指します!

SimpleHTTPServer

CGIは動かないシンプルなもの。

Python 2系列

$ sudo python -m SimpleHTTPServer 80

CGIHTTPServer

CGIが動きます。

$ sudo python -m CGIHTTPServer 80

ファイル作成

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import cgitb
cgitb.enable()

print "Content-Type: text/html;charset=utf-8"
print

print "Hello World!"

パーミッションを 755 にする

$ chmod 755 index.py

GPIO を触る

せっかくのpython なので、GPIO も触ってみる。

ところが import RPi.GPIO as GPIOなどすると、ROOT で実行しなさいとエラーが出る。

これは、 nobody で cgi が起動しているからだ。

20.20. CGIHTTPServer — CGI 実行機能付き HTTP リクエスト処理機構 — Python 2.7.13 ドキュメント

さて、どうしたものか・・・。

試したこと その1(NG)

nobody を group gpio に入れる

コマンドラインからは、 sudo -u nobody ./index.py などで動作するが、cgi からは動かない。

print(os.getuid())
commands.getoutput("cat /sys/class/gpio/gpio4/value")

で、 nobody (65534) で有ることの確認をとりつつ動かしましたが、

cat: /sys/class/gpio/gpio4/value: 許可がありません

を、得る。

どこで止まっているのか不明。

試したこと その2(NG)

Python3 で試す。

pip3 install wiringpi2

sudo python3 -m http.server –cgi 80

FileNotFoundError: [Errno 2] No such file or directory: ‘cat /sys/class/gpio/gpio4/value’

/bin/cat: /sys/class/gpio/gpio4/value: 許可がありません

諦めて apache

諦めて apache2 でやります。

  1. apache2 install
  2. mod python とか色々入れます
$ sudo apt-get install libapache2-mod-python
  1. /etc/apache2/envvars

  2. 実行ユーザを変えてしまう

/etc/apache2/envvars

で、

export APACHE_RUN_USER=pi
export APACHE_RUN_GROUP=pi

などしてしまう。

  1. pythoncgiで動かす設定
<Directory /home/pi/public_html>
        Options Indexes FollowSymLinks
        AllowOverride None
        Require all granted

        AddHandler mod_python .py
        PythonHandler mod_python.publisher
        PythonDebug On

        Options +ExecCGI
        AddHandler cgi-script .py
</Directory>
ハマり所

cgi-bin の許可

permission 755 にし忘れる

これで、GPIOに触れる様になった。
ただし、GPIOの初期設定には、やはりroot権限が必要なのが注意だ。

コーディングとアパッチの圧縮OFF

手順

  1. ROOT権限で(ターミナルから) GPIO の初期設定を行っておきます(export と direction設定)

  2. apache + python を pi 権限で動かします

$ cat ../set_gpio_pins.sh 
#!/bin/bash
/usr/local/bin/gpio export 4 out
/usr/local/bin/gpio -g mode 4 out

チカチカ終了を待たずに、htmlを吐き出せるように、スレッドを使用しています。

ここで注意が必要なのは、Pythonレベルでバッファリングを指定無くても、Apacheは、初期設定のまま使っていると、Gzip圧縮してくれるので、結果的にバッファリングされます。

なので、apachegzip 圧縮機能を設定します。

# cat /etc/apache2/mods-enabled/deflate.conf 
<IfModule mod_deflate.c>
        <IfModule mod_filter.c>
                # these are known to be safe with MSIE 6

# Changed
                #AddOutputFilterByType DEFLATE text/html text/plain text/xml

                # everything else may cause problems with MSIE 6
                AddOutputFilterByType DEFLATE text/css
                AddOutputFilterByType DEFLATE application/x-javascript application/javascript application/ecmascript
                AddOutputFilterByType DEFLATE application/rss+xml
                AddOutputFilterByType DEFLATE application/xml
        </IfModule>
</IfModule>

text/html に対して、圧縮をOFFにしました。

それを前提のコード。

#!/usr/bin/python -u
# -*- coding: utf-8 -*-
import sys
import wiringpi as wp
import time
import threading

from datetime import datetime

import cgi
import cgitb
cgitb.enable()

PIN     = 4

class MyThread(threading.Thread):
        def __init__(self):
                threading.Thread.__init__(self)

        def run(self):
                time.sleep(1)
                io = wp.GPIO(wp.GPIO.WPI_MODE_GPIO_SYS) # WPI_MODE_PINS WPI_MODE_GPIO WPI_MODE_GPIO_SYS WPI_MODE_PHYS WPI_MODE_PIFACE

                counter = 0
                while counter < 10:
                        io.digitalWrite(PIN,io.HIGH)
                        time.sleep(0.2)
                        io.digitalWrite(PIN,io.LOW)
                        time.sleep(0.2)
                        counter += 1

th = MyThread()
th.setDaemon(True)
th.start()

form = cgi.FieldStorage()
action          = form.getvalue('action','')

print("Content-Type: text/html;charset=utf-8")
print("\r\n\r\n")

print("""
<html>
<body>
<a href="?action=led">LED</a>
{action}
</body>
</html>
"""[1:-1].format(**vars()))

print( datetime.now().strftime("%Y/%m/%d %H:%M:%S") )
th.join()

ちゃんと、先にHTMLが出力されてから、チカチカします。